Stock e inventario Guía

Importar un conteo de inventario en Neges

Guía completa para descargar la plantilla, registrar un conteo físico, validar errores, aplicar el inventario de forma atómica y mantener o evolucionar el feature.

24 min de lectura

El importador convierte un conteo físico realizado en una bodega en movimientos de inventario trazables. La primera versión trabaja con una plantilla CSV generada por Neges, admite hasta 1.000 productos y aplica todos los cambios juntos o ninguno. Esta guía combina instrucciones operativas, explicación de errores y documentación técnica para mantenimiento futuro.

Alcance de la primera versión

Contrato funcional vigente del importador.
CapacidadComportamiento
BodegaUna sola bodega por archivo.
FormatoCSV descargado desde Neges; no se aceptan planillas arbitrarias.
Tipo de conteoAbsoluto: se informa cuánto existe físicamente, no cuánto sumar o restar.
TamañoHasta 1.000 filas de producto por plantilla.
PrevisualizaciónMuestra todas las filas contadas, sus diferencias y todos sus errores.
AplicaciónAtómica: se aplican todos los cambios o ninguno.
ConcurrenciaRechaza el conteo completo si un saldo cambió después de la previsualización.
ProductosNo crea productos ni variantes. Cada producto debe existir y estar activo.
ResultadoGenera movimientos, historial y un comprobante consultable.

Permisos y requisitos previos

  • La empresa correcta está seleccionada en el backoffice.
  • El módulo Stock está habilitado para la empresa.
  • Existe al menos una bodega activa.
  • Los productos que se contarán ya existen y están activos.
  • El usuario tiene el permiso stock.import para descargar, previsualizar y aplicar.
  • El usuario tiene stock.read para consultar historial y comprobantes.

Los roles base Administrador y Manager incluyen stock.import. Viewer no lo incluye. Un rol personalizado puede recibir o perder el permiso independientemente de stock.adjust, por lo que restringir importaciones masivas no impide mantener ajustes manuales.

Flujo completo de uso

flujo-operativo-conteo.mmdmermaid
El diagrama se renderiza al cargar la pagina.
  1. Abre Inventario → Importar conteo

    Confirma la empresa activa. En el primer paso selecciona una bodega, el motivo del conteo y, opcionalmente, una nota interna.

    Tip: La bodega queda incorporada dentro de cada fila de la plantilla para impedir que un archivo se aplique por error en otra ubicación.
  2. Descarga la plantilla oficial

    Neges genera un CSV con todos los productos activos, su identificador estable, SKU, nombre y stock esperado en la bodega.

    Tip: Si existen más de 1.000 productos activos, esta versión no genera la plantilla. El filtrado por categorías o último conteo queda como evolución futura.
  3. Realiza el conteo físico

    Completa únicamente counted_on_hand. Escribe la cantidad total encontrada físicamente para cada producto que quieras contar.

    Tip: Una celda vacía omite el producto. Un cero explícito significa que el producto fue contado y no se encontró ninguna unidad.
  4. Carga el CSV

    Selecciona el mismo archivo CSV. El navegador revisa estructura, columnas obligatorias y límite antes de enviarlo a Strapi.

  5. Revisa la previsualización completa

    La tabla muestra stock esperado, cantidad contada, diferencia y estado. Si hay errores, ninguna fila puede aplicarse.

  6. Confirma la aplicación

    Neges vuelve a leer y bloquear todos los saldos. Si siguen iguales al snapshot, crea los movimientos y actualiza el conteo dentro de una única transacción.

  7. Conserva el comprobante

    El resultado muestra identificador del conteo, fecha, usuario, bodega, archivo, motivo, productos contados, movimientos generados y detalle por producto.

Formato de la plantilla CSV

