feat: Enhance proforma issuance with tax regime support
- Added tax regime to ProformaIssueReadModel and related services. - Implemented tax regime resolution in ProformaToIssuedInvoiceConverter. - Updated IssuedInvoiceFullSnapshotBuilder to include tax regime details. - Enhanced ProformaIssueReadModelAssembler to handle tax regime fetching. - Introduced IssueProformaResponseSnapshotBuilder for response mapping. - Updated routes and controllers to accommodate new proforma issuance logic. - Refactored request and response DTOs to include tax regime information. - Added command structure for issuing proformas.
This commit is contained in:
parent
9d7e18d73d
commit
1c0235de75
@ -1,5 +1,8 @@
|
|||||||
import type { Proforma } from "../../../domain";
|
import type { Proforma } from "../../../domain";
|
||||||
import type { InvoicePaymentMethodReadModel } from "../../common/models";
|
import type {
|
||||||
|
InvocieTaxRegimeFullReadModel,
|
||||||
|
InvoicePaymentMethodReadModel,
|
||||||
|
} from "../../common/models";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modelo de lectura usado exclusivamente durante la emisión de una proforma.
|
* Modelo de lectura usado exclusivamente durante la emisión de una proforma.
|
||||||
@ -10,4 +13,5 @@ import type { InvoicePaymentMethodReadModel } from "../../common/models";
|
|||||||
export interface ProformaIssueReadModel {
|
export interface ProformaIssueReadModel {
|
||||||
proforma: Proforma;
|
proforma: Proforma;
|
||||||
paymentMethod: InvoicePaymentMethodReadModel;
|
paymentMethod: InvoicePaymentMethodReadModel;
|
||||||
|
taxRegime: InvocieTaxRegimeFullReadModel;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
type IIssuedInvoiceCreateProps,
|
type IIssuedInvoiceCreateProps,
|
||||||
InvoicePaymentMethod,
|
InvoicePaymentMethod,
|
||||||
InvoiceStatus,
|
InvoiceStatus,
|
||||||
|
InvoiceTaxRegime,
|
||||||
IssuedInvoiceItem,
|
IssuedInvoiceItem,
|
||||||
IssuedInvoiceRecipient,
|
IssuedInvoiceRecipient,
|
||||||
IssuedInvoiceTax,
|
IssuedInvoiceTax,
|
||||||
@ -57,6 +58,12 @@ export class ProformaToIssuedInvoiceConverter implements IProformaToIssuedInvoic
|
|||||||
return Result.fail(paymentResult.error);
|
return Result.fail(paymentResult.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const taxRegimeResult = this.resolveTaxRegime(source);
|
||||||
|
|
||||||
|
if (taxRegimeResult.isFailure) {
|
||||||
|
return Result.fail(taxRegimeResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
const proformaTotals = proforma.totals();
|
const proformaTotals = proforma.totals();
|
||||||
|
|
||||||
return Result.ok({
|
return Result.ok({
|
||||||
@ -78,6 +85,7 @@ export class ProformaToIssuedInvoiceConverter implements IProformaToIssuedInvoic
|
|||||||
reference: proforma.reference,
|
reference: proforma.reference,
|
||||||
|
|
||||||
paymentMethod: paymentResult.data,
|
paymentMethod: paymentResult.data,
|
||||||
|
taxRegime: taxRegimeResult.data,
|
||||||
|
|
||||||
customerId: proforma.customerId,
|
customerId: proforma.customerId,
|
||||||
recipient: recipientResult.data,
|
recipient: recipientResult.data,
|
||||||
@ -251,4 +259,17 @@ export class ProformaToIssuedInvoiceConverter implements IProformaToIssuedInvoic
|
|||||||
|
|
||||||
return Result.ok(paymentMethodResult.data);
|
return Result.ok(paymentMethodResult.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private resolveTaxRegime(source: ProformaIssueReadModel): Result<InvoiceTaxRegime, Error> {
|
||||||
|
const taxRegimeResult = InvoiceTaxRegime.create({
|
||||||
|
code: source.taxRegime.code,
|
||||||
|
description: source.taxRegime.description || source.taxRegime.code,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (taxRegimeResult.isFailure) {
|
||||||
|
return Result.fail(taxRegimeResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(taxRegimeResult.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,11 @@ export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnaps
|
|||||||
payment_description: invoice.paymentMethod.name.toString(),
|
payment_description: invoice.paymentMethod.name.toString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const tax_regime = {
|
||||||
|
code: invoice.taxRegime.code,
|
||||||
|
description: invoice.taxRegime.description,
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: invoice.id.toString(),
|
id: invoice.id.toString(),
|
||||||
company_id: invoice.companyId.toString(),
|
company_id: invoice.companyId.toString(),
|
||||||
@ -58,6 +63,7 @@ export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnaps
|
|||||||
taxes,
|
taxes,
|
||||||
|
|
||||||
payment_method,
|
payment_method,
|
||||||
|
tax_regime,
|
||||||
|
|
||||||
subtotal_amount: invoice.subtotalAmount.toObjectString(),
|
subtotal_amount: invoice.subtotalAmount.toObjectString(),
|
||||||
|
|
||||||
|
|||||||
@ -44,6 +44,10 @@ export interface IIssuedInvoiceFullSnapshot {
|
|||||||
payment_id: string;
|
payment_id: string;
|
||||||
payment_description: string;
|
payment_description: string;
|
||||||
} | null;
|
} | null;
|
||||||
|
tax_regime: {
|
||||||
|
code: string;
|
||||||
|
description: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
subtotal_amount: { value: string; scale: string; currency_code: string };
|
subtotal_amount: { value: string; scale: string; currency_code: string };
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./issue-proforma.command";
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
export type IssueProformaCommand = {
|
||||||
|
companyId: string;
|
||||||
|
proformaId: string;
|
||||||
|
actorUserId?: string;
|
||||||
|
};
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import type { IPaymentMethodPublicFinder } from "@erp/catalogs/api";
|
import type { IPaymentMethodPublicFinder, ITaxRegimePublicFinder } from "@erp/catalogs/api";
|
||||||
|
|
||||||
import type { IProformaToIssuedInvoiceConverter } from "../../issued-invoices";
|
import type { IProformaToIssuedInvoiceConverter } from "../../issued-invoices";
|
||||||
import { type IProformaIssuer, ProformaIssueReadModelAssembler, ProformaIssuer } from "../services";
|
import { type IProformaIssuer, ProformaIssueReadModelAssembler, ProformaIssuer } from "../services";
|
||||||
@ -13,8 +13,10 @@ export const buildProformaIssuer = (params: {
|
|||||||
|
|
||||||
export const buildProformaIssueReadModelAssembler = (params: {
|
export const buildProformaIssueReadModelAssembler = (params: {
|
||||||
paymentMethodFinder: IPaymentMethodPublicFinder;
|
paymentMethodFinder: IPaymentMethodPublicFinder;
|
||||||
|
taxRegimeFinder: ITaxRegimePublicFinder;
|
||||||
}) => {
|
}) => {
|
||||||
return new ProformaIssueReadModelAssembler({
|
return new ProformaIssueReadModelAssembler({
|
||||||
paymentMethodFinder: params.paymentMethodFinder,
|
paymentMethodFinder: params.paymentMethodFinder,
|
||||||
|
taxRegimeFinder: params.taxRegimeFinder,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
// application/issued-invoices/di/snapshot-builders.di.ts
|
// application/issued-invoices/di/snapshot-builders.di.ts
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
IssueProformaResponseSnapshotBuilder,
|
||||||
ProformaFullSnapshotBuilder,
|
ProformaFullSnapshotBuilder,
|
||||||
ProformaItemReportSnapshotBuilder,
|
ProformaItemReportSnapshotBuilder,
|
||||||
ProformaItemsFullSnapshotBuilder,
|
ProformaItemsFullSnapshotBuilder,
|
||||||
@ -32,9 +33,11 @@ export function buildProformaSnapshotBuilders() {
|
|||||||
itemsReportBuilder,
|
itemsReportBuilder,
|
||||||
taxesReportBuilder
|
taxesReportBuilder
|
||||||
);
|
);
|
||||||
|
const issueResponseSnapshotBuilder = new IssueProformaResponseSnapshotBuilder();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
full: fullSnapshotBuilder,
|
full: fullSnapshotBuilder,
|
||||||
|
issue: issueResponseSnapshotBuilder,
|
||||||
summary: summarySnapshotBuilder,
|
summary: summarySnapshotBuilder,
|
||||||
report: reportSnapshotBuilder,
|
report: reportSnapshotBuilder,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import type {
|
|||||||
} from "../services";
|
} from "../services";
|
||||||
import type {
|
import type {
|
||||||
IProformaFullSnapshotBuilder,
|
IProformaFullSnapshotBuilder,
|
||||||
|
IIssueProformaResponseSnapshotBuilder,
|
||||||
IProformaReportSnapshotBuilder,
|
IProformaReportSnapshotBuilder,
|
||||||
IProformaSummarySnapshotBuilder,
|
IProformaSummarySnapshotBuilder,
|
||||||
} from "../snapshot-builders";
|
} from "../snapshot-builders";
|
||||||
@ -121,6 +122,7 @@ export function buildIssueProformaUseCase(deps: {
|
|||||||
finder: IProformaFinder;
|
finder: IProformaFinder;
|
||||||
issuer: IProformaIssuer;
|
issuer: IProformaIssuer;
|
||||||
issueReadModelAssembler: IProformaIssueReadModelAssembler;
|
issueReadModelAssembler: IProformaIssueReadModelAssembler;
|
||||||
|
issueResponseSnapshotBuilder: IIssueProformaResponseSnapshotBuilder;
|
||||||
repository: IProformaRepository;
|
repository: IProformaRepository;
|
||||||
transactionManager: ITransactionManager;
|
transactionManager: ITransactionManager;
|
||||||
}) {
|
}) {
|
||||||
@ -128,6 +130,7 @@ export function buildIssueProformaUseCase(deps: {
|
|||||||
finder,
|
finder,
|
||||||
issuer,
|
issuer,
|
||||||
issueReadModelAssembler,
|
issueReadModelAssembler,
|
||||||
|
issueResponseSnapshotBuilder,
|
||||||
repository,
|
repository,
|
||||||
transactionManager,
|
transactionManager,
|
||||||
publicServices: { issuedInvoiceServices },
|
publicServices: { issuedInvoiceServices },
|
||||||
@ -138,6 +141,7 @@ export function buildIssueProformaUseCase(deps: {
|
|||||||
finder,
|
finder,
|
||||||
issuer,
|
issuer,
|
||||||
issueReadModelAssembler,
|
issueReadModelAssembler,
|
||||||
|
issueResponseSnapshotBuilder,
|
||||||
repository,
|
repository,
|
||||||
transactionManager,
|
transactionManager,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
export * from "./commands";
|
||||||
export * from "./di";
|
export * from "./di";
|
||||||
export * from "./errors";
|
export * from "./errors";
|
||||||
export * from "./mappers";
|
export * from "./mappers";
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import { Maybe, NumberHelper, Result } from "@repo/rdx-utils";
|
|||||||
|
|
||||||
import type { CreateProformaRequestDTO } from "../../../../common";
|
import type { CreateProformaRequestDTO } from "../../../../common";
|
||||||
import {
|
import {
|
||||||
InvoiceNumber,
|
|
||||||
type InvoiceRecipient,
|
type InvoiceRecipient,
|
||||||
InvoiceSerie,
|
InvoiceSerie,
|
||||||
InvoiceStatus,
|
InvoiceStatus,
|
||||||
@ -59,12 +58,6 @@ export class CreateProformaInputMapper implements ICreateProformaInputMapper {
|
|||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const invoiceNumber = extractOrPushError(
|
|
||||||
InvoiceNumber.create(dto.invoice_number),
|
|
||||||
"invoice_number",
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const series = extractOrPushError(
|
const series = extractOrPushError(
|
||||||
maybeFromNullableResult(dto.series, (value) => InvoiceSerie.create(value)),
|
maybeFromNullableResult(dto.series, (value) => InvoiceSerie.create(value)),
|
||||||
"series",
|
"series",
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { IPaymentMethodPublicFinder } from "@erp/catalogs/api";
|
import type { IPaymentMethodPublicFinder, ITaxRegimePublicFinder } from "@erp/catalogs/api";
|
||||||
import type { UniqueID } from "@repo/rdx-ddd";
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ export class ProformaIssueReadModelAssembler implements IProformaIssueReadModelA
|
|||||||
public constructor(
|
public constructor(
|
||||||
private readonly deps: {
|
private readonly deps: {
|
||||||
paymentMethodFinder: IPaymentMethodPublicFinder;
|
paymentMethodFinder: IPaymentMethodPublicFinder;
|
||||||
|
taxRegimeFinder: ITaxRegimePublicFinder;
|
||||||
}
|
}
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -47,6 +48,12 @@ export class ProformaIssueReadModelAssembler implements IProformaIssueReadModelA
|
|||||||
return Result.fail(paymentMethodResult.error);
|
return Result.fail(paymentMethodResult.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const taxRegimeResult = await this.resolveTaxRegime(params);
|
||||||
|
|
||||||
|
if (taxRegimeResult.isFailure) {
|
||||||
|
return Result.fail(taxRegimeResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
const paymentMethod = paymentMethodResult.data;
|
const paymentMethod = paymentMethodResult.data;
|
||||||
|
|
||||||
return Result.ok({
|
return Result.ok({
|
||||||
@ -56,6 +63,43 @@ export class ProformaIssueReadModelAssembler implements IProformaIssueReadModelA
|
|||||||
name: paymentMethod.name,
|
name: paymentMethod.name,
|
||||||
description: paymentMethod.description,
|
description: paymentMethod.description,
|
||||||
},
|
},
|
||||||
|
taxRegime: taxRegimeResult.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async resolveTaxRegime(params: {
|
||||||
|
companyId: UniqueID;
|
||||||
|
proforma: Proforma;
|
||||||
|
transaction?: unknown;
|
||||||
|
}) {
|
||||||
|
if (params.proforma.taxRegimeCode.isNone()) {
|
||||||
|
return Result.fail(new Error("Tax regime is required to issue proforma"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const taxRegimeCode = params.proforma.taxRegimeCode.unwrap();
|
||||||
|
|
||||||
|
const taxRegimeResult = await this.deps.taxRegimeFinder.findByCodeInCompany({
|
||||||
|
companyId: params.companyId,
|
||||||
|
code: taxRegimeCode,
|
||||||
|
transaction: params.transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (taxRegimeResult.isFailure) {
|
||||||
|
return Result.fail(taxRegimeResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (taxRegimeResult.data.isNone()) {
|
||||||
|
return Result.ok({
|
||||||
|
code: taxRegimeCode,
|
||||||
|
description: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const taxRegime = taxRegimeResult.data.unwrap();
|
||||||
|
|
||||||
|
return Result.ok({
|
||||||
|
code: taxRegime.code,
|
||||||
|
description: taxRegime.description,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
export * from "./full";
|
export * from "./full";
|
||||||
|
export * from "./issue";
|
||||||
export * from "./report";
|
export * from "./report";
|
||||||
export * from "./summary";
|
export * from "./summary";
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./issue-proforma-response.snapshot-builder";
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { IssueProformaByIdResponseDTO } from "../../../../../common";
|
||||||
|
import type { IssuedInvoice } from "../../../../domain";
|
||||||
|
|
||||||
|
export interface IIssueProformaResponseSnapshotBuilder
|
||||||
|
extends ISnapshotBuilder<IssuedInvoice, IssueProformaByIdResponseDTO> {}
|
||||||
|
|
||||||
|
export class IssueProformaResponseSnapshotBuilder
|
||||||
|
implements IIssueProformaResponseSnapshotBuilder
|
||||||
|
{
|
||||||
|
toOutput(invoice: IssuedInvoice): IssueProformaByIdResponseDTO {
|
||||||
|
return {
|
||||||
|
issued_invoice: {
|
||||||
|
id: invoice.id.toString(),
|
||||||
|
proforma_id: invoice.linkedProformaId.toString(),
|
||||||
|
series: invoice.series.toString(),
|
||||||
|
invoice_number: invoice.invoiceNumber.toString(),
|
||||||
|
invoice_date: invoice.invoiceDate.toDateString(),
|
||||||
|
status: invoice.status.toPrimitive(),
|
||||||
|
recipient: {
|
||||||
|
name: invoice.recipient.name.toString(),
|
||||||
|
tin: invoice.recipient.tin.toString(),
|
||||||
|
},
|
||||||
|
tax_regime: {
|
||||||
|
code: invoice.taxRegime.code,
|
||||||
|
description: invoice.taxRegime.description,
|
||||||
|
},
|
||||||
|
totals: {
|
||||||
|
taxable_base: invoice.taxableAmount.toObjectString(),
|
||||||
|
tax_total: invoice.taxesAmount.toObjectString(),
|
||||||
|
retention_total: invoice.retentionAmount.toObjectString(),
|
||||||
|
grand_total: invoice.totalAmount.toObjectString(),
|
||||||
|
currency_code: invoice.currencyCode.toPrimitive(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,17 +3,14 @@ import { UniqueID } from "@repo/rdx-ddd";
|
|||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { IIssuedInvoicePublicServices } from "../../issued-invoices";
|
import type { IIssuedInvoicePublicServices } from "../../issued-invoices";
|
||||||
|
import type { IssueProformaCommand } from "../commands";
|
||||||
import type { IProformaRepository } from "../repositories";
|
import type { IProformaRepository } from "../repositories";
|
||||||
import type {
|
import type {
|
||||||
IProformaFinder,
|
IProformaFinder,
|
||||||
IProformaIssueReadModelAssembler,
|
IProformaIssueReadModelAssembler,
|
||||||
IProformaIssuer,
|
IProformaIssuer,
|
||||||
} from "../services";
|
} from "../services";
|
||||||
|
import type { IIssueProformaResponseSnapshotBuilder } from "../snapshot-builders";
|
||||||
type IssueProformaUseCaseInput = {
|
|
||||||
companyId: UniqueID;
|
|
||||||
proforma_id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caso de uso: conversión de una proforma en factura definitiva.
|
* Caso de uso: conversión de una proforma en factura definitiva.
|
||||||
@ -31,15 +28,22 @@ export class IssueProformaUseCase {
|
|||||||
finder: IProformaFinder;
|
finder: IProformaFinder;
|
||||||
issuer: IProformaIssuer;
|
issuer: IProformaIssuer;
|
||||||
issueReadModelAssembler: IProformaIssueReadModelAssembler;
|
issueReadModelAssembler: IProformaIssueReadModelAssembler;
|
||||||
|
issueResponseSnapshotBuilder: IIssueProformaResponseSnapshotBuilder;
|
||||||
repository: IProformaRepository;
|
repository: IProformaRepository;
|
||||||
transactionManager: ITransactionManager;
|
transactionManager: ITransactionManager;
|
||||||
}
|
}
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public execute(params: IssueProformaUseCaseInput) {
|
public execute(command: IssueProformaCommand) {
|
||||||
const { proforma_id, companyId } = params;
|
const companyIdResult = UniqueID.create(command.companyId);
|
||||||
|
|
||||||
const proformaIdResult = UniqueID.create(proforma_id);
|
if (companyIdResult.isFailure) {
|
||||||
|
return Result.fail(companyIdResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const companyId = companyIdResult.data;
|
||||||
|
|
||||||
|
const proformaIdResult = UniqueID.create(command.proformaId);
|
||||||
|
|
||||||
if (proformaIdResult.isFailure) {
|
if (proformaIdResult.isFailure) {
|
||||||
return Result.fail(proformaIdResult.error);
|
return Result.fail(proformaIdResult.error);
|
||||||
@ -106,11 +110,7 @@ export class IssueProformaUseCase {
|
|||||||
return Result.fail(markAsIssuedResult.error);
|
return Result.fail(markAsIssuedResult.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.ok({
|
return Result.ok(this.deps.issueResponseSnapshotBuilder.toOutput(invoiceResult.data));
|
||||||
issuedinvoice_id: issuedInvoiceId.toString(),
|
|
||||||
proforma_id: proformaId.toString(),
|
|
||||||
customer_id: proforma.customerId.toString(),
|
|
||||||
});
|
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
return Result.fail(error as Error);
|
return Result.fail(error as Error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import type {
|
|||||||
InvoicePaymentMethod,
|
InvoicePaymentMethod,
|
||||||
InvoiceSerie,
|
InvoiceSerie,
|
||||||
InvoiceStatus,
|
InvoiceStatus,
|
||||||
|
InvoiceTaxRegime,
|
||||||
} from "../../common";
|
} from "../../common";
|
||||||
import {
|
import {
|
||||||
type IIssuedInvoiceItemCreateProps,
|
type IIssuedInvoiceItemCreateProps,
|
||||||
@ -51,6 +52,7 @@ export interface IIssuedInvoiceCreateProps {
|
|||||||
currencyCode: CurrencyCode;
|
currencyCode: CurrencyCode;
|
||||||
|
|
||||||
paymentMethod: InvoicePaymentMethod;
|
paymentMethod: InvoicePaymentMethod;
|
||||||
|
taxRegime: InvoiceTaxRegime;
|
||||||
|
|
||||||
items: IIssuedInvoiceItemCreateProps[];
|
items: IIssuedInvoiceItemCreateProps[];
|
||||||
taxes: IssuedInvoiceTaxes;
|
taxes: IssuedInvoiceTaxes;
|
||||||
@ -139,6 +141,16 @@ export class IssuedInvoice
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!props.taxRegime) {
|
||||||
|
return Result.fail(
|
||||||
|
new DomainValidationError(
|
||||||
|
"MISSING_TAX_REGIME",
|
||||||
|
"taxRegime",
|
||||||
|
"Issued invoice requires tax regime"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Result.ok();
|
return Result.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,6 +234,10 @@ export class IssuedInvoice
|
|||||||
return this.props.paymentMethod;
|
return this.props.paymentMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get taxRegime(): InvoiceTaxRegime {
|
||||||
|
return this.props.taxRegime;
|
||||||
|
}
|
||||||
|
|
||||||
public get languageCode(): LanguageCode {
|
public get languageCode(): LanguageCode {
|
||||||
return this.props.languageCode;
|
return this.props.languageCode;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { DiscountPercentage, type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
import { DiscountPercentage, type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
||||||
import {
|
import {
|
||||||
CurrencyCode,
|
CurrencyCode,
|
||||||
|
DomainValidationError,
|
||||||
LanguageCode,
|
LanguageCode,
|
||||||
TextValue,
|
TextValue,
|
||||||
UniqueID,
|
UniqueID,
|
||||||
@ -20,6 +21,7 @@ import {
|
|||||||
InvoicePaymentMethod,
|
InvoicePaymentMethod,
|
||||||
InvoiceSerie,
|
InvoiceSerie,
|
||||||
InvoiceStatus,
|
InvoiceStatus,
|
||||||
|
InvoiceTaxRegime,
|
||||||
IssuedInvoice,
|
IssuedInvoice,
|
||||||
IssuedInvoiceItems,
|
IssuedInvoiceItems,
|
||||||
IssuedInvoiceTaxes,
|
IssuedInvoiceTaxes,
|
||||||
@ -154,6 +156,23 @@ export class SequelizeIssuedInvoiceDomainMapper extends SequelizeDomainMapper<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const taxRegime = extractOrPushError(
|
||||||
|
!isNullishOrEmpty(raw.tax_regime_code)
|
||||||
|
? InvoiceTaxRegime.create({
|
||||||
|
code: String(raw.tax_regime_code),
|
||||||
|
description: String(raw.tax_regime_description ?? raw.tax_regime_code),
|
||||||
|
})
|
||||||
|
: Result.fail(
|
||||||
|
new DomainValidationError(
|
||||||
|
"MISSING_TAX_REGIME",
|
||||||
|
"tax_regime",
|
||||||
|
"Issued invoice requires persisted tax regime"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"tax_regime",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
const subtotalAmount = extractOrPushError(
|
const subtotalAmount = extractOrPushError(
|
||||||
InvoiceAmount.create({
|
InvoiceAmount.create({
|
||||||
value: raw.subtotal_amount_value,
|
value: raw.subtotal_amount_value,
|
||||||
@ -270,6 +289,7 @@ export class SequelizeIssuedInvoiceDomainMapper extends SequelizeDomainMapper<
|
|||||||
languageCode,
|
languageCode,
|
||||||
currencyCode,
|
currencyCode,
|
||||||
paymentMethod,
|
paymentMethod,
|
||||||
|
taxRegime,
|
||||||
|
|
||||||
subtotalAmount,
|
subtotalAmount,
|
||||||
itemsDiscountAmount,
|
itemsDiscountAmount,
|
||||||
@ -384,6 +404,7 @@ export class SequelizeIssuedInvoiceDomainMapper extends SequelizeDomainMapper<
|
|||||||
totalAmount: attributes.totalAmount!,
|
totalAmount: attributes.totalAmount!,
|
||||||
|
|
||||||
paymentMethod: attributes.paymentMethod!,
|
paymentMethod: attributes.paymentMethod!,
|
||||||
|
taxRegime: attributes.taxRegime!,
|
||||||
|
|
||||||
taxes,
|
taxes,
|
||||||
verifactu,
|
verifactu,
|
||||||
@ -483,6 +504,8 @@ export class SequelizeIssuedInvoiceDomainMapper extends SequelizeDomainMapper<
|
|||||||
|
|
||||||
payment_method_id: source.paymentMethod.toObjectString().id,
|
payment_method_id: source.paymentMethod.toObjectString().id,
|
||||||
payment_method_description: source.paymentMethod.toObjectString().name,
|
payment_method_description: source.paymentMethod.toObjectString().name,
|
||||||
|
tax_regime_code: source.taxRegime.code,
|
||||||
|
tax_regime_description: source.taxRegime.description,
|
||||||
|
|
||||||
subtotal_amount_value: source.subtotalAmount.value,
|
subtotal_amount_value: source.subtotalAmount.value,
|
||||||
subtotal_amount_scale: source.subtotalAmount.scale,
|
subtotal_amount_scale: source.subtotalAmount.scale,
|
||||||
|
|||||||
@ -117,6 +117,7 @@ export function buildProformasDependencies(params: ModuleParams): ProformasInter
|
|||||||
|
|
||||||
const issueReadModelAssembler = buildProformaIssueReadModelAssembler({
|
const issueReadModelAssembler = buildProformaIssueReadModelAssembler({
|
||||||
paymentMethodFinder: catalogs.paymentMethod.finder,
|
paymentMethodFinder: catalogs.paymentMethod.finder,
|
||||||
|
taxRegimeFinder: catalogs.taxRegime.finder,
|
||||||
});
|
});
|
||||||
|
|
||||||
const companyReportProfileFinder = new CompanyReportProfileFinder({
|
const companyReportProfileFinder = new CompanyReportProfileFinder({
|
||||||
@ -188,6 +189,7 @@ export function buildProformasDependencies(params: ModuleParams): ProformasInter
|
|||||||
finder,
|
finder,
|
||||||
issuer,
|
issuer,
|
||||||
issueReadModelAssembler,
|
issueReadModelAssembler,
|
||||||
|
issueResponseSnapshotBuilder: snapshotBuilders.issue,
|
||||||
repository,
|
repository,
|
||||||
transactionManager,
|
transactionManager,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -5,11 +5,16 @@ import {
|
|||||||
requireCompanyContextGuard,
|
requireCompanyContextGuard,
|
||||||
} from "@erp/core/api";
|
} from "@erp/core/api";
|
||||||
|
|
||||||
|
import { IssueProformaByIdResponseSchema } from "../../../../../common";
|
||||||
import type { IssueProformaUseCase } from "../../../../application/proformas";
|
import type { IssueProformaUseCase } from "../../../../application/proformas";
|
||||||
|
import type { IIssueProformaByIdRequestMapper } from "../mappers";
|
||||||
import { proformasApiErrorMapper } from "../proformas-api-error-mapper.ts";
|
import { proformasApiErrorMapper } from "../proformas-api-error-mapper.ts";
|
||||||
|
|
||||||
export class IssueProformaController extends ExpressController {
|
export class IssueProformaController extends ExpressController {
|
||||||
public constructor(private readonly useCase: IssueProformaUseCase) {
|
public constructor(
|
||||||
|
private readonly useCase: IssueProformaUseCase,
|
||||||
|
private readonly requestMapper: IIssueProformaByIdRequestMapper
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
this.errorMapper = proformasApiErrorMapper;
|
this.errorMapper = proformasApiErrorMapper;
|
||||||
|
|
||||||
@ -27,15 +32,26 @@ export class IssueProformaController extends ExpressController {
|
|||||||
return this.forbiddenError("Tenant ID not found");
|
return this.forbiddenError("Tenant ID not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { proforma_id } = this.req.params;
|
const params = this.req.params;
|
||||||
if (!proforma_id) {
|
const proformaId = params.id ?? params.proforma_id;
|
||||||
|
|
||||||
|
if (!proformaId) {
|
||||||
return this.invalidInputError("Proforma ID missing");
|
return this.invalidInputError("Proforma ID missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await this.useCase.execute({ proforma_id, companyId });
|
const command = this.requestMapper.map({
|
||||||
|
companyId: companyId.toString(),
|
||||||
|
params: params as
|
||||||
|
| { id: string }
|
||||||
|
| {
|
||||||
|
proforma_id: string;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await this.useCase.execute(command);
|
||||||
|
|
||||||
return result.match(
|
return result.match(
|
||||||
(data) => this.ok(data),
|
(data) => this.ok(IssueProformaByIdResponseSchema.parse(data)),
|
||||||
(err) => this.handleError(err)
|
(err) => this.handleError(err)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +1,3 @@
|
|||||||
export * from "./controllers";
|
export * from "./controllers";
|
||||||
|
export * from "./mappers";
|
||||||
export * from "./proformas.routes";
|
export * from "./proformas.routes";
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./issue-proforma-by-id-request.mapper";
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import type {
|
||||||
|
IssueProformaByIdParamsRequestDTO,
|
||||||
|
LegacyIssueProformaByIdParamsRequestDTO,
|
||||||
|
} from "../../../../../common";
|
||||||
|
import type { IssueProformaCommand } from "../../../../application/proformas";
|
||||||
|
|
||||||
|
type IssueProformaRequestMapperInput = {
|
||||||
|
companyId: string;
|
||||||
|
params: IssueProformaByIdParamsRequestDTO | LegacyIssueProformaByIdParamsRequestDTO;
|
||||||
|
actorUserId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IIssueProformaByIdRequestMapper {
|
||||||
|
map(params: IssueProformaRequestMapperInput): IssueProformaCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IssueProformaByIdRequestMapper implements IIssueProformaByIdRequestMapper {
|
||||||
|
map(params: IssueProformaRequestMapperInput): IssueProformaCommand {
|
||||||
|
const proformaId = "id" in params.params ? params.params.id : params.params.proforma_id;
|
||||||
|
|
||||||
|
return {
|
||||||
|
companyId: params.companyId,
|
||||||
|
proformaId,
|
||||||
|
actorUserId: params.actorUserId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,6 +15,7 @@ import {
|
|||||||
ChangeStatusProformaByIdParamsRequestSchema,
|
ChangeStatusProformaByIdParamsRequestSchema,
|
||||||
ChangeStatusProformaByIdRequestSchema,
|
ChangeStatusProformaByIdRequestSchema,
|
||||||
CreateProformaRequestSchema,
|
CreateProformaRequestSchema,
|
||||||
|
LegacyIssueProformaByIdParamsRequestSchema,
|
||||||
GetProformaByIdRequestSchema,
|
GetProformaByIdRequestSchema,
|
||||||
IssueProformaByIdParamsRequestSchema,
|
IssueProformaByIdParamsRequestSchema,
|
||||||
ListProformasRequestSchema,
|
ListProformasRequestSchema,
|
||||||
@ -27,6 +28,7 @@ import type { IIssuedInvoicePublicServices } from "../../../application";
|
|||||||
|
|
||||||
import { ChangeStatusProformaController } from "./controllers/change-status-proforma.controller";
|
import { ChangeStatusProformaController } from "./controllers/change-status-proforma.controller";
|
||||||
import { CreateProformaController } from "./controllers/create-proforma.controller";
|
import { CreateProformaController } from "./controllers/create-proforma.controller";
|
||||||
|
import { IssueProformaByIdRequestMapper } from "./mappers";
|
||||||
import { UpdateProformaController } from "./controllers/update-proforma.controller";
|
import { UpdateProformaController } from "./controllers/update-proforma.controller";
|
||||||
|
|
||||||
export const proformasRouter = (params: StartParams) => {
|
export const proformasRouter = (params: StartParams) => {
|
||||||
@ -141,17 +143,29 @@ export const proformasRouter = (params: StartParams) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const issueProformaHandler = (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const useCase = deps.useCases.issueProforma(publicServices);
|
||||||
|
const requestMapper = new IssueProformaByIdRequestMapper();
|
||||||
|
const controller = new IssueProformaController(useCase, requestMapper);
|
||||||
|
return controller.execute(req, res, next);
|
||||||
|
};
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/:id/issue",
|
||||||
|
validateRequest(IssueProformaByIdParamsRequestSchema, "params"),
|
||||||
|
issueProformaHandler
|
||||||
|
);
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/:proforma_id/issue",
|
||||||
|
validateRequest(LegacyIssueProformaByIdParamsRequestSchema, "params"),
|
||||||
|
issueProformaHandler
|
||||||
|
);
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
"/:proforma_id/issue",
|
"/:proforma_id/issue",
|
||||||
//checkTabContext,
|
validateRequest(LegacyIssueProformaByIdParamsRequestSchema, "params"),
|
||||||
|
issueProformaHandler
|
||||||
validateRequest(IssueProformaByIdParamsRequestSchema, "params"),
|
|
||||||
|
|
||||||
(req: Request, res: Response, next: NextFunction) => {
|
|
||||||
const useCase = deps.useCases.issueProforma(publicServices);
|
|
||||||
const controller = new IssueProformaController(useCase);
|
|
||||||
return controller.execute(req, res, next);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
app.use(`${config.server.apiBasePath}/proformas`, router);
|
app.use(`${config.server.apiBasePath}/proformas`, router);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { UniqueID } from "@repo/rdx-ddd";
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
import { type Maybe, Result } from "@repo/rdx-utils";
|
import { type Maybe, Result } from "@repo/rdx-utils";
|
||||||
import { type Transaction, type WhereOptions, literal } from "sequelize";
|
import { type Transaction, type WhereOptions } from "sequelize";
|
||||||
|
|
||||||
import type { IProformaNumberGenerator } from "../../../../../application/proformas";
|
import type { IProformaNumberGenerator } from "../../../../../application/proformas";
|
||||||
import { InvoiceNumber, type InvoiceSerie } from "../../../../../domain";
|
import { InvoiceNumber, type InvoiceSerie } from "../../../../../domain";
|
||||||
@ -11,6 +11,9 @@ import { CustomerInvoiceModel } from "../../../../common/persistence";
|
|||||||
* La serie elegida en la proforma solo referencia la futura InvoiceSeries.
|
* La serie elegida en la proforma solo referencia la futura InvoiceSeries.
|
||||||
*/
|
*/
|
||||||
export class SequelizeProformaNumberGenerator implements IProformaNumberGenerator {
|
export class SequelizeProformaNumberGenerator implements IProformaNumberGenerator {
|
||||||
|
private static readonly PREFIX = "PF-";
|
||||||
|
private static readonly DIGITS = 4;
|
||||||
|
|
||||||
public async getNextForCompany(
|
public async getNextForCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
_series: Maybe<InvoiceSerie>,
|
_series: Maybe<InvoiceSerie>,
|
||||||
@ -22,24 +25,20 @@ export class SequelizeProformaNumberGenerator implements IProformaNumberGenerato
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const lastInvoice = await CustomerInvoiceModel.findOne({
|
const existingInvoices = await CustomerInvoiceModel.findAll({
|
||||||
attributes: ["invoice_number"],
|
attributes: ["invoice_number"],
|
||||||
where,
|
where,
|
||||||
// Orden numérico real: CAST(... AS UNSIGNED)
|
|
||||||
order: [literal("CAST(invoice_number AS UNSIGNED) DESC")],
|
|
||||||
transaction,
|
transaction,
|
||||||
raw: true,
|
raw: true,
|
||||||
// Bloqueo opcional para evitar carreras si estás dentro de una TX
|
lock: transaction.LOCK.UPDATE,
|
||||||
lock: transaction.LOCK.UPDATE, // requiere InnoDB y TX abierta
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let nextValue = "0001"; // valor inicial por defecto
|
const maxSequence = existingInvoices.reduce((max, currentInvoice) => {
|
||||||
|
const currentSequence = this.extractSequence(currentInvoice.invoice_number);
|
||||||
|
return Math.max(max, currentSequence);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
if (lastInvoice) {
|
const nextValue = this.formatSequence(maxSequence + 1);
|
||||||
const current = Number(lastInvoice.invoice_number);
|
|
||||||
const next = Number.isFinite(current) && current > 0 ? current + 1 : 1;
|
|
||||||
nextValue = String(next).padStart(4, "0");
|
|
||||||
}
|
|
||||||
|
|
||||||
const numberResult = InvoiceNumber.create(nextValue);
|
const numberResult = InvoiceNumber.create(nextValue);
|
||||||
if (numberResult.isFailure) {
|
if (numberResult.isFailure) {
|
||||||
@ -50,9 +49,27 @@ export class SequelizeProformaNumberGenerator implements IProformaNumberGenerato
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return Result.fail(
|
return Result.fail(
|
||||||
new Error(
|
new Error(
|
||||||
`Error generating invoice number for company ${companyId}: ${(error as Error).message}`
|
`Error generating proforma number for company ${companyId}: ${(error as Error).message}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extractSequence(invoiceNumber: string): number {
|
||||||
|
const normalized = invoiceNumber.trim().toUpperCase();
|
||||||
|
|
||||||
|
if (normalized.startsWith(SequelizeProformaNumberGenerator.PREFIX)) {
|
||||||
|
const suffix = normalized.slice(SequelizeProformaNumberGenerator.PREFIX.length);
|
||||||
|
const parsed = Number(suffix);
|
||||||
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed = Number(normalized);
|
||||||
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatSequence(sequence: number): string {
|
||||||
|
const padded = String(sequence).padStart(SequelizeProformaNumberGenerator.DIGITS, "0");
|
||||||
|
return `${SequelizeProformaNumberGenerator.PREFIX}${padded}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ export type CreateProformaItemRequestDTO = z.infer<typeof CreateProformaItemRequ
|
|||||||
export const CreateProformaRequestSchema = z.object({
|
export const CreateProformaRequestSchema = z.object({
|
||||||
id: z.uuid(),
|
id: z.uuid(),
|
||||||
|
|
||||||
invoice_number: z.string(),
|
invoice_number: z.string().nullable().optional(),
|
||||||
series: z.string().nullable(),
|
series: z.string().nullable(),
|
||||||
|
|
||||||
invoice_date: IsoDateSchema,
|
invoice_date: IsoDateSchema,
|
||||||
|
|||||||
@ -1,9 +1,17 @@
|
|||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
export const IssueProformaByIdParamsRequestSchema = z.object({
|
export const IssueProformaByIdParamsRequestSchema = z.object({
|
||||||
proforma_id: z.string(),
|
id: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type IssueProformaByIdParamsRequestDTO = z.infer<
|
export type IssueProformaByIdParamsRequestDTO = z.infer<
|
||||||
typeof IssueProformaByIdParamsRequestSchema
|
typeof IssueProformaByIdParamsRequestSchema
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export const LegacyIssueProformaByIdParamsRequestSchema = z.object({
|
||||||
|
proforma_id: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type LegacyIssueProformaByIdParamsRequestDTO = z.infer<
|
||||||
|
typeof LegacyIssueProformaByIdParamsRequestSchema
|
||||||
|
>;
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export const GetIssuedInvoiceByIdResponseSchema = z.object({
|
|||||||
|
|
||||||
payment_method: PaymentMethodRefSchema.nullable(),
|
payment_method: PaymentMethodRefSchema.nullable(),
|
||||||
|
|
||||||
tax_regime: TaxRegimeRefSchema.nullable(),
|
tax_regime: TaxRegimeRefSchema,
|
||||||
|
|
||||||
subtotal_amount: MoneySchema,
|
subtotal_amount: MoneySchema,
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,35 @@
|
|||||||
|
import { CurrencyCodeSchema, IsoDateSchema, MoneySchema } from "@erp/core";
|
||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
|
import { IssuedInvoiceStatusSchema, TaxRegimeRefSchema } from "../../shared";
|
||||||
|
|
||||||
|
export const IssueProformaIssuedInvoiceRecipientSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
tin: z.string().nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const IssueProformaIssuedInvoiceTotalsSchema = z.object({
|
||||||
|
taxable_base: MoneySchema,
|
||||||
|
tax_total: MoneySchema,
|
||||||
|
retention_total: MoneySchema,
|
||||||
|
grand_total: MoneySchema,
|
||||||
|
currency_code: CurrencyCodeSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const IssueProformaIssuedInvoiceSchema = z.object({
|
||||||
|
id: z.uuid(),
|
||||||
|
proforma_id: z.uuid().nullable(),
|
||||||
|
series: z.string(),
|
||||||
|
invoice_number: z.string(),
|
||||||
|
invoice_date: IsoDateSchema,
|
||||||
|
status: IssuedInvoiceStatusSchema,
|
||||||
|
recipient: IssueProformaIssuedInvoiceRecipientSchema,
|
||||||
|
tax_regime: TaxRegimeRefSchema,
|
||||||
|
totals: IssueProformaIssuedInvoiceTotalsSchema,
|
||||||
|
});
|
||||||
|
|
||||||
export const IssueProformaByIdResponseSchema = z.object({
|
export const IssueProformaByIdResponseSchema = z.object({
|
||||||
issuedinvoice_id: z.string(),
|
issued_invoice: IssueProformaIssuedInvoiceSchema,
|
||||||
proforma_id: z.string(),
|
|
||||||
customer_id: z.string(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type IssueProformaByIdResponseDTO = z.infer<typeof IssueProformaByIdResponseSchema>;
|
export type IssueProformaByIdResponseDTO = z.infer<typeof IssueProformaByIdResponseSchema>;
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { z } from "zod/v4";
|
|||||||
|
|
||||||
export const TaxRegimeRefSchema = z.object({
|
export const TaxRegimeRefSchema = z.object({
|
||||||
code: z.string(),
|
code: z.string(),
|
||||||
description: z.string(),
|
description: z.string().nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TaxRegimeRefDTO = z.infer<typeof TaxRegimeRefSchema>;
|
export type TaxRegimeRefDTO = z.infer<typeof TaxRegimeRefSchema>;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user