Ubicaciones y Zonas (Places)

Base path distinto de /inventory

Las Ubicaciones viven en https://api.fenicia.io/locations/* — un base path y un servicio distintos de /inventory/*. Tienen su propio namespace de permisos (locations:*), su propia convención de paginación y su propia forma de respuesta. No asumas que las reglas documentadas para /inventory/* aplican aquí.

Una Ubicación (Location) es un almacén, tienda física, centro de fulfillment de un marketplace (FBA, Mercado Full) u otro punto donde se guarda o gestiona stock. Un Place es una zona dentro de una ubicación — un anaquel, una zona de recepción, una zona de dañados — que a su vez puede desglosarse en placements (pasillo, isla, cabecera, bin, rack, etc.), la posición física exacta dentro de esa zona.

Modelo de datos

Location

id, locationCode? (único por tenant, sparse), tenantId, name,
type (enum, ver abajo, default 'principal'), active (bool, default true),
isDefault?, isSystem?,
locationAddress?: Address, phoneNumber?, email?, contactName?,
location?: { type: 'Point', coordinates: number[] },
capabilities?: Capability[] (enum, ver abajo),
channelBindings?: [{ channel, mappingId, overrides: [{ path, value }] }],
places?: Place[],
inventoryHandler?: {
  type ('manual'|'integration'|'channel', default 'manual'),
  sourceId?, sourceName?, sourceLocationId?, sourceLocationName?,
  allowManualOverride? (bool, default false)
}

Valores de type: principal, warehouse, store, factory, service, fba, mercado-full, claro-express, shopify-fullfillment, fenicia-managed, virtual.

shopify-fullfillment se escribe así

shopify-fullfillment (con doble "ll") es el valor literal del enum en producción — no es un error de esta documentación. No lo "corrijas" a shopify-fulfillment en tu integración; el valor exacto es el que ves aquí.

Valores de capabilities: pos, pick-up, fulfillment, inventory, one-way-sync, two-way-sync.

Place

id, code, type (enum, ver abajo), name, description?, active (default true),
maxCapacity?, pickPriority?, availableForSale?,
placements?: [{ code, type (enum, ver abajo), label? }]

Valores de type (zona): on-shelf, backstock, receiving, returns, damaged, mezzanine, showcase, cold-storage, custom.

Valores de type (placement, posición dentro de la zona): aisle, island, endcap, shelf, bin, rack, bay, pallet, display, counter, custom.

