* Añadido el método `lockByCompanyAndDocumentType` a `SequelizeDocumentSeriesRepository` para bloquear las series documentales durante las actualizaciones. * Introducido `AdvanceDocumentSeriesNumberUseCase` para gestionar el avance del número de la serie documental con control de transacciones. * Creado `AdvanceDocumentSeriesNumberController` para exponer la funcionalidad de avance mediante HTTP. * Actualizado el DTO de respuesta de series documentales para incluir el campo `has_assigned_numbers`. * Añadido un esquema de validación para avanzar el número de la serie documental. * Implementadas pruebas para la creación, asignación y avance de series documentales, garantizando el comportamiento correcto y las transiciones de estado esperadas. * Introducidos nuevos errores para gestionar condiciones específicas de las series documentales. * Actualizado el servicio de administración de series documentales existente para dar soporte a la nueva funcionalidad.
5.5 KiB
Document Series Design
Objetivo del módulo
document-series es un módulo propio porque la serie documental no es un catálogo estático: mantiene reglas de vigencia, flags de activación, defaults y estado transaccional de numeración.
Por qué no vive en catalogs
Los catálogos del ERP resuelven valores de referencia. Aquí hay concurrencia, bloqueo de fila y mutación transaccional de next_number, así que el comportamiento es operativo y no meramente descriptivo.
Por qué numeración y serie viven juntas
La numeración depende del estado de la serie:
- si está activa
- si está vigente
- si es la serie por defecto
- cuál es su prefijo, sufijo y padding
Separarlo introduciría doble escritura o lecturas no atómicas.
Modelo conceptual
DocumentType: tipo documental cerrado en V1DocumentSeries: agregado con configuración y contadorAssignedDocumentNumber: resultado de asignación atómica
Campos principales
companyId,branchIddocumentTypecode,name,descriptionprefix,suffixnextNumber,paddingvalidFrom,validToisDefault,isActive
Reglas V1
- tipos soportados:
proforma,issued_invoice - no asigna si la serie está inactiva
- no asigna si la serie está fuera de vigencia
- default por empresa + tipo, con prioridad de
branch_idcuando aplica - formato de referencia:
prefix + padded(next_number) + suffix code,documentTypeybranchIdquedan fijos tras crear la serie; branch continúa sin administración funcional.- La configuración ordinaria editable es
name,description,prefix,suffix,paddingy la vigencia. Los cambios de formato solo afectan futuras asignaciones. nextNumberno se modifica mediante update: solo puede avanzarse estrictamente mediante una operación administrativa auditada.- Una serie registra
hasAssignedNumbersal confirmar su primera asignación; la marca no se revierte salvo rollback de esa misma transacción. - El default es opcional, pero como máximo existe uno por empresa y tipo documental. Una serie inactiva no puede ser default; al desactivar un default se desmarca sin elegir sustituto.
Concurrencia
La asignación usa transacción obligatoria y bloqueo de fila con LOCK.UPDATE sobre document_series. El incremento de next_number se persiste dentro de la misma transacción y con comprobación optimista del valor previo para evitar dobles consumos.
Integracion con customer-invoices
En V1:
issued-invoicesdelega la asignacion de numero endocument-seriesdocument-series.assignNextNumber(...)devuelveseries_id,series_code,numberyreferenceproformasdelega la asignacion de referencia endocument-seriesproformaspersistedocument_series_idyproforma_numbercomo snapshot tecnico adicional aproforma_referenceissued-invoicespersisteinvoice_referencecomo snapshot documental inmutable adicional ainvoice_numberissued-invoices.invoice_referenceno se recalcula si luego cambiaprefix,suffixopaddingde la serieproformas.document_series_ididentifica la serie documental usada para numerar la proformaproformas.proforma_numberguarda el numero propio de la proformaproformas.proforma_referenceguarda la referencia visible de la proformaproforma_series_codees el input funcional de create para elegir la serie dedocument_type = proformaproformas.target_invoice_series_codees opcional y, cuando existe, apunta a una serie deissued_invoice, nunca a una serie deproforma- create de proforma solo usa
document-seriespara numerar la cabecera inicial - create de proforma puede persistir
tax_configsin materializar impuestos - una proforma puede crearse sin lineas y seguir siendo valida como borrador inicial
- si no hay lineas valoradas,
proforma_taxesdebe quedar vacia y los totales monetarios a0 - si
proformas.target_invoice_series_codeesNULL, la emision resuelve la serie default activa deissued_invoice - en Fase 2E,
seriesdeja de ser el contrato nuevo para create/update de proformas - la UI consulta
GET /document-seriescon filtro pordocument_typeyis_active = true - la UI no consume
POST /document-series/assign-nextpara previsualizar numeracion /catalogs/invoice-seriesdeja de existir como endpoint runtimecustomer_invoice_seriesno participa ya en flujos activos de lectura o numeracion
Migracion conservadora
- la migracion historica de
issued_invoiceparte decustomer_invoice_series - la creacion de series default de
proformausacompaniescomo fuente - las validaciones SQL se mantienen separadas del script de migracion
- no hay ejecucion automatica ni cutover productivo en esta fase
- la DDL de
proformasusainformation_schema+ SQL dinamico para mantener compatibilidad MariaDB/MySQL sin depender deIF NOT EXISTSenALTER TABLE
Branch scope pendiente
El modelo ya contempla branchId, y la resolucion default da prioridad a branch_id cuando existe. Aun asi, customer-invoices no propaga un contexto real de sucursal hasta assignNextNumber(...), asi que en esta fase toda la integracion sigue operando con alcance de empresa.
Límites V1
- sin
format_pattern - sin reseteos anuales automáticos
- sin reservas de numeración
- sin huecos justificados
- sin rename de código, cambio de tipo documental ni administración de branch
- sin migración productiva automática de legacy
Extensiones futuras
- nuevos
DocumentType - seeds/migraciones controladas de series heredadas