FactuGES_ERP/docs/document-series/document-series-design.md
David 2122ed562b feat: implementar la funcionalidad para avanzar el número de serie documental
* 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.
2026-09-18 18:38:49 +02:00

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 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