Filiales (Locations)
La API de Filiales permite gestionar las ubicaciones de stock (Stock Locations) del club. Cada filial representa un punto fisico donde se almacena y/o dispensa producto.
Las Filiales son el componente central del sistema de inventario multi-ubicacion. Cada producto puede tener stock en multiples filiales simultaneamente.
El modelo de StockLocation
- Name
id- Type
- string
- Description
ID unico de la ubicacion.
- Name
name- Type
- string
- Description
Nombre de la filial.
- Name
address- Type
- StockLocationAddress
- Description
Direccion fisica de la ubicacion.
- Name
addressId- Type
- string
- Description
ID de la direccion asociada.
- Name
shippingEnabled- Type
- boolean
- Description
Si permite envios desde esta ubicacion.
- Name
pickupEnabled- Type
- boolean
- Description
Si permite retiro en esta ubicacion.
- Name
salesChannels- Type
- array
- Description
Canales de venta asociados.
- Name
metadata- Type
- object
- Description
Datos adicionales (horarios, tipo, etc).
- Name
createdAt- Type
- string
- Description
Fecha de creacion.
- Name
updatedAt- Type
- string
- Description
Fecha de ultima actualizacion.
Interface StockLocation
interface StockLocation {
id: string
name: string
address?: StockLocationAddress
addressId?: string
metadata?: Record<string, unknown>
createdAt?: string
updatedAt?: string
shippingEnabled?: boolean
pickupEnabled?: boolean
salesChannels?: SalesChannel[]
}
interface StockLocationAddress {
id?: string
addressLine1?: string
addressLine2?: string
city?: string
province?: string
countryCode?: string
postalCode?: string
phone?: string
}
interface SalesChannel {
id: string
name: string
description?: string
isDisabled?: boolean
}
Ejemplo
{
"id": "sloc_high_up",
"name": "High Up - Dispensario Principal",
"address": {
"addressLine1": "Av. Corrientes 1234",
"city": "Buenos Aires",
"province": "CABA",
"countryCode": "AR",
"postalCode": "C1043"
},
"shippingEnabled": false,
"pickupEnabled": true,
"salesChannels": [
{ "id": "sc_default", "name": "Default Sales Channel" }
]
}
Listar filiales
Lista todas las filiales del club.
Parametros de Consulta
- Name
include_sales_channels- Type
- boolean
- Description
Incluir canales de venta en la respuesta.
Request
curl https://app.cannahub.tech/api/locations \
-H "Authorization: Bearer {token}"
Response
{
"locations": [
{
"id": "sloc_high_up",
"name": "High Up - Dispensario",
"address": {
"city": "Buenos Aires",
"countryCode": "AR"
},
"pickupEnabled": true,
"shippingEnabled": false
},
{
"id": "sloc_deposito",
"name": "Deposito Central",
"address": {
"city": "Buenos Aires",
"countryCode": "AR"
},
"pickupEnabled": false,
"shippingEnabled": false
}
]
}
Obtener filial
Obtiene el detalle de una filial especifica.
Request
curl https://app.cannahub.tech/api/locations/sloc_high_up \
-H "Authorization: Bearer {token}"
Response
{
"location": {
"id": "sloc_high_up",
"name": "High Up - Dispensario Principal",
"address": {
"addressLine1": "Av. Corrientes 1234",
"city": "Buenos Aires",
"province": "CABA",
"countryCode": "AR"
},
"pickupEnabled": true,
"shippingEnabled": false,
"salesChannels": [
{ "id": "sc_default", "name": "Default Sales Channel" }
]
}
}
Crear filial
Crea una nueva filial.
Body
- Name
name- Type
- string
- Description
Nombre de la filial.
- Name
address- Type
- object
- Description
Direccion de la filial.
- Name
addressLine1- Type
- string
- Description
Direccion principal.
- Name
addressLine2- Type
- string
- Description
Direccion secundaria.
- Name
city- Type
- string
- Description
Ciudad.
- Name
province- Type
- string
- Description
Provincia/Estado.
- Name
countryCode- Type
- string
- Description
Codigo de pais (ej: "AR").
- Name
postalCode- Type
- string
- Description
Codigo postal.
- Name
phone- Type
- string
- Description
Telefono de contacto.
- Name
shippingEnabled- Type
- boolean
- Description
Habilitar envios (default: false).
- Name
pickupEnabled- Type
- boolean
- Description
Habilitar retiro (default: true).
- Name
salesChannelIds- Type
- string[]
- Description
IDs de canales de venta a asociar.
Request
curl -X POST https://app.cannahub.tech/api/locations \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"name": "Nueva Filial Palermo",
"address": {
"addressLine1": "Av. Santa Fe 3200",
"city": "Buenos Aires",
"province": "CABA",
"countryCode": "AR",
"postalCode": "C1425"
},
"pickupEnabled": true,
"shippingEnabled": false
}'
Response
{
"location": {
"id": "sloc_palermo_01",
"name": "Nueva Filial Palermo",
"address": {
"addressLine1": "Av. Santa Fe 3200",
"city": "Buenos Aires",
"province": "CABA",
"countryCode": "AR",
"postalCode": "C1425"
},
"pickupEnabled": true,
"shippingEnabled": false
}
}
Actualizar filial
Actualiza una filial existente.
Body
- Name
name- Type
- string
- Description
Nuevo nombre.
- Name
address- Type
- object
- Description
Nueva direccion (parcial o completa).
- Name
shippingEnabled- Type
- boolean
- Description
Actualizar estado de envios.
- Name
pickupEnabled- Type
- boolean
- Description
Actualizar estado de retiro.
Request
curl -X PATCH https://app.cannahub.tech/api/locations/sloc_high_up \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"name": "High Up - Dispensario Central",
"pickupEnabled": true
}'
Response
{
"location": {
"id": "sloc_high_up",
"name": "High Up - Dispensario Central",
"pickupEnabled": true
}
}
Eliminar filial
Elimina una filial. La filial no debe tener inventario activo.
Solo se pueden eliminar filiales que no tengan stock asociado.
Request
curl -X DELETE https://app.cannahub.tech/api/locations/sloc_palermo_01 \
-H "Authorization: Bearer {token}"
Response
{
"success": true
}
Arquitectura del Dominio
La feature Locations sigue el patron DOMAINS.md:
src/features/Club/Locations/
├── components/ # Componentes UI
│ ├── Wrapper.tsx # Contenedor principal
│ ├── Grid/ # Vista de grilla
│ ├── Table/ # Vista de tabla
│ ├── Modals/ # Modales CRUD
│ └── Skeleton.tsx # Estados de carga
├── hooks/
│ ├── keys.ts # Query key factory
│ ├── queries.ts # useLocationsQuery, useLocationQuery
│ ├── mutations.ts # CRUD mutations
│ └── index.ts # Barrel export
├── services/
│ └── location.client.ts # Cliente HTTP
└── index.ts # Barrel export principal
Query Keys
export const locationKeys = {
all: ['locations'] as const,
lists: () => [...locationKeys.all, 'list'] as const,
list: (includeSalesChannels?: boolean) =>
[...locationKeys.lists(), { includeSalesChannels }] as const,
details: () => [...locationKeys.all, 'detail'] as const,
detail: (id: string) => [...locationKeys.details(), id] as const,
}
Relacion con Inventario
Las filiales son fundamentales para el sistema de inventario multi-ubicacion:
StockLocation (Filial)
│
├── InventoryLevel (Stock por variante)
│ │
│ └── stockedQuantity, reservedQuantity
│
└── ShippingOption (Opciones de envio/retiro)
Consultar stock por filial
import { useLocationsQuery } from '@/features/Club/Locations'
import { useInventoryLevelsQuery } from '@/features/Club/Inventory'
// Obtener filiales
const { data: locationsData } = useLocationsQuery()
// Obtener niveles de stock para una variante
const { data: levelsData } = useInventoryLevelsQuery(inventoryItemId)
// Mapear stock por filial
const stockByLocation = locationsData?.locations.map(loc => {
const level = levelsData?.levels.find(l => l.locationId === loc.id)
return {
locationId: loc.id,
locationName: loc.name,
quantity: level?.stockedQuantity || 0,
available: level?.availableQuantity || 0
}
})
Mapeo Medusa
La API BFF transforma las respuestas de Medusa de snake_case a camelCase:
| UI (camelCase) | Medusa (snake_case) |
|---|---|
addressLine1 | address_1 |
addressLine2 | address_2 |
countryCode | country_code |
postalCode | postal_code |
shippingEnabled | derivado de fulfillment_sets |
pickupEnabled | derivado de fulfillment_sets |
salesChannels | sales_channels |
createdAt | created_at |
updatedAt | updated_at |
Las funciones de parser estan en /src/lib/medusa/locations.ts:
import { parseMedusaLocation, parseMedusaLocations } from '@/lib/medusa/locations'
// Transforma respuesta Medusa a camelCase
const locations = parseMedusaLocations(medusaResponse.stock_locations)