* 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.
106 lines
5.5 KiB
Markdown
106 lines
5.5 KiB
Markdown
# 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 V1
|
|
- `DocumentSeries`: agregado con configuración y contador
|
|
- `AssignedDocumentNumber`: resultado de asignación atómica
|
|
|
|
## Campos principales
|
|
|
|
- `companyId`, `branchId`
|
|
- `documentType`
|
|
- `code`, `name`, `description`
|
|
- `prefix`, `suffix`
|
|
- `nextNumber`, `padding`
|
|
- `validFrom`, `validTo`
|
|
- `isDefault`, `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_id` cuando aplica
|
|
- formato de referencia: `prefix + padded(next_number) + suffix`
|
|
- `code`, `documentType` y `branchId` quedan fijos tras crear la serie; branch continúa sin administración funcional.
|
|
- La configuración ordinaria editable es `name`, `description`, `prefix`, `suffix`, `padding` y la vigencia. Los cambios de formato solo afectan futuras asignaciones.
|
|
- `nextNumber` no se modifica mediante update: solo puede avanzarse estrictamente mediante una operación administrativa auditada.
|
|
- Una serie registra `hasAssignedNumbers` al 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-invoices` delega la asignacion de numero en `document-series`
|
|
- `document-series.assignNextNumber(...)` devuelve `series_id`, `series_code`, `number` y `reference`
|
|
- `proformas` delega la asignacion de referencia en `document-series`
|
|
- `proformas` persiste `document_series_id` y `proforma_number` como snapshot tecnico adicional a `proforma_reference`
|
|
- `issued-invoices` persiste `invoice_reference` como snapshot documental inmutable adicional a `invoice_number`
|
|
- `issued-invoices.invoice_reference` no se recalcula si luego cambia `prefix`, `suffix` o `padding` de la serie
|
|
- `proformas.document_series_id` identifica la serie documental usada para numerar la proforma
|
|
- `proformas.proforma_number` guarda el numero propio de la proforma
|
|
- `proformas.proforma_reference` guarda la referencia visible de la proforma
|
|
- `proforma_series_code` es el input funcional de create para elegir la serie de `document_type = proforma`
|
|
- `proformas.target_invoice_series_code` es opcional y, cuando existe, apunta a una serie de `issued_invoice`, nunca a una serie de `proforma`
|
|
- create de proforma solo usa `document-series` para numerar la cabecera inicial
|
|
- create de proforma puede persistir `tax_config` sin materializar impuestos
|
|
- una proforma puede crearse sin lineas y seguir siendo valida como borrador inicial
|
|
- si no hay lineas valoradas, `proforma_taxes` debe quedar vacia y los totales monetarios a `0`
|
|
- si `proformas.target_invoice_series_code` es `NULL`, la emision resuelve la serie default activa de `issued_invoice`
|
|
- en Fase 2E, `series` deja de ser el contrato nuevo para create/update de proformas
|
|
- la UI consulta `GET /document-series` con filtro por `document_type` y `is_active = true`
|
|
- la UI no consume `POST /document-series/assign-next` para previsualizar numeracion
|
|
- `/catalogs/invoice-series` deja de existir como endpoint runtime
|
|
- `customer_invoice_series` no participa ya en flujos activos de lectura o numeracion
|
|
|
|
## Migracion conservadora
|
|
|
|
- la migracion historica de `issued_invoice` parte de `customer_invoice_series`
|
|
- la creacion de series default de `proforma` usa `companies` como fuente
|
|
- las validaciones SQL se mantienen separadas del script de migracion
|
|
- no hay ejecucion automatica ni cutover productivo en esta fase
|
|
- la DDL de `proformas` usa `information_schema` + SQL dinamico para mantener compatibilidad MariaDB/MySQL sin depender de `IF NOT EXISTS` en `ALTER 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
|