Paginación y forma de respuesta — distintas de /inventory/*

Advertencia

GET /locations pagina 0-indexado con limit por defecto 10 (no 20). Su respuesta es { locations, totalCount, page, limit } — nota totalCount, no total como en el resto del dominio de inventario.

Listar ubicaciones

Lista las ubicaciones del tenant con filtros. Requiere locations:read.

pagenumber

Página, base 0. Predeterminado: 0.

limitnumber

Resultados por página. Predeterminado: 10.

searchstring

Búsqueda por nombre/código.

typestring

Filtrar por type.

activeboolean

Filtrar por estado activo.

capabilitiesstring

Lista de capabilities separadas por coma.

200
{
  "locations": [
    { "id": "loc_cedis_cdmx", "name": "CEDIS Ciudad de México", "type": "warehouse", "active": true, "isDefault": true }
  ],
  "totalCount": 6,
  "page": 0,
  "limit": 10
}
500
{ "code": "internal-server-error", "message": "..." }

Conteo de ubicaciones

Cuenta las ubicaciones del tenant. Requiere locations:read.

200
{ "count": 6 }
400
{ "code": "bad-request/not-found", "message": "..." }

Información

Este endpoint devuelve 400 (no 404) con code: "bad-request/not-found" cuando el servicio no puede resolver el conteo. Es una combinación código/status inusual — documentada tal cual se comporta hoy, no "corregida".

Obtener una ubicación

Obtiene una ubicación por ID o por locationCode (lookup unificado). Requiere locations:read.

idstringrequired

ID de MongoDB o locationCode — ambos se resuelven en el mismo path param.

200
{ "location": { "id": "loc_cedis_cdmx", "name": "CEDIS Ciudad de México", "type": "warehouse" } }
404
{ "code": "not-found", "message": "Location not found" }

Único endpoint del dominio que usa code:'not-found' en un 404

Dentro de lambda-locations, solo GET /locations/{id} usa el código literal not-found en un 404. Todos los demás 404 de este dominio (ver tabla más abajo) usan bad-request/not-found — un código con prefijo bad-request/ sobre un status 404. Documentado literal, no es un typo de esta guía.

Crear una ubicación

Crea una ubicación nueva. Requiere locations:create.

namestringrequired

Nombre de la ubicación.

typestringrequired

Uno de los valores del enum type (ver Modelo de datos).

locationCodestring

Código único por tenant.

activeboolean

Predeterminado: true.

isDefaultboolean

Marca la ubicación como predeterminada del tenant.

isSystemboolean

Marca la ubicación como gestionada por el sistema (no eliminable).

capabilitiesstring[]

Subconjunto del enum capabilities.

inventoryHandlerobject

Configuración de manejo de inventario (manual/integration/channel).

{
  "name": "Tienda Polanco",
  "type": "store",
  "locationCode": "TDA-POL",
  "active": true,
  "capabilities": ["pos", "pick-up"]
}
201
{ "location": { "id": "loc_new123", "name": "Tienda Polanco", "type": "store", "active": true } }

Información

capabilities no se valida contra el catálogo al crear ni al actualizar — solo type se verifica contra su enum. Un valor de capabilities que no exista en el catálogo se acepta en silencio. (Observación de código: no se confirmó con una prueba negativa independiente; es la ausencia de una verificación de catálogo para capabilities en el controlador, análoga a la que sí existe para type.)

Actualizar una ubicación

Actualiza parcialmente una ubicación. Requiere locations:update.

idstringrequired

ID de la ubicación.

typestring

Si se envía, se revalida contra el enum type.

{ "name": "Tienda Polanco Centro" }
200
{ "location": { "id": "loc_new123", "name": "Tienda Polanco Centro", "type": "store" } }

Acepta cualquier subconjunto de los campos de Location en el body.

Marcar como ubicación predeterminada

Marca (o desmarca) una ubicación como predeterminada del tenant. Requiere locations:update.

idstringrequired

ID de la ubicación.

isDefaultboolean

Body opcional.

{ "isDefault": true }
200
{
  "message": "Default location updated",
  "defaultLocation": { "id": "loc_cedis_cdmx", "isDefault": true }
}
400
{ "code": "bad-request/system-location", "message": "Cannot remove default flag from a system location" }

Escritura en dos pasos, sin transacción

Internamente, marcar una ubicación como predeterminada primero limpia isDefault en todas las demás ubicaciones del tenant (updateMany) y luego marca la ubicación objetivo — son dos escrituras separadas, sin una sesión/transacción de Mongo que las envuelva. En teoría, una falla entre ambos pasos podría dejar al tenant sin ninguna ubicación predeterminada. Esta es una observación de la forma del código, no un incidente confirmado en producción.

Eliminar una ubicación

Elimina una ubicación. Requiere locations:delete.

idstringrequired

ID de la ubicación.

200
{ "locationDelete": { "acknowledged": true, "deletedCount": 1 } }
400
{ "code": "bad-request/system-location", "message": "Cannot delete a system location" }

Sin chequeo de integridad referencial

Eliminar una ubicación no verifica si todavía existen registros de Inventory que la referencian como locationId. Es posible dejar registros de inventario huérfanos apuntando a una ubicación eliminada.

Zonas (places) de una ubicación

Lista las zonas (places) de una ubicación. Requiere locations:read.

idstringrequired

ID de la ubicación.

200
{
  "places": [
    { "id": "plc_01H...", "code": "A-01", "type": "on-shelf", "name": "Anaquel A-01", "active": true }
  ],
  "total": 1
}

Crear una zona

Crea una zona dentro de una ubicación. Requiere locations:update.

idstringrequired

ID de la ubicación.

codestringrequired

Código de la zona, único dentro de la ubicación.

namestringrequired

Nombre de la zona.

typestringrequired

on-shelf, backstock, receiving, returns, damaged, mezzanine, showcase, cold-storage o custom.

descriptionstring

Descripción libre.

activeboolean

Predeterminado: true.

maxCapacitynumber

Capacidad máxima de la zona.

pickPrioritynumber

Si se omite, se calcula por defecto según type.

availableForSaleboolean

Si se omite, se calcula por defecto según type.

placementsarray

Posiciones dentro de la zona (aisle, island, endcap, shelf, bin, rack, bay, pallet, display, counter, custom).

{
  "code": "B-02",
  "name": "Backstock B-02",
  "type": "backstock"
}
201
{
  "place": { "id": "plc_new456", "code": "B-02", "type": "backstock", "name": "Backstock B-02", "pickPriority": 5, "availableForSale": false },
  "location": { "id": "loc_cedis_cdmx", "places": [ "..." ] }
}

Información

El id de la zona lo genera el servidor (UUID). La unicidad de code dentro de una ubicación se verifica en memoria al momento de crear, no con un índice único de base de datos.

Actualizar una zona

Actualiza parcialmente una zona. Requiere locations:update.

idstringrequired

ID de la ubicación.

placeIdstringrequired

ID de la zona.

{ "name": "Backstock B-02 Norte" }
200
{ "place": { "id": "plc_new456", "code": "B-02", "name": "Backstock B-02 Norte" }, "location": { "id": "loc_cedis_cdmx" } }

Acepta cualquier subconjunto de los campos de Place y hace merge sobre la zona existente.

Eliminar una zona

Elimina una zona de una ubicación. Requiere locations:update.

idstringrequired

ID de la ubicación.

placeIdstringrequired

ID de la zona.

200
{ "message": "Place deleted", "deletedPlace": { "id": "plc_new456", "code": "B-02" }, "location": { "id": "loc_cedis_cdmx" } }

Errores

CódigoHTTPCausa
not-found404GET /locations/{id} — no existe la ubicación (único endpoint del dominio con este código literal en 404).
bad-request/not-found404El resto de los endpoints con lookup por ID (places, set-default, delete) cuando el recurso no existe.
bad-request/not-found400GET /locations/count cuando el servicio no resuelve el conteo.
bad-request/system-location400Intentaste eliminar una ubicación de sistema, o quitarle el flag isDefault.
bad-request/missing-id400PUT /locations/{id} invocado sin el parámetro de ruta id.
bad-request/can't-create400El servicio no pudo crear la ubicación.
bad-request/can't-update400El servicio no pudo actualizar la ubicación.
bad-request/can't-delete400El servicio no pudo eliminar la ubicación.
internal-server-error500Error genérico del lambda de ubicaciones.

Consulta el Catálogo de Errores para el detalle completo, incluyendo los errores comunes de autenticación, permisos y tenant.

Ejemplos

curl -G https://api.fenicia.io/locations \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  --data-urlencode "type=warehouse" \
  --data-urlencode "active=true"
 
curl -X POST https://api.fenicia.io/locations/loc_cedis_cdmx/places \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{"code":"B-02","name":"Backstock B-02","type":"backstock"}'

Siguientes pasos