⚠ Vigencia por confirmar. Los widgets que describe este documento (
location-geozones.tsx,location-shipping-config.tsx) no existen en las ramas publicadas (main/staging/develop, todas en14749d6al 2026-08-19).
Widgets de Administración de Envíos¶
Este documento describe los widgets de administración implementados para gestionar la configuración de envíos en el panel de administración de Medusa v2.
Ubicación¶
Todos los widgets se inyectan en la zona location.details.after, es decir, aparecen en la vista de detalle de un Stock Location.
Widget 1: Gestión de GeoZones¶
Archivo: src/admin/widgets/location-geozones.tsx
Propósito¶
Permite gestionar las ciudades (geo_zones) de las zonas de servicio asociadas a un location, incluyendo la configuración de proveedores de envío disponibles para cada ciudad.
Funcionalidades¶
- Selector de zona de servicio (filtra solo fulfillment sets de tipo "shipping")
- Tabla de ciudades configuradas con búsqueda
- Agregar nueva ciudad con:
- Nombre de ciudad
- Departamento (lista de departamentos de Paraguay)
- Configuración de proveedores (CLX, MC Group, AEX)
- Horas de entrega por proveedor
- Editar ciudad existente
- Eliminar ciudad
Estructura de Datos¶
// GeoZone.metadata
{
"city_code": "PY001",
"clx_available": true,
"clx_hours": 24,
"mc_available": true,
"mc_hours": 48,
"aex_available": false,
"aex_hours": null
}
Archivos Relacionados¶
src/admin/components/geozones/
├── index.ts
├── types.ts
├── constants.ts # Departamentos de Paraguay
├── utils.ts
├── hooks/
│ ├── useServiceZonesLoader.ts
│ └── useGeoZonesManager.ts
└── components/
├── ServiceZoneSelector.tsx
├── GeoZonesTable.tsx
├── AddCityModal.tsx
├── EditCityModal.tsx
└── ProviderMetadataForm.tsx
Widget 2: Configuración de Envíos¶
Archivo: src/admin/widgets/location-shipping-config.tsx
Propósito¶
Permite gestionar los tipos de caja para cálculo de envío y los precios por zona de servicio, incluyendo precios especiales por categoría de producto.
Pestañas¶
Pestaña 1: Tipos de Caja¶
Gestiona los tamaños de caja disponibles para el cálculo de envío. Los tipos de caja se almacenan en FulfillmentSet.metadata.box_types y son compartidos por todas las zonas de servicio.
Funcionalidades:
- Listar tipos de caja con dimensiones y peso máximo
- Agregar nuevo tipo de caja
- Editar tipo existente
- Eliminar tipo de caja
- Ordenamiento por sort_order (prioridad de selección)
- Caja default (fallback): Permite crear una caja sin dimensiones que se usa cuando el producto excede el tamaño de todas las demás cajas
Caja Default:
Una caja puede marcarse como "default" (fallback) para manejar productos que exceden las dimensiones de las cajas regulares. Características: - Solo puede existir una caja default por FulfillmentSet - No requiere configurar dimensiones (largo, ancho, alto) ni peso máximo - Se usa automáticamente cuando ninguna caja regular es suficiente para el producto - Debe tener un precio configurado en la pestaña de Precios para funcionar - En la tabla se muestra con un badge "Default" y "Sin límite" en dimensiones
Estructura de Datos:
// FulfillmentSet.metadata.box_types
[
{
"code": "sobre",
"name": "Sobre",
"max_length": 30,
"max_width": 20,
"max_height": 5,
"max_weight": 1,
"sort_order": 1
},
{
"code": "caja_1",
"name": "Caja 1",
"max_length": 40,
"max_width": 30,
"max_height": 20,
"max_weight": 5,
"sort_order": 2
},
{
"code": "caja_especial",
"name": "Caja Especial (productos grandes)",
"max_length": 0,
"max_width": 0,
"max_height": 0,
"max_weight": 0,
"sort_order": 99,
"is_default": true // Caja fallback
}
]
Pestaña 2: Precios por Zona¶
Gestiona los precios de envío por tipo de caja para cada opción de envío (ShippingOption), además de precios especiales por categoría de producto.
Funcionalidades: - Selector de zona de servicio - Selector de opción de envío (MC Group, CLX, etc.) - Tabla editable de precios por tipo de caja - Gestión de precios especiales por categoría: - Agregar categoría con precio fijo - Editar precio de categoría - Eliminar categoría - Indicador de cambios sin guardar - Advertencia de tipos de caja sin precio asignado
Estructura de Datos:
// ShippingOption.metadata
{
"prices": {
"sobre": 25000,
"caja_1": 30000,
"caja_2": 35000,
"caja_3": 40000
},
"special_categories": [
{
"category_id": "pcat_01ABC123",
"category_name": "Sillas Gamer",
"price": 50000
},
{
"category_id": "pcat_02DEF456",
"category_name": "Colchones",
"price": 60000
}
]
}
Archivos Relacionados¶
src/admin/components/shipping-config/
├── index.ts
├── types.ts
├── constants.ts
├── utils.ts
├── hooks/
│ ├── useBoxTypesManager.ts
│ ├── useShippingOptionsLoader.ts
│ └── useShippingPrices.ts
└── components/
├── BoxTypesTab.tsx
├── BoxTypesTable.tsx
├── BoxTypeModal.tsx
├── PricesTab.tsx
├── PricesTable.tsx
├── SpecialCategoriesTable.tsx
└── SpecialCategoryModal.tsx
Endpoints API Personalizados¶
Dado que el SDK de Medusa v2 no expone métodos directos para actualizar metadata de FulfillmentSet y ShippingOption, se crearon endpoints personalizados:
GET/POST /admin/fulfillment-sets/:id¶
Archivo: src/api/admin/fulfillment-sets/[id]/route.ts
- GET: Obtiene un fulfillment set con sus relaciones
- POST: Actualiza el fulfillment set (incluyendo metadata)
POST /admin/shipping-options/:id/metadata¶
Archivo: src/api/admin/shipping-options/[id]/metadata/route.ts
- POST: Actualiza solo la metadata de una shipping option (merge con metadata existente)
Jerarquía de Datos¶
StockLocation
└── FulfillmentSet (type: "shipping")
├── metadata.box_types[] ← Tipos de caja (global)
│
└── ServiceZone
├── GeoZone[] ← Ciudades con proveedores
│ └── metadata ← CLX/MC/AEX config
│
└── ShippingOption
└── metadata
├── prices{} ← Precios por caja
└── special_categories[] ← Precios por categoría
Lógica de Cálculo de Precio (Referencia)¶
El fulfillment provider implementa la siguiente lógica:
-
Verificar categoría especial: Si el producto pertenece a una categoría con precio especial, usar ese precio directamente.
-
Si no hay categoría especial:
- Obtener dimensiones del producto más grande del carrito (desde base de datos)
- Nota: Las dimensiones del producto están en mm, las cajas en cm. Se divide por 10 para convertir.
- Buscar el tipo de caja más pequeño que contenga el producto entre las cajas regulares (no default)
- Si ninguna caja regular es suficiente, usar la caja default como fallback
- Si no hay caja default configurada y ninguna caja regular es suficiente, retornar error
NO_FITTING_BOX -
Obtener el precio de la caja seleccionada desde
ShippingOption.metadata.prices[caja.code] -
Retornar precio final
Ejemplo de flujo:
Producto: Silla Gamer (850mm x 650mm x 350mm = 85cm x 65cm x 35cm)
1. Evaluar caja_1 (40x30x20 cm): NO CABE
2. Evaluar caja_2 (50x40x30 cm): NO CABE
3. Evaluar caja_3 (60x50x40 cm): NO CABE
4. Ninguna caja regular es suficiente
5. Usar caja_especial (is_default: true): PRECIO = 80.000 Gs
Formato de Precios¶
- Los precios se almacenan en guaraníes (PYG) como enteros
- El widget formatea con separador de miles (ej:
25.000) - Los inputs aceptan formato con puntos/comas y los parsean automáticamente
Validaciones¶
Tipos de Caja¶
- Código único (alfanumérico + guiones bajos)
- Nombre requerido
- Cajas regulares: Dimensiones > 0 y Peso máximo > 0
- Caja default: No requiere dimensiones ni peso (se ignoran)
- Solo puede existir una caja default por FulfillmentSet
Precios¶
- Precio >= 0
- Advertencia visual si faltan precios para algún tipo de caja
Categorías Especiales¶
- Categoría única por shipping option
- Precio > 0
Uso¶
- Ir a Inventory → Stock Locations en el admin
- Seleccionar un location con fulfillment set de tipo "shipping"
- Los widgets aparecen automáticamente debajo de los detalles del location:
- Gestión de GeoZones: Para configurar ciudades y proveedores
- Configuración de Envíos: Para tipos de caja y precios