Skip to content

Plataforma técnica · Orchestrator

Inventario

Orchestrator Inventarios Existencias

El dominio Inventario convierte compras del SII en existencias contabilizadas, mantiene el stock por movimiento, calcula el costo de ventas mensual con prorrateo por categoría y registra provisiones de cierre. Es uno de los dominios más grandes del Orchestrator: 8 servicios y ~5200 LOC entre service + repository.

InventarioService es una fachada delgada: re-exporta métodos de 7 services especializados para que las rutas HTTP existentes sigan funcionando sin cambios. Cada subservicio mantiene su propia responsabilidad y se documenta por separado.

ServicioResponsabilidadDoc
InventarioServiceFachada que re-exporta operaciones de los 7 servicios especializados.
ExistenciasServiceCRUD de productos/existencias. Autogeneración de código por categoría+tipo.
StockServiceStock actual, movimientos (entrada/salida), ajuste de saldo (emite hook), procesar/reversar saldo mensual por categoría.
ComprasInventarioServicePipeline compra SII → existencia: clasificación, procesamiento por producto, ingreso por categoría, devolución de NC, contabilización. Emite hook.
CostoVentasServiceCálculo y registro de costo de ventas mensual con factor de prorrateo por categoría. Emite hook por cada detalle.
CategoriasExistenciaServiceCRUD de categorías. Restringe cuenta_existencia_codigo al grupo 1109 (Existencias del manual).
ProveedoresClasificacionServiceClasificación proveedor → genera_existencias. Lista pendientes desde compras no clasificadas.
ProvisionesInventarioServiceProvisiones de cierre por compras no procesadas + ajustes de cierre por movimientos sin asiento.

Las páginas dedicadas cubren los 4 servicios principales. Los 4 menores (CostoVentas, Categorias, Proveedores, Provisiones) se describen abajo y en sus secciones específicas de la fachada.

EventoTriggerPayload
compra_inv:contabilizadaComprasInventarioService.procesarCompraExistencia y procesarCompraIngresoCategoria (1 evento por movimiento){ movimientoCostoId, compraDetalleId, categoriaId, monto }
costo:egreso_calculadoCostoVentasService.calcularCostoVentas (1 evento por cada detalle de categoría){ movimientoCostoId, categoriaId, periodo: { anio, mes }, monto, factorProrrateo }
existencia:castigadaStockService.ajustarSaldoInventario cuando el ajuste es de tipo SALIDA{ movimientoCostoId, categoriaId, monto, motivo }

Catálogo completo en Domain Events › Inventario / Costos.

flowchart LR
  subgraph SRC["Fuentes externas"]
    SII[("operaciones_sii.compras<br/>+ compras_detalle")]
    PROV[("administracion.proveedores")]
    PLN[("administracion.plan_contable<br/>(grupo 1109)")]
  end

  subgraph SVC["domain/inventario/"]
    PRV["ProveedoresClasificacionService"]
    CAT["CategoriasExistenciaService"]
    EXI["ExistenciasService"]
    STK["StockService"]
    COM["ComprasInventarioService"]
    COS["CostoVentasService"]
    PRO["ProvisionesInventarioService"]
    INV["InventarioService<br/>(facade)"]
  end

  subgraph OUT["Tablas inventario"]
    EXT[("existencias")]
    CTG[("categorias_existencia")]
    MOV[("movimientos_inventario")]
    SAL[("saldo_stock_mensual")]
    CV[("costo_ventas_cierre")]
    PRC[("provisiones_existencias_cierre")]
    PAJ[("provisiones_ajustes_cierre")]
    PCL[("proveedores_clasificacion")]
  end

  EVT["DomainEventBus<br/>compra_inv:* · costo:* · existencia:*"]

  PROV --> PRV --> PCL
  PLN --> CAT --> CTG
  CAT --> EXI --> EXT
  EXI --> STK --> MOV
  STK --> SAL
  SII --> COM --> MOV
  COM --> COS --> CV
  SII --> PRO --> PRC & PAJ
  COM & COS & STK -. post-COMMIT .-> EVT

  INV -. delega .-> CAT & EXI & STK & COM & COS & PRO & PRV