inventario-bodega-centro.csvcsv
warehouse_document_id,warehouse_code,product_document_id,sku,product_name,expected_on_hand,counted_on_hand
wh_01,CENTRO,prod_01,SKU-001,Café molido 250 g,12,10
wh_01,CENTRO,prod_02,SKU-002,Té negro 20 bolsas,8,8
wh_01,CENTRO,prod_03,SKU-003,Azúcar 1 kg,4,0
wh_01,CENTRO,prod_04,SKU-004,Leche 1 L,15,
No cambies los encabezados ni los identificadores generados por Neges.
ColumnaQuién la completaUso
warehouse_document_idNegesIdentificador estable de la bodega seleccionada.
warehouse_codeNegesCódigo legible de referencia; no identifica por sí solo la bodega.
product_document_idNegesIdentificador Strapi v5 del producto. Es la clave usada al validar.
skuNegesCódigo visible para facilitar el conteo.
product_nameNegesNombre visible al momento de generar la plantilla.
expected_on_handNegesStock físico registrado al descargar. No debe editarse.
counted_on_handUsuarioCantidad física absoluta, entera y mayor o igual a cero.

Validaciones y cómo corregir errores

ValidaciónQué significaCómo resolverla
Columnas obligatoriasEl archivo no conserva el formato oficial.Descarga una plantilla nueva y copia sólo counted_on_hand.
Más de 1.000 filasEl archivo supera el límite de esta versión.No dividas manualmente una plantilla oficial; espera la versión con filtros o reduce productos activos de forma controlada.
Sin filas contadasTodas las celdas counted_on_hand están vacías.Registra al menos una cantidad, incluido 0 cuando corresponda.
Cantidad inválidaEl valor no es entero o es negativo.Usa enteros desde 0; no uses decimales, texto ni fórmulas.
Producto repetidoEl mismo product_document_id aparece más de una vez.Mantén una sola fila. Neges marca todas las apariciones para encontrarlas fácilmente.
Producto no encontradoFue eliminado, está inactivo o pertenece a otra empresa.Descarga una plantilla actualizada desde la empresa correcta.
Bodega incorrectaLa fila pertenece a otra bodega.No combines archivos. Descarga la plantilla de la bodega seleccionada.
Stock cambióexpected_on_hand ya no coincide con el saldo actual.Descarga una plantilla nueva y repite o reconcilia el conteo.

La previsualización persiste todas las líneas contadas y devuelve todos los errores de una vez. No existe “aplicar filas válidas”: una sola fila errónea bloquea el conteo completo.

Qué ocurre con productos reservados

counted_on_hand representa existencia física y siempre debe ser mayor o igual a cero. Las reservas son un concepto separado: quantityAvailable se calcula como quantityOnHand menos quantityReserved.

Antes del conteoConteo físicoResultado
On hand 10 · Reservado 3 · Disponible 78On hand 8 · Reservado 3 · Disponible 5
On hand 10 · Reservado 3 · Disponible 70On hand 0 · Reservado 3 · Disponible -3

Estados, historial y trazabilidad

estados-inventory-count.mmdmermaid
El diagrama se renderiza al cargar la pagina.
Estado internoSignificadoModifica stockVisible en historial
invalidLa previsualización contiene al menos una línea errónea.NoNo
readyTodas las líneas son válidas y existe un snapshot para confirmar.NoNo
completedEl journal, las entradas, los saldos y el comprobante fueron confirmados.

El comprobante conserva sourceFilename, sourceChecksum, snapshotAt, motivo, notas, usuario creador, usuario aplicador, fecha de aplicación, bodega, cantidades resumidas y líneas. journalDocumentId conecta el comprobante con el ledger cuando hubo diferencias; un conteo sin diferencias puede completarse sin journal.

Cómo se garantiza la aplicación atómica

aplicacion-atomica.mmdmermaid
El diagrama se renderiza al cargar la pagina.

La confirmación bloquea primero el registro inventory_count y luego los stock_balances existentes en un orden estable. Después vuelve a comparar quantityOnHand con expectedOnHand. Sólo si todos coinciden crea el inventory_journal, sus inventory_entries, actualiza saldos y marca el conteo completed. Cuando un saldo aún no existe, la restricción única protege su creación concurrente.

