Procurement
El dominio de Compras mayoristas (Procurement) gestiona el ciclo completo de abastecimiento del club: desde el pedido de insumos de cultivo (RFQ), pasando por las cotizaciones de proveedores y su comparación, hasta la adjudicación y la orden de compra resultante. En esta página vamos a repasar los distintos endpoints BFF que expone el webapp para operar programáticamente sobre proveedores, pedidos, cotizaciones, órdenes de compra y métricas.
Estas rutas viven bajo /api/procurement/* y actúan como capa BFF sobre el agregador de procurement (Strapi). Salvo las de sólo lectura (analytics, compare), las operaciones de escritura requieren permisos específicos del dominio (canManageRequests, canManageQuotes, canManageSuppliers, canManagePurchaseOrders, canAward).
Todas las rutas requieren Authorization: Bearer {token}. En entornos no mockeados también se exige el header Tenant-id: {tenantId} para resolver el club sobre el que se opera. Base URL: https://api.cannahub.tech.
The procurement flow
The SupplyRequest model
El pedido de insumos (RFQ, Request for Quotation) es el punto de partida del flujo. Agrupa una o más líneas de insumos que el club necesita comprar y controla su ciclo de vida hasta la adjudicación.
Properties
- Name
id- Type
- string
- Description
Identificador único del pedido.
- Name
code- Type
- string
- Description
Código legible del pedido (ej.
RFQ-2025-0042).
- Name
status- Type
- enum
- Description
Estado del pedido. Puede ser uno de:
- DRAFT: Borrador, aún no publicado
- OPEN: Publicado, esperando cotizaciones
- QUOTING: Recibiendo cotizaciones de proveedores
- EVALUATING: Comparando y evaluando ofertas
- AWARDED: Adjudicado a una cotización ganadora
- CLOSED: Cerrado
- CANCELLED: Cancelado
- Name
title- Type
- string
- Description
Título descriptivo del pedido.
- Name
lines- Type
- SupplyLine[]
- Description
Líneas de insumos solicitados.
- Name
id- Type
- string
- Description
Identificador de la línea.
- Name
category- Type
- string
- Description
Categoría del insumo (ej.
Sustrato,Nutrientes).
- Name
description- Type
- string
- Description
Descripción del insumo.
- Name
quantity- Type
- number
- Description
Cantidad solicitada.
- Name
unit- Type
- string
- Description
Unidad de medida (ej.
kg,L,unidad).
- Name
specs- Type
- string
- Description
Especificaciones técnicas.
- Name
targetUnitPrice- Type
- number
- Description
Precio unitario objetivo (referencia).
- Name
neededBy- Type
- string
- Description
Fecha límite en la que se necesita el insumo (ISO).
- Name
priority- Type
- enum
- Description
Prioridad del pedido. Puede ser uno de:
- LOW
- NORMAL
- HIGH
- URGENT
- Name
requesterUserId- Type
- string
- Description
ID del usuario que creó el pedido.
- Name
requesterName- Type
- string
- Description
Nombre del solicitante.
- Name
notes- Type
- string
- Description
Notas del pedido.
- Name
awardedQuoteId- Type
- string
- Description
ID de la cotización adjudicada (una vez adjudicado).
- Name
purchaseOrderId- Type
- string
- Description
ID de la orden de compra generada.
- Name
createdAt- Type
- string
- Description
Timestamp de creación.
- Name
updatedAt- Type
- string
- Description
Timestamp de última actualización.
SupplyRequest example
{
"id": "req_01HZR8XYZ0001",
"code": "RFQ-2025-0042",
"status": "QUOTING",
"title": "Reposición sustrato y nutrientes floración",
"lines": [
{
"id": "line_01",
"category": "Sustrato",
"description": "Sustrato coco-perlita premium",
"quantity": 500,
"unit": "L",
"specs": "Mezcla 70/30, pH estabilizado",
"targetUnitPrice": 1800
},
{
"id": "line_02",
"category": "Nutrientes",
"description": "Nutriente base floración",
"quantity": 40,
"unit": "L"
}
],
"neededBy": "2025-10-15",
"priority": "HIGH",
"requesterUserId": "usr_01HQ8XYZABC123",
"requesterName": "Lucía Fernández",
"notes": "Coordinar entrega antes del cambio de ciclo",
"createdAt": "2025-09-01T12:00:00Z",
"updatedAt": "2025-09-05T09:30:00Z"
}
The SupplyQuote model
Una cotización representa la oferta de un proveedor para un pedido. Incluye el desglose por línea, descuentos por volumen y los totales calculados del lado del servidor.
Properties
- Name
id- Type
- string
- Description
Identificador único de la cotización.
- Name
supplyRequestId- Type
- string
- Description
ID del pedido (RFQ) al que pertenece.
- Name
supplierId- Type
- string
- Description
ID del proveedor que cotiza.
- Name
status- Type
- enum
- Description
Estado de la cotización. Puede ser uno de:
- RECEIVED: Recibida
- SHORTLISTED: Preseleccionada
- SELECTED: Seleccionada / adjudicada
- REJECTED: Rechazada
- EXPIRED: Vencida
- Name
paymentTerm- Type
- enum
- Description
Condición de pago ofrecida. Puede ser una de:
- CASH
- NET_30
- NET_60
- NET_90
- Name
validUntil- Type
- string
- Description
Fecha de validez de la oferta (ISO).
- Name
receivedAt- Type
- string
- Description
Fecha en la que se recibió la cotización.
- Name
channel- Type
- enum
- Description
Canal por el que llegó la cotización. Puede ser uno de:
- PHONE
- IN_PERSON
- PORTAL
- OTHER
- Name
lines- Type
- QuoteLine[]
- Description
Desglose por línea con precios y descuentos aplicados.
- Name
id- Type
- string
- Description
ID de la línea (coincide con la línea del pedido).
- Name
category- Type
- string
- Description
Categoría del insumo.
- Name
description- Type
- string
- Description
Descripción del insumo.
- Name
quantity- Type
- number
- Description
Cantidad cotizada.
- Name
unit- Type
- string
- Description
Unidad de medida.
- Name
unitPrice- Type
- number
- Description
Precio unitario base cotizado.
- Name
unitPriceEffective- Type
- number
- Description
Precio unitario efectivo tras aplicar descuentos por volumen.
- Name
lineSubtotal- Type
- number
- Description
Subtotal de la línea (sin descuento).
- Name
lineDiscount- Type
- number
- Description
Descuento aplicado a la línea.
- Name
lineTotal- Type
- number
- Description
Total de la línea (con descuento).
- Name
volumeDiscounts- Type
- VolumeDiscountsByLine[]
- Description
Tramos de descuento por volumen, por línea.
- Name
lineId- Type
- string
- Description
ID de la línea a la que aplica.
- Name
tiers- Type
- VolumeTier[]
- Description
Tramos. Cada tramo define
minQuantityyunitPriceXORdiscountPercent.
- Name
subtotal- Type
- number
- Description
Subtotal de la cotización (suma de subtotales de línea).
- Name
discountTotal- Type
- number
- Description
Descuento total aplicado.
- Name
total- Type
- number
- Description
Total final de la cotización.
- Name
currency- Type
- enum
- Description
Moneda. Puede ser una de:
ARS,USD,EUR.
- Name
notes- Type
- string
- Description
Notas de la cotización.
- Name
createdAt- Type
- string
- Description
Timestamp de creación.
- Name
updatedAt- Type
- string
- Description
Timestamp de última actualización.
SupplyQuote example
{
"id": "quo_01HZR8ABC0001",
"supplyRequestId": "req_01HZR8XYZ0001",
"supplierId": "sup_01HZR8SUP0001",
"status": "SHORTLISTED",
"paymentTerm": "NET_30",
"validUntil": "2025-09-30",
"receivedAt": "2025-09-04T15:20:00Z",
"channel": "WHATSAPP",
"lines": [
{
"id": "line_01",
"category": "Sustrato",
"description": "Sustrato coco-perlita premium",
"quantity": 500,
"unit": "L",
"unitPrice": 1750,
"unitPriceEffective": 1650,
"lineSubtotal": 875000,
"lineDiscount": 50000,
"lineTotal": 825000
}
],
"volumeDiscounts": [
{
"lineId": "line_01",
"tiers": [
{ "minQuantity": 300, "discountPercent": 5 }
]
}
],
"subtotal": 875000,
"discountTotal": 50000,
"total": 825000,
"currency": "ARS",
"notes": "Incluye flete a CABA",
"createdAt": "2025-09-04T15:20:00Z",
"updatedAt": "2025-09-05T10:00:00Z"
}
The PurchaseOrder model
La orden de compra se genera al adjudicar (seleccionar) una cotización. Es un registro de sólo-lectura en cuanto a su creación (no hay endpoint de creación directo) y luego se actualiza su recepción y estado de pago.
Properties
- Name
id- Type
- string
- Description
Identificador único de la orden de compra.
- Name
code- Type
- string
- Description
Código legible (ej.
PO-2025-0031).
- Name
supplyRequestId- Type
- string
- Description
ID del pedido de origen.
- Name
winningQuoteId- Type
- string
- Description
ID de la cotización ganadora.
- Name
supplierId- Type
- string
- Description
ID del proveedor adjudicado.
- Name
status- Type
- enum
- Description
Estado de la orden. Puede ser uno de:
- ORDERED: Ordenada, pendiente de recepción
- PARTIAL_RECEIVED: Recepción parcial
- RECEIVED: Recibida por completo
- CANCELLED: Cancelada
- Name
paymentTerm- Type
- enum
- Description
Condición de pago (
CASH,NET_30,NET_60,NET_90).
- Name
paymentStatus- Type
- enum
- Description
Estado del pago. Puede ser uno de:
- PENDING
- PARTIAL
- PAID
- OVERDUE
- Name
orderedAt- Type
- string
- Description
Fecha en que se emitió la orden.
- Name
expectedDelivery- Type
- string
- Description
Fecha estimada de entrega.
- Name
dueDate- Type
- string
- Description
Fecha de vencimiento del pago (según condición).
- Name
lines- Type
- QuoteLine[]
- Description
Líneas heredadas de la cotización ganadora, con su desglose de precios.
- Name
subtotal- Type
- number
- Description
Subtotal de la orden.
- Name
discountTotal- Type
- number
- Description
Descuento total.
- Name
total- Type
- number
- Description
Total de la orden.
- Name
currency- Type
- enum
- Description
Moneda (
ARS,USD,EUR).
- Name
orderedById- Type
- string
- Description
ID del usuario que emitió la orden.
- Name
orderedByName- Type
- string
- Description
Nombre del usuario que emitió la orden.
- Name
notes- Type
- string
- Description
Notas de la orden.
- Name
createdAt- Type
- string
- Description
Timestamp de creación.
- Name
updatedAt- Type
- string
- Description
Timestamp de última actualización.
PurchaseOrder example
{
"id": "po_01HZR8PO00001",
"code": "PO-2025-0031",
"supplyRequestId": "req_01HZR8XYZ0001",
"winningQuoteId": "quo_01HZR8ABC0001",
"supplierId": "sup_01HZR8SUP0001",
"status": "ORDERED",
"paymentTerm": "NET_30",
"paymentStatus": "PENDING",
"orderedAt": "2025-09-06T11:00:00Z",
"expectedDelivery": "2025-09-12",
"dueDate": "2025-10-06",
"lines": [
{
"id": "line_01",
"category": "Sustrato",
"description": "Sustrato coco-perlita premium",
"quantity": 500,
"unit": "L",
"unitPrice": 1750,
"unitPriceEffective": 1650,
"lineSubtotal": 875000,
"lineDiscount": 50000,
"lineTotal": 825000
}
],
"subtotal": 875000,
"discountTotal": 50000,
"total": 825000,
"currency": "ARS",
"orderedById": "usr_01HQ8XYZABC123",
"orderedByName": "Lucía Fernández",
"notes": "Retira depósito Avellaneda",
"createdAt": "2025-09-06T11:00:00Z",
"updatedAt": "2025-09-06T11:00:00Z"
}
The WholesaleSupplier model
El proveedor mayorista al que se le solicitan cotizaciones. Incluye datos de contacto, CUIT, categorías que abastece y condiciones de pago habituales.
Properties
- Name
id- Type
- string
- Description
Identificador único del proveedor.
- Name
name- Type
- string
- Description
Razón social o nombre del proveedor.
- Name
contactName- Type
- string
- Description
Nombre de la persona de contacto.
- Name
email- Type
- string
- Description
Email de contacto.
- Name
phone- Type
- string
- Description
Teléfono de contacto.
- Name
cuit- Type
- string
- Description
CUIT (identificador fiscal argentino).
- Name
categories- Type
- string[]
- Description
Categorías de insumos que abastece.
- Name
defaultPaymentTerms- Type
- enum[]
- Description
Condiciones de pago que el proveedor suele ofrecer (
CASH,NET_30,NET_60,NET_90).
- Name
address- Type
- string
- Description
Dirección del proveedor.
- Name
notes- Type
- string
- Description
Notas internas.
- Name
isActive- Type
- boolean
- Description
Si el proveedor está activo.
- Name
createdAt- Type
- string
- Description
Timestamp de creación.
- Name
updatedAt- Type
- string
- Description
Timestamp de última actualización.
WholesaleSupplier example
{
"id": "sup_01HZR8SUP0001",
"name": "Insumos del Sur S.A.",
"contactName": "Roberto Álvarez",
"email": "ventas@insumosdelsur.com.ar",
"phone": "+541148887766",
"cuit": "30-71234567-9",
"categories": ["Sustrato", "Nutrientes", "Macetas"],
"defaultPaymentTerms": ["CASH", "NET_30"],
"address": "Parque Industrial Avellaneda, Buenos Aires",
"notes": "Descuento por volumen a partir de 300 L",
"isActive": true,
"createdAt": "2025-01-10T09:00:00Z",
"updatedAt": "2025-08-20T14:00:00Z"
}
Listar pedidos (RFQ)
Retorna la lista de pedidos de insumos del club. Soporta filtro por estado y paginación.
Query params
- Name
status- Type
- SupplyRequestStatus
- Description
Filtra por estado (
DRAFT,OPEN,QUOTING,EVALUATING,AWARDED,CLOSED,CANCELLED).
- Name
page- Type
- integer
- Description
Número de página.
- Name
limit- Type
- integer
- Description
Cantidad de resultados por página.
Request
curl -G https://api.cannahub.tech/api/procurement/requests \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-d status=QUOTING \
-d limit=10
Response
{
"requests": [
{
"id": "req_01HZR8XYZ0001",
"code": "RFQ-2025-0042",
"status": "QUOTING",
"title": "Reposición sustrato y nutrientes floración",
"priority": "HIGH",
"neededBy": "2025-10-15",
"lines": [
{ "id": "line_01", "category": "Sustrato", "description": "Sustrato coco-perlita premium", "quantity": 500, "unit": "L" }
],
"createdAt": "2025-09-01T12:00:00Z"
}
],
"count": 1,
"page": 1,
"limit": 10
}
Crear un pedido (RFQ)
Crea un nuevo pedido de insumos. Requiere el permiso canManageRequests. Se puede guardar como borrador (DRAFT) o publicar directamente (OPEN).
Atributos requeridos
- Name
title- Type
- string
- Description
Título del pedido.
- Name
lines- Type
- SupplyLine[]
- Description
Al menos una línea. Cada línea requiere
category,description,quantity(> 0) yunit. OpcionalmentespecsytargetUnitPrice.
Atributos opcionales
- Name
neededBy- Type
- string
- Description
Fecha límite de necesidad.
- Name
priority- Type
- enum
- Description
Prioridad (
LOW,NORMAL,HIGH,URGENT).
- Name
notes- Type
- string
- Description
Notas del pedido.
- Name
status- Type
- enum
- Description
DRAFT(borrador) uOPEN(publicar).
Request
curl -X POST https://api.cannahub.tech/api/procurement/requests \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-H "Content-Type: application/json" \
-d '{
"title": "Reposición sustrato y nutrientes floración",
"priority": "HIGH",
"status": "OPEN",
"lines": [
{ "category": "Sustrato", "description": "Sustrato coco-perlita premium", "quantity": 500, "unit": "L", "targetUnitPrice": 1800 }
]
}'
Response (201)
{
"id": "req_01HZR8XYZ0001",
"code": "RFQ-2025-0042",
"status": "OPEN",
"title": "Reposición sustrato y nutrientes floración",
"priority": "HIGH",
"lines": [
{ "id": "line_01", "category": "Sustrato", "description": "Sustrato coco-perlita premium", "quantity": 500, "unit": "L", "targetUnitPrice": 1800 }
],
"createdAt": "2025-09-01T12:00:00Z"
}
Detalle de un pedido
Retorna el detalle completo de un pedido: el pedido en sí, sus cotizaciones y el timeline de eventos, en una sola llamada.
Response
- Name
request- Type
- SupplyRequest
- Description
El pedido.
- Name
quotes- Type
- SupplyQuote[]
- Description
Cotizaciones asociadas al pedido.
- Name
events- Type
- SupplyEvent[]
- Description
Timeline de eventos del pedido.
Request
curl https://api.cannahub.tech/api/procurement/requests/req_01HZR8XYZ0001 \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}"
Response
{
"request": {
"id": "req_01HZR8XYZ0001",
"code": "RFQ-2025-0042",
"status": "QUOTING",
"title": "Reposición sustrato y nutrientes floración",
"priority": "HIGH",
"lines": [
{ "id": "line_01", "category": "Sustrato", "description": "Sustrato coco-perlita premium", "quantity": 500, "unit": "L" }
]
},
"quotes": [
{ "id": "quo_01HZR8ABC0001", "supplierId": "sup_01HZR8SUP0001", "status": "SHORTLISTED", "total": 825000, "currency": "ARS" }
],
"events": [
{ "id": "evt_01", "type": "REQUEST_CREATED", "label": "Pedido creado", "createdAt": "2025-09-01T12:00:00Z" }
]
}
Actualizar un pedido
Actualiza los datos editables de un pedido. Requiere el permiso canManageRequests. Todos los campos son opcionales.
Atributos opcionales
- Name
title- Type
- string
- Description
Título del pedido.
- Name
lines- Type
- SupplyLine[]
- Description
Líneas de insumos (al menos una si se envía).
- Name
neededBy- Type
- string
- Description
Fecha límite de necesidad.
- Name
priority- Type
- enum
- Description
Prioridad (
LOW,NORMAL,HIGH,URGENT).
- Name
notes- Type
- string
- Description
Notas del pedido.
Request
curl -X PATCH https://api.cannahub.tech/api/procurement/requests/req_01HZR8XYZ0001 \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-H "Content-Type: application/json" \
-d '{ "priority": "URGENT", "neededBy": "2025-10-01" }'
Response
{
"id": "req_01HZR8XYZ0001",
"code": "RFQ-2025-0042",
"status": "QUOTING",
"title": "Reposición sustrato y nutrientes floración",
"priority": "URGENT",
"neededBy": "2025-10-01",
"updatedAt": "2025-09-07T08:15:00Z"
}
Eliminar un pedido
Elimina un pedido. Requiere el permiso canManageRequests.
Request
curl -X DELETE https://api.cannahub.tech/api/procurement/requests/req_01HZR8XYZ0001 \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}"
Response
{
"success": true
}
Cambiar estado de un pedido
Transiciona el estado de un pedido a lo largo de su máquina de estados. Requiere el permiso canManageRequests. Una transición inválida devuelve 409 con el hint validTransitions.
Atributos requeridos
- Name
status- Type
- SupplyRequestStatus
- Description
Nuevo estado (
DRAFT,OPEN,QUOTING,EVALUATING,AWARDED,CLOSED,CANCELLED).
Atributos opcionales
- Name
supplierId- Type
- string
- Description
Proveedor contactado (para transiciones que registran contacto).
- Name
channel- Type
- enum
- Description
Canal de contacto (
EMAIL,WHATSAPP,PHONE,IN_PERSON,PORTAL,OTHER).
- Name
performedById- Type
- string
- Description
ID del usuario que realiza la acción.
- Name
performedByName- Type
- string
- Description
Nombre del usuario que realiza la acción.
Request
curl -X PATCH https://api.cannahub.tech/api/procurement/requests/req_01HZR8XYZ0001/status \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-H "Content-Type: application/json" \
-d '{ "status": "EVALUATING", "performedByName": "Lucía Fernández" }'
Response
{
"id": "req_01HZR8XYZ0001",
"code": "RFQ-2025-0042",
"status": "EVALUATING",
"updatedAt": "2025-09-07T10:00:00Z"
}
Listar cotizaciones de un pedido
Retorna las cotizaciones recibidas para un pedido.
Request
curl https://api.cannahub.tech/api/procurement/requests/req_01HZR8XYZ0001/quotes \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}"
Response
[
{
"id": "quo_01HZR8ABC0001",
"supplyRequestId": "req_01HZR8XYZ0001",
"supplierId": "sup_01HZR8SUP0001",
"status": "SHORTLISTED",
"paymentTerm": "NET_30",
"total": 825000,
"currency": "ARS"
}
]
Agregar una cotización
Registra la cotización de un proveedor para un pedido. Requiere el permiso canManageQuotes. Los totales y el desglose de líneas se calculan del lado del servidor a partir de los precios y descuentos por volumen enviados.
Atributos requeridos
- Name
supplierId- Type
- string
- Description
ID del proveedor que cotiza.
- Name
paymentTerm- Type
- enum
- Description
Condición de pago (
CASH,NET_30,NET_60,NET_90).
- Name
lines- Type
- QuoteLinePrice[]
- Description
Precios por línea. Cada entrada requiere
lineIdyunitPrice(≥ 0).
Atributos opcionales
- Name
volumeDiscounts- Type
- VolumeDiscountsByLine[]
- Description
Tramos de descuento por volumen por línea. Cada tramo define
minQuantityyunitPriceXORdiscountPercent.
- Name
validUntil- Type
- string
- Description
Fecha de validez de la oferta.
- Name
receivedAt- Type
- string
- Description
Fecha de recepción de la cotización.
- Name
channel- Type
- enum
- Description
Canal (
EMAIL,WHATSAPP,PHONE,IN_PERSON,PORTAL,OTHER).
- Name
currency- Type
- enum
- Description
Moneda (
ARS,USD,EUR).
- Name
notes- Type
- string
- Description
Notas.
Request
curl -X POST https://api.cannahub.tech/api/procurement/requests/req_01HZR8XYZ0001/quotes \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-H "Content-Type: application/json" \
-d '{
"supplierId": "sup_01HZR8SUP0001",
"paymentTerm": "NET_30",
"currency": "ARS",
"channel": "WHATSAPP",
"lines": [ { "lineId": "line_01", "unitPrice": 1750 } ],
"volumeDiscounts": [
{ "lineId": "line_01", "tiers": [ { "minQuantity": 300, "discountPercent": 5 } ] }
]
}'
Response (201)
{
"id": "quo_01HZR8ABC0001",
"supplyRequestId": "req_01HZR8XYZ0001",
"supplierId": "sup_01HZR8SUP0001",
"status": "RECEIVED",
"paymentTerm": "NET_30",
"channel": "WHATSAPP",
"lines": [
{
"id": "line_01",
"category": "Sustrato",
"quantity": 500,
"unit": "L",
"unitPrice": 1750,
"unitPriceEffective": 1662,
"lineSubtotal": 875000,
"lineDiscount": 43750,
"lineTotal": 831250
}
],
"subtotal": 875000,
"discountTotal": 43750,
"total": 831250,
"currency": "ARS"
}
Comparar cotizaciones
Retorna una comparación lado a lado de todas las cotizaciones de un pedido, con desglose por línea, totales y la fecha de vencimiento de pago si se ordena hoy. Ruta de sólo lectura (no requiere permiso especial más allá de la autenticación).
Response
- Name
requestId- Type
- string
- Description
ID del pedido comparado.
- Name
lines- Type
- SupplyLine[]
- Description
Líneas del pedido (base de comparación).
- Name
comparisons- Type
- QuoteComparison[]
- Description
Una entrada por cotización.
- Name
quoteId- Type
- string
- Description
ID de la cotización.
- Name
supplierId- Type
- string
- Description
ID del proveedor.
- Name
supplierName- Type
- string
- Description
Nombre del proveedor.
- Name
paymentTerm- Type
- enum
- Description
Condición de pago.
- Name
status- Type
- enum
- Description
Estado de la cotización.
- Name
lineBreakdown- Type
- QuoteLine[]
- Description
Desglose por línea.
- Name
subtotal- Type
- number
- Description
Subtotal.
- Name
discountTotal- Type
- number
- Description
Descuento total.
- Name
total- Type
- number
- Description
Total.
- Name
dueDateIfOrderedToday- Type
- string
- Description
Fecha de vencimiento de pago si se ordenara hoy.
- Name
validUntil- Type
- string
- Description
Validez de la oferta.
Request
curl https://api.cannahub.tech/api/procurement/requests/req_01HZR8XYZ0001/compare \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}"
Response
{
"requestId": "req_01HZR8XYZ0001",
"lines": [
{ "id": "line_01", "category": "Sustrato", "description": "Sustrato coco-perlita premium", "quantity": 500, "unit": "L" }
],
"comparisons": [
{
"quoteId": "quo_01HZR8ABC0001",
"supplierId": "sup_01HZR8SUP0001",
"supplierName": "Insumos del Sur S.A.",
"paymentTerm": "NET_30",
"status": "SHORTLISTED",
"lineBreakdown": [
{ "id": "line_01", "quantity": 500, "unitPrice": 1750, "unitPriceEffective": 1662, "lineTotal": 831250 }
],
"subtotal": 875000,
"discountTotal": 43750,
"total": 831250,
"dueDateIfOrderedToday": "2025-10-07",
"validUntil": "2025-09-30"
}
]
}
Timeline de eventos de un pedido
Retorna el timeline de eventos de un pedido (auditoría del ciclo de vida).
El modelo SupplyEvent
- Name
id- Type
- string
- Description
ID del evento.
- Name
supplyRequestId- Type
- string
- Description
Pedido al que pertenece.
- Name
type- Type
- enum
- Description
Tipo de evento. Puede ser uno de:
- REQUEST_CREATED
- REQUEST_STATUS_CHANGED
- SUPPLIER_CONTACTED
- QUOTE_RECEIVED
- QUOTE_SHORTLISTED
- QUOTE_SELECTED
- QUOTE_REJECTED
- PO_CREATED
- PO_STATUS_CHANGED
- RECEIPT_RECORDED
- NOTE_ADDED
- Name
label- Type
- string
- Description
Etiqueta legible del evento.
- Name
detail- Type
- string
- Description
Detalle del evento.
- Name
metadata- Type
- object
- Description
Metadata adicional.
- Name
performedById- Type
- string
- Description
ID del usuario que lo generó.
- Name
performedByName- Type
- string
- Description
Nombre del usuario que lo generó.
- Name
createdAt- Type
- string
- Description
Timestamp del evento.
Request
curl https://api.cannahub.tech/api/procurement/requests/req_01HZR8XYZ0001/events \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}"
Response
[
{
"id": "evt_01",
"supplyRequestId": "req_01HZR8XYZ0001",
"type": "REQUEST_CREATED",
"label": "Pedido creado",
"performedByName": "Lucía Fernández",
"createdAt": "2025-09-01T12:00:00Z"
},
{
"id": "evt_02",
"supplyRequestId": "req_01HZR8XYZ0001",
"type": "QUOTE_RECEIVED",
"label": "Cotización recibida de Insumos del Sur S.A.",
"createdAt": "2025-09-04T15:20:00Z"
}
]
Agregar una nota al pedido
Agrega una nota manual (evento NOTE_ADDED) al timeline de un pedido. Requiere el permiso canManageRequests.
Atributos requeridos
- Name
detail- Type
- string
- Description
Texto de la nota (no puede estar vacío).
Atributos opcionales
- Name
performedById- Type
- string
- Description
ID del usuario que agrega la nota.
- Name
performedByName- Type
- string
- Description
Nombre del usuario que agrega la nota.
Request
curl -X POST https://api.cannahub.tech/api/procurement/requests/req_01HZR8XYZ0001/events \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-H "Content-Type: application/json" \
-d '{ "detail": "Proveedor confirma stock, entrega en 5 días", "performedByName": "Lucía Fernández" }'
Response (201)
{
"id": "evt_03",
"supplyRequestId": "req_01HZR8XYZ0001",
"type": "NOTE_ADDED",
"label": "Nota agregada",
"detail": "Proveedor confirma stock, entrega en 5 días",
"performedByName": "Lucía Fernández",
"createdAt": "2025-09-05T16:00:00Z"
}
Detalle de una cotización
Retorna una cotización individual con su desglose completo.
Request
curl https://api.cannahub.tech/api/procurement/quotes/quo_01HZR8ABC0001 \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}"
Response
{
"id": "quo_01HZR8ABC0001",
"supplyRequestId": "req_01HZR8XYZ0001",
"supplierId": "sup_01HZR8SUP0001",
"status": "SHORTLISTED",
"paymentTerm": "NET_30",
"validUntil": "2025-09-30",
"channel": "WHATSAPP",
"lines": [
{ "id": "line_01", "quantity": 500, "unit": "L", "unitPrice": 1750, "unitPriceEffective": 1662, "lineTotal": 831250 }
],
"subtotal": 875000,
"discountTotal": 43750,
"total": 831250,
"currency": "ARS"
}
Actualizar una cotización
Actualiza los datos editables de una cotización. Requiere el permiso canManageQuotes. Todos los campos son opcionales.
Atributos opcionales
- Name
paymentTerm- Type
- enum
- Description
Condición de pago (
CASH,NET_30,NET_60,NET_90).
- Name
lines- Type
- array
- Description
Líneas con
unitPrice(recalcula totales del lado del servidor).
- Name
volumeDiscounts- Type
- VolumeDiscountsByLine[]
- Description
Tramos de descuento por volumen.
- Name
validUntil- Type
- string
- Description
Validez de la oferta.
- Name
channel- Type
- enum
- Description
Canal (
EMAIL,WHATSAPP,PHONE,IN_PERSON,PORTAL,OTHER).
- Name
currency- Type
- enum
- Description
Moneda (
ARS,USD,EUR).
- Name
notes- Type
- string
- Description
Notas.
Request
curl -X PATCH https://api.cannahub.tech/api/procurement/quotes/quo_01HZR8ABC0001 \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-H "Content-Type: application/json" \
-d '{ "paymentTerm": "NET_60", "validUntil": "2025-10-15" }'
Response
{
"id": "quo_01HZR8ABC0001",
"paymentTerm": "NET_60",
"validUntil": "2025-10-15",
"total": 831250,
"currency": "ARS",
"updatedAt": "2025-09-06T09:00:00Z"
}
Eliminar una cotización
Elimina una cotización. Requiere el permiso canManageQuotes.
Request
curl -X DELETE https://api.cannahub.tech/api/procurement/quotes/quo_01HZR8ABC0001 \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}"
Response
{
"success": true
}
Cambiar estado de una cotización
Cambia el estado de una cotización (preseleccionar / rechazar / etc.). Requiere el permiso canManageQuotes.
Atributos requeridos
- Name
status- Type
- SupplyQuoteStatus
- Description
Nuevo estado (
RECEIVED,SHORTLISTED,SELECTED,REJECTED,EXPIRED).
Atributos opcionales
- Name
reason- Type
- string
- Description
Motivo del cambio (ej. rechazo).
- Name
performedById- Type
- string
- Description
ID del usuario que realiza la acción.
- Name
performedByName- Type
- string
- Description
Nombre del usuario que realiza la acción.
Request
curl -X PATCH https://api.cannahub.tech/api/procurement/quotes/quo_01HZR8ABC0001/status \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-H "Content-Type: application/json" \
-d '{ "status": "SHORTLISTED", "performedByName": "Lucía Fernández" }'
Response
{
"id": "quo_01HZR8ABC0001",
"status": "SHORTLISTED",
"updatedAt": "2025-09-05T12:00:00Z"
}
Adjudicar una cotización
Adjudica (selecciona) una cotización como ganadora. Esta acción genera una orden de compra (PurchaseOrder) a partir de la cotización. Requiere el permiso canAward.
Atributos opcionales
- Name
orderedAt- Type
- string
- Description
Fecha de emisión de la orden.
- Name
expectedDelivery- Type
- string
- Description
Fecha estimada de entrega.
- Name
orderedById- Type
- string
- Description
ID del usuario que adjudica.
- Name
orderedByName- Type
- string
- Description
Nombre del usuario que adjudica.
- Name
notes- Type
- string
- Description
Notas para la orden.
Response
- Name
purchaseOrder- Type
- PurchaseOrder
- Description
La orden de compra generada.
- Name
quote- Type
- SupplyQuote
- Description
La cotización adjudicada (status
SELECTED).
Request
curl -X POST https://api.cannahub.tech/api/procurement/quotes/quo_01HZR8ABC0001/select \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-H "Content-Type: application/json" \
-d '{ "expectedDelivery": "2025-09-12", "orderedByName": "Lucía Fernández" }'
Response (201)
{
"purchaseOrder": {
"id": "po_01HZR8PO00001",
"code": "PO-2025-0031",
"supplyRequestId": "req_01HZR8XYZ0001",
"winningQuoteId": "quo_01HZR8ABC0001",
"supplierId": "sup_01HZR8SUP0001",
"status": "ORDERED",
"paymentTerm": "NET_30",
"paymentStatus": "PENDING",
"expectedDelivery": "2025-09-12",
"total": 831250,
"currency": "ARS"
},
"quote": {
"id": "quo_01HZR8ABC0001",
"status": "SELECTED"
}
}
Listar órdenes de compra
Retorna las órdenes de compra del club. No existe endpoint de creación directa: las órdenes se generan al adjudicar una cotización. Soporta filtro por estado y paginación.
Query params
- Name
status- Type
- PurchaseOrderStatus
- Description
Filtra por estado (
ORDERED,PARTIAL_RECEIVED,RECEIVED,CANCELLED).
- Name
page- Type
- integer
- Description
Número de página.
- Name
limit- Type
- integer
- Description
Resultados por página.
Request
curl -G https://api.cannahub.tech/api/procurement/purchase-orders \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-d status=ORDERED
Response
{
"purchaseOrders": [
{
"id": "po_01HZR8PO00001",
"code": "PO-2025-0031",
"supplierId": "sup_01HZR8SUP0001",
"status": "ORDERED",
"paymentStatus": "PENDING",
"total": 831250,
"currency": "ARS",
"expectedDelivery": "2025-09-12"
}
],
"count": 1,
"page": 1,
"limit": 20
}
Detalle de una orden de compra
Retorna una orden de compra individual con su desglose completo.
Request
curl https://api.cannahub.tech/api/procurement/purchase-orders/po_01HZR8PO00001 \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}"
Response
{
"id": "po_01HZR8PO00001",
"code": "PO-2025-0031",
"supplyRequestId": "req_01HZR8XYZ0001",
"winningQuoteId": "quo_01HZR8ABC0001",
"supplierId": "sup_01HZR8SUP0001",
"status": "ORDERED",
"paymentTerm": "NET_30",
"paymentStatus": "PENDING",
"orderedAt": "2025-09-06T11:00:00Z",
"expectedDelivery": "2025-09-12",
"dueDate": "2025-10-06",
"lines": [
{ "id": "line_01", "category": "Sustrato", "quantity": 500, "unit": "L", "unitPrice": 1750, "lineTotal": 831250 }
],
"subtotal": 875000,
"discountTotal": 43750,
"total": 831250,
"currency": "ARS"
}
Actualizar una orden de compra
Actualiza la entrega estimada, notas o el estado de pago de una orden. Requiere el permiso canManagePurchaseOrders. Todos los campos son opcionales.
Atributos opcionales
- Name
expectedDelivery- Type
- string
- Description
Nueva fecha estimada de entrega.
- Name
notes- Type
- string
- Description
Notas de la orden.
- Name
paymentStatus- Type
- enum
- Description
Estado del pago (
PENDING,PARTIAL,PAID,OVERDUE).
Request
curl -X PATCH https://api.cannahub.tech/api/procurement/purchase-orders/po_01HZR8PO00001 \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-H "Content-Type: application/json" \
-d '{ "paymentStatus": "PAID", "notes": "Pagado por transferencia" }'
Response
{
"id": "po_01HZR8PO00001",
"code": "PO-2025-0031",
"status": "ORDERED",
"paymentStatus": "PAID",
"notes": "Pagado por transferencia",
"updatedAt": "2025-09-08T10:00:00Z"
}
Cambiar estado de una orden de compra
Registra la recepción (parcial o total) o cancela una orden de compra. Requiere el permiso canManagePurchaseOrders. Una transición inválida devuelve 409 con el hint validTransitions.
Atributos requeridos
- Name
status- Type
- PurchaseOrderStatus
- Description
Nuevo estado (
ORDERED,PARTIAL_RECEIVED,RECEIVED,CANCELLED).
Atributos opcionales
- Name
performedById- Type
- string
- Description
ID del usuario que realiza la acción.
- Name
performedByName- Type
- string
- Description
Nombre del usuario que realiza la acción.
Request
curl -X PATCH https://api.cannahub.tech/api/procurement/purchase-orders/po_01HZR8PO00001/status \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-H "Content-Type: application/json" \
-d '{ "status": "RECEIVED", "performedByName": "Lucía Fernández" }'
Response
{
"id": "po_01HZR8PO00001",
"code": "PO-2025-0031",
"status": "RECEIVED",
"updatedAt": "2025-09-12T13:00:00Z"
}
Listar proveedores
Retorna la lista de proveedores mayoristas. Soporta búsqueda por texto, filtro por categoría y por estado activo, más paginación.
Query params
- Name
q- Type
- string
- Description
Búsqueda por texto (alias:
search).
- Name
category- Type
- string
- Description
Filtra por categoría abastecida.
- Name
isActive- Type
- boolean
- Description
Filtra por estado activo (
true/false).
- Name
page- Type
- integer
- Description
Número de página.
- Name
limit- Type
- integer
- Description
Resultados por página.
Request
curl -G https://api.cannahub.tech/api/procurement/suppliers \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-d q=insumos \
-d isActive=true
Response
{
"suppliers": [
{
"id": "sup_01HZR8SUP0001",
"name": "Insumos del Sur S.A.",
"contactName": "Roberto Álvarez",
"cuit": "30-71234567-9",
"categories": ["Sustrato", "Nutrientes"],
"defaultPaymentTerms": ["CASH", "NET_30"],
"isActive": true
}
],
"count": 1,
"page": 1,
"limit": 20
}
Crear un proveedor
Crea un nuevo proveedor mayorista. Requiere el permiso canManageSuppliers.
Atributos requeridos
- Name
name- Type
- string
- Description
Razón social o nombre del proveedor.
Atributos opcionales
- Name
contactName- Type
- string
- Description
Nombre de contacto.
- Name
email- Type
- string
- Description
Email de contacto.
- Name
phone- Type
- string
- Description
Teléfono de contacto.
- Name
cuit- Type
- string
- Description
CUIT del proveedor.
- Name
categories- Type
- string[]
- Description
Categorías que abastece.
- Name
defaultPaymentTerms- Type
- enum[]
- Description
Condiciones de pago habituales (
CASH,NET_30,NET_60,NET_90).
- Name
address- Type
- string
- Description
Dirección.
- Name
notes- Type
- string
- Description
Notas internas.
- Name
isActive- Type
- boolean
- Description
Si el proveedor está activo.
Request
curl -X POST https://api.cannahub.tech/api/procurement/suppliers \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-H "Content-Type: application/json" \
-d '{
"name": "Insumos del Sur S.A.",
"contactName": "Roberto Álvarez",
"email": "ventas@insumosdelsur.com.ar",
"cuit": "30-71234567-9",
"categories": ["Sustrato", "Nutrientes"],
"defaultPaymentTerms": ["CASH", "NET_30"]
}'
Response (201)
{
"id": "sup_01HZR8SUP0001",
"name": "Insumos del Sur S.A.",
"contactName": "Roberto Álvarez",
"email": "ventas@insumosdelsur.com.ar",
"cuit": "30-71234567-9",
"categories": ["Sustrato", "Nutrientes"],
"defaultPaymentTerms": ["CASH", "NET_30"],
"isActive": true,
"createdAt": "2025-09-01T09:00:00Z"
}
Detalle de un proveedor
Retorna un proveedor individual.
Request
curl https://api.cannahub.tech/api/procurement/suppliers/sup_01HZR8SUP0001 \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}"
Response
{
"id": "sup_01HZR8SUP0001",
"name": "Insumos del Sur S.A.",
"contactName": "Roberto Álvarez",
"email": "ventas@insumosdelsur.com.ar",
"phone": "+541148887766",
"cuit": "30-71234567-9",
"categories": ["Sustrato", "Nutrientes", "Macetas"],
"defaultPaymentTerms": ["CASH", "NET_30"],
"address": "Parque Industrial Avellaneda, Buenos Aires",
"isActive": true,
"createdAt": "2025-01-10T09:00:00Z",
"updatedAt": "2025-08-20T14:00:00Z"
}
Actualizar un proveedor
Actualiza los datos de un proveedor. Requiere el permiso canManageSuppliers. Todos los campos son opcionales (mismos que en la creación).
Request
curl -X PATCH https://api.cannahub.tech/api/procurement/suppliers/sup_01HZR8SUP0001 \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-H "Content-Type: application/json" \
-d '{ "phone": "+541148887799", "isActive": true }'
Response
{
"id": "sup_01HZR8SUP0001",
"name": "Insumos del Sur S.A.",
"phone": "+541148887799",
"isActive": true,
"updatedAt": "2025-09-08T11:00:00Z"
}
Eliminar un proveedor
Elimina un proveedor. Por defecto realiza una baja lógica (soft delete, permiso canManageSuppliers). Con el query param hard=true realiza una baja física, que requiere el permiso canDeleteSupplier.
Query params
- Name
hard- Type
- boolean
- Description
Si es
true, elimina definitivamente el proveedor (requierecanDeleteSupplier).
Request
# Baja lógica (soft delete)
curl -X DELETE https://api.cannahub.tech/api/procurement/suppliers/sup_01HZR8SUP0001 \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}"
# Baja física (hard delete)
curl -X DELETE "https://api.cannahub.tech/api/procurement/suppliers/sup_01HZR8SUP0001?hard=true" \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}"
Response
{
"success": true
}
Analytics de compras
Conjunto de endpoints de sólo lectura que agregan métricas del dominio de compras. Todos requieren Authorization (y Tenant-id en entornos no mockeados), pero no un permiso de escritura.
Endpoints
- Name
GET /api/procurement/analytics/summary- Type
- ProcurementSummary
- Description
Resumen general. Acepta query params opcionales
fromyto(rango de fechas). DevuelvetotalSpend,openPurchaseOrders,activeRequests,volumeSavings,overduePayments,purchaseOrderCount.
- Name
GET /api/procurement/analytics/pipeline- Type
- { pipeline }
- Description
Conteo de pedidos por estado (más
ORDEREDde órdenes).pipelinees un mapaestado → cantidad.
- Name
GET /api/procurement/analytics/funnel- Type
- ProcurementFunnel
- Description
Embudo de conversión:
published,withQuote,awarded,closed.
- Name
GET /api/procurement/analytics/spend-by-category- Type
- { categories }
- Description
Gasto agregado por categoría de insumo. Array de
{ category, total }.
- Name
GET /api/procurement/analytics/top-suppliers- Type
- { suppliers }
- Description
Proveedores con mayor gasto. Acepta query param opcional
limit. Array de{ supplierId, name, total }.
Request
curl -G https://api.cannahub.tech/api/procurement/analytics/summary \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}" \
-d from=2025-01-01 \
-d to=2025-09-30
curl https://api.cannahub.tech/api/procurement/analytics/pipeline \
-H "Authorization: Bearer {token}" -H "Tenant-id: {tenantId}"
curl https://api.cannahub.tech/api/procurement/analytics/funnel \
-H "Authorization: Bearer {token}" -H "Tenant-id: {tenantId}"
curl https://api.cannahub.tech/api/procurement/analytics/spend-by-category \
-H "Authorization: Bearer {token}" -H "Tenant-id: {tenantId}"
curl -G https://api.cannahub.tech/api/procurement/analytics/top-suppliers \
-H "Authorization: Bearer {token}" -H "Tenant-id: {tenantId}" \
-d limit=5
summary — Response
{
"totalSpend": 4821500,
"openPurchaseOrders": 3,
"activeRequests": 5,
"volumeSavings": 218400,
"overduePayments": 1,
"purchaseOrderCount": 12
}
pipeline — Response
{
"pipeline": {
"DRAFT": 2,
"OPEN": 3,
"QUOTING": 4,
"EVALUATING": 1,
"AWARDED": 2,
"CLOSED": 6,
"ORDERED": 3
}
}
funnel — Response
{
"published": 18,
"withQuote": 14,
"awarded": 9,
"closed": 6
}
spend-by-category — Response
{
"categories": [
{ "category": "Sustrato", "total": 1980000 },
{ "category": "Nutrientes", "total": 1450500 },
{ "category": "Macetas", "total": 391000 }
]
}
top-suppliers — Response
{
"suppliers": [
{ "supplierId": "sup_01HZR8SUP0001", "name": "Insumos del Sur S.A.", "total": 2310000 },
{ "supplierId": "sup_01HZR8SUP0002", "name": "Grow Mayorista SRL", "total": 1620500 }
]
}
React Query Hooks
El feature de Procurement expone hooks pre-armados que consumen estas rutas BFF y manejan caché, invalidación y errores automáticamente. Las query keys viven en @/features/Club/Procurement/hooks/keys.
Queries
import {
useRequestsQuery,
useRequestDetailQuery,
useRequestQuotesQuery,
useCompareQuery,
useRequestEventsQuery,
} from '@/features/Club/Procurement/Requests/hooks/queries'
import { usePurchaseOrdersQuery } from '@/features/Club/Procurement/PurchaseOrders/hooks/queries'
import { useSuppliersQuery } from '@/features/Club/Procurement/Suppliers/hooks/queries'
// Listar pedidos con filtro por estado
const { data } = useRequestsQuery({ status: 'QUOTING' })
// Detalle de un pedido (request + quotes + events)
const { data: detail } = useRequestDetailQuery('req_01HZR8XYZ0001')
// Comparar cotizaciones de un pedido
const { data: comparison } = useCompareQuery('req_01HZR8XYZ0001')
Mutations
import {
useCreateRequestMutation,
useUpdateRequestStatusMutation,
useAddQuoteMutation,
} from '@/features/Club/Procurement/Requests/hooks/mutations'
import { useSelectQuoteMutation } from '@/features/Club/Procurement/Quotes/hooks/mutations'
import { useUpdatePurchaseOrderStatusMutation } from '@/features/Club/Procurement/PurchaseOrders/hooks/mutations'
// Crear un pedido (RFQ)
const { mutate: createRequest } = useCreateRequestMutation()
// Adjudicar una cotización → genera la orden de compra
const { mutate: selectQuote } = useSelectQuoteMutation()
// Recepcionar una orden de compra
const { mutate: updatePoStatus } = useUpdatePurchaseOrderStatusMutation()
Máquina de estados del pedido
El ciclo de vida de un pedido (RFQ) sigue una máquina de estados. Las transiciones inválidas devuelven 409 Conflict con un hint validTransitions que indica los estados válidos desde el estado actual.
DRAFT → OPEN → QUOTING → EVALUATING → AWARDED → CLOSED
↑
(adjudicación de cotización)
cualquiera → CANCELLED
| Modelo | Estados |
|---|---|
| SupplyRequest | DRAFT, OPEN, QUOTING, EVALUATING, AWARDED, CLOSED, CANCELLED |
| SupplyQuote | RECEIVED, SHORTLISTED, SELECTED, REJECTED, EXPIRED |
| PurchaseOrder | ORDERED, PARTIAL_RECEIVED, RECEIVED, CANCELLED |
| PurchaseOrder (pago) | PENDING, PARTIAL, PAID, OVERDUE |