From 538c081d58ec010b677fdd9799174764609d82d8 Mon Sep 17 00:00:00 2001 From: david Date: Thu, 20 Aug 2026 10:12:09 +0200 Subject: [PATCH] Repaso de Verifactu --- ...ued-invoice-verifactu-snapshot-columns.sql | 81 +++++++ .../models/proforma-issue-read.model.ts | 7 +- .../issued-invoices/services/index.ts | 2 +- ...-to-issued-invoice-props-converter.test.ts | 15 +- ...forma-to-issued-invoice-props-converter.ts | 32 --- ...lve-proforma-issue-fiscal-context.test.ts} | 30 ++- ... resolve-proforma-issue-fiscal-context.ts} | 31 +-- .../issued-invoice-full-snapshot-builder.ts | 4 - .../issued-invoice-full-snapshot.interface.ts | 5 - ...ed-invoice-report-snapshot-builder.test.ts | 1 - .../proforma-issue-read-model.assembler.ts | 10 +- ...ssue-proforma-response.snapshot-builder.ts | 3 - .../use-cases/issue-proforma.use-case.test.ts | 198 +++++++++++++++++- .../use-cases/issue-proforma.use-case.ts | 19 +- .../aggregates/issued-invoice.aggregate.ts | 16 -- .../issued-invoices/value-objects/index.ts | 1 - ...issued-invoice-verifactu-policy.vo.test.ts | 59 ------ .../issued-invoice-verifactu-policy.vo.ts | 72 ------- .../models/sequelize-issued-invoice.model.ts | 18 -- ...ze-issued-invoice-v2-domain.mapper.test.ts | 36 +--- ...quelize-issued-invoice-v2-domain.mapper.ts | 97 ++------- ...invoice-v2-verifactu-domain.mapper.test.ts | 55 +++++ ...sued-invoice-v2-verifactu-domain.mapper.ts | 15 +- ...equelize-verifactu-record-domain.mapper.ts | 135 ------------ .../get-issued-invoice-by-id.response.dto.ts | 2 - .../issue-proforma-by-id.response.dto.ts | 2 - .../dto/shared/issued-invoices/index.ts | 1 - .../issued-invoice-verifactu-policy.dto.ts | 18 -- .../src/common/locales/en.json | 13 +- .../src/common/locales/es.json | 15 +- ...ssued-invoice-detail-view-models.entity.ts | 1 - .../pages/issued-invoice-detail-page.test.tsx | 1 - .../issued-invoice-fiscal-section.tsx | 3 - ...-issued-invoice-detail-view-models.test.ts | 6 - ...build-issued-invoice-detail-view-models.ts | 32 --- .../get-issued-invoice-by-id.adapter.test.ts | 8 +- .../get-issued-invoice-by-id.adapter.ts | 29 +-- .../api/get-issued-invoice-by-id.api.ts | 2 +- .../shared/entities/issued-invoice.entity.ts | 11 - 39 files changed, 419 insertions(+), 667 deletions(-) create mode 100644 docs/customer-invoices/sql/drop-issued-invoice-verifactu-snapshot-columns.sql rename modules/customer-invoices/src/api/application/issued-invoices/services/{resolve-proforma-issue-verifactu-snapshots.test.ts => resolve-proforma-issue-fiscal-context.test.ts} (71%) rename modules/customer-invoices/src/api/application/issued-invoices/services/{resolve-proforma-issue-verifactu-snapshots.ts => resolve-proforma-issue-fiscal-context.ts} (59%) delete mode 100644 modules/customer-invoices/src/api/domain/issued-invoices/value-objects/issued-invoice-verifactu-policy.vo.test.ts delete mode 100644 modules/customer-invoices/src/api/domain/issued-invoices/value-objects/issued-invoice-verifactu-policy.vo.ts create mode 100644 modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.test.ts delete mode 100644 modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-verifactu-record-domain.mapper.ts delete mode 100644 modules/customer-invoices/src/common/dto/shared/issued-invoices/issued-invoice-verifactu-policy.dto.ts diff --git a/docs/customer-invoices/sql/drop-issued-invoice-verifactu-snapshot-columns.sql b/docs/customer-invoices/sql/drop-issued-invoice-verifactu-snapshot-columns.sql new file mode 100644 index 00000000..a0f96046 --- /dev/null +++ b/docs/customer-invoices/sql/drop-issued-invoice-verifactu-snapshot-columns.sql @@ -0,0 +1,81 @@ +-- Elimina las columnas legacy de snapshot VeriFactu de `issued_invoices`. +-- Ejecutar solo en una BD de desarrollo segura o mediante procedimiento controlado. +-- No ejecutar automaticamente en produccion. + +SET @target_schema = DATABASE(); + +-- Comprobacion previa recomendada. +-- SELECT column_name, is_nullable, column_type +-- FROM information_schema.columns +-- WHERE table_schema = @target_schema +-- AND table_name = 'issued_invoices' +-- AND column_name IN ( +-- 'verifactu_profile_status', +-- 'uses_verifactu', +-- 'verifactu_effective_at' +-- ); + +SET + @ddl = ( + SELECT IF( + EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE + table_schema = @target_schema + AND table_name = 'issued_invoices' + AND column_name = 'verifactu_effective_at' + ), 'ALTER TABLE issued_invoices DROP COLUMN verifactu_effective_at', 'SELECT ''verifactu_effective_at already dropped''' + ) + ); + +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET + @ddl = ( + SELECT IF( + EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE + table_schema = @target_schema + AND table_name = 'issued_invoices' + AND column_name = 'uses_verifactu' + ), 'ALTER TABLE issued_invoices DROP COLUMN uses_verifactu', 'SELECT ''uses_verifactu already dropped''' + ) + ); + +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET + @ddl = ( + SELECT IF( + EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE + table_schema = @target_schema + AND table_name = 'issued_invoices' + AND column_name = 'verifactu_profile_status' + ), 'ALTER TABLE issued_invoices DROP COLUMN verifactu_profile_status', 'SELECT ''verifactu_profile_status already dropped''' + ) + ); + +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- Validacion minima posterior a la DDL. +-- SELECT column_name +-- FROM information_schema.columns +-- WHERE table_schema = DATABASE() +-- AND table_name = 'issued_invoices' +-- AND column_name IN ( +-- 'verifactu_profile_status', +-- 'uses_verifactu', +-- 'verifactu_effective_at' +-- ); diff --git a/modules/customer-invoices/src/api/application/issued-invoices/models/proforma-issue-read.model.ts b/modules/customer-invoices/src/api/application/issued-invoices/models/proforma-issue-read.model.ts index 45e15719..96777f3c 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/models/proforma-issue-read.model.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/models/proforma-issue-read.model.ts @@ -1,4 +1,3 @@ -import type { CompanyVerifactuProfileStatus } from "../../../domain"; import type { Proforma } from "../../../domain"; import type { InvocieTaxRegimeFullReadModel, @@ -22,9 +21,5 @@ export interface ProformaIssueReadModel { operationQualificationCode: string | null; exemptionReasonCode: string | null; }; - verifactuPolicy: { - profileStatus: CompanyVerifactuProfileStatus; - usesVerifactu: boolean; - effectiveAt: Date | null; - }; + usesVerifactu: boolean; } diff --git a/modules/customer-invoices/src/api/application/issued-invoices/services/index.ts b/modules/customer-invoices/src/api/application/issued-invoices/services/index.ts index 02cc60ec..1241c664 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/services/index.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/services/index.ts @@ -5,4 +5,4 @@ export * from "./issued-invoice-document-properties-factory"; export * from "./issued-invoice-finder"; export * from "./issued-invoice-public-services.interface"; export * from "./proforma-to-issued-invoice-props-converter"; -export * from "./resolve-proforma-issue-verifactu-snapshots"; +export * from "./resolve-proforma-issue-fiscal-context"; diff --git a/modules/customer-invoices/src/api/application/issued-invoices/services/proforma-to-issued-invoice-props-converter.test.ts b/modules/customer-invoices/src/api/application/issued-invoices/services/proforma-to-issued-invoice-props-converter.test.ts index 083c0cb3..da8b3da0 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/services/proforma-to-issued-invoice-props-converter.test.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/services/proforma-to-issued-invoice-props-converter.test.ts @@ -2,14 +2,12 @@ import assert from "node:assert/strict"; import test from "node:test"; import { DiscountPercentage } from "@erp/core/api"; -import { UniqueID, type UtcDate as UtcDateType } from "@repo/rdx-ddd"; +import { UniqueID } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; -import { CompanyVerifactuProfileStatus } from "../../../domain"; - import { ProformaToIssuedInvoiceConverter } from "./proforma-to-issued-invoice-props-converter"; -test("toCreateProps normaliza verifactu effectiveAt a YYYY-MM-DD antes de crear UtcDate", () => { +test("toCreateProps no materializa VerifactuRecord ni policy en el converter", () => { const converter = new ProformaToIssuedInvoiceConverter(); Object.assign(converter as object, { @@ -64,14 +62,9 @@ test("toCreateProps normaliza verifactu effectiveAt a YYYY-MM-DD antes de crear operationQualificationCode: null, exemptionReasonCode: null, }, - verifactuPolicy: { - profileStatus: CompanyVerifactuProfileStatus.ObligatedWithVerifactu, - usesVerifactu: true, - effectiveAt: new Date("2026-08-20T08:30:00.000Z"), - }, + usesVerifactu: true, }); assert.equal(result.isSuccess, true); - const effectiveAt = result.data.verifactuPolicy.unwrap().effectiveAt.unwrap() as UtcDateType; - assert.equal(effectiveAt.toPrimitive(), "2026-08-20"); + assert.equal(result.data.verifactu.isNone(), true); }); diff --git a/modules/customer-invoices/src/api/application/issued-invoices/services/proforma-to-issued-invoice-props-converter.ts b/modules/customer-invoices/src/api/application/issued-invoices/services/proforma-to-issued-invoice-props-converter.ts index 46bed6b3..d7f276f3 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/services/proforma-to-issued-invoice-props-converter.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/services/proforma-to-issued-invoice-props-converter.ts @@ -15,7 +15,6 @@ import { IssuedInvoiceRecipient, IssuedInvoiceTax, IssuedInvoiceTaxes, - IssuedInvoiceVerifactuPolicy, type Proforma, } from "../../../domain"; import type { ProformaIssueReadModel } from "../models"; @@ -85,12 +84,6 @@ export class ProformaToIssuedInvoiceConverter implements IProformaToIssuedInvoic return Result.fail(fiscalClassificationResult.error); } - const verifactuPolicyResult = this.resolveVerifactuPolicy(source); - - if (verifactuPolicyResult.isFailure) { - return Result.fail(verifactuPolicyResult.error); - } - const proformaTotals = proforma.totals(); return Result.ok({ @@ -115,7 +108,6 @@ export class ProformaToIssuedInvoiceConverter implements IProformaToIssuedInvoic paymentTerm: paymentTermResult.data, taxRegime: taxRegimeResult.data, fiscalClassification: Maybe.some(fiscalClassificationResult.data), - verifactuPolicy: Maybe.some(verifactuPolicyResult.data), customerId: proforma.customerId, recipient: recipientResult.data, @@ -361,28 +353,4 @@ export class ProformaToIssuedInvoiceConverter implements IProformaToIssuedInvoic : Maybe.some(source.fiscalClassification.exemptionReasonCode), }); } - - private resolveVerifactuPolicy( - source: ProformaIssueReadModel - ): Result { - let effectiveAt = Maybe.none(); - - if (source.verifactuPolicy.effectiveAt != null) { - const effectiveAtResult = UtcDate.createFromISO( - source.verifactuPolicy.effectiveAt.toISOString().slice(0, 10) - ); - - if (effectiveAtResult.isFailure) { - return Result.fail(effectiveAtResult.error); - } - - effectiveAt = Maybe.some(effectiveAtResult.data); - } - - return IssuedInvoiceVerifactuPolicy.create({ - profileStatus: source.verifactuPolicy.profileStatus, - usesVerifactu: source.verifactuPolicy.usesVerifactu, - effectiveAt, - }); - } } diff --git a/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-verifactu-snapshots.test.ts b/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.test.ts similarity index 71% rename from modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-verifactu-snapshots.test.ts rename to modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.test.ts index 1f93c823..3d2eab1f 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-verifactu-snapshots.test.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.test.ts @@ -8,7 +8,7 @@ import { CompanyVerifactuProfile, CompanyVerifactuProfileStatus, } from "../../../domain"; -import { resolveProformaIssueVerifactuSnapshots } from "./resolve-proforma-issue-verifactu-snapshots"; +import { resolveProformaIssueFiscalContext } from "./resolve-proforma-issue-fiscal-context"; function buildCompanyProfile(params?: { profileStatus?: CompanyVerifactuProfileStatus; @@ -55,7 +55,7 @@ function buildCompanyProfile(params?: { return result.data; } -test("resolveProformaIssueVerifactuSnapshots prioriza document over customer and company", () => { +test("resolveProformaIssueFiscalContext prioriza document over customer and company", () => { const profile = buildCompanyProfile({ taxRegimeCode: "03", operationQualificationCode: "op-company", @@ -63,7 +63,7 @@ test("resolveProformaIssueVerifactuSnapshots prioriza document over customer and verifactuEffectiveAt: new Date("2026-08-15T00:00:00.000Z"), }); - const result = resolveProformaIssueVerifactuSnapshots({ + const result = resolveProformaIssueFiscalContext({ proformaTaxRegimeCode: Maybe.some("01"), customerTaxRegimeCode: Maybe.some("02"), companyProfile: profile, @@ -74,25 +74,23 @@ test("resolveProformaIssueVerifactuSnapshots prioriza document over customer and assert.equal(result.fiscalClassification.exemptionReasonCode.unwrap(), "ex-company"); }); -test("resolveProformaIssueVerifactuSnapshots usa customer cuando documento no aporta tax regime", () => { +test("resolveProformaIssueFiscalContext usa customer cuando documento no aporta tax regime", () => { const profile = buildCompanyProfile({ taxRegimeCode: "03", profileStatus: CompanyVerifactuProfileStatus.ObligatedWithoutVerifactu, }); - const result = resolveProformaIssueVerifactuSnapshots({ + const result = resolveProformaIssueFiscalContext({ proformaTaxRegimeCode: Maybe.none(), customerTaxRegimeCode: Maybe.some("02"), companyProfile: profile, }); assert.equal(result.taxRegimeCode.unwrap(), "02"); - assert.equal(result.verifactuPolicy.profileStatus, "obligated_without_verifactu"); - assert.equal(result.verifactuPolicy.usesVerifactu, false); - assert.equal(result.verifactuPolicy.effectiveAt.isNone(), true); + assert.equal(result.usesVerifactu, false); }); -test("resolveProformaIssueVerifactuSnapshots usa company defaults y politica cuando faltan documento y customer", () => { +test("resolveProformaIssueFiscalContext usa company defaults y politica cuando faltan documento y customer", () => { const effectiveAt = new Date("2026-08-20T08:30:00.000Z"); const profile = buildCompanyProfile({ taxRegimeCode: "05", @@ -101,20 +99,18 @@ test("resolveProformaIssueVerifactuSnapshots usa company defaults y politica cua verifactuEffectiveAt: effectiveAt, }); - const result = resolveProformaIssueVerifactuSnapshots({ + const result = resolveProformaIssueFiscalContext({ proformaTaxRegimeCode: Maybe.none(), customerTaxRegimeCode: Maybe.none(), companyProfile: profile, }); assert.equal(result.taxRegimeCode.unwrap(), "05"); - assert.equal(result.verifactuPolicy.profileStatus, "obligated_with_verifactu"); - assert.equal(result.verifactuPolicy.usesVerifactu, true); - assert.equal(result.verifactuPolicy.effectiveAt.unwrap(), effectiveAt); + assert.equal(result.usesVerifactu, true); }); -test("resolveProformaIssueVerifactuSnapshots mantiene comportamiento legacy sin perfil", () => { - const result = resolveProformaIssueVerifactuSnapshots({ +test("resolveProformaIssueFiscalContext mantiene comportamiento legacy sin perfil", () => { + const result = resolveProformaIssueFiscalContext({ proformaTaxRegimeCode: Maybe.none(), customerTaxRegimeCode: Maybe.none(), companyProfile: null, @@ -123,7 +119,5 @@ test("resolveProformaIssueVerifactuSnapshots mantiene comportamiento legacy sin assert.equal(result.taxRegimeCode.isNone(), true); assert.equal(result.fiscalClassification.operationQualificationCode.isNone(), true); assert.equal(result.fiscalClassification.exemptionReasonCode.isNone(), true); - assert.equal(result.verifactuPolicy.profileStatus, "not_configured"); - assert.equal(result.verifactuPolicy.usesVerifactu, false); - assert.equal(result.verifactuPolicy.effectiveAt.isNone(), true); + assert.equal(result.usesVerifactu, false); }); diff --git a/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-verifactu-snapshots.ts b/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.ts similarity index 59% rename from modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-verifactu-snapshots.ts rename to modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.ts index 0e1ae216..4ef27830 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-verifactu-snapshots.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.ts @@ -1,25 +1,20 @@ import { Maybe } from "@repo/rdx-utils"; import type { CompanyVerifactuProfile } from "../../../domain"; -import { CompanyVerifactuProfileStatus } from "../../../domain"; -export interface ResolveProformaIssueVerifactuSnapshotsInput { +export interface ResolveProformaIssueFiscalContextInput { proformaTaxRegimeCode: Maybe; customerTaxRegimeCode: Maybe; companyProfile: CompanyVerifactuProfile | null; } -export interface ResolveProformaIssueVerifactuSnapshotsOutput { +export interface ResolveProformaIssueFiscalContextOutput { taxRegimeCode: Maybe; fiscalClassification: { operationQualificationCode: Maybe; exemptionReasonCode: Maybe; }; - verifactuPolicy: { - profileStatus: CompanyVerifactuProfileStatus; - usesVerifactu: boolean; - effectiveAt: Maybe; - }; + usesVerifactu: boolean; } /** @@ -30,9 +25,9 @@ export interface ResolveProformaIssueVerifactuSnapshotsOutput { * - operationQualificationCode: company -> sin valor * - exemptionReasonCode: company default -> sin valor */ -export function resolveProformaIssueVerifactuSnapshots( - input: ResolveProformaIssueVerifactuSnapshotsInput -): ResolveProformaIssueVerifactuSnapshotsOutput { +export function resolveProformaIssueFiscalContext( + input: ResolveProformaIssueFiscalContextInput +): ResolveProformaIssueFiscalContextOutput { const companyProfile = input.companyProfile; const taxRegimeCode = input.proformaTaxRegimeCode.isSome() @@ -46,24 +41,12 @@ export function resolveProformaIssueVerifactuSnapshots( const exemptionReasonCode = companyProfile?.defaults.exemptionReasonCode ?? Maybe.none(); - const profileStatus = companyProfile?.profileStatus ?? CompanyVerifactuProfileStatus.NotConfigured; - const usesVerifactu = - profileStatus === CompanyVerifactuProfileStatus.ObligatedWithVerifactu; - const effectiveAt = - usesVerifactu && companyProfile?.verifactuEffectiveAt.isSome() - ? companyProfile.verifactuEffectiveAt - : Maybe.none(); - return { taxRegimeCode, fiscalClassification: { operationQualificationCode, exemptionReasonCode, }, - verifactuPolicy: { - profileStatus, - usesVerifactu, - effectiveAt, - }, + usesVerifactu: companyProfile?.usesVerifactu() ?? false, }; } diff --git a/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/full/issued-invoice-full-snapshot-builder.ts b/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/full/issued-invoice-full-snapshot-builder.ts index 0f0933ff..9c2131f5 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/full/issued-invoice-full-snapshot-builder.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/full/issued-invoice-full-snapshot-builder.ts @@ -53,9 +53,6 @@ export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnaps const fiscal_classification = maybeToNullable(invoice.fiscalClassification, (value) => value.toObjectString() ); - const verifactu_policy = maybeToNullable(invoice.verifactuPolicy, (value) => - value.toObjectString() - ); return { id: invoice.id.toString(), @@ -89,7 +86,6 @@ export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnaps payment_term, tax_regime, fiscal_classification, - verifactu_policy, subtotal_amount: invoice.subtotalAmount.toObjectString(), diff --git a/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/full/issued-invoice-full-snapshot.interface.ts b/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/full/issued-invoice-full-snapshot.interface.ts index 05dfdaac..2ded5b3c 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/full/issued-invoice-full-snapshot.interface.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/full/issued-invoice-full-snapshot.interface.ts @@ -71,11 +71,6 @@ export interface IIssuedInvoiceFullSnapshot { exemption_reason_code: string | null; exemption_reason_label: string | null; } | null; - verifactu_policy: { - profile_status: string; - uses_verifactu: boolean; - effective_at: string | null; - } | null; subtotal_amount: { value: string; scale: string; currency_code: string }; diff --git a/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/report/issued-invoice-report-snapshot-builder.test.ts b/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/report/issued-invoice-report-snapshot-builder.test.ts index a35cfe30..8cfbf94e 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/report/issued-invoice-report-snapshot-builder.test.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/report/issued-invoice-report-snapshot-builder.test.ts @@ -69,7 +69,6 @@ test("reporting usa el payment term histórico del snapshot emitido", () => { description: "Régimen general", }, fiscal_classification: null, - verifactu_policy: null, subtotal_amount: MoneyDTOHelper.fromNumber(100, "EUR"), items_discount_amount: MoneyDTOHelper.fromNumber(0, "EUR"), global_discount_percentage: PercentageDTOHelper.fromNumber(0), diff --git a/modules/customer-invoices/src/api/application/proformas/services/assemblers/proforma-issue-read-model.assembler.ts b/modules/customer-invoices/src/api/application/proformas/services/assemblers/proforma-issue-read-model.assembler.ts index c5614faf..cb7c36af 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/assemblers/proforma-issue-read-model.assembler.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/assemblers/proforma-issue-read-model.assembler.ts @@ -11,7 +11,7 @@ import type { Proforma } from "../../../../domain"; import type { InvoicePaymentTermReadModel } from "../../../common/models"; import type { ICompanyVerifactuProfileRepository } from "../../../company-verifactu-profile"; import type { ProformaIssueReadModel } from "../../../issued-invoices"; -import { resolveProformaIssueVerifactuSnapshots } from "../../../issued-invoices"; +import { resolveProformaIssueFiscalContext } from "../../../issued-invoices"; export interface IProformaIssueReadModelAssembler { assemble(params: { @@ -94,7 +94,7 @@ export class ProformaIssueReadModelAssembler implements IProformaIssueReadModelA return Result.fail(companyProfileResult.error); } - const resolution = resolveProformaIssueVerifactuSnapshots({ + const resolution = resolveProformaIssueFiscalContext({ proformaTaxRegimeCode: params.proforma.taxRegimeCode, customerTaxRegimeCode: customerResult.data.taxRegimeCode, companyProfile: companyProfileResult.data.getOrUndefined() ?? null, @@ -126,11 +126,7 @@ export class ProformaIssueReadModelAssembler implements IProformaIssueReadModelA exemptionReasonCode: resolution.fiscalClassification.exemptionReasonCode.getOrUndefined() ?? null, }, - verifactuPolicy: { - profileStatus: resolution.verifactuPolicy.profileStatus, - usesVerifactu: resolution.verifactuPolicy.usesVerifactu, - effectiveAt: resolution.verifactuPolicy.effectiveAt.getOrUndefined() ?? null, - }, + usesVerifactu: resolution.usesVerifactu, }); } diff --git a/modules/customer-invoices/src/api/application/proformas/snapshot-builders/issue/issue-proforma-response.snapshot-builder.ts b/modules/customer-invoices/src/api/application/proformas/snapshot-builders/issue/issue-proforma-response.snapshot-builder.ts index 4e5eec66..1e86c25e 100644 --- a/modules/customer-invoices/src/api/application/proformas/snapshot-builders/issue/issue-proforma-response.snapshot-builder.ts +++ b/modules/customer-invoices/src/api/application/proformas/snapshot-builders/issue/issue-proforma-response.snapshot-builder.ts @@ -31,9 +31,6 @@ export class IssueProformaResponseSnapshotBuilder implements IIssueProformaRespo fiscal_classification: maybeToNullable(invoice.fiscalClassification, (value) => value.toObjectString() ), - verifactu_policy: maybeToNullable(invoice.verifactuPolicy, (value) => - value.toObjectString() - ), totals: { taxable_base: invoice.taxableAmount.toObjectString(), tax_total: invoice.taxesAmount.toObjectString(), diff --git a/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.test.ts b/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.test.ts index 8cb0f0ac..5d47878a 100644 --- a/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.test.ts +++ b/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.test.ts @@ -6,8 +6,9 @@ import type { ITransactionManager } from "@erp/core/api"; import { CurrencyCode, LanguageCode, UtcDate, UniqueID } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; +import type { ProformaToIssuedInvoiceCreateProps } from "../../issued-invoices/services/proforma-to-issued-invoice-props-converter"; +import type { IIssuedInvoicePublicServices } from "../../issued-invoices/services/issued-invoice-public-services.interface"; import { - CompanyVerifactuProfileStatus, InvoiceNumber, InvoiceStatus, Proforma, @@ -17,6 +18,8 @@ import { import { ProformaIssuer } from "../services/proforma-issuer"; import { IssueProformaUseCase } from "./issue-proforma.use-case"; +type CreateIssuedInvoiceProps = Parameters[1]; + const buildTransactionManager = (): ITransactionManager => ({ start: async () => undefined, getTransaction: () => undefined, @@ -25,6 +28,11 @@ const buildTransactionManager = (): ITransactionManager => ({ rollback: async () => undefined, }); +const buildCreateIssuedInvoiceProps = (): CreateIssuedInvoiceProps => + ({ + verifactu: Maybe.none(), + }) as CreateIssuedInvoiceProps; + const buildIssueableProforma = (params?: { operationDate?: Maybe; }) => { @@ -190,11 +198,7 @@ test("issue falla antes de persistir si la proforma no tiene operation date", as operationQualificationCode: null, exemptionReasonCode: null, }, - verifactuPolicy: { - profileStatus: CompanyVerifactuProfileStatus.NotConfigured, - usesVerifactu: false, - effectiveAt: null, - }, + usesVerifactu: false, }), } as never, issueResponseSnapshotBuilder: { @@ -219,3 +223,185 @@ test("issue falla antes de persistir si la proforma no tiene operation date", as assert.equal(createIssuedInvoiceCalls, 0); assert.equal(markAsIssuedCalls, 0); }); + +test("issue crea VerifactuRecord pending cuando la empresa usa VeriFactu", async () => { + const proforma = buildIssueableProforma(); + let capturedProps: CreateIssuedInvoiceProps | undefined; + + const useCase = new IssueProformaUseCase({ + issuedInvoiceServices: { + createIssuedInvoice: async (_id: UniqueID, props: CreateIssuedInvoiceProps) => { + capturedProps = props; + return Result.ok({ id: UniqueID.generateNewID() } as never); + }, + } as never, + finder: { + findProformaById: async () => Result.ok(proforma), + } as never, + issuer: { + issueProforma: (): Result => + Result.ok(buildCreateIssuedInvoiceProps()), + } as never, + issueReadModelAssembler: { + assemble: async () => + Result.ok({ + proforma, + paymentMethod: { + id: UniqueID.generateNewID(), + name: "Transferencia", + description: Maybe.none(), + }, + paymentTerm: Maybe.none(), + taxRegime: { + code: "01", + description: "Régimen general", + }, + fiscalClassification: { + operationQualificationCode: null, + exemptionReasonCode: null, + }, + usesVerifactu: true, + }), + } as never, + issueResponseSnapshotBuilder: { + toOutput: () => ({ id: "unused" }), + } as never, + repository: { + markAsIssuedByIdInCompany: async () => Result.ok(undefined), + } as never, + transactionManager: buildTransactionManager(), + }); + + const result = await useCase.execute({ + companyId: proforma.companyId.toPrimitive(), + proformaId: proforma.id.toPrimitive(), + }); + + assert.equal(result.isSuccess, true); + if (!capturedProps) { + assert.fail("Expected createIssuedInvoice to be called"); + } + + assert.equal(capturedProps.verifactu.isSome(), true); + const verifactuRecord = capturedProps.verifactu.unwrap(); + assert.equal(verifactuRecord.estado.toPrimitive(), "Pendiente"); +}); + +test("issue no crea VerifactuRecord cuando la empresa no usa VeriFactu", async () => { + const proforma = buildIssueableProforma(); + let capturedProps: CreateIssuedInvoiceProps | undefined; + + const useCase = new IssueProformaUseCase({ + issuedInvoiceServices: { + createIssuedInvoice: async (_id: UniqueID, props: CreateIssuedInvoiceProps) => { + capturedProps = props; + return Result.ok({ id: UniqueID.generateNewID() } as never); + }, + } as never, + finder: { + findProformaById: async () => Result.ok(proforma), + } as never, + issuer: { + issueProforma: (): Result => + Result.ok(buildCreateIssuedInvoiceProps()), + } as never, + issueReadModelAssembler: { + assemble: async () => + Result.ok({ + proforma, + paymentMethod: { + id: UniqueID.generateNewID(), + name: "Transferencia", + description: Maybe.none(), + }, + paymentTerm: Maybe.none(), + taxRegime: { + code: "01", + description: "Régimen general", + }, + fiscalClassification: { + operationQualificationCode: null, + exemptionReasonCode: null, + }, + usesVerifactu: false, + }), + } as never, + issueResponseSnapshotBuilder: { + toOutput: () => ({ id: "unused" }), + } as never, + repository: { + markAsIssuedByIdInCompany: async () => Result.ok(undefined), + } as never, + transactionManager: buildTransactionManager(), + }); + + const result = await useCase.execute({ + companyId: proforma.companyId.toPrimitive(), + proformaId: proforma.id.toPrimitive(), + }); + + assert.equal(result.isSuccess, true); + if (!capturedProps) { + assert.fail("Expected createIssuedInvoice to be called"); + } + + assert.equal(capturedProps.verifactu.isNone(), true); +}); + +test("issue propaga el fallo de persistencia VeriFactu y no marca la proforma como emitida", async () => { + const proforma = buildIssueableProforma(); + let markAsIssuedCalls = 0; + + const useCase = new IssueProformaUseCase({ + issuedInvoiceServices: { + createIssuedInvoice: async () => Result.fail(new Error("verifactu insert failed")), + } as never, + finder: { + findProformaById: async () => Result.ok(proforma), + } as never, + issuer: { + issueProforma: (): Result => + Result.ok(buildCreateIssuedInvoiceProps()), + } as never, + issueReadModelAssembler: { + assemble: async () => + Result.ok({ + proforma, + paymentMethod: { + id: UniqueID.generateNewID(), + name: "Transferencia", + description: Maybe.none(), + }, + paymentTerm: Maybe.none(), + taxRegime: { + code: "01", + description: "Régimen general", + }, + fiscalClassification: { + operationQualificationCode: null, + exemptionReasonCode: null, + }, + usesVerifactu: true, + }), + } as never, + issueResponseSnapshotBuilder: { + toOutput: () => ({ id: "unused" }), + } as never, + repository: { + markAsIssuedByIdInCompany: async () => { + markAsIssuedCalls += 1; + return Result.ok(undefined); + }, + } as never, + transactionManager: buildTransactionManager(), + }); + + const result = await useCase.execute({ + companyId: proforma.companyId.toPrimitive(), + proformaId: proforma.id.toPrimitive(), + }); + + assert.equal(result.isFailure, true); + assert.match(result.error.message, /verifactu insert failed/i); + assert.equal(markAsIssuedCalls, 0); +}); diff --git a/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.ts b/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.ts index 2952c67f..12b6933c 100644 --- a/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.ts +++ b/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.ts @@ -1,8 +1,9 @@ import type { ITransactionManager } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; -import { Result } from "@repo/rdx-utils"; +import { Maybe, Result } from "@repo/rdx-utils"; import type { IIssuedInvoicePublicServices } from "../../issued-invoices"; +import { VerifactuRecord, VerifactuRecordEstado } from "../../../domain"; import type { IssueProformaCommand } from "../commands"; import type { IProformaRepository } from "../repositories"; import type { @@ -87,6 +88,22 @@ export class IssueProformaUseCase { return Result.fail(createPropsResult.error); } + if (issueReadModelResult.data.usesVerifactu) { + const pendingVerifactuRecordResult = VerifactuRecord.create({ + estado: VerifactuRecordEstado.createPendiente(), + qrCode: Maybe.none(), + url: Maybe.none(), + uuid: Maybe.none(), + operacion: Maybe.none(), + }); + + if (pendingVerifactuRecordResult.isFailure) { + return Result.fail(pendingVerifactuRecordResult.error); + } + + createPropsResult.data.verifactu = Maybe.some(pendingVerifactuRecordResult.data); + } + const invoiceResult = await this.deps.issuedInvoiceServices.createIssuedInvoice( issuedInvoiceId, createPropsResult.data, diff --git a/modules/customer-invoices/src/api/domain/issued-invoices/aggregates/issued-invoice.aggregate.ts b/modules/customer-invoices/src/api/domain/issued-invoices/aggregates/issued-invoice.aggregate.ts index ba7a7303..0b0a323e 100644 --- a/modules/customer-invoices/src/api/domain/issued-invoices/aggregates/issued-invoice.aggregate.ts +++ b/modules/customer-invoices/src/api/domain/issued-invoices/aggregates/issued-invoice.aggregate.ts @@ -31,7 +31,6 @@ import { IssuedInvoiceItemMismatch } from "../errors"; import type { IssuedInvoiceFiscalClassification, IssuedInvoiceRecipient, - IssuedInvoiceVerifactuPolicy, } from "../value-objects"; export interface IIssuedInvoiceCreateProps { @@ -82,7 +81,6 @@ export interface IIssuedInvoiceCreateProps { totalAmount: InvoiceAmount; fiscalClassification: Maybe; - verifactuPolicy: Maybe; verifactu: Maybe; } @@ -182,16 +180,6 @@ export class IssuedInvoice ); } - if (props.verifactuPolicy.isNone()) { - return Result.fail( - new DomainValidationError( - "MISSING_VERIFACTU_POLICY", - "verifactuPolicy", - "Issued invoice requires a VeriFactu policy snapshot" - ) - ); - } - return Result.ok(); } @@ -307,10 +295,6 @@ export class IssuedInvoice return this.props.fiscalClassification; } - public get verifactuPolicy(): Maybe { - return this.props.verifactuPolicy; - } - public get subtotalAmount(): InvoiceAmount { return this.props.subtotalAmount; } diff --git a/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/index.ts b/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/index.ts index 3442626f..0ffdcad8 100644 --- a/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/index.ts +++ b/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/index.ts @@ -1,5 +1,4 @@ export * from "./issued-invoice-fiscal-classification.vo"; export * from "./issued-invoice-recipient.vo"; export * from "./issued-invoice-tax-group.vo"; -export * from "./issued-invoice-verifactu-policy.vo"; export * from "./verifactu-status.vo"; diff --git a/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/issued-invoice-verifactu-policy.vo.test.ts b/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/issued-invoice-verifactu-policy.vo.test.ts deleted file mode 100644 index 429a03b7..00000000 --- a/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/issued-invoice-verifactu-policy.vo.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import assert from "node:assert/strict"; -import test from "node:test"; - -import { UtcDate } from "@repo/rdx-ddd"; -import { Maybe } from "@repo/rdx-utils"; - -import { IssuedInvoiceVerifactuPolicy } from "./issued-invoice-verifactu-policy.vo"; - -test("IssuedInvoiceVerifactuPolicy exige effectiveAt cuando usesVerifactu es true", () => { - const result = IssuedInvoiceVerifactuPolicy.create({ - profileStatus: "obligated_with_verifactu", - usesVerifactu: true, - effectiveAt: Maybe.none(), - }); - - assert.equal(result.isFailure, true); - assert.match(result.error.message, /requires effectiveAt/i); -}); - -test("IssuedInvoiceVerifactuPolicy rechaza effectiveAt cuando profileStatus no usa VeriFactu", async () => { - const effectiveAt = UtcDate.createFromISO("2026-08-20T08:30:00.000Z"); - - if (effectiveAt.isFailure) { - throw effectiveAt.error; - } - - const result = IssuedInvoiceVerifactuPolicy.create({ - profileStatus: "not_configured", - usesVerifactu: false, - effectiveAt: Maybe.some(effectiveAt.data), - }); - - assert.equal(result.isFailure, true); - assert.match(result.error.message, /cannot define effectiveAt/i); -}); - -test("IssuedInvoiceVerifactuPolicy serializa la politica vigente", async () => { - const effectiveAt = UtcDate.createFromISO("2026-08-20T08:30:00.000Z"); - - if (effectiveAt.isFailure) { - throw effectiveAt.error; - } - - const result = IssuedInvoiceVerifactuPolicy.create({ - profileStatus: "obligated_with_verifactu", - usesVerifactu: true, - effectiveAt: Maybe.some(effectiveAt.data), - }); - - if (result.isFailure) { - throw result.error; - } - - assert.deepEqual(result.data.toObjectString(), { - profile_status: "obligated_with_verifactu", - uses_verifactu: true, - effective_at: "2026-08-20T08:30:00.000Z", - }); -}); diff --git a/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/issued-invoice-verifactu-policy.vo.ts b/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/issued-invoice-verifactu-policy.vo.ts deleted file mode 100644 index 54f16e59..00000000 --- a/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/issued-invoice-verifactu-policy.vo.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { type UtcDate, ValueObject, maybeToNullable } from "@repo/rdx-ddd"; -import { type Maybe, Result } from "@repo/rdx-utils"; - -import type { CompanyVerifactuProfileStatus } from "../../company-verifactu-profile"; -import { CompanyVerifactuProfileStatus as CompanyVerifactuProfileStatusValues } from "../../company-verifactu-profile"; - -export type IssuedInvoiceVerifactuPolicyProps = { - profileStatus: CompanyVerifactuProfileStatus; - usesVerifactu: boolean; - effectiveAt: Maybe; -}; - -export class IssuedInvoiceVerifactuPolicy extends ValueObject { - protected static validate(values: IssuedInvoiceVerifactuPolicyProps) { - const expectedUsesVerifactu = - values.profileStatus === CompanyVerifactuProfileStatusValues.ObligatedWithVerifactu; - - if (values.usesVerifactu !== expectedUsesVerifactu) { - return Result.fail(new Error("Issued invoice VeriFactu policy usesVerifactu does not match profile status")); - } - - if (values.usesVerifactu && values.effectiveAt.isNone()) { - return Result.fail(new Error("Issued invoice VeriFactu policy requires effectiveAt when VeriFactu is used")); - } - - if (!values.usesVerifactu && values.effectiveAt.isSome()) { - return Result.fail(new Error("Issued invoice VeriFactu policy cannot define effectiveAt when VeriFactu is not used")); - } - - return Result.ok(values); - } - - public static create( - values: IssuedInvoiceVerifactuPolicyProps - ): Result { - const validationResult = IssuedInvoiceVerifactuPolicy.validate(values); - - if (validationResult.isFailure) { - return Result.fail(validationResult.error); - } - - return Result.ok(new IssuedInvoiceVerifactuPolicy(values)); - } - - public get profileStatus(): CompanyVerifactuProfileStatus { - return this.props.profileStatus; - } - - public get usesVerifactu(): boolean { - return this.props.usesVerifactu; - } - - public get effectiveAt(): Maybe { - return this.props.effectiveAt; - } - - public getProps(): IssuedInvoiceVerifactuPolicyProps { - return this.props; - } - - public toPrimitive(): IssuedInvoiceVerifactuPolicyProps { - return this.getProps(); - } - - public toObjectString() { - return { - profile_status: this.profileStatus, - uses_verifactu: this.usesVerifactu, - effective_at: maybeToNullable(this.effectiveAt, (value) => value.toISOString()), - }; - } -} diff --git a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/sequelize-issued-invoice.model.ts b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/sequelize-issued-invoice.model.ts index 9e510544..f05ea96b 100644 --- a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/sequelize-issued-invoice.model.ts +++ b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/sequelize-issued-invoice.model.ts @@ -75,9 +75,6 @@ export class IssuedInvoiceModel extends Model< declare tax_regime_description: string; declare operation_qualification_code: CreationOptional; declare exemption_reason_code: CreationOptional; - declare verifactu_profile_status: CreationOptional; - declare uses_verifactu: CreationOptional; - declare verifactu_effective_at: CreationOptional; // Subtotal declare subtotal_amount_value: number; @@ -351,21 +348,6 @@ export default (database: Sequelize) => { allowNull: true, defaultValue: null, }, - verifactu_profile_status: { - type: DataTypes.STRING(64), - allowNull: true, - defaultValue: null, - }, - uses_verifactu: { - type: DataTypes.BOOLEAN, - allowNull: true, - defaultValue: null, - }, - verifactu_effective_at: { - type: DataTypes.DATE, - allowNull: true, - defaultValue: null, - }, subtotal_amount_value: { type: new DataTypes.BIGINT(), allowNull: false, diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-domain.mapper.test.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-domain.mapper.test.ts index e38f4c72..a137f793 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-domain.mapper.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-domain.mapper.test.ts @@ -1,7 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; -import { UtcDate } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; import { SequelizeIssuedInvoiceV2DomainMapper } from "./sequelize-issued-invoice-v2-domain.mapper"; @@ -120,7 +119,6 @@ const buildIssuedInvoiceSource = (params: { description: "Régimen general", }, fiscalClassification: Maybe.none(), - verifactuPolicy: Maybe.none(), subtotalAmount: { value: "10000", scale: 2 }, itemsDiscountAmount: { value: "0", scale: 2 }, globalDiscountPercentage: { @@ -270,26 +268,17 @@ test("rehidrata el snapshot persistido histórico sin depender del catálogo viv ]); }); -test("normaliza verifactu_effective_at Date a YYYY-MM-DD al rehidratar", () => { +test("rehidrata fiscalClassification exclusivamente desde sus columnas fiscales", () => { const mapper = buildMapper(); - const raw = { - verifactu_effective_at: new Date("2027-01-12T00:00:00.000Z"), - } as { - verifactu_effective_at: Date; - }; - - const result = mapper.mapToDomain.bind(mapper) as unknown; - assert.ok(result); - - const effectiveAt = mapper as unknown as { + const internals = mapper as unknown as { _mapAttributesToDomain: ( source: unknown, params: { errors: Array<{ message: string; path: string; value?: unknown }> } - ) => { hasVerifactuSnapshots: boolean }; + ) => { taxRegime: { code: string; description: string } }; }; const errors: Array<{ message: string; path: string; value?: unknown }> = []; - const attributes = effectiveAt._mapAttributesToDomain( + const attributes = internals._mapAttributesToDomain( { id: "11111111-1111-4111-8111-111111111111", company_id: "22222222-2222-4222-8222-222222222222", @@ -327,22 +316,13 @@ test("normaliza verifactu_effective_at Date a YYYY-MM-DD al rehidratar", () => { retention_amount_value: 0, taxes_amount_value: 2100, total_amount_value: 12100, - verifactu_profile_status: "obligated_with_verifactu", - uses_verifactu: true, - verifactu_effective_at: raw.verifactu_effective_at, + operation_qualification_code: "S1", + exemption_reason_code: "E1", }, { errors } - ) as { - hasVerifactuSnapshots: boolean; - }; - - assert.equal(attributes.hasVerifactuSnapshots, true); - - const utcDateResult = UtcDate.createFromISO( - raw.verifactu_effective_at.toISOString().slice(0, 10) ); - assert.equal(utcDateResult.isSuccess, true); - assert.equal(utcDateResult.data.toPrimitive(), "2027-01-12"); + + assert.equal(attributes.taxRegime.code, "04"); assert.deepEqual(errors, []); }); diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-domain.mapper.ts index 997366bf..d13d4eb0 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-domain.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-domain.mapper.ts @@ -15,7 +15,6 @@ import { import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils"; import { - type CompanyVerifactuProfileStatus, type InternalIssuedInvoiceProps, InvoiceAmount, InvoiceNumber, @@ -28,7 +27,6 @@ import { IssuedInvoiceFiscalClassification, IssuedInvoiceItems, IssuedInvoiceTaxes, - IssuedInvoiceVerifactuPolicy, } from "../../../../../../domain"; import type { IssuedInvoiceCreationAttributes, IssuedInvoiceModel } from "../../../../../common"; @@ -143,17 +141,17 @@ export class SequelizeIssuedInvoiceV2DomainMapper extends SequelizeDomainMapper< } const taxRegime = extractOrPushError( - isNullishOrEmpty(raw.tax_regime_code) + isNullishOrEmpty(raw.tax_regime_description) ? Result.fail( new DomainValidationError( - "MISSING_TAX_REGIME", - "tax_regime", - "Issued invoice requires persisted tax regime" + "MISSING_TAX_REGIME_DESCRIPTION", + "tax_regime_description", + "Issued invoice requires persisted tax regime description" ) ) : InvoiceTaxRegime.create({ code: String(raw.tax_regime_code), - description: String(raw.tax_regime_description ?? raw.tax_regime_code), + description: String(raw.tax_regime_description), }), "tax_regime", errors @@ -226,8 +224,6 @@ export class SequelizeIssuedInvoiceV2DomainMapper extends SequelizeDomainMapper< "total_amount_value", errors ); - const hasVerifactuSnapshots = raw.verifactu_profile_status != null; - return { invoiceId, companyId, @@ -259,7 +255,6 @@ export class SequelizeIssuedInvoiceV2DomainMapper extends SequelizeDomainMapper< retentionAmount, taxesAmount, totalAmount, - hasVerifactuSnapshots, }; } @@ -308,60 +303,24 @@ export class SequelizeIssuedInvoiceV2DomainMapper extends SequelizeDomainMapper< languageCode: attributes.languageCode!, currencyCode: attributes.currencyCode!, }); - let fiscalClassification = Maybe.none(); - let verifactuPolicy = Maybe.none(); + const fiscalClassificationResult = IssuedInvoiceFiscalClassification.create({ + taxRegimeCode: attributes.taxRegime!.code, + taxRegimeLabel: + attributes.taxRegime!.description.trim().length > 0 + ? Maybe.some(attributes.taxRegime!.description) + : Maybe.none(), + operationQualificationCode: + raw.operation_qualification_code == null + ? Maybe.none() + : Maybe.some(String(raw.operation_qualification_code)), + exemptionReasonCode: + raw.exemption_reason_code == null + ? Maybe.none() + : Maybe.some(String(raw.exemption_reason_code)), + }); - if (attributes.hasVerifactuSnapshots) { - const fiscalClassificationResult = IssuedInvoiceFiscalClassification.create({ - taxRegimeCode: attributes.taxRegime!.code, - taxRegimeLabel: - attributes.taxRegime!.description.trim().length > 0 - ? Maybe.some(attributes.taxRegime!.description) - : Maybe.none(), - operationQualificationCode: - raw.operation_qualification_code == null - ? Maybe.none() - : Maybe.some(String(raw.operation_qualification_code)), - exemptionReasonCode: - raw.exemption_reason_code == null - ? Maybe.none() - : Maybe.some(String(raw.exemption_reason_code)), - }); - - if (fiscalClassificationResult.isFailure) { - return Result.fail(fiscalClassificationResult.error); - } - - fiscalClassification = Maybe.some(fiscalClassificationResult.data); - - const effectiveAt = extractOrPushError( - maybeFromNullableResult(raw.verifactu_effective_at, (value) => - UtcDate.createFromISO( - value instanceof Date ? value.toISOString().slice(0, 10) : String(value).slice(0, 10) - ) - ), - "verifactu_effective_at", - errors - ); - - if (errors.length > 0) { - console.error(errors); - return Result.fail( - new ValidationErrorCollection("Issued invoice V2 mapping failed [mapToDomain]", errors) - ); - } - - const verifactuPolicyResult = IssuedInvoiceVerifactuPolicy.create({ - profileStatus: String(raw.verifactu_profile_status) as CompanyVerifactuProfileStatus, - usesVerifactu: Boolean(raw.uses_verifactu), - effectiveAt: effectiveAt ?? Maybe.none(), - }); - - if (verifactuPolicyResult.isFailure) { - return Result.fail(verifactuPolicyResult.error); - } - - verifactuPolicy = Maybe.some(verifactuPolicyResult.data); + if (fiscalClassificationResult.isFailure) { + return Result.fail(fiscalClassificationResult.error); } const invoiceProps: InternalIssuedInvoiceProps = { @@ -394,8 +353,7 @@ export class SequelizeIssuedInvoiceV2DomainMapper extends SequelizeDomainMapper< totalAmount: attributes.totalAmount!, paymentMethod: attributes.paymentMethod!, taxRegime: attributes.taxRegime!, - fiscalClassification, - verifactuPolicy, + fiscalClassification: Maybe.some(fiscalClassificationResult.data), taxes, verifactu: verifactuResult.data, linkedProformaId: attributes.linkedProformaId!, @@ -487,15 +445,6 @@ export class SequelizeIssuedInvoiceV2DomainMapper extends SequelizeDomainMapper< exemption_reason_code: source.fiscalClassification.isSome() ? (source.fiscalClassification.unwrap().exemptionReasonCode.getOrUndefined() ?? null) : null, - verifactu_profile_status: source.verifactuPolicy.isSome() - ? source.verifactuPolicy.unwrap().profileStatus - : null, - uses_verifactu: source.verifactuPolicy.isSome() - ? source.verifactuPolicy.unwrap().usesVerifactu - : null, - verifactu_effective_at: source.verifactuPolicy.isSome() - ? (source.verifactuPolicy.unwrap().effectiveAt.getOrUndefined()?.toDate() ?? null) - : null, subtotal_amount_value: source.subtotalAmount.value, subtotal_amount_scale: source.subtotalAmount.scale, items_discount_amount_value: source.itemsDiscountAmount.value, diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.test.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.test.ts new file mode 100644 index 00000000..d224d9b2 --- /dev/null +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.test.ts @@ -0,0 +1,55 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { Maybe } from "@repo/rdx-utils"; + +import { SequelizeIssuedInvoiceV2VerifactuDomainMapper } from "./sequelize-issued-invoice-v2-verifactu-domain.mapper"; + +test("rehidrata un VerifactuRecord histórico cuando existe en persistencia", () => { + const mapper = new SequelizeIssuedInvoiceV2VerifactuDomainMapper(); + + const result = mapper.mapToDomain({ + id: "11111111-1111-4111-8111-111111111111", + estado: "Correcto", + qr: "qr-value", + url: "https://example.com/verifactu/1", + uuid: "vf-uuid", + operacion: "alta", + } as never, { + errors: [], + attributes: {}, + }); + + assert.equal(result.isSuccess, true); + assert.equal(result.data.isSome(), true); + assert.equal(result.data.unwrap().estado.toPrimitive(), "Correcto"); + assert.equal(result.data.unwrap().qrCode.getOrUndefined(), "qr-value"); +}); + +test("devuelve ausencia cuando la factura histórica no tiene VerifactuRecord persistido", () => { + const mapper = new SequelizeIssuedInvoiceV2VerifactuDomainMapper(); + + const result = mapper.mapToDomain(null, { + errors: [], + attributes: {}, + }); + + assert.equal(result.isSuccess, true); + assert.equal(result.data.isNone(), true); +}); + +test("no inventa un registro pending al persistir una factura que no usa VeriFactu", () => { + const mapper = new SequelizeIssuedInvoiceV2VerifactuDomainMapper(); + + const result = mapper.mapToPersistence(Maybe.none(), { + parent: { + id: { + toPrimitive: () => "11111111-1111-4111-8111-111111111111", + }, + }, + errors: [], + } as never); + + assert.equal(result.isSuccess, true); + assert.equal(result.data, undefined); +}); diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.ts index b8e7c4d6..077b53ab 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.ts @@ -21,7 +21,7 @@ import type { VerifactuRecordCreationAttributes, VerifactuRecordModel } from ".. export class SequelizeIssuedInvoiceV2VerifactuDomainMapper extends SequelizeDomainMapper< VerifactuRecordModel, - VerifactuRecordCreationAttributes, + VerifactuRecordCreationAttributes | undefined, Maybe > { public mapToDomain( @@ -91,23 +91,14 @@ export class SequelizeIssuedInvoiceV2VerifactuDomainMapper extends SequelizeDoma mapToPersistence( source: Maybe, params?: MapperParamsType - ): Result { + ): Result { const { parent } = params as { parent: IssuedInvoice; errors: ValidationErrorDetail[]; }; if (source.isNone()) { - return Result.ok({ - id: UniqueID.generateNewID().toPrimitive(), - invoice_id: null, - issued_invoice_id: parent.id.toPrimitive(), - estado: VerifactuRecordEstado.createPendiente().toPrimitive(), - qr: "", - url: "", - uuid: "", - operacion: "", - }); + return Result.ok(undefined); } const verifactu = source.unwrap(); diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-verifactu-record-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-verifactu-record-domain.mapper.ts deleted file mode 100644 index 4a868c7d..00000000 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-verifactu-record-domain.mapper.ts +++ /dev/null @@ -1,135 +0,0 @@ -import type { MapperParamsType } from "@erp/core/api"; -import { SequelizeDomainMapper } from "@erp/core/api"; -import { - URLAddress, - UniqueID, - ValidationErrorCollection, - type ValidationErrorDetail, - extractOrPushError, - maybeFromNullableResult, - maybeToEmptyString, -} from "@repo/rdx-ddd"; -import { Maybe, Result } from "@repo/rdx-utils"; - -import { - type IIssuedInvoiceCreateProps, - type IssuedInvoice, - VerifactuRecord, - VerifactuRecordEstado, -} from "../../../../../../domain"; -import type { - VerifactuRecordCreationAttributes, - VerifactuRecordModel, -} from "../../../../../common"; - -export class SequelizeIssuedInvoiceVerifactuDomainMapper extends SequelizeDomainMapper< - VerifactuRecordModel, - VerifactuRecordCreationAttributes, - Maybe -> { - public mapToDomain( - source: VerifactuRecordModel | null | undefined, - params?: MapperParamsType - ): Result, Error> { - const { errors, attributes } = params as { - errors: ValidationErrorDetail[]; - attributes: Partial; - }; - - if (!source) { - return Result.ok(Maybe.none()); - } - - const recordId = extractOrPushError(UniqueID.create(source.id), "id", errors); - const estado = extractOrPushError( - VerifactuRecordEstado.create(source.estado), - "estado", - errors - ); - - const qr = extractOrPushError( - maybeFromNullableResult(source.qr, (value) => Result.ok(String(value))), - "qr", - errors - ); - - const url = extractOrPushError( - maybeFromNullableResult(source.url, (value) => URLAddress.create(value)), - "url", - errors - ); - - const uuid = extractOrPushError( - maybeFromNullableResult(source.uuid, (value) => Result.ok(String(value))), - "uuid", - errors - ); - - const operacion = extractOrPushError( - maybeFromNullableResult(source.operacion, (value) => Result.ok(String(value))), - "operacion", - errors - ); - - if (errors.length > 0) { - return Result.fail( - new ValidationErrorCollection("Verifactu record mapping failed [mapToDTO]", errors) - ); - } - - const createResult = VerifactuRecord.create( - { - estado: estado!, - qrCode: qr!, - url: url!, - uuid: uuid!, - operacion: operacion!, - }, - recordId! - ); - - if (createResult.isFailure) { - return Result.fail( - new ValidationErrorCollection("Invoice verifactu entity creation failed", [ - { path: "verifactu", message: createResult.error.message }, - ]) - ); - } - - return Result.ok(Maybe.some(createResult.data)); - } - - mapToPersistence( - source: Maybe, - params?: MapperParamsType - ): Result { - const { errors, parent } = params as { - parent: IssuedInvoice; - errors: ValidationErrorDetail[]; - }; - - if (source.isNone()) { - return Result.ok({ - id: UniqueID.generateNewID().toPrimitive(), - invoice_id: parent.id.toPrimitive(), - estado: VerifactuRecordEstado.createPendiente().toPrimitive(), - qr: "", - url: "", - uuid: "", - operacion: "", - }); - } - - const verifactu = source.unwrap(); - - return Result.ok({ - id: verifactu.id.toPrimitive(), - invoice_id: parent.id.toPrimitive(), - estado: verifactu.estado.toPrimitive(), - qr: maybeToEmptyString(verifactu.qrCode, (v) => v), - url: maybeToEmptyString(verifactu.url, (v) => v.toPrimitive()), - uuid: maybeToEmptyString(verifactu.uuid, (v) => v), - operacion: maybeToEmptyString(verifactu.operacion, (v) => v), - }); - } -} diff --git a/modules/customer-invoices/src/common/dto/response/issued-invoices/get-issued-invoice-by-id.response.dto.ts b/modules/customer-invoices/src/common/dto/response/issued-invoices/get-issued-invoice-by-id.response.dto.ts index 9abf9167..046cd2d5 100644 --- a/modules/customer-invoices/src/common/dto/response/issued-invoices/get-issued-invoice-by-id.response.dto.ts +++ b/modules/customer-invoices/src/common/dto/response/issued-invoices/get-issued-invoice-by-id.response.dto.ts @@ -17,7 +17,6 @@ import { PaymentMethodRefSchema, TaxRegimeRefSchema, TaxesBreakdownSchema, - IssuedInvoiceVerifactuPolicySchema, VerifactuRecordSchema, } from "../../shared"; @@ -54,7 +53,6 @@ export const GetIssuedInvoiceByIdResponseSchema = z.object({ tax_regime: TaxRegimeRefSchema, fiscal_classification: IssuedInvoiceFiscalClassificationSchema.nullable(), - verifactu_policy: IssuedInvoiceVerifactuPolicySchema.nullable(), subtotal_amount: MoneySchema, diff --git a/modules/customer-invoices/src/common/dto/response/proformas/issue-proforma-by-id.response.dto.ts b/modules/customer-invoices/src/common/dto/response/proformas/issue-proforma-by-id.response.dto.ts index 4e11cba6..679769e4 100644 --- a/modules/customer-invoices/src/common/dto/response/proformas/issue-proforma-by-id.response.dto.ts +++ b/modules/customer-invoices/src/common/dto/response/proformas/issue-proforma-by-id.response.dto.ts @@ -4,7 +4,6 @@ import { z } from "zod/v4"; import { IssuedInvoiceFiscalClassificationSchema, IssuedInvoiceStatusSchema, - IssuedInvoiceVerifactuPolicySchema, TaxRegimeRefSchema, } from "../../shared"; @@ -34,7 +33,6 @@ export const IssueProformaIssuedInvoiceSchema = z.object({ recipient: IssueProformaIssuedInvoiceRecipientSchema, tax_regime: TaxRegimeRefSchema, fiscal_classification: IssuedInvoiceFiscalClassificationSchema.nullable(), - verifactu_policy: IssuedInvoiceVerifactuPolicySchema.nullable(), totals: IssueProformaIssuedInvoiceTotalsSchema, }); diff --git a/modules/customer-invoices/src/common/dto/shared/issued-invoices/index.ts b/modules/customer-invoices/src/common/dto/shared/issued-invoices/index.ts index 3478fad5..66ee734b 100644 --- a/modules/customer-invoices/src/common/dto/shared/issued-invoices/index.ts +++ b/modules/customer-invoices/src/common/dto/shared/issued-invoices/index.ts @@ -4,5 +4,4 @@ export * from "./issued-invoice-payment-term.dto"; export * from "./issued-invoice-recipient-summary.dto"; export * from "./issued-invoice-status.dto"; export * from "./issued-invoice-summary.dto"; -export * from "./issued-invoice-verifactu-policy.dto"; export * from "./verifactu-record.dto"; diff --git a/modules/customer-invoices/src/common/dto/shared/issued-invoices/issued-invoice-verifactu-policy.dto.ts b/modules/customer-invoices/src/common/dto/shared/issued-invoices/issued-invoice-verifactu-policy.dto.ts deleted file mode 100644 index eab46fcf..00000000 --- a/modules/customer-invoices/src/common/dto/shared/issued-invoices/issued-invoice-verifactu-policy.dto.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { z } from "zod/v4"; - -const CompanyVerifactuProfileStatusSchema = z.enum([ - "not_configured", - "exempt", - "obligated_without_verifactu", - "obligated_with_verifactu", -]); - -export const IssuedInvoiceVerifactuPolicySchema = z.object({ - profile_status: CompanyVerifactuProfileStatusSchema, - uses_verifactu: z.boolean(), - effective_at: z.iso.datetime().nullable(), -}); - -export type IssuedInvoiceVerifactuPolicyDTO = z.infer< - typeof IssuedInvoiceVerifactuPolicySchema ->; diff --git a/modules/customer-invoices/src/common/locales/en.json b/modules/customer-invoices/src/common/locales/en.json index f20d67df..4bac7658 100644 --- a/modules/customer-invoices/src/common/locales/en.json +++ b/modules/customer-invoices/src/common/locales/en.json @@ -844,9 +844,6 @@ "fiscal_tax_regime": "Historical tax regime", "operation_qualification": "Operation qualification", "exemption_reason": "Exemption reason", - "verifactu_profile_status": "VeriFactu status", - "verifactu_policy": "VeriFactu policy", - "verifactu_effective_at": "VeriFactu effective date", "subtotal_amount": "Subtotal", "items_discount_amount": "Item discount", "global_discount_amount": "Global discount", @@ -888,15 +885,7 @@ "verifactu": { "empty": "This invoice has no VeriFactu block available in the current detail view.", "qr_alt": "VeriFactu QR code", - "available": "Available", - "uses_verifactu": "Uses VeriFactu", - "does_not_use_verifactu": "Does not use VeriFactu", - "policy_status": { - "not_configured": "Not configured", - "exempt": "Exempt or not applicable", - "obligated_without_verifactu": "Obligated without VeriFactu", - "obligated_with_verifactu": "Obligated with VeriFactu" - } + "available": "Available" }, "fiscal": { "unknown_tax_code": "Tax" diff --git a/modules/customer-invoices/src/common/locales/es.json b/modules/customer-invoices/src/common/locales/es.json index baecc2af..8f52257d 100644 --- a/modules/customer-invoices/src/common/locales/es.json +++ b/modules/customer-invoices/src/common/locales/es.json @@ -845,9 +845,6 @@ "fiscal_tax_regime": "Régimen fiscal histórico", "operation_qualification": "Calificación de operación", "exemption_reason": "Motivo de exención", - "verifactu_profile_status": "Situación VeriFactu", - "verifactu_policy": "Política VeriFactu", - "verifactu_effective_at": "Fecha efectiva VeriFactu", "items_discount_amount": "Descuento de líneas", "global_discount_amount": "Descuento global", "subtotal_amount": "Subtotal", @@ -889,15 +886,7 @@ "verifactu": { "empty": "Esta factura no tiene un bloque VeriFactu disponible en el detalle actual.", "qr_alt": "Código QR de VeriFactu", - "available": "Disponible", - "uses_verifactu": "Usa VeriFactu", - "does_not_use_verifactu": "No usa VeriFactu", - "policy_status": { - "not_configured": "No configurado", - "exempt": "Exenta o no aplicable", - "obligated_without_verifactu": "Obligada sin VeriFactu", - "obligated_with_verifactu": "Obligada con VeriFactu" - } + "available": "Disponible" }, "fiscal": { "unknown_tax_code": "Impuesto" @@ -1195,4 +1184,4 @@ } } } -} \ No newline at end of file +} diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/entities/issued-invoice-detail-view-models.entity.ts b/modules/customer-invoices/src/web/issued-invoices/detail/entities/issued-invoice-detail-view-models.entity.ts index bd9e8a3d..9a5cb09e 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/entities/issued-invoice-detail-view-models.entity.ts +++ b/modules/customer-invoices/src/web/issued-invoices/detail/entities/issued-invoice-detail-view-models.entity.ts @@ -95,7 +95,6 @@ export interface IssuedInvoiceDetailFiscalBreakdownItemViewModel { export interface IssuedInvoiceDetailFiscalViewModel { taxRegimeLabel: string; fiscalClassification: IssuedInvoiceDetailFiscalFieldViewModel[]; - verifactuPolicy: IssuedInvoiceDetailFiscalFieldViewModel[]; subtotalLabel: string; itemsDiscountLabel: string | null; globalDiscountLabel: string | null; diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/ui/pages/issued-invoice-detail-page.test.tsx b/modules/customer-invoices/src/web/issued-invoices/detail/ui/pages/issued-invoice-detail-page.test.tsx index 4d6a5a86..8996ccc8 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/ui/pages/issued-invoice-detail-page.test.tsx +++ b/modules/customer-invoices/src/web/issued-invoices/detail/ui/pages/issued-invoice-detail-page.test.tsx @@ -65,7 +65,6 @@ const buildController = () => ({ fiscal: { taxRegimeLabel: "Régimen general", fiscalClassification: [], - verifactuPolicy: [], subtotalLabel: "100,00 €", itemsDiscountLabel: null, globalDiscountLabel: null, diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-fiscal-section.tsx b/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-fiscal-section.tsx index ead6d4d2..fcd92ccc 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-fiscal-section.tsx +++ b/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-fiscal-section.tsx @@ -24,9 +24,6 @@ export const IssuedInvoiceFiscalSection = ({ {fiscal.fiscalClassification.map((field) => ( ))} - {fiscal.verifactuPolicy.map((field) => ( - - ))} ); diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.test.ts b/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.test.ts index 84d05df7..fa4bd6c4 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.test.ts +++ b/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.test.ts @@ -119,11 +119,6 @@ const buildInvoice = (): IssuedInvoice => ({ exemptionReasonCode: null, exemptionReasonLabel: null, }, - verifactuPolicy: { - profileStatus: "obligated_with_verifactu", - usesVerifactu: true, - effectiveAt: "2026-01-01T00:00:00.000Z", - }, subtotalAmount: 150, itemsDiscountAmount: 0, globalDiscountPercentage: 0, @@ -209,7 +204,6 @@ test("construye el view model general con resumen fiscal, unidades y enlace a pr assert.equal(viewModel.commercial.paymentTermDueRules[0]?.dueDaysLabel, "30 días"); assert.equal(viewModel.fiscal.taxRegimeLabel, "general"); assert.equal(viewModel.fiscal.fiscalClassification[0]?.value, "Régimen general"); - assert.equal(viewModel.fiscal.verifactuPolicy[0]?.value, "obligated_with_verifactu"); assert.equal(viewModel.fiscal.usesRec, true); assert.equal(viewModel.fiscal.usesRetention, true); assert.equal(viewModel.fiscal.breakdown[1]?.taxLabel, "Impuesto"); diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.ts b/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.ts index 5a47f50b..2bb949bf 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.ts +++ b/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.ts @@ -109,15 +109,6 @@ const buildPercentageLabel = (value: number | null, scale = 2) => { return `${NumberHelper.formatNumber(value, scale)}%`; }; -const buildPolicyStatusLabel = ( - profileStatus: NonNullable["profileStatus"], - translate: IssuedInvoiceDetailTranslate -) => { - return translate(`pages.issued_invoices.detail.verifactu.policy_status.${profileStatus}`, { - defaultValue: profileStatus, - }); -}; - const buildItemTaxesLabel = ( item: IssuedInvoice["items"][number], translate: IssuedInvoiceDetailTranslate @@ -369,32 +360,9 @@ export const buildIssuedInvoiceDetailGeneralViewModel = ( ), ].filter((item): item is IssuedInvoiceDetailFiscalFieldViewModel => item !== null); - const verifactuPolicy = issuedInvoice.verifactuPolicy - ? [ - buildFiscalField( - "profile-status", - context.translate("pages.issued_invoices.detail.fields.verifactu_profile_status"), - buildPolicyStatusLabel(issuedInvoice.verifactuPolicy.profileStatus, context.translate) - ), - buildFiscalField( - "uses-verifactu", - context.translate("pages.issued_invoices.detail.fields.verifactu_policy"), - issuedInvoice.verifactuPolicy.usesVerifactu - ? context.translate("pages.issued_invoices.detail.verifactu.uses_verifactu") - : context.translate("pages.issued_invoices.detail.verifactu.does_not_use_verifactu") - ), - buildFiscalField( - "effective-at", - context.translate("pages.issued_invoices.detail.fields.verifactu_effective_at"), - formatDate(issuedInvoice.verifactuPolicy.effectiveAt) - ), - ].filter((item): item is IssuedInvoiceDetailFiscalFieldViewModel => item !== null) - : []; - const fiscal: IssuedInvoiceDetailFiscalViewModel = { taxRegimeLabel: issuedInvoice.taxRegimeDescription ?? issuedInvoice.taxRegimeCode, fiscalClassification, - verifactuPolicy, subtotalLabel: formatMoney(issuedInvoice, issuedInvoice.subtotalAmount), itemsDiscountLabel, globalDiscountLabel, diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/adapters/get-issued-invoice-by-id.adapter.test.ts b/modules/customer-invoices/src/web/issued-invoices/shared/adapters/get-issued-invoice-by-id.adapter.test.ts index ae6553ae..d26570c6 100644 --- a/modules/customer-invoices/src/web/issued-invoices/shared/adapters/get-issued-invoice-by-id.adapter.test.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/adapters/get-issued-invoice-by-id.adapter.test.ts @@ -78,11 +78,6 @@ const buildDto = (): GetIssuedInvoiceByIdResponseDTO => ({ exemption_reason_code: null, exemption_reason_label: null, }, - verifactu_policy: { - profile_status: "obligated_with_verifactu", - uses_verifactu: true, - effective_at: "2026-01-01T00:00:00.000Z", - }, subtotal_amount: MoneyDTOHelper.fromNumber(100, "EUR"), items_discount_amount: MoneyDTOHelper.fromNumber(5, "EUR"), global_discount_percentage: PercentageDTOHelper.fromNumber(10), @@ -139,11 +134,10 @@ test("adapta el DTO de detalle a una entidad con nullables y valores numéricos assert.equal(invoice.paymentTerm?.name, "30 días"); assert.equal(invoice.paymentTerm?.dueRules[0]?.dueDays, "30"); assert.equal(invoice.fiscalClassification?.operationQualificationCode, "S1"); - assert.equal(invoice.verifactuPolicy?.usesVerifactu, true); assert.equal(invoice.globalDiscountPercentage, 10); assert.equal(invoice.taxableAmount, 85); assert.equal(invoice.totalAmount, 91); - assert.equal(invoice.verifactu.url, null); + assert.equal(invoice.verifactu?.url, null); assert.equal(invoice.items[0]?.quantity, 2); assert.equal(invoice.items[0]?.unitAmount, 50); assert.equal(invoice.items[0]?.retentionPercentage, 15); diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/adapters/get-issued-invoice-by-id.adapter.ts b/modules/customer-invoices/src/web/issued-invoices/shared/adapters/get-issued-invoice-by-id.adapter.ts index c095e131..22082da0 100644 --- a/modules/customer-invoices/src/web/issued-invoices/shared/adapters/get-issued-invoice-by-id.adapter.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/adapters/get-issued-invoice-by-id.adapter.ts @@ -1,6 +1,5 @@ import { MoneyDTOHelper, PercentageDTOHelper, QuantityDTOHelper } from "@erp/core"; -import type { GetIssuedInvoiceByIdResponseDTO } from "../../../../common"; import type { GetIssuedInvoiceByIdResult } from "../api"; import type { IssuedInvoice, @@ -10,7 +9,6 @@ import type { IssuedInvoiceRecipientDetail, IssuedInvoiceStatus, IssuedInvoiceTaxSummary, - IssuedInvoiceVerifactuPolicy, IssuedInvoiceVerifactuRecord, VerifactuRecordStatus, } from "../entities"; @@ -44,7 +42,6 @@ export const GetIssuedInvoiceByIdAdapter = { taxRegimeCode: dto.tax_regime.code, taxRegimeDescription: dto.tax_regime.description, fiscalClassification: mapFiscalClassification(dto.fiscal_classification), - verifactuPolicy: mapVerifactuPolicy(dto.verifactu_policy), subtotalAmount: MoneyDTOHelper.toNumber(dto.subtotal_amount), itemsDiscountAmount: MoneyDTOHelper.toNumber(dto.items_discount_amount), globalDiscountPercentage: PercentageDTOHelper.toNumber(dto.global_discount_percentage), @@ -62,7 +59,7 @@ export const GetIssuedInvoiceByIdAdapter = { }, }; -const mapRecipient = (dto: GetIssuedInvoiceByIdResponseDTO): IssuedInvoiceRecipientDetail => { +const mapRecipient = (dto: GetIssuedInvoiceByIdResult): IssuedInvoiceRecipientDetail => { return { id: dto.recipient.id, name: dto.recipient.name, @@ -77,7 +74,7 @@ const mapRecipient = (dto: GetIssuedInvoiceByIdResponseDTO): IssuedInvoiceRecipi }; const mapTaxSummary = ( - dto: GetIssuedInvoiceByIdResponseDTO["taxes"][number] + dto: GetIssuedInvoiceByIdResult["taxes"][number] ): IssuedInvoiceTaxSummary => { return { taxableAmount: MoneyDTOHelper.toNumber(dto.taxable_amount), @@ -94,7 +91,7 @@ const mapTaxSummary = ( }; }; -const mapItem = (dto: GetIssuedInvoiceByIdResponseDTO["items"][number]): IssuedInvoiceItem => { +const mapItem = (dto: GetIssuedInvoiceByIdResult["items"][number]): IssuedInvoiceItem => { return { id: dto.id, position: Number(dto.position), @@ -123,7 +120,7 @@ const mapItem = (dto: GetIssuedInvoiceByIdResponseDTO["items"][number]): IssuedI }; const mapPaymentTerm = ( - dto: GetIssuedInvoiceByIdResponseDTO["payment_term"] + dto: GetIssuedInvoiceByIdResult["payment_term"] ): IssuedInvoicePaymentTermDetail | null => { if (dto == null) { return null; @@ -144,7 +141,7 @@ const mapPaymentTerm = ( }; const mapVerifactu = ( - dto: GetIssuedInvoiceByIdResponseDTO["verifactu"] | null + dto: GetIssuedInvoiceByIdResult["verifactu"] ): IssuedInvoiceVerifactuRecord | null => { if (dto == null) { return null; @@ -158,7 +155,7 @@ const mapVerifactu = ( }; const mapFiscalClassification = ( - dto: GetIssuedInvoiceByIdResponseDTO["fiscal_classification"] + dto: GetIssuedInvoiceByIdResult["fiscal_classification"] ): IssuedInvoiceFiscalClassification | null => { if (dto == null) { return null; @@ -173,17 +170,3 @@ const mapFiscalClassification = ( exemptionReasonLabel: dto.exemption_reason_label, }; }; - -const mapVerifactuPolicy = ( - dto: GetIssuedInvoiceByIdResponseDTO["verifactu_policy"] -): IssuedInvoiceVerifactuPolicy | null => { - if (dto == null) { - return null; - } - - return { - profileStatus: dto.profile_status, - usesVerifactu: dto.uses_verifactu, - effectiveAt: dto.effective_at, - }; -}; diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/api/get-issued-invoice-by-id.api.ts b/modules/customer-invoices/src/web/issued-invoices/shared/api/get-issued-invoice-by-id.api.ts index 908eab67..85b822ba 100644 --- a/modules/customer-invoices/src/web/issued-invoices/shared/api/get-issued-invoice-by-id.api.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/api/get-issued-invoice-by-id.api.ts @@ -24,5 +24,5 @@ export function getIssuedInvoiceById( throw new Error("issuedInvoiceId is required"); } - return dataSource.getOne("issued-invoices", id, { signal }); + return dataSource.getOne("issued-invoices", id, { signal }); } diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/entities/issued-invoice.entity.ts b/modules/customer-invoices/src/web/issued-invoices/shared/entities/issued-invoice.entity.ts index ac5c88ca..59ce7e3e 100644 --- a/modules/customer-invoices/src/web/issued-invoices/shared/entities/issued-invoice.entity.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/entities/issued-invoice.entity.ts @@ -68,16 +68,6 @@ export interface IssuedInvoiceFiscalClassification { exemptionReasonLabel: string | null; } -export interface IssuedInvoiceVerifactuPolicy { - profileStatus: - | "not_configured" - | "exempt" - | "obligated_without_verifactu" - | "obligated_with_verifactu"; - usesVerifactu: boolean; - effectiveAt: string | null; -} - export interface IssuedInvoicePaymentTermDueRule { dueDays: string; percentage: { @@ -123,7 +113,6 @@ export interface IssuedInvoice { taxRegimeCode: string; taxRegimeDescription: string | null; fiscalClassification: IssuedInvoiceFiscalClassification | null; - verifactuPolicy: IssuedInvoiceVerifactuPolicy | null; subtotalAmount: number; itemsDiscountAmount: number; globalDiscountPercentage: number;