Cualquier excepción produce rollback. Esto incluye conflictos, productos que ya no pueden resolverse, errores al crear movimientos y fallos al actualizar un saldo. La restricción única tenant/product/warehouse evita crear dos balances válidos para la misma ubicación.

Arquitectura técnica

arquitectura-importador.mmdmermaid
El diagrama se renderiza al cargar la pagina.
ResponsabilidadUbicación principal
Rutas y pantallas Angularneges-backoffice/src/app/features/stock/stock.routes.ts y pages/inventory-count-*
Parser CSV del navegadorneges-backoffice/src/app/features/stock/inventory-counts.csv.ts
Cliente HTTP y modelosneges-backoffice/src/app/features/stock/data-access/inventory-counts.service.ts e inventory-counts.models.ts
Rutas y controlador Strapineges-strapi/src/api/inventory-count/routes e inventory-count/controllers
Validación y orquestaciónneges-strapi/src/api/inventory-count/services
Persistencia de cabecera y líneasinventory-count y inventory-count-line content-types
Ledger y actualización de saldosinventory-journal/services e stock-balance/services
Permisos y móduloneges-strapi/src/utils/rbac/default-rbac.ts y default-tenant-modules.ts
Restricción de saldosdatabase/migrations/2026-07-14-000002-unique-stock-balances.js

Contrato HTTP y seguridad multi-tenant

inventory-count-endpoints.txthttp
GET  /api/stock/counts/template?warehouseDocumentId=<documentId>  stock.import
POST /api/stock/counts/preview                                  stock.import
POST /api/stock/counts/:documentId/apply                        stock.import
GET  /api/stock/counts?page=1&pageSize=25                       stock.read
GET  /api/stock/counts/:documentId                              stock.read

Authorization: Bearer <token>
X-Tenant-Id: <tenant-documentId>

Todas las rutas pasan por require-auth, resolve-tenant, require-membership y require-feature. El tenant se obtiene del contexto autenticado; no se confía en un tenant enviado dentro del CSV. Las búsquedas de bodega, producto, conteo y saldo incluyen el tenant activo.

preview-request.jsonjson
{
  "data": {
    "warehouseDocumentId": "wh_01",
    "reasonCode": "physical_count",
    "notes": "Conteo de cierre de mes",
    "sourceFilename": "inventario-centro-2026-07-14.csv",
    "rows": [
      {
        "lineNumber": 2,
        "warehouseDocumentId": "wh_01",
        "productDocumentId": "prod_01",
        "productCode": "SKU-001",
        "productName": "Café molido 250 g",
        "expectedOnHand": "12",
        "countedOnHand": "10"
      }
    ]
  }
}
Código relevanteHTTPUso
INVENTORY_COUNT_ROW_LIMIT_EXCEEDED422El archivo supera 1.000 filas.
INVENTORY_COUNT_PRODUCT_DUPLICATEDPrevisualización inválidaEl producto aparece más de una vez.
INVENTORY_COUNT_STOCK_CHANGED409Un saldo cambió antes de aplicar. Incluye todos los conflictos.
INVENTORY_COUNT_NOT_READY409El conteo es inválido, fue aplicado o ya no está disponible.
INVENTORY_JOURNAL_ALREADY_POSTED409Protección de idempotencia: ya existe un journal para esa fuente.

Modelo de datos y decisiones importantes

EntidadDatos esencialesPropósito
inventory_counttenant, warehouse, workflowStatus, reasonCode, checksum, snapshot, contadores, usuarios, journalDocumentIdCabecera, auditoría y comprobante.
inventory_count_lineproductDocumentId estable, código/nombre snapshot, expected, counted, delta, estado y erroresDetalle completo incluso cuando una referencia de producto falla.
inventory_journalsourceType inventory-count, sourceId count.documentId, estado postedDocumento contable del ledger de inventario.
inventory_entryproducto, bodega, quantityDelta, sourceLineIdMovimiento trazable por cada diferencia no cero.
stock_balancequantityOnHand, quantityReserved, quantityAvailableLectura rápida del saldo actual; clave única por tenant/producto/bodega.

