- Implemented `evaluateProformaCustomerChange` function to assess changes in customer details for proforma invoices. - Introduced types for customer change details, blockers, field differences, and fiscal targets. - Added utility functions for normalizing text and formatting recipient information. test: add unit tests for customer payment resolver - Created tests for `CustomerPaymentResolver` to validate payment term retrieval and handling of inactive terms. - Ensured proper behavior when clearing payment terms and handling non-existent terms. test: add unit tests for customer default payment term resolver - Developed tests for `CustomerDefaultPaymentTermResolver` to verify default payment term resolution logic. - Included scenarios for creating customers with and without explicit payment term IDs. feat: implement customer default payment term resolver - Created `CustomerDefaultPaymentTermResolver` to manage default payment term resolution during customer creation. - Integrated with company finder and payment resolver services. test: add unit tests for create customer use case - Implemented tests for `CreateCustomerUseCase` to ensure correct handling of payment term IDs during customer creation. - Validated behavior when no explicit payment term ID is provided. test: add unit tests for update customer use case - Developed tests for `UpdateCustomerUseCase` to verify payment term handling during customer updates. - Ensured that inactive payment terms are not assigned and that explicit null values are respected. feat: add dependency injection for customer company services - Implemented DI for customer company dependencies to facilitate service resolution. test: add tests for customer create data utility - Created tests for `buildCustomerCreateData` to validate payment term handling based on user input.
26 KiB
26 KiB
Split Proformas / Issued Invoices
Estado: historico / transicional.
No describe el runtime minimo vigente por si solo; la fuente operativa actual para proformas es la combinación de:
docs/document-series/README.mddocs/customer-invoices/proforma-series-contract.mddocs/customer-invoices/proforma-create-contract.mddocs/customer-invoices/proforma-tax-config.mddocs/catalogs/payment-terms.md
Estado: diseno tecnico / fases 1A-1D + 2A-2B
Estado del backend: persistencia V2 activada; integracion con document-series activa para numeracion nueva; sin ejecucion confirmada de migracion DB
Objetivo
Preparar el esquema fisico para separar:
customer_invoices->proformascustomer_invoices->issued_invoicescustomer_invoice_items->proforma_itemscustomer_invoice_items->issued_invoice_itemscustomer_invoice_taxes->proforma_taxescustomer_invoice_taxes->issued_invoice_taxesverifactu_records.invoice_id->verifactu_records.issued_invoice_id
Decisiones de fase 1A
proforma_referencees el identificador visible canonico de proforma.target_invoice_series_codesustituye la semantica legacy deseriesen proformas.document_series_ididentifica la serie documental usada para numerar la proforma.proforma_numberguarda el numero propio asignado a la proforma.target_invoice_series_codees opcional y solo puede apuntar a una serie deissued_invoice.- una serie
PFo cualquier otra serie dedocument_type = proformanunca debe persistirse entarget_invoice_series_code. - si
target_invoice_series_codequedaNULL, la emision de la factura usara la serie default activa deissued_invoice. issued_invoicesusainvoice_series_code,invoice_number,invoice_reference,invoice_dateysource_proforma_id.proformasno guardaissued_invoice_id; la relacion se resuelve conissued_invoices.source_proforma_id.- Los impuestos del esquema actual son por documento, no por linea:
proforma_taxesyissued_invoice_taxes. payment_methodypayment_termenissued_invoicesson snapshot historico, no estado vivo de cobro.- No hay doble escritura.
- Legacy queda congelado solo para validacion y rollback cuando se haga el cutover.
Estado de fase 1B
- Existen mappers Sequelize V2 sobre
proformas,proforma_items,proforma_taxes. - Existen mappers Sequelize V2 sobre
issued_invoices,issued_invoice_items,issued_invoice_taxes. - Existen repositorios Sequelize V2 listos para convivir con los repositorios legacy.
- El wiring activo legacy no se ha sustituido todavia.
- El dominio/application sigue arrastrando naming legacy en algunos contratos (
series,invoiceNumber,linkedProformaId); la traduccion queda encapsulada en Infrastructure V2. - Esta afirmación quedó obsoleta. El runtime actual ya soporta
payment_termen Company, Customer, Proforma e Issued Invoice segúndocs/catalogs/payment-terms.md.
Estado de fase 1C
- El wiring activo del modulo usa persistencia V2 en los composition roots principales.
buildV2ProformaPersistence(...)queda activo en:modules/customer-invoices/src/api/infrastructure/proformas/di/proformas.di.tsmodules/customer-invoices/src/api/infrastructure/proformas/di/proforma-public-services.ts
buildV2IssuedInvoicePersistence(...)queda activo en:modules/customer-invoices/src/api/infrastructure/issued-invoices/di/issued-invoices.di.tsmodules/customer-invoices/src/api/infrastructure/issued-invoices/di/issued-invoice-public-services.ts
- La persistencia legacy no se borra, pero deja de ser la ruta operativa principal.
- La emision
Proforma -> IssuedInvoiceahora encadena sobre V2:- lee
proformas - crea
issued_invoices - crea
issued_invoice_items - crea
issued_invoice_taxes - marca la proforma como
issued - usa
issued_invoices.source_proforma_id
- lee
- No hay doble escritura: cada composition root sigue instanciando una sola familia de repositorios.
VerifactuRecordqueda preparado para la ruta V2 medianteissued_invoice_idcomo relacion principal.- La migracion/backfill real sigue pendiente. Si la BD real no tiene las tablas nuevas, el backend no podra arrancar correctamente con este wiring.
Estado de fase 1D
- Existen scripts SQL separados para desarrollo:
docs/customer-invoices/sql/dev-split-proformas-issued-invoices.sqldocs/customer-invoices/sql/validate-split-proformas-issued-invoices.sql
- El SQL de desarrollo es idempotente y conservador:
- crea tablas V2 con
CREATE TABLE IF NOT EXISTS - hace backfill con
NOT EXISTS - no usa
DROP,TRUNCATEniDELETE - no sobrescribe filas ya migradas
- no toca datos legacy salvo la columna transicional
verifactu_records.issued_invoice_id
- crea tablas V2 con
- Se documenta el orden de ejecucion en
docs/customer-invoices/sql/README.md. - En este workspace no se encontro una configuracion de BD de desarrollo inequivoca (
.env*no presente en raiz del repo), por lo que no se ejecuto SQL real. - Tampoco se hizo validacion funcional contra runtime real porque falta una BD de desarrollo confirmada y migrada para esta fase.
Dependencias compartidas y exclusivas
Compartidas entre legacy y V2
- contratos de Application (
IProformaRepository,IIssuedInvoiceRepository) - use cases, servicios de dominio y assemblers
TransactionManager- adaptadores legacy de
InvoiceSeriesNumberAssignerparaIssuedInvoice - resolvers de catalogos
- servicios de documentos PDF/preview
Exclusivas de legacy
CustomerInvoiceModelcomo storage real de Proforma e IssuedInvoice- numerador de proformas sobre
customer_invoices.invoice_numberconis_proforma = true - repositorios Sequelize legacy de
customer_invoices CustomerInvoiceSeriesModelySequelizeInvoiceSeriesRepositorycomo wiring transicional no prioritario
Exclusivas de V2
- tablas
proformas,proforma_items,proforma_taxes - tablas
issued_invoices,issued_invoice_items,issued_invoice_taxes - numerador V2 de proformas sobre
document-series, con snapshot enproformas.document_series_id,proforma_numberyproforma_reference - mappers y repositorios Sequelize V2
Regla operativa actual de series en proformas
La semantica operativa vigente ya no se mantiene en este documento historico.
Fuente canonica actual:
docs/customer-invoices/proforma-series-contract.mddocs/customer-invoices/proforma-create-contract.mddocs/customer-invoices/proforma-tax-config.md
Este documento conserva solo el contexto de migracion, fases y SQL historico.
Estado Fase 2B
customer-invoicesya no usacustomer_invoice_seriescomo fuente primaria para listar series activas ni para asignar numeracion nueva enissued_invoiceyproforma/catalogs/invoice-seriesfue transicional y queda eliminado antes de producciondocument-seriesqueda como API canonica para listar series activas y asignar numeracion- la DDL de
proformasy la migracion/validacion SQL quedan documentadas, no ejecutadas - la propagacion de
branchIdsigue pendiente porque el contexto de sucursal no llega todavia a los casos de uso activos
Estado Fase 2C
- se detecto un entorno de desarrollo en
apps/server/.env.development - produccion quedo descartada para ejecucion en esta fase
- no se ejecuto SQL real desde esta terminal por bloqueo operativo del cliente de BD local
- la DDL de proformas se endurecio para compatibilidad MariaDB/MySQL mediante
information_schemay SQL dinamico - la validacion funcional y la prueba de concurrencia quedaron documentadas para ejecucion manual sobre la BD de desarrollo segura
Estado Fase 2D
- el contrato publico de proformas ya soporta
target_invoice_series_codeen create y update - la semantica detallada del alias legacy
seriesse consolido despues enproforma-series-contract.md - el cliente interno del modulo ya emite
target_invoice_series_codeen requests nuevos
Estado Fase 2E
- create separa
proforma_series_codedetarget_invoice_series_code - las reglas operativas exactas se consolidaron despues en
proforma-series-contract.md - la UI nueva de proformas ya envia
proforma_series_codeytarget_invoice_series_code
Estado Fase 2F
- create de proforma se consolida como creacion de cabecera/documento inicial
- la semantica funcional exacta de create se consolido despues en
proforma-create-contract.md
Estado Fase 2G
DELETE /proformas/:proforma_idvuelve a estar operativo sobre persistencia V2- el borrado es logico mediante
deleted_at - el archivado es logico mediante
archived_at - el listado V2 de proformas usa
Criteria filters[]paraarchived_atystatus GET /proformas/:idpermite abrir proformas archivadas- las reglas funcionales exactas de borrado y numeracion se consolidaron despues en
proforma-series-contract.md - la validacion de borrado comprueba tambien
linked_invoice_idyissued_invoices.source_proforma_id - la numeracion de
document-seriesno se reutiliza ni se decrementa tras un borrado
Rollback recomendado
- no aplicar nada en produccion en esta fase
- si en desarrollo se ejecuta la DDL/migracion y hay que revertir, hacerlo con backup previo o clon de desarrollo, nunca con borrado destructivo improvisado
- conservar
customer_invoice_seriessolo como legado historico hasta ejecutar la limpieza operativa documentada
Tablas nuevas previstas
proformas
idcompany_idstatusproforma_referenceproforma_dateoperation_datetarget_invoice_series_codereferencedescriptionnoteslanguage_codecurrency_codecustomer_id- snapshot cliente
payment_method_idpayment_term_idtax_regime_code- totales monetarios actuales
created_atupdated_atdeleted_at
issued_invoices
idcompany_idstatussource_proforma_idinvoice_series_codeinvoice_numberinvoice_referenceinvoice_dateoperation_datereferencedescriptionnoteslanguage_codecurrency_codecustomer_id- snapshot cliente
payment_method_idpayment_method_namepayment_method_descriptionpayment_term_idpayment_term_namepayment_term_descriptionpayment_term_snapshot_jsontax_regime_codetax_regime_description- totales monetarios actuales
created_atupdated_atdeleted_at
Items y taxes
proforma_items.proforma_idissued_invoice_items.issued_invoice_idproforma_taxes.proforma_idissued_invoice_taxes.issued_invoice_id
Verifactu
verifactu_records.issued_invoice_idnuevoverifactu_records.invoice_idlegacy temporal durante la transicion de fase 1A
SQL orientativo de backfill
Proformas
INSERT INTO proformas (
id,
company_id,
status,
proforma_reference,
proforma_date,
operation_date,
target_invoice_series_code,
reference,
description,
notes,
language_code,
currency_code,
customer_id,
payment_method_id,
payment_term_id,
tax_regime_code,
subtotal_amount_value,
subtotal_amount_scale,
items_discount_amount_value,
items_discount_amount_scale,
global_discount_percentage_value,
global_discount_percentage_scale,
global_discount_amount_value,
global_discount_amount_scale,
total_discount_amount_value,
total_discount_amount_scale,
taxable_amount_value,
taxable_amount_scale,
iva_amount_value,
iva_amount_scale,
rec_amount_value,
rec_amount_scale,
retention_amount_value,
retention_amount_scale,
taxes_amount_value,
taxes_amount_scale,
total_amount_value,
total_amount_scale,
created_at,
updated_at,
deleted_at
)
SELECT
id,
company_id,
status,
CASE
WHEN series IS NULL OR series = '' THEN invoice_number
ELSE CONCAT(series, '-', invoice_number)
END AS proforma_reference,
invoice_date,
operation_date,
series,
reference,
description,
notes,
language_code,
currency_code,
customer_id,
payment_method_id,
NULL AS payment_term_id,
tax_regime_code,
subtotal_amount_value,
subtotal_amount_scale,
items_discount_amount_value,
items_discount_amount_scale,
global_discount_percentage_value,
global_discount_percentage_scale,
global_discount_amount_value,
global_discount_amount_scale,
total_discount_amount_value,
total_discount_amount_scale,
taxable_amount_value,
taxable_amount_scale,
iva_amount_value,
iva_amount_scale,
rec_amount_value,
rec_amount_scale,
retention_amount_value,
retention_amount_scale,
taxes_amount_value,
taxes_amount_scale,
total_amount_value,
total_amount_scale,
created_at,
updated_at,
deleted_at
FROM customer_invoices
WHERE is_proforma = 1;
Issued invoices
INSERT INTO issued_invoices (
id,
company_id,
status,
source_proforma_id,
invoice_series_code,
invoice_number,
invoice_date,
operation_date,
reference,
description,
notes,
language_code,
currency_code,
customer_id,
customer_tin,
customer_name,
customer_street,
customer_street2,
customer_city,
customer_province,
customer_postal_code,
customer_country,
payment_method_id,
payment_method_name,
payment_method_description,
payment_term_id,
payment_term_name,
payment_term_description,
payment_term_snapshot_json,
tax_regime_code,
tax_regime_description,
subtotal_amount_value,
subtotal_amount_scale,
items_discount_amount_value,
items_discount_amount_scale,
global_discount_percentage_value,
global_discount_percentage_scale,
global_discount_amount_value,
global_discount_amount_scale,
total_discount_amount_value,
total_discount_amount_scale,
taxable_amount_value,
taxable_amount_scale,
iva_amount_value,
iva_amount_scale,
rec_amount_value,
rec_amount_scale,
retention_amount_value,
retention_amount_scale,
taxes_amount_value,
taxes_amount_scale,
total_amount_value,
total_amount_scale,
created_at,
updated_at,
deleted_at
)
SELECT
id,
company_id,
status,
proforma_id,
series,
invoice_number,
invoice_date,
operation_date,
reference,
description,
notes,
language_code,
currency_code,
customer_id,
customer_tin,
customer_name,
customer_street,
customer_street2,
customer_city,
customer_province,
customer_postal_code,
customer_country,
payment_method_id,
payment_method_description,
payment_method_description,
NULL AS payment_term_id,
NULL AS payment_term_name,
NULL AS payment_term_description,
NULL AS payment_term_snapshot_json,
COALESCE(tax_regime_code, "01"),
COALESCE(tax_regime_description, "01: Operación de régimen general."),
subtotal_amount_value,
subtotal_amount_scale,
items_discount_amount_value,
items_discount_amount_scale,
global_discount_percentage_value,
global_discount_percentage_scale,
global_discount_amount_value,
global_discount_amount_scale,
total_discount_amount_value,
total_discount_amount_scale,
taxable_amount_value,
taxable_amount_scale,
iva_amount_value,
iva_amount_scale,
rec_amount_value,
rec_amount_scale,
retention_amount_value,
retention_amount_scale,
taxes_amount_value,
taxes_amount_scale,
total_amount_value,
total_amount_scale,
created_at,
updated_at,
deleted_at
FROM customer_invoices
WHERE is_proforma = 0;
Items
INSERT INTO proforma_items (
item_id,
proforma_id,
position,
description,
quantity_value,
quantity_scale,
unit_amount_value,
unit_amount_scale,
subtotal_amount_value,
subtotal_amount_scale,
item_discount_percentage_value,
item_discount_percentage_scale,
item_discount_amount_value,
item_discount_amount_scale,
global_discount_percentage_value,
global_discount_percentage_scale,
global_discount_amount_value,
global_discount_amount_scale,
total_discount_amount_value,
total_discount_amount_scale,
taxable_amount_value,
taxable_amount_scale,
iva_code,
iva_percentage_value,
iva_percentage_scale,
iva_amount_value,
iva_amount_scale,
rec_code,
rec_percentage_value,
rec_percentage_scale,
rec_amount_value,
rec_amount_scale,
retention_code,
retention_percentage_value,
retention_percentage_scale,
retention_amount_value,
retention_amount_scale,
taxes_amount_value,
taxes_amount_scale,
total_amount_value,
total_amount_scale,
created_at,
updated_at
)
SELECT
i.item_id,
i.invoice_id,
i.position,
i.description,
i.quantity_value,
i.quantity_scale,
i.unit_amount_value,
i.unit_amount_scale,
i.subtotal_amount_value,
i.subtotal_amount_scale,
i.item_discount_percentage_value,
i.item_discount_percentage_scale,
i.item_discount_amount_value,
i.item_discount_amount_scale,
i.global_discount_percentage_value,
i.global_discount_percentage_scale,
i.global_discount_amount_value,
i.global_discount_amount_scale,
i.total_discount_amount_value,
i.total_discount_amount_scale,
i.taxable_amount_value,
i.taxable_amount_scale,
i.iva_code,
i.iva_percentage_value,
i.iva_percentage_scale,
i.iva_amount_value,
i.iva_amount_scale,
i.rec_code,
i.rec_percentage_value,
i.rec_percentage_scale,
i.rec_amount_value,
i.rec_amount_scale,
i.retention_code,
i.retention_percentage_value,
i.retention_percentage_scale,
i.retention_amount_value,
i.retention_amount_scale,
i.taxes_amount_value,
i.taxes_amount_scale,
i.total_amount_value,
i.total_amount_scale,
now(),
now()
FROM customer_invoice_items i
JOIN customer_invoices ci ON ci.id = i.invoice_id
WHERE ci.is_proforma = 1;
INSERT INTO issued_invoice_items (
item_id,
issued_invoice_id,
position,
description,
quantity_value,
quantity_scale,
unit_amount_value,
unit_amount_scale,
subtotal_amount_value,
subtotal_amount_scale,
item_discount_percentage_value,
item_discount_percentage_scale,
item_discount_amount_value,
item_discount_amount_scale,
global_discount_percentage_value,
global_discount_percentage_scale,
global_discount_amount_value,
global_discount_amount_scale,
total_discount_amount_value,
total_discount_amount_scale,
taxable_amount_value,
taxable_amount_scale,
iva_code,
iva_percentage_value,
iva_percentage_scale,
iva_amount_value,
iva_amount_scale,
rec_code,
rec_percentage_value,
rec_percentage_scale,
rec_amount_value,
rec_amount_scale,
retention_code,
retention_percentage_value,
retention_percentage_scale,
retention_amount_value,
retention_amount_scale,
taxes_amount_value,
taxes_amount_scale,
total_amount_value,
total_amount_scale,
created_at,
updated_at
)
SELECT
i.item_id,
i.invoice_id,
i.position,
i.description,
i.quantity_value,
i.quantity_scale,
i.unit_amount_value,
i.unit_amount_scale,
i.subtotal_amount_value,
i.subtotal_amount_scale,
i.item_discount_percentage_value,
i.item_discount_percentage_scale,
i.item_discount_amount_value,
i.item_discount_amount_scale,
i.global_discount_percentage_value,
i.global_discount_percentage_scale,
i.global_discount_amount_value,
i.global_discount_amount_scale,
i.total_discount_amount_value,
i.total_discount_amount_scale,
i.taxable_amount_value,
i.taxable_amount_scale,
i.iva_code,
i.iva_percentage_value,
i.iva_percentage_scale,
i.iva_amount_value,
i.iva_amount_scale,
i.rec_code,
i.rec_percentage_value,
i.rec_percentage_scale,
i.rec_amount_value,
i.rec_amount_scale,
i.retention_code,
i.retention_percentage_value,
i.retention_percentage_scale,
i.retention_amount_value,
i.retention_amount_scale,
i.taxes_amount_value,
i.taxes_amount_scale,
i.total_amount_value,
i.total_amount_scale,
now(),
now()
FROM customer_invoice_items i
JOIN customer_invoices ci ON ci.id = i.invoice_id
WHERE ci.is_proforma = 0;
Taxes
INSERT INTO proforma_taxes (
tax_id,
proforma_id,
taxable_amount_value,
taxable_amount_scale,
iva_code,
iva_percentage_value,
iva_percentage_scale,
iva_amount_value,
iva_amount_scale,
rec_code,
rec_percentage_value,
rec_percentage_scale,
rec_amount_value,
rec_amount_scale,
retention_code,
retention_percentage_value,
retention_percentage_scale,
retention_amount_value,
retention_amount_scale,
taxes_amount_value,
taxes_amount_scale,
created_at,
updated_at
)
SELECT
t.tax_id,
t.invoice_id,
t.taxable_amount_value,
t.taxable_amount_scale,
t.iva_code,
t.iva_percentage_value,
t.iva_percentage_scale,
t.iva_amount_value,
t.iva_amount_scale,
t.rec_code,
t.rec_percentage_value,
t.rec_percentage_scale,
t.rec_amount_value,
t.rec_amount_scale,
t.retention_code,
t.retention_percentage_value,
t.retention_percentage_scale,
t.retention_amount_value,
t.retention_amount_scale,
t.taxes_amount_value,
t.taxes_amount_scale,
now(),
now()
FROM customer_invoice_taxes t
JOIN customer_invoices ci ON ci.id = t.invoice_id
WHERE ci.is_proforma = 1;
INSERT INTO issued_invoice_taxes (tax_id,
issued_invoice_id,
taxable_amount_value,
taxable_amount_scale,
iva_code,
iva_percentage_value,
iva_percentage_scale,
iva_amount_value,
iva_amount_scale,
rec_code,
rec_percentage_value,
rec_percentage_scale,
rec_amount_value,
rec_amount_scale,
retention_code,
retention_percentage_value,
retention_percentage_scale,
retention_amount_value,
retention_amount_scale,
taxes_amount_value,
taxes_amount_scale,
created_at,
updated_at)
SELECT
t.tax_id,
t.invoice_id,
t.taxable_amount_value,
t.taxable_amount_scale,
t.iva_code,
t.iva_percentage_value,
t.iva_percentage_scale,
t.iva_amount_value,
t.iva_amount_scale,
t.rec_code,
t.rec_percentage_value,
t.rec_percentage_scale,
t.rec_amount_value,
t.rec_amount_scale,
t.retention_code,
t.retention_percentage_value,
t.retention_percentage_scale,
t.retention_amount_value,
t.retention_amount_scale,
t.taxes_amount_value,
t.taxes_amount_scale,
now(),
now()
FROM customer_invoice_taxes t
JOIN customer_invoices ci ON ci.id = t.invoice_id
WHERE ci.is_proforma = 0;
Verifactu
UPDATE verifactu_records vr
JOIN customer_invoices ci ON ci.id = vr.invoice_id
SET vr.issued_invoice_id = vr.invoice_id
WHERE ci.is_proforma = 0;
Validaciones SQL
Conteos
SELECT COUNT(*) FROM customer_invoices WHERE is_proforma = 1;
SELECT COUNT(*) FROM proformas;
SELECT COUNT(*) FROM customer_invoices WHERE is_proforma = 0;
SELECT COUNT(*) FROM issued_invoices;
SELECT COUNT(*)
FROM customer_invoice_items i
JOIN customer_invoices ci ON ci.id = i.invoice_id
WHERE ci.is_proforma = 1;
SELECT COUNT(*) FROM proforma_items;
SELECT COUNT(*)
FROM customer_invoice_taxes t
JOIN customer_invoices ci ON ci.id = t.invoice_id
WHERE ci.is_proforma = 0;
SELECT COUNT(*) FROM issued_invoice_taxes;
Duplicados fiscales
SELECT company_id, invoice_series_code, invoice_number, COUNT(*)
FROM issued_invoices
GROUP BY company_id, invoice_series_code, invoice_number
HAVING COUNT(*) > 1;
Huerfanos
SELECT COUNT(*)
FROM issued_invoices ii
LEFT JOIN proformas p ON p.id = ii.source_proforma_id
WHERE ii.source_proforma_id IS NOT NULL
AND p.id IS NULL;
SELECT COUNT(*)
FROM verifactu_records vr
LEFT JOIN issued_invoices ii ON ii.id = vr.issued_invoice_id
WHERE vr.issued_invoice_id IS NOT NULL
AND ii.id IS NULL;
Totales
SELECT SUM(total_amount_value) FROM customer_invoices WHERE is_proforma = 1;
SELECT SUM(total_amount_value) FROM proformas;
SELECT SUM(total_amount_value) FROM customer_invoices WHERE is_proforma = 0;
SELECT SUM(total_amount_value) FROM issued_invoices;
Proformas sin lineas con impuestos materializados
SELECT
p.id,
p.proforma_reference,
COUNT(DISTINCT pi.item_id) AS item_rows,
COUNT(DISTINCT pt.tax_id) AS tax_rows
FROM proformas p
LEFT JOIN proforma_items pi
ON pi.proforma_id = p.id
LEFT JOIN proforma_taxes pt
ON pt.proforma_id = p.id
GROUP BY p.id, p.proforma_reference
HAVING COUNT(DISTINCT pi.item_id) = 0
AND COUNT(DISTINCT pt.tax_id) > 0;
Resultado esperado:
0filas
No incluido en fase 1A
- no cutover del backend
- no borrado de tablas legacy
- no doble escritura
- no ejecucion de migraciones en una BD real
- no integracion Verifactu externa
- no dominio de cobros, vencimientos vivos o conciliacion
No incluido en fase 1C
- no borrado de legacy
- no backfill real
- no ejecucion de migraciones
- no doble escritura
- no integracion Verifactu funcional nueva
- no cambios en controllers, rutas o DTOs publicos
No incluido en fase 1D
- no ejecucion en produccion
- no borrado de tablas legacy
- no eliminacion de modelos/repositorios legacy
- no integracion Verifacti funcional
- no dominio de cobros
- no doble escritura
Resultado de validacion tecnica fase 1D
tscmodulocustomer-invoices: esperado ejecutar en esta fasebiome lintsobresrc/api/infrastructureydocs/customer-invoices: esperado ejecutar en esta fasetscraiz: esperado ejecutar en esta fase- Validacion SQL real: no ejecutada por falta de entorno de desarrollo inequivoco
- Validacion runtime real contra BD migrada: no ejecutada por la misma razon
Siguiente fase recomendada
- confirmar
.env/credenciales de una BD de desarrollo segura - ejecutar solo los runbooks SQL que sigan siendo necesarios para el entorno real afectado
- levantar el backend contra esa BD y validar
create,update,get,listeissue proforma - validar numeracion de proformas V2 y asignacion de serie/numero de factura emitida
- revisar la ruta
VerifactuRecord -> issued_invoice_idya con persistencia V2 activa
Nota document-series
customer-invoices ya migró funcionalmente desde CustomerInvoiceSeriesModel hacia document-series. Como nota histórica de Fase 2A:
issued_invoiceya asigna numeración nueva mediantedocument-seriesproformaya asigna referencia nueva mediantedocument-series- la UI debe consumir
GET /document-seriesfiltrando pordocument_type CustomerInvoiceSeriesModeldeja de existir como modelo runtime activo
Fase 2B no incluida
- no ejecucion de SQL en una BD real
- no borrado de tablas/modelos/repositorios legacy
- no propagacion completa de
branchId - no cambios de frontend salvo compatibilidad ya existente
- no nueva infraestructura de tests automatizados para estos modulos