Servicios menores (no tienen doc dedicada)

Section titled “Servicios menores (no tienen doc dedicada)”

CRUD de categorias_existencia. Cada categoría tiene una cuenta_existencia_codigo (cuenta contable del activo) que debe pertenecer al grupo 1109 (Existencias) — el servicio rechaza códigos fuera de ese rango con ValidationError. Los services downstream (ExistenciasService, ComprasInventarioService) validan que el categoria_id exista al usarlo.

Operaciones: getCategorias(activas?), createCategoria(data), updateCategoria(id, data), deleteCategoria(id).

Clasificación operacional de proveedores. Antes de procesar una compra como existencia, el proveedor debe estar en proveedores_clasificacion con genera_existencias = true. Si no, ComprasInventarioService rechaza el procesamiento.

Operaciones clave:

  • getProveedoresExistencias(giroCodes?) — lista de proveedores ya clasificados como existencias.
  • getProveedoresPendientesDesdeCompras({ año, mes }) — lista de RUTs que aparecen en compras pero aún no están clasificados.
  • create/update/delete ProveedorClasificacion.

Sirve de bandeja de clasificación: el operador ve los pendientes, los clasifica una vez, y a partir de ahí sus compras se vuelven procesables como inventario.

Calcula el costo de ventas mensual usando movimientos de inventario y un factor de prorrateo por categoría. Métodos clave:

  • getCostoVentas(filtros) — query agrupada por proveedor / categoría / cuenta, con resumen y metadata.
  • calcularCostoVentas({ año, mes, categoria_id? }) — calcula y persiste; emite costo:egreso_calculado por cada detalle (categoría) con su factorProrrateo.
  • reversarCostoVentas({ año, mes, categoria_id? }) — borra los registros del período.
  • crearCostoVentasCierre, actualizarEstadoCostoVentasCierre, eliminarCostoVentasCierre — gestión de la fila de cierre (BORRADOR → CONTABILIZADO → APROBADO).

El algoritmo de prorrateo vive en CostoVentasRepository.calcularYregistrarCostoVentas (~390 LOC) y no se documenta acá — es lógica SQL densa que merece su propia página si se requiere profundizar.

Dos conceptos distintos:

  1. provisiones_existencias_cierre — provisión por compras pendientes de procesar al cierre del período. crearProvisionExistenciaCierre(compra_id) valida el proveedor, la compra y crea/upserta el row con cuenta_pasivo_codigo (default 2105003).

  2. provisiones_ajustes_cierre — ajustes de provisión sobre movimientos del libro mensual. contabilizarProvisionAjustesCierre({ año_documento, mes_documento?, año_libro?, mes_libro?, referencia_tipo? }) itera los candidatos y los persiste; reversarProvisionAjustesCierre({ año_libro, mes_libro }) revierte el período.

Estados:

  • Provisión existencia cierre: BORRADOR | CONTABILIZADO | REVERSADO.
  • Provisión ajuste cierre: BORRADOR | CONTABILIZADO.
sequenceDiagram
  autonumber
  participant SII as SII (compras sincronizadas)
  participant Adm as Admin (Sevastopol)
  participant PRV as ProveedoresClasificacion
  participant COM as ComprasInventario
  participant STK as Stock
  participant COS as CostoVentas
  participant PRO as Provisiones

  SII->>Adm: compras pendientes
  Adm->>PRV: clasificar proveedor (genera_existencias)
  Adm->>COM: procesarCompraExistencia o procesarCompraIngresoCategoria
  COM->>STK: ENTRADA en movimientos_inventario
  COM-->>Adm: compra_inv:contabilizada (hook)
  Note over STK,COS: cierre de mes
  Adm->>COS: calcularCostoVentas(año, mes)
  COS->>STK: lee saldos + movimientos del período
  COS-->>Adm: costo:egreso_calculado (× N detalles)
  Adm->>PRO: crear provisiones de cierre para compras no procesadas