# API administrativa: farmacias

Contrato técnico para la SPA React del módulo administrativo de farmacias.

## Objetivo

Permitir listar, filtrar, consultar, crear, editar y activar o desactivar farmacias usando siempre UUID público en React.

## Permisos

- `pharmacies.view_any`: mostrar el menú **Farmacias**, consultar listado y opciones.
- `pharmacies.view`: consultar detalle y dependientes asociados.
- `pharmacies.create`: crear farmacias.
- `pharmacies.update`: editar datos generales.
- `pharmacies.change_status`: activar o desactivar.

Laravel siempre valida el permiso aunque React oculte acciones.

## Endpoints

| Método | Ruta | Permiso | Uso |
| --- | --- | --- | --- |
| `GET` | `/api/v1/admin/pharmacies` | `pharmacies.view_any` | listado paginado |
| `POST` | `/api/v1/admin/pharmacies` | `pharmacies.create` | creación |
| `GET` | `/api/v1/admin/pharmacies/options` | `pharmacies.view_any` | selectores/autocompletado |
| `GET` | `/api/v1/admin/pharmacies/{id}` | `pharmacies.view` | detalle |
| `GET` | `/api/v1/admin/pharmacies/{id}/dependents` | `pharmacies.view` | dependientes resumidos |
| `PATCH` | `/api/v1/admin/pharmacies/{id}` | `pharmacies.update` | edición de datos |
| `PATCH` | `/api/v1/admin/pharmacies/{id}/status` | `pharmacies.change_status` | activar o desactivar |
| `GET` | `/api/v1/public/pharmacies/by-code/{code}` | público | búsqueda por código para registro |

`{id}` siempre es el UUID público, nunca el ID numérico interno.

## Filtros y paginación

`GET /api/v1/admin/pharmacies` admite:

- `page`
- `per_page` entre `1` y `100`, por defecto `15`
- `search`
- `status`: `active` o `inactive`
- `city`
- `province`
- `created_from`
- `created_to`
- `sort`: `created_at`, `code`, `name`, `city`, `province`, `status`
- `direction`: `asc`, `desc`

Orden predeterminado: `name asc`.

`created_to` incluye el día completo.

La respuesta sigue el contrato global:

```json
{
  "success": true,
  "message": "Farmacias obtenidas correctamente.",
  "data": [],
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 1,
    "last_page": 1
  }
}
```

## Recurso público y administrativo

Formato base de farmacia:

```json
{
  "id": "uuid-publico",
  "code": "FAR-001",
  "name": "Farmacia Central",
  "city": "Santiago",
  "province": "Santiago"
}
```

Listado administrativo añade:

```json
{
  "status": "active",
  "dependents_count": 4,
  "created_at": "2026-07-15T12:00:00.000000Z",
  "updated_at": "2026-07-15T12:00:00.000000Z"
}
```

## Payloads

Creación:

```json
{
  "code": "FAR-001",
  "name": "Farmacia Central",
  "city": "Santiago",
  "province": "Santiago",
  "status": "active"
}
```

Actualización:

```json
{
  "code": "FAR-001",
  "name": "Farmacia Central Editada",
  "city": "Santiago",
  "province": "Santiago"
}
```

Cambio de estado:

```json
{
  "status": "inactive"
}
```

No enviar `id`, `uuid`, timestamps, relaciones ni IDs internos.

## Options

`GET /api/v1/admin/pharmacies/options`

- Devuelve solo farmacias activas.
- Acepta `search`.
- Limita resultados a 20 por defecto.
- Responde con `id`, `code`, `name`, `city`, `province`.

## Dependientes

`GET /api/v1/admin/pharmacies/{id}/dependents`

- Devuelve solo usuarios con rol `dependent`.
- No devuelve password, roles completos ni IDs internos.
- Respuesta paginada en `meta`.

Ejemplo de item:

```json
{
  "id": "uuid-usuario",
  "full_name": "Ana Pérez",
  "username": "anaperez",
  "email": "ana@example.com",
  "whatsapp": "8095551234",
  "status": "active"
}
```

## Normalización

- `code`: trim, sin espacios internos, mayúsculas.
- `name`, `city`, `province`: trim y compresión de espacios duplicados, conservando acentos.

El código es único en base de datos y se compara ya normalizado.

## Errores

- `PHARMACY_CODE_ALREADY_EXISTS`
- `PHARMACY_NOT_FOUND`
- `VALIDATION_ERROR`
- `UNAUTHENTICATED`
- `FORBIDDEN`
- `TOO_MANY_REQUESTS`

Cuando `code` entra en conflicto, el backend responde `422` con error en `errors.code`.

## Axios

```ts
await ensureCsrfCookie()
const created = await api.post('/api/v1/admin/pharmacies', {
  code: 'FAR-001',
  name: 'Farmacia Central',
  city: 'Santiago',
  province: 'Santiago',
  status: 'active',
})

const list = await api.get('/api/v1/admin/pharmacies', {
  params: { search: 'central', status: 'active', sort: 'name', direction: 'asc' },
})
```

## Registro público

`GET /api/v1/public/pharmacies/by-code/{code}`:

- devuelve `id`, `code`, `name`, `city`, `province`
- solo encuentra farmacias activas
- usa código normalizado

En registro React:

- guardar `data.id` como `pharmacy_id`
- mostrar `code`, `name`, `city` y `province` en solo lectura
- no enviar nombre, ciudad, provincia ni estado de la farmacia como fuente confiable
- si la farmacia está inactiva, el backend devuelve `404 PHARMACY_NOT_FOUND`

## Sidebar

Mostrar **Farmacias** solamente cuando `user.permissions` incluya `pharmacies.view_any`.

## Pruebas manuales sugeridas

- listar y filtrar por estado, ciudad, provincia y fechas
- crear farmacia con código en minúsculas y confirmar normalización
- intentar crear código duplicado y validar `errors.code`
- editar datos sin poder cambiar estado en el formulario general
- desactivar farmacia y verificar que desaparece de `options`
- confirmar que una farmacia inactiva no aparece en la búsqueda pública ni permite nuevo registro
- abrir detalle y paginar dependientes asociados