Se usa documentId en contratos públicos de Strapi v5. workflowStatus evita el nombre reservado status. Las líneas guardan nombre y código como snapshot para que un comprobante histórico siga siendo legible aunque el producto cambie posteriormente.

La migración del índice único ignora claves legacy incompletas con relaciones nulas, porque PostgreSQL permite múltiples NULL en una restricción única. Las claves completas duplicadas sí detienen la migración para impedir una consolidación destructiva o una suma incorrecta.

Pruebas y checklist para modificar el código

  • Mantener una bodega por archivo o versionar explícitamente el contrato CSV y API.
  • Conservar la diferencia entre celda vacía y cero.
  • Validar duplicados sobre todas sus apariciones, no sólo desde la segunda.
  • No confiar en tenant, producto o bodega enviados por el cliente sin volver a resolverlos.
  • No crear productos automáticamente dentro de este flujo.
  • Releer y bloquear saldos dentro de la misma transacción usada para aplicar.
  • Mantener sourceId/sourceType idempotentes en inventory-journal.
  • Actualizar modelos, parser, tablas, comprobante y documentación si cambia una columna CSV.
  • Actualizar neges-mcp y MCP-CONTRACT.md si el MCP comienza a consumir estos endpoints o content-types.
  • Agregar migraciones reversibles y revisar datos legacy antes de imponer nuevas restricciones.
verificacion-local.shbash
# neges-backoffice
npm run typecheck
npm run test:ci -- --include='src/app/features/stock/**/*.spec.ts'
npm run build:prod

# neges-strapi
npm run build:server
node --test tests/unit/inventory-count.test.cjs \
  tests/unit/inventory-stock-ledger.test.cjs \
  tests/unit/tenant-module-catalog.test.cjs \
  tests/unit/unique-stock-balances-migration.test.cjs

# neges-mcp
npm run check:contract

Cómo evolucionar el feature de forma segura

Necesidad futuraEvolución recomendada
Más de 1.000 productosGenerar plantillas por categoría, ubicación o fecha de último conteo, conservando una bodega y un snapshot por archivo.
Conteos cíclicosGuardar alcance/filtro del conteo y priorizar productos nunca contados o con lastCountedAt más antiguo.
Archivos grandesProcesar preview en job durable, pero mantener confirmación transaccional y comprobante único.
Productos nuevosCrear un flujo separado de alta masiva con permiso products.create, validación global y transacción propia; no mezclarlo silenciosamente con el conteo.
VariantesIntroducir variantDocumentId como nueva identidad de línea y migrar el contrato de forma versionada.
Correcciones parcialesPreferir generar un nuevo archivo sólo con errores corregidos. No debilitar la atomicidad del conteo original.
Auditoría avanzadaAgregar exportación del comprobante, hash verificable y referencia al dispositivo o sesión.

La regla central para cualquier evolución es separar preparación de aplicación: la preparación puede ser asíncrona, filtrada o más flexible; la aplicación debe seguir validando identidad, tenant, snapshot e idempotencia dentro de una transacción.

Resumen operativo

  • Descarga una plantilla nueva para la bodega correcta.
  • Completa sólo counted_on_hand con cantidades absolutas enteras.
  • Deja vacío lo no contado y usa 0 cuando verificaste ausencia física.
  • Corrige todos los errores antes de confirmar.
  • Si aparece un conflicto, no fuerces el archivo: descarga una plantilla nueva.
  • Comprueba el resultado desde Historial de conteos.

¿Te fue útil este contenido?

Tu feedback nos ayuda a mejorar la documentación de Neges.

Soporte humano

¿No encuentras lo que buscas?

Conversa con nuestro equipo de soporte en español. Respondemos por WhatsApp, correo o ticket en menos de 24 horas hábiles.

contacto@neges.clLun a Vie · 9 a 18 hrs
+56 9 0000 0000WhatsApp Business