Compare commits

..

No commits in common. "7f77b18d52e36a6abc5b8df090a792044c58adf4" and "97544b012dd1bfe766277624a6410c8e787f8872" have entirely different histories.

82 changed files with 355 additions and 422 deletions

View File

@ -1,4 +1,4 @@
import type { CurrencyCode, LanguageCode, UniqueID, UtcDate } from "@repo/rdx-ddd"; import type { CurrencyCode, LanguageCode, Percentage, UniqueID, UtcDate } from "@repo/rdx-ddd";
import type { Maybe } from "@repo/rdx-utils"; import type { Maybe } from "@repo/rdx-utils";
import type { import type {
@ -31,8 +31,10 @@ export type IssuedInvoiceListDTO = {
languageCode: LanguageCode; languageCode: LanguageCode;
currencyCode: CurrencyCode; currencyCode: CurrencyCode;
discountPercentage: Percentage;
subtotalAmount: InvoiceAmount; subtotalAmount: InvoiceAmount;
totalDiscountAmount: InvoiceAmount; discountAmount: InvoiceAmount;
taxableAmount: InvoiceAmount; taxableAmount: InvoiceAmount;
taxesAmount: InvoiceAmount; taxesAmount: InvoiceAmount;
totalAmount: InvoiceAmount; totalAmount: InvoiceAmount;

View File

@ -14,13 +14,6 @@ export interface IIssuedInvoiceRepository {
transaction: unknown transaction: unknown
): Promise<Result<IssuedInvoice, Error>>; ): Promise<Result<IssuedInvoice, Error>>;
existsByIdInCompany(
companyId: UniqueID,
id: UniqueID,
transaction: unknown,
options: unknown
): Promise<Result<boolean, Error>>;
findByCriteriaInCompany( findByCriteriaInCompany(
companyId: UniqueID, companyId: UniqueID,
criteria: Criteria, criteria: Criteria,

View File

@ -1,18 +1,17 @@
import type { CustomerInvoiceListDTO } from "@erp/customer-invoices/api/infrastructure";
import type { Criteria } from "@repo/rdx-criteria/server"; import type { Criteria } from "@repo/rdx-criteria/server";
import type { UniqueID } from "@repo/rdx-ddd"; import type { UniqueID } from "@repo/rdx-ddd";
import type { Collection, Result } from "@repo/rdx-utils"; import type { Collection, Result } from "@repo/rdx-utils";
import type { Transaction } from "sequelize"; import type { Transaction } from "sequelize";
import type { IssuedInvoice } from "../../../domain"; import type { ICustomerInvoiceRepository, Proforma } from "../../../domain";
import type { IssuedInvoiceListDTO } from "../dtos";
import type { IIssuedInvoiceRepository } from "../repositories";
export interface IIssuedInvoiceFinder { export interface IIssuedInvoiceFinder {
findIssuedInvoiceById( findIssuedInvoiceById(
companyId: UniqueID, companyId: UniqueID,
invoiceId: UniqueID, invoiceId: UniqueID,
transaction?: Transaction transaction?: Transaction
): Promise<Result<IssuedInvoice, Error>>; ): Promise<Result<Proforma, Error>>;
issuedInvoiceExists( issuedInvoiceExists(
companyId: UniqueID, companyId: UniqueID,
@ -24,18 +23,18 @@ export interface IIssuedInvoiceFinder {
companyId: UniqueID, companyId: UniqueID,
criteria: Criteria, criteria: Criteria,
transaction?: Transaction transaction?: Transaction
): Promise<Result<Collection<IssuedInvoiceListDTO>, Error>>; ): Promise<Result<Collection<CustomerInvoiceListDTO>, Error>>;
} }
export class IssuedInvoiceFinder implements IIssuedInvoiceFinder { export class IssuedInvoiceFinder implements IIssuedInvoiceFinder {
constructor(private readonly repository: IIssuedInvoiceRepository) {} constructor(private readonly repository: ICustomerInvoiceRepository) {}
async findIssuedInvoiceById( async findIssuedInvoiceById(
companyId: UniqueID, companyId: UniqueID,
invoiceId: UniqueID, invoiceId: UniqueID,
transaction?: Transaction transaction?: Transaction
): Promise<Result<IssuedInvoice, Error>> { ): Promise<Result<Proforma, Error>> {
return this.repository.getByIdInCompany(companyId, invoiceId, transaction); return this.repository.getIssuedInvoiceByIdInCompany(companyId, invoiceId, transaction, {});
} }
async issuedInvoiceExists( async issuedInvoiceExists(
@ -52,7 +51,12 @@ export class IssuedInvoiceFinder implements IIssuedInvoiceFinder {
companyId: UniqueID, companyId: UniqueID,
criteria: Criteria, criteria: Criteria,
transaction?: Transaction transaction?: Transaction
): Promise<Result<Collection<IssuedInvoiceListDTO>, Error>> { ): Promise<Result<Collection<CustomerInvoiceListDTO>, Error>> {
return this.repository.findByCriteriaInCompany(companyId, criteria, transaction); return this.repository.findIssuedInvoicesByCriteriaInCompany(
companyId,
criteria,
transaction,
{}
);
} }
} }

View File

@ -44,7 +44,7 @@ export class ProformaToIssuedInvoiceMaterializer implements IProformaToIssuedInv
languageCode: proforma.languageCode, languageCode: proforma.languageCode,
currencyCode: proforma.currencyCode, currencyCode: proforma.currencyCode,
paymentMethod: proforma.paymentMethod, paymentMethod: proforma.paymentMethod,
discountPercentage: proforma.globalDiscountPercentage, discountPercentage: proforma.discountPercentage,
items: new Collection(issuedItems), items: new Collection(issuedItems),
taxes: new Collection(issuedTaxes), taxes: new Collection(issuedTaxes),

View File

@ -1,15 +1,15 @@
import type { ISnapshotBuilder } from "@erp/core/api"; import type { ISnapshotBuilder } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd"; import { toEmptyString } from "@repo/rdx-ddd";
import { InvoiceAmount, type IssuedInvoice } from "../../../../domain"; import { InvoiceAmount, type Proforma } from "../../../../domain";
import type { IIssuedInvoiceFullSnapshot } from "./issued-invoice-full-snapshot.interface"; import type { IssuedInvoiceFullSnapshot } from "./issued-invoice-full-snapshot.interface";
import type { IIssuedInvoiceItemsFullSnapshotBuilder } from "./issued-invoice-items-full-snapshot-builder"; import type { IIssuedInvoiceItemsFullSnapshotBuilder } from "./issued-invoice-items-full-snapshot-builder";
import type { IIssuedInvoiceRecipientFullSnapshotBuilder } from "./issued-invoice-recipient-full-snapshot-builder"; import type { IIssuedInvoiceRecipientFullSnapshotBuilder } from "./issued-invoice-recipient-full-snapshot-builder";
import type { IIssuedInvoiceVerifactuFullSnapshotBuilder } from "./issued-invoice-verifactu-full-snapshot-builder"; import type { IIssuedInvoiceVerifactuFullSnapshotBuilder } from "./issued-invoice-verifactu-full-snapshot-builder";
export interface IIssuedInvoiceFullSnapshotBuilder export interface IIssuedInvoiceFullSnapshotBuilder
extends ISnapshotBuilder<IssuedInvoice, IIssuedInvoiceFullSnapshot> {} extends ISnapshotBuilder<Proforma, IssuedInvoiceFullSnapshot> {}
export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnapshotBuilder { export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnapshotBuilder {
constructor( constructor(
@ -18,11 +18,13 @@ export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnaps
private readonly verifactuBuilder: IIssuedInvoiceVerifactuFullSnapshotBuilder private readonly verifactuBuilder: IIssuedInvoiceVerifactuFullSnapshotBuilder
) {} ) {}
toOutput(invoice: IssuedInvoice): IIssuedInvoiceFullSnapshot { toOutput(invoice: Proforma): IssuedInvoiceFullSnapshot {
const items = this.itemsBuilder.toOutput(invoice.items); const items = this.itemsBuilder.toOutput(invoice.items);
const recipient = this.recipientBuilder.toOutput(invoice); const recipient = this.recipientBuilder.toOutput(invoice);
const verifactu = this.verifactuBuilder.toOutput(invoice); const verifactu = this.verifactuBuilder.toOutput(invoice);
const allAmounts = invoice.calculateAllAmounts();
const payment = invoice.paymentMethod.match( const payment = invoice.paymentMethod.match(
(payment) => { (payment) => {
const { id, payment_description } = payment.toObjectString(); const { id, payment_description } = payment.toObjectString();
@ -38,7 +40,7 @@ export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnaps
let totalRecAmount = InvoiceAmount.zero(invoice.currencyCode.code); let totalRecAmount = InvoiceAmount.zero(invoice.currencyCode.code);
let totalRetentionAmount = InvoiceAmount.zero(invoice.currencyCode.code); let totalRetentionAmount = InvoiceAmount.zero(invoice.currencyCode.code);
const invoiceTaxes = invoice.taxes().map((taxGroup) => { const invoiceTaxes = invoice.getTaxes().map((taxGroup) => {
const { ivaAmount, recAmount, retentionAmount, totalAmount } = taxGroup.calculateAmounts(); const { ivaAmount, recAmount, retentionAmount, totalAmount } = taxGroup.calculateAmounts();
totalIvaAmount = totalIvaAmount.add(ivaAmount); totalIvaAmount = totalIvaAmount.add(ivaAmount);
@ -107,7 +109,7 @@ export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnaps
subtotal_amount: allAmounts.subtotalAmount.toObjectString(), subtotal_amount: allAmounts.subtotalAmount.toObjectString(),
items_discount_amount: allAmounts.itemDiscountAmount.toObjectString(), items_discount_amount: allAmounts.itemDiscountAmount.toObjectString(),
discount_percentage: invoice.globalDiscountPercentage.toObjectString(), discount_percentage: invoice.discountPercentage.toObjectString(),
discount_amount: allAmounts.globalDiscountAmount.toObjectString(), discount_amount: allAmounts.globalDiscountAmount.toObjectString(),
taxable_amount: allAmounts.taxableAmount.toObjectString(), taxable_amount: allAmounts.taxableAmount.toObjectString(),

View File

@ -1,8 +1,8 @@
import type { IIssuedInvoiceItemFullSnapshot } from "./issued-invoice-item-full-snapshot.interface"; import type { IssuedInvoiceItemFullSnapshot } from "./issued-invoice-item-full-snapshot.interface";
import type { IIssuedInvoiceRecipientFullSnapshot } from "./issued-invoice-recipient-full-snapshot.interfce"; import type { IssuedInvoiceRecipientFullSnapshot } from "./issued-invoice-recipient-full-snapshot.interfce";
import type { IIssuedInvoiceVerifactuFullSnapshot } from "./issued-invoice-verifactu-full-snapshot.interface"; import type { IssuedInvoiceVerifactuFullSnapshot } from "./issued-invoice-verifactu-full-snapshot.interface";
export interface IIssuedInvoiceFullSnapshot { export interface IssuedInvoiceFullSnapshot {
id: string; id: string;
company_id: string; company_id: string;
@ -22,7 +22,7 @@ export interface IIssuedInvoiceFullSnapshot {
currency_code: string; currency_code: string;
customer_id: string; customer_id: string;
recipient: IIssuedInvoiceRecipientFullSnapshot; recipient: IssuedInvoiceRecipientFullSnapshot;
payment_method?: { payment_method?: {
payment_id: string; payment_id: string;
@ -62,8 +62,8 @@ export interface IIssuedInvoiceFullSnapshot {
taxes_amount: { value: string; scale: string; currency_code: string }; taxes_amount: { value: string; scale: string; currency_code: string };
}>; }>;
verifactu: IIssuedInvoiceVerifactuFullSnapshot; verifactu: IssuedInvoiceVerifactuFullSnapshot;
items: IIssuedInvoiceItemFullSnapshot[]; items: IssuedInvoiceItemFullSnapshot[];
metadata?: Record<string, string>; metadata?: Record<string, string>;
} }

View File

@ -1,4 +1,4 @@
export interface IIssuedInvoiceItemFullSnapshot { export interface IssuedInvoiceItemFullSnapshot {
id: string; id: string;
is_valued: string; is_valued: string;
position: string; position: string;

View File

@ -1,17 +1,18 @@
import type { ISnapshotBuilder } from "@erp/core/api"; import type { ISnapshotBuilder } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd"; import { toEmptyString } from "@repo/rdx-ddd";
import type { IssuedInvoiceItem, IssuedInvoiceItems } from "../../../../domain"; import type { CustomerInvoiceItems, IssuedInvoiceItem } from "../../../../domain";
import type { IssuedInvoiceItemFullSnapshot } from "../../application-models";
import type { IIssuedInvoiceItemFullSnapshot } from "./issued-invoice-item-full-snapshot.interface";
export interface IIssuedInvoiceItemsFullSnapshotBuilder export interface IIssuedInvoiceItemsFullSnapshotBuilder
extends ISnapshotBuilder<IssuedInvoiceItems, IIssuedInvoiceItemFullSnapshot[]> {} extends ISnapshotBuilder<CustomerInvoiceItems, IssuedInvoiceItemFullSnapshot[]> {}
export class IssuedInvoiceItemsFullSnapshotBuilder export class IssuedInvoiceItemsFullSnapshotBuilder
implements IIssuedInvoiceItemsFullSnapshotBuilder implements IIssuedInvoiceItemsFullSnapshotBuilder
{ {
private mapItem(invoiceItem: IssuedInvoiceItem, index: number): IIssuedInvoiceItemFullSnapshot { private mapItem(invoiceItem: IssuedInvoiceItem, index: number): IssuedInvoiceItemFullSnapshot {
const allAmounts = invoiceItem.calculateAllAmounts();
return { return {
id: invoiceItem.id.toPrimitive(), id: invoiceItem.id.toPrimitive(),
is_valued: String(invoiceItem.isValued), is_valued: String(invoiceItem.isValued),
@ -28,23 +29,23 @@ export class IssuedInvoiceItemsFullSnapshotBuilder
() => ({ value: "", scale: "", currency_code: "" }) () => ({ value: "", scale: "", currency_code: "" })
), ),
subtotal_amount: invoiceItem.subtotalAmount.toObjectString(), subtotal_amount: allAmounts.subtotalAmount.toObjectString(),
discount_percentage: invoiceItem.itemDiscountPercentage.match( discount_percentage: invoiceItem.itemDiscountPercentage.match(
(discountPercentage) => discountPercentage.toObjectString(), (discountPercentage) => discountPercentage.toObjectString(),
() => ({ value: "", scale: "" }) () => ({ value: "", scale: "" })
), ),
discount_amount: invoiceItem.itemDiscountAmount.toObjectString(), discount_amount: allAmounts.itemDiscountAmount.toObjectString(),
global_discount_percentage: invoiceItem.globalDiscountPercentage.match( global_discount_percentage: invoiceItem.globalDiscountPercentage.match(
(discountPercentage) => discountPercentage.toObjectString(), (discountPercentage) => discountPercentage.toObjectString(),
() => ({ value: "", scale: "" }) () => ({ value: "", scale: "" })
), ),
global_discount_amount: invoiceItem.globalDiscountAmount.toObjectString(), global_discount_amount: allAmounts.globalDiscountAmount.toObjectString(),
taxable_amount: invoiceItem.taxableAmount.toObjectString(), taxable_amount: allAmounts.taxableAmount.toObjectString(),
iva_code: invoiceItem.taxes.iva.match( iva_code: invoiceItem.taxes.iva.match(
(iva) => iva.code, (iva) => iva.code,
@ -56,7 +57,7 @@ export class IssuedInvoiceItemsFullSnapshotBuilder
() => ({ value: "", scale: "" }) () => ({ value: "", scale: "" })
), ),
iva_amount: invoiceItem.ivaAmount.toObjectString(), iva_amount: allAmounts.ivaAmount.toObjectString(),
rec_code: invoiceItem.taxes.rec.match( rec_code: invoiceItem.taxes.rec.match(
(rec) => rec.code, (rec) => rec.code,
@ -68,7 +69,7 @@ export class IssuedInvoiceItemsFullSnapshotBuilder
() => ({ value: "", scale: "" }) () => ({ value: "", scale: "" })
), ),
rec_amount: invoiceItem.recAmount.toObjectString(), rec_amount: allAmounts.recAmount.toObjectString(),
retention_code: invoiceItem.taxes.retention.match( retention_code: invoiceItem.taxes.retention.match(
(retention) => retention.code, (retention) => retention.code,
@ -80,15 +81,15 @@ export class IssuedInvoiceItemsFullSnapshotBuilder
() => ({ value: "", scale: "" }) () => ({ value: "", scale: "" })
), ),
retention_amount: invoiceItem.retentionAmount.toObjectString(), retention_amount: allAmounts.retentionAmount.toObjectString(),
taxes_amount: invoiceItem.taxesAmount.toObjectString(), taxes_amount: allAmounts.taxesAmount.toObjectString(),
total_amount: invoiceItem.totalAmount.toObjectString(), total_amount: allAmounts.totalAmount.toObjectString(),
}; };
} }
toOutput(invoiceItems: IssuedInvoiceItems): IIssuedInvoiceItemFullSnapshot[] { toOutput(invoiceItems: CustomerInvoiceItems): IssuedInvoiceItemFullSnapshot[] {
return invoiceItems.map((item, index) => this.mapItem(item, index)); return invoiceItems.map((item, index) => this.mapItem(item, index));
} }
} }

View File

@ -1,17 +1,16 @@
import type { ISnapshotBuilder } from "@erp/core/api"; import type { ISnapshotBuilder } from "@erp/core/api";
import { DomainValidationError, toEmptyString } from "@repo/rdx-ddd"; import { DomainValidationError, toEmptyString } from "@repo/rdx-ddd";
import type { InvoiceRecipient, IssuedInvoice } from "../../../../domain"; import type { InvoiceRecipient, Proforma } from "../../../../domain";
import type { IssuedInvoiceRecipientFullSnapshot } from "../../application-models";
import type { IIssuedInvoiceRecipientFullSnapshot } from "./issued-invoice-recipient-full-snapshot.interfce";
export interface IIssuedInvoiceRecipientFullSnapshotBuilder export interface IIssuedInvoiceRecipientFullSnapshotBuilder
extends ISnapshotBuilder<IssuedInvoice, IIssuedInvoiceRecipientFullSnapshot> {} extends ISnapshotBuilder<Proforma, IssuedInvoiceRecipientFullSnapshot> {}
export class IssuedInvoiceRecipientFullSnapshotBuilder export class IssuedInvoiceRecipientFullSnapshotBuilder
implements IIssuedInvoiceRecipientFullSnapshotBuilder implements IIssuedInvoiceRecipientFullSnapshotBuilder
{ {
toOutput(invoice: IssuedInvoice): IIssuedInvoiceRecipientFullSnapshot { toOutput(invoice: Proforma): IssuedInvoiceRecipientFullSnapshot {
if (!invoice.recipient) { if (!invoice.recipient) {
throw DomainValidationError.requiredValue("recipient", { throw DomainValidationError.requiredValue("recipient", {
cause: invoice, cause: invoice,

View File

@ -1,4 +1,4 @@
export interface IIssuedInvoiceRecipientFullSnapshot { export interface IssuedInvoiceRecipientFullSnapshot {
id: string; id: string;
name: string; name: string;
tin: string; tin: string;

View File

@ -1,17 +1,16 @@
import type { ISnapshotBuilder } from "@erp/core/api"; import type { ISnapshotBuilder } from "@erp/core/api";
import { DomainValidationError } from "@repo/rdx-ddd"; import { DomainValidationError } from "@repo/rdx-ddd";
import type { IssuedInvoice } from "../../../../domain"; import type { Proforma } from "../../../../domain";
import type { IssuedInvoiceVerifactuFullSnapshot } from "../../application-models";
import type { IIssuedInvoiceVerifactuFullSnapshot } from "./issued-invoice-verifactu-full-snapshot.interface";
export interface IIssuedInvoiceVerifactuFullSnapshotBuilder export interface IIssuedInvoiceVerifactuFullSnapshotBuilder
extends ISnapshotBuilder<IssuedInvoice, IIssuedInvoiceVerifactuFullSnapshot> {} extends ISnapshotBuilder<Proforma, IssuedInvoiceVerifactuFullSnapshot> {}
export class IssuedInvoiceVerifactuFullSnapshotBuilder export class IssuedInvoiceVerifactuFullSnapshotBuilder
implements IIssuedInvoiceVerifactuFullSnapshotBuilder implements IIssuedInvoiceVerifactuFullSnapshotBuilder
{ {
toOutput(invoice: IssuedInvoice): IIssuedInvoiceVerifactuFullSnapshot { toOutput(invoice: Proforma): IssuedInvoiceVerifactuFullSnapshot {
if (!invoice.verifactu) { if (!invoice.verifactu) {
throw DomainValidationError.requiredValue("verifactu", { throw DomainValidationError.requiredValue("verifactu", {
cause: invoice, cause: invoice,

View File

@ -1,4 +1,4 @@
export interface IIssuedInvoiceVerifactuFullSnapshot { export interface IssuedInvoiceVerifactuFullSnapshot {
id: string; id: string;
status: string; status: string;
url: string; url: string;

View File

@ -1,15 +1,14 @@
import type { ISnapshotBuilder } from "@erp/core/api"; import type { ISnapshotBuilder } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd"; import { toEmptyString } from "@repo/rdx-ddd";
import type { IssuedInvoiceListDTO } from "../../dtos"; import type { CustomerInvoiceListDTO } from "../../../../infrastructure";
import type { IssuedInvoiceListItemSnapshot } from "../../application-models";
import type { IIssuedInvoiceListItemSnapshot } from "./issued-invoice-list-item-snapshot.interface";
export interface IIssuedInvoiceListItemSnapshotBuilder export interface IIssuedInvoiceListItemSnapshotBuilder
extends ISnapshotBuilder<IssuedInvoiceListDTO, IIssuedInvoiceListItemSnapshot> {} extends ISnapshotBuilder<CustomerInvoiceListDTO, IssuedInvoiceListItemSnapshot> {}
export class IssuedInvoiceListItemSnapshotBuilder implements IIssuedInvoiceListItemSnapshotBuilder { export class IssuedInvoiceListItemSnapshotBuilder implements IIssuedInvoiceListItemSnapshotBuilder {
toOutput(invoice: IssuedInvoiceListDTO): IIssuedInvoiceListItemSnapshot { toOutput(invoice: CustomerInvoiceListDTO): IssuedInvoiceListItemSnapshot {
const recipient = invoice.recipient.toObjectString(); const recipient = invoice.recipient.toObjectString();
const verifactu = invoice.verifactu.match( const verifactu = invoice.verifactu.match(
@ -43,7 +42,8 @@ export class IssuedInvoiceListItemSnapshotBuilder implements IIssuedInvoiceListI
currency_code: invoice.currencyCode.code, currency_code: invoice.currencyCode.code,
subtotal_amount: invoice.subtotalAmount.toObjectString(), subtotal_amount: invoice.subtotalAmount.toObjectString(),
total_discount_amount: invoice.totalDiscountAmount.toObjectString(), discount_percentage: invoice.discountPercentage.toObjectString(),
discount_amount: invoice.discountAmount.toObjectString(),
taxable_amount: invoice.taxableAmount.toObjectString(), taxable_amount: invoice.taxableAmount.toObjectString(),
taxes_amount: invoice.taxesAmount.toObjectString(), taxes_amount: invoice.taxesAmount.toObjectString(),
total_amount: invoice.totalAmount.toObjectString(), total_amount: invoice.totalAmount.toObjectString(),

View File

@ -1,4 +1,4 @@
export interface IIssuedInvoiceListItemSnapshot { export interface IssuedInvoiceListItemSnapshot {
id: string; id: string;
company_id: string; company_id: string;
is_proforma: boolean; is_proforma: boolean;
@ -30,7 +30,8 @@ export interface IIssuedInvoiceListItemSnapshot {
}; };
subtotal_amount: { value: string; scale: string; currency_code: string }; subtotal_amount: { value: string; scale: string; currency_code: string };
total_discount_amount: { value: string; scale: string; currency_code: string }; discount_percentage: { value: string; scale: string };
discount_amount: { value: string; scale: string; currency_code: string };
taxable_amount: { value: string; scale: string; currency_code: string }; taxable_amount: { value: string; scale: string; currency_code: string };
taxes_amount: { value: string; scale: string; currency_code: string }; taxes_amount: { value: string; scale: string; currency_code: string };
total_amount: { value: string; scale: string; currency_code: string }; total_amount: { value: string; scale: string; currency_code: string };

View File

@ -1,14 +1,14 @@
import type { ICustomerInvoiceRepository } from "../../../domain/repositories";
import { ProformaFactory } from "../factories"; import { ProformaFactory } from "../factories";
import type { IProformaRepository } from "../repositories";
import { type IProformaCreator, type IProformaNumberGenerator, ProformaCreator } from "../services"; import { type IProformaCreator, type IProformaNumberGenerator, ProformaCreator } from "../services";
export const buildProformaCreator = ( export function buildProformaCreator(
numberService: IProformaNumberGenerator, numberService: IProformaNumberGenerator,
repository: IProformaRepository repository: ICustomerInvoiceRepository
): IProformaCreator => { ): IProformaCreator {
return new ProformaCreator({ return new ProformaCreator({
numberService, numberService,
factory: new ProformaFactory(), factory: new ProformaFactory(),
repository, repository,
}); });
}; }

View File

@ -1,4 +1,4 @@
import type { CurrencyCode, LanguageCode, UniqueID, UtcDate } from "@repo/rdx-ddd"; import type { CurrencyCode, LanguageCode, Percentage, UniqueID, UtcDate } from "@repo/rdx-ddd";
import type { Maybe } from "@repo/rdx-utils"; import type { Maybe } from "@repo/rdx-utils";
import type { import type {
@ -30,8 +30,10 @@ export type ProformaListDTO = {
languageCode: LanguageCode; languageCode: LanguageCode;
currencyCode: CurrencyCode; currencyCode: CurrencyCode;
discountPercentage: Percentage;
subtotalAmount: InvoiceAmount; subtotalAmount: InvoiceAmount;
totalDiscountAmount: InvoiceAmount; discountAmount: InvoiceAmount;
taxableAmount: InvoiceAmount; taxableAmount: InvoiceAmount;
taxesAmount: InvoiceAmount; taxesAmount: InvoiceAmount;
totalAmount: InvoiceAmount; totalAmount: InvoiceAmount;

View File

@ -1,4 +1,4 @@
export * from "./create-proforma-props.mapper"; export * from "./create-proforma-props.mapper";
export * from "./proforma-domain-mapper.interface"; export * from "./proforma-domain-mapper.interface";
export * from "./proforma-list-mapper.interface"; export * from "./proforma-list-mapper.interface";
//export * from "./update-proforma-props.mapper"; export * from "./update-proforma-props.mapper";

View File

@ -106,7 +106,7 @@ export class ProformaFullSnapshotBuilder implements IProformaFullSnapshotBuilder
subtotal_amount: allAmounts.subtotalAmount.toObjectString(), subtotal_amount: allAmounts.subtotalAmount.toObjectString(),
items_discount_amount: allAmounts.itemDiscountAmount.toObjectString(), items_discount_amount: allAmounts.itemDiscountAmount.toObjectString(),
discount_percentage: invoice.globalDiscountPercentage.toObjectString(), discount_percentage: invoice.discountPercentage.toObjectString(),
discount_amount: allAmounts.globalDiscountAmount.toObjectString(), discount_amount: allAmounts.globalDiscountAmount.toObjectString(),
taxable_amount: allAmounts.taxableAmount.toObjectString(), taxable_amount: allAmounts.taxableAmount.toObjectString(),

View File

@ -1,4 +1,4 @@
export interface IProformaListItemSnapshot { export interface ProformaListItemSnapshot {
id: string; id: string;
company_id: string; company_id: string;
is_proforma: boolean; is_proforma: boolean;

View File

@ -2,6 +2,5 @@ export * from "./proforma-items-report-snapshot-builder";
export * from "./proforma-report-item-snapshot.interface"; export * from "./proforma-report-item-snapshot.interface";
export * from "./proforma-report-snapshot.interface"; export * from "./proforma-report-snapshot.interface";
export * from "./proforma-report-snapshot-builder"; export * from "./proforma-report-snapshot-builder";
export * from "./proforma-report-snapshot-builder";
export * from "./proforma-report-tax-snapshot.interface"; export * from "./proforma-report-tax-snapshot.interface";
export * from "./proforma-tax-report-snapshot-builder"; export * from "./proforma-tax-report-snapshot-builder";

View File

@ -109,7 +109,7 @@ export class ProformaFullPresenter extends Presenter<Proforma, GetProformaByIdRe
subtotal_amount: allAmounts.subtotalAmount.toObjectString(), subtotal_amount: allAmounts.subtotalAmount.toObjectString(),
items_discount_amount: allAmounts.itemDiscountAmount.toObjectString(), items_discount_amount: allAmounts.itemDiscountAmount.toObjectString(),
discount_percentage: proforma.globalDiscountPercentage.toObjectString(), discount_percentage: proforma.discountPercentage.toObjectString(),
discount_amount: allAmounts.globalDiscountAmount.toObjectString(), discount_amount: allAmounts.globalDiscountAmount.toObjectString(),
taxable_amount: allAmounts.taxableAmount.toObjectString(), taxable_amount: allAmounts.taxableAmount.toObjectString(),

View File

@ -1 +0,0 @@
export * from "./issued-invoice.aggregate";

View File

@ -1,4 +1,4 @@
export * from "./issued-invoice-items"; export * from "./issued-invoice-items";
export * from "./issued-invoice-tax.entity"; export * from "./issued-invoice-tax-group.entity";
export * from "./issued-invoice-taxes.collection"; export * from "./issued-invoice-taxes.entity";
export * from "./verifactu-record.entity"; export * from "./verifactu-record.entity";

View File

@ -0,0 +1,35 @@
import { DomainEntity, type Percentage, type UniqueID } from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils";
import type { InvoiceAmount } from "../../common";
export type IssuedInvoiceTaxGroupProps = {
taxableAmount: InvoiceAmount;
ivaCode: string;
ivaPercentage: Percentage;
ivaAmount: InvoiceAmount;
recCode: Maybe<string>;
recPercentage: Maybe<Percentage>;
recAmount: InvoiceAmount;
retentionCode: Maybe<string>;
retentionPercentage: Maybe<Percentage>;
retentionAmount: InvoiceAmount;
totalAmount: InvoiceAmount;
};
export class IssuedInvoiceTaxGroup extends DomainEntity<IssuedInvoiceTaxGroupProps> {
public static create(
props: IssuedInvoiceTaxGroupProps,
id?: UniqueID
): Result<IssuedInvoiceTaxGroup, Error> {
return Result.ok(new IssuedInvoiceTaxGroup(props, id));
}
public getProps(): IssuedInvoiceTaxGroupProps {
return this.props;
}
}

View File

@ -1,73 +0,0 @@
import { DomainEntity, type Percentage, type UniqueID } from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils";
import type { InvoiceAmount } from "../../common";
export type IssuedInvoiceTaxProps = {
taxableAmount: InvoiceAmount;
ivaCode: string;
ivaPercentage: Percentage;
ivaAmount: InvoiceAmount;
recCode: Maybe<string>;
recPercentage: Maybe<Percentage>;
recAmount: InvoiceAmount;
retentionCode: Maybe<string>;
retentionPercentage: Maybe<Percentage>;
retentionAmount: InvoiceAmount;
taxesAmount: InvoiceAmount;
};
export class IssuedInvoiceTax extends DomainEntity<IssuedInvoiceTaxProps> {
public static create(
props: IssuedInvoiceTaxProps,
id?: UniqueID
): Result<IssuedInvoiceTax, Error> {
return Result.ok(new IssuedInvoiceTax(props, id));
}
public get taxableAmount(): InvoiceAmount {
return this.props.taxableAmount;
}
public get ivaCode(): string {
return this.props.ivaCode;
}
public get ivaPercentage(): Percentage {
return this.props.ivaPercentage;
}
public get ivaAmount(): InvoiceAmount {
return this.props.ivaAmount;
}
public get recCode(): Maybe<string> {
return this.props.recCode;
}
public get recPercentage(): Maybe<Percentage> {
return this.props.recPercentage;
}
public get recAmount(): InvoiceAmount {
return this.props.recAmount;
}
public get retentionCode(): Maybe<string> {
return this.props.retentionCode;
}
public get retentionPercentage(): Maybe<Percentage> {
return this.props.retentionPercentage;
}
public get retentionAmount(): InvoiceAmount {
return this.props.retentionAmount;
}
public get taxesAmount(): InvoiceAmount {
return this.props.taxesAmount;
}
public getProps(): IssuedInvoiceTaxProps {
return this.props;
}
}

View File

@ -1,13 +0,0 @@
import { Collection } from "@repo/rdx-utils";
import type { IssuedInvoiceTax } from "./issued-invoice-tax.entity";
export class IssuedInvoiceTaxes extends Collection<IssuedInvoiceTax> {
constructor(items: IssuedInvoiceTax[] = []) {
super(items);
}
public static create(items: IssuedInvoiceTax[] = []): IssuedInvoiceTaxes {
return new IssuedInvoiceTaxes(items);
}
}

View File

@ -0,0 +1,13 @@
import { Collection } from "@repo/rdx-utils";
import type { IssuedInvoiceTaxGroup } from "./issued-invoice-tax-group.entity";
export class IssuedInvoiceTaxes extends Collection<IssuedInvoiceTaxGroup> {
constructor(items: IssuedInvoiceTaxGroup[] = []) {
super(items);
}
public static create(items: IssuedInvoiceTaxGroup[] = []): IssuedInvoiceTaxes {
return new IssuedInvoiceTaxes(items);
}
}

View File

@ -1,4 +1,3 @@
export * from "./aggregates";
export * from "./entities"; export * from "./entities";
export * from "./errors"; export * from "./issued-invoice.aggregate";
export * from "./value-objects"; export * from "./value-objects";

View File

@ -10,15 +10,16 @@ import {
} from "@repo/rdx-ddd"; } from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils"; import { type Maybe, Result } from "@repo/rdx-utils";
import type { InvoicePaymentMethod } from "../common/entities";
import type { import type {
InvoiceAmount, InvoiceAmount,
InvoiceNumber, InvoiceNumber,
InvoicePaymentMethod,
InvoiceRecipient, InvoiceRecipient,
InvoiceSerie, InvoiceSerie,
InvoiceStatus, InvoiceStatus,
} from "../../common"; } from "../common/value-objects";
import { IssuedInvoiceItems, type IssuedInvoiceTaxes, type VerifactuRecord } from "../entities";
import { IssuedInvoiceItems, type IssuedInvoiceTaxes, type VerifactuRecord } from "./entities";
export type IssuedInvoiceProps = { export type IssuedInvoiceProps = {
companyId: UniqueID; companyId: UniqueID;
@ -50,7 +51,7 @@ export type IssuedInvoiceProps = {
subtotalAmount: InvoiceAmount; subtotalAmount: InvoiceAmount;
itemDiscountAmount: InvoiceAmount; itemDiscountAmount: InvoiceAmount;
globalDiscountPercentage: Percentage; discountPercentage: Percentage;
globalDiscountAmount: InvoiceAmount; globalDiscountAmount: InvoiceAmount;
totalDiscountAmount: InvoiceAmount; totalDiscountAmount: InvoiceAmount;
@ -76,7 +77,7 @@ export class IssuedInvoice extends AggregateRoot<IssuedInvoiceProps> {
IssuedInvoiceItems.create({ IssuedInvoiceItems.create({
languageCode: props.languageCode, languageCode: props.languageCode,
currencyCode: props.currencyCode, currencyCode: props.currencyCode,
globalDiscountPercentage: props.globalDiscountPercentage, globalDiscountPercentage: props.discountPercentage,
}); });
} }
@ -169,36 +170,8 @@ export class IssuedInvoice extends AggregateRoot<IssuedInvoiceProps> {
return this.props.verifactu; return this.props.verifactu;
} }
public get subtotalAmount(): InvoiceAmount { public get discountPercentage(): Percentage {
return this.props.subtotalAmount; return this.props.discountPercentage;
}
public get itemDiscountAmount(): InvoiceAmount {
return this.props.itemDiscountAmount;
}
public get globalDiscountPercentage(): Percentage {
return this.props.globalDiscountPercentage;
}
public get globalDiscountAmount(): InvoiceAmount {
return this.props.globalDiscountAmount;
}
public get totalDiscountAmount(): InvoiceAmount {
return this.props.totalDiscountAmount;
}
public get taxableAmount(): InvoiceAmount {
return this.props.taxableAmount;
}
public get taxesAmount(): InvoiceAmount {
return this.props.taxesAmount;
}
public get totalAmount(): InvoiceAmount {
return this.props.totalAmount;
} }
public get taxes(): IssuedInvoiceTaxes { public get taxes(): IssuedInvoiceTaxes {

View File

@ -1 +1,2 @@
export * from "./invoice-tax-group.vo";
export * from "./verifactu-status.vo"; export * from "./verifactu-status.vo";

View File

@ -1 +0,0 @@
export * from "./proforma.aggregate";

View File

@ -1 +0,0 @@
export * from "./proforma-items";

View File

@ -1,3 +1,2 @@
export * from "./aggregates"; export * from "./proforma.aggregate";
export * from "./entities"; export * from "./proforma-items";
export * from "./errors";

View File

@ -7,7 +7,7 @@ import {
ItemDiscount, ItemDiscount,
ItemQuantity, ItemQuantity,
type ItemTaxGroup, type ItemTaxGroup,
} from "../../../common"; } from "../../common";
/** /**
* *

View File

@ -1,7 +1,7 @@
import type { CurrencyCode, LanguageCode, Percentage } from "@repo/rdx-ddd"; import type { CurrencyCode, LanguageCode, Percentage } from "@repo/rdx-ddd";
import { Collection } from "@repo/rdx-utils"; import { Collection } from "@repo/rdx-utils";
import { ItemAmount, ItemDiscount, type ItemTaxGroup } from "../../../common"; import { ItemAmount, ItemDiscount, type ItemTaxGroup } from "../../common";
import type { ProformaItem } from "./proforma-item.entity"; import type { ProformaItem } from "./proforma-item.entity";

View File

@ -10,7 +10,7 @@ import {
} from "@repo/rdx-ddd"; } from "@repo/rdx-ddd";
import { Collection, type Maybe, Result } from "@repo/rdx-utils"; import { Collection, type Maybe, Result } from "@repo/rdx-utils";
import type { InvoicePaymentMethod } from "../../common/entities"; import type { InvoicePaymentMethod } from "../common/entities";
import { import {
InvoiceAmount, InvoiceAmount,
type InvoiceNumber, type InvoiceNumber,
@ -19,8 +19,9 @@ import {
type InvoiceStatus, type InvoiceStatus,
InvoiceTaxGroup, InvoiceTaxGroup,
type ItemAmount, type ItemAmount,
} from "../../common/value-objects"; } from "../common/value-objects";
import { ProformaItems } from "../entities/proforma-items";
import { ProformaItems } from "./proforma-items";
export type ProformaProps = { export type ProformaProps = {
companyId: UniqueID; companyId: UniqueID;
@ -48,7 +49,7 @@ export type ProformaProps = {
paymentMethod: Maybe<InvoicePaymentMethod>; paymentMethod: Maybe<InvoicePaymentMethod>;
globalDiscountPercentage: Percentage; discountPercentage: Percentage;
}; };
export type ProformaPatchProps = Partial<Omit<ProformaProps, "companyId" | "items">> & { export type ProformaPatchProps = Partial<Omit<ProformaProps, "companyId" | "items">> & {
@ -65,7 +66,7 @@ export class Proforma extends AggregateRoot<ProformaProps> {
ProformaItems.create({ ProformaItems.create({
languageCode: props.languageCode, languageCode: props.languageCode,
currencyCode: props.currencyCode, currencyCode: props.currencyCode,
globalDiscountPercentage: props.globalDiscountPercentage, globalDiscountPercentage: props.discountPercentage,
}); });
} }
@ -176,8 +177,8 @@ export class Proforma extends AggregateRoot<ProformaProps> {
return this.props.currencyCode; return this.props.currencyCode;
} }
public get globalDiscountPercentage(): Percentage { public get discountPercentage(): Percentage {
return this.props.globalDiscountPercentage; return this.props.discountPercentage;
} }
// Method to get the complete list of line items // Method to get the complete list of line items
@ -272,6 +273,10 @@ export class Proforma extends AggregateRoot<ProformaProps> {
return this.calculateAllAmounts().totalAmount; return this.calculateAllAmounts().totalAmount;
} }
/**
* @summary Agrupa impuestos a nivel factura usando el trío (iva|rec|ret),
* construyendo InvoiceTaxGroup desde los datos de los ítems.
*/
/** /**
* @summary Agrupa impuestos a nivel factura usando el trío (iva|rec|ret), * @summary Agrupa impuestos a nivel factura usando el trío (iva|rec|ret),
* construyendo InvoiceTaxGroup desde los datos de los ítems. * construyendo InvoiceTaxGroup desde los datos de los ítems.

View File

@ -4,9 +4,9 @@ import {
type IssuedInvoicesInternalDeps, type IssuedInvoicesInternalDeps,
buildIssuedInvoicesDependencies, buildIssuedInvoicesDependencies,
buildProformaServices, buildProformaServices,
issuedInvoicesRouter,
models, models,
} from "./infrastructure"; } from "./infrastructure";
import { issuedInvoicesRouter } from "./infrastructure/express";
export const customerInvoicesAPIModule: IModuleServer = { export const customerInvoicesAPIModule: IModuleServer = {
name: "customer-invoices", name: "customer-invoices",

View File

@ -379,8 +379,8 @@ export class CustomerInvoiceDomainMapper
subtotal_amount_value: allAmounts.subtotalAmount.value, subtotal_amount_value: allAmounts.subtotalAmount.value,
subtotal_amount_scale: allAmounts.subtotalAmount.scale, subtotal_amount_scale: allAmounts.subtotalAmount.scale,
discount_percentage_value: source.globalDiscountPercentage.toPrimitive().value, discount_percentage_value: source.discountPercentage.toPrimitive().value,
discount_percentage_scale: source.globalDiscountPercentage.toPrimitive().scale, discount_percentage_scale: source.discountPercentage.toPrimitive().scale,
discount_amount_value: allAmounts.globalDiscountAmount.value, discount_amount_value: allAmounts.globalDiscountAmount.value,
discount_amount_scale: allAmounts.globalDiscountAmount.scale, discount_amount_scale: allAmounts.globalDiscountAmount.scale,

View File

@ -68,21 +68,13 @@ export class CustomerInvoiceModel extends Model<
declare subtotal_amount_value: number; declare subtotal_amount_value: number;
declare subtotal_amount_scale: number; declare subtotal_amount_scale: number;
// Items discount amount (suma de descuentos individuales por ítem) // Discount percentage
declare items_discount_amount_value: number; declare discount_percentage_value: number;
declare items_discount_amount_scale: number; declare discount_percentage_scale: number;
// Global/header discount percentage // Discount amount
declare global_discount_percentage_value: number; declare discount_amount_value: number;
declare global_discount_percentage_scale: number; declare discount_amount_scale: number;
// Global/header discount amount
declare global_discount_amount_value: number;
declare global_discount_amount_scale: number;
// Total discount amount (subtotal - descuentos)
declare total_discount_amount_value: number;
declare total_discount_amount_scale: number;
// Taxable amount (base imponible) // Taxable amount (base imponible)
declare taxable_amount_value: number; declare taxable_amount_value: number;
@ -291,49 +283,25 @@ export default (database: Sequelize) => {
defaultValue: 2, defaultValue: 2,
}, },
items_discount_amount_value: { discount_percentage_value: {
type: new DataTypes.BIGINT(), type: new DataTypes.SMALLINT(),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
}, },
items_discount_amount_scale: { discount_percentage_scale: {
type: new DataTypes.SMALLINT(), type: new DataTypes.SMALLINT(),
allowNull: false, allowNull: false,
defaultValue: 2, defaultValue: 2,
}, },
global_discount_percentage_value: { discount_amount_value: {
type: new DataTypes.SMALLINT(),
allowNull: false,
defaultValue: 0,
},
global_discount_percentage_scale: {
type: new DataTypes.SMALLINT(),
allowNull: false,
defaultValue: 2,
},
global_discount_amount_value: {
type: new DataTypes.BIGINT(), type: new DataTypes.BIGINT(),
allowNull: false, allowNull: false,
defaultValue: 0, defaultValue: 0,
}, },
global_discount_amount_scale: { discount_amount_scale: {
type: new DataTypes.SMALLINT(),
allowNull: false,
defaultValue: 2,
},
total_discount_amount_value: {
type: new DataTypes.BIGINT(),
allowNull: false,
defaultValue: 0,
},
total_discount_amount_scale: {
type: new DataTypes.SMALLINT(), type: new DataTypes.SMALLINT(),
allowNull: false, allowNull: false,
defaultValue: 2, defaultValue: 2,

View File

@ -5,9 +5,9 @@ import {
requireCompanyContextGuard, requireCompanyContextGuard,
} from "@erp/core/api"; } from "@erp/core/api";
import { GetIssuedInvoiceByIdResponseSchema } from "../../../../common/index.ts"; import { GetIssuedInvoiceByIdResponseSchema } from "../../../../../common";
import type { GetIssuedInvoiceByIdUseCase } from "../../../application/issued-invoices/index.ts"; import type { GetIssuedInvoiceByIdUseCase } from "../../../../application/issued-invoices";
import { customerInvoicesApiErrorMapper } from "../../express/proformas/proformas-api-error-mapper.ts"; import { customerInvoicesApiErrorMapper } from "../../proformas/proformas-api-error-mapper.ts";
export class GetIssuedInvoiceByIdController extends ExpressController { export class GetIssuedInvoiceByIdController extends ExpressController {
public constructor(private readonly useCase: GetIssuedInvoiceByIdUseCase) { public constructor(private readonly useCase: GetIssuedInvoiceByIdUseCase) {

View File

@ -0,0 +1,3 @@
export * from "./get-issued-invoice-by-id.controller";
//export * from "./list-issued-invoices.controller";
//export * from "./report-issued-invoice.controller";

View File

@ -6,9 +6,9 @@ import {
} from "@erp/core/api"; } from "@erp/core/api";
import { Criteria } from "@repo/rdx-criteria/server"; import { Criteria } from "@repo/rdx-criteria/server";
import { ListIssuedInvoicesResponseSchema } from "../../../../common/index.ts"; import { ListIssuedInvoicesResponseSchema } from "../../../../../common";
import type { ListIssuedInvoicesUseCase } from "../../../application/issued-invoices/index.ts"; import type { ListIssuedInvoicesUseCase } from "../../../../application/issued-invoices";
import { customerInvoicesApiErrorMapper } from "../../express/proformas/proformas-api-error-mapper.ts"; import { customerInvoicesApiErrorMapper } from "../../proformas/proformas-api-error-mapper.ts";
export class ListIssuedInvoicesController extends ExpressController { export class ListIssuedInvoicesController extends ExpressController {
public constructor(private readonly useCase: ListIssuedInvoicesUseCase) { public constructor(private readonly useCase: ListIssuedInvoicesUseCase) {

View File

@ -7,8 +7,8 @@ import {
} from "@erp/core/api"; } from "@erp/core/api";
import type { ReportIssueInvoiceByIdQueryRequestDTO } from "@erp/customer-invoices/common"; import type { ReportIssueInvoiceByIdQueryRequestDTO } from "@erp/customer-invoices/common";
import type { ReportIssuedInvoiceUseCase } from "../../../application/index.ts"; import type { ReportIssuedInvoiceUseCase } from "../../../../application";
import { customerInvoicesApiErrorMapper } from "../../express/proformas/proformas-api-error-mapper.ts"; import { customerInvoicesApiErrorMapper } from "../../proformas/proformas-api-error-mapper.ts";
export class ReportIssuedInvoiceController extends ExpressController { export class ReportIssuedInvoiceController extends ExpressController {
public constructor(private readonly useCase: ReportIssuedInvoiceUseCase) { public constructor(private readonly useCase: ReportIssuedInvoiceUseCase) {

View File

@ -1,3 +1,2 @@
export * from "../../issued-invoices/express"; export * from "./controllers";
export * from "./issued-invoices.routes"; export * from "./issued-invoices.routes";

View File

@ -8,12 +8,11 @@ import {
ReportIssueInvoiceByIdParamsRequestSchema, ReportIssueInvoiceByIdParamsRequestSchema,
ReportIssueInvoiceByIdQueryRequestSchema, ReportIssueInvoiceByIdQueryRequestSchema,
} from "../../../../common/dto"; } from "../../../../common/dto";
import {
GetIssuedInvoiceByIdController,
ReportIssuedInvoiceController,
} from "../../issued-invoices";
import type { IssuedInvoicesInternalDeps } from "../../issued-invoices/di"; import type { IssuedInvoicesInternalDeps } from "../../issued-invoices/di";
import { ListIssuedInvoicesController } from "../../issued-invoices/express/list-issued-invoices.controller";
import { GetIssuedInvoiceByIdController } from "./controllers";
import { ListIssuedInvoicesController } from "./controllers/list-issued-invoices.controller";
import { ReportIssuedInvoiceController } from "./controllers/report-issued-invoice.controller";
export const issuedInvoicesRouter = (params: ModuleParams, deps: IssuedInvoicesInternalDeps) => { export const issuedInvoicesRouter = (params: ModuleParams, deps: IssuedInvoicesInternalDeps) => {
const { app, config } = params; const { app, config } = params;

View File

@ -5,7 +5,9 @@ import {
requireCompanyContextGuard, requireCompanyContextGuard,
} from "@erp/core/api"; } from "@erp/core/api";
import type { ChangeStatusProformaByIdRequestDTO } from "../../../../common/dto/index.ts"; import type { ChangeStatusProformaByIdRequestDTO } from "../../../../../common/dto";
import type { ChangeStatusProformaUseCase } from "../../../../application";
import { customerInvoicesApiErrorMapper } from "../proformas-api-error-mapper.ts";
export class ChangeStatusProformaController extends ExpressController { export class ChangeStatusProformaController extends ExpressController {
public constructor(private readonly useCase: ChangeStatusProformaUseCase) { public constructor(private readonly useCase: ChangeStatusProformaUseCase) {

View File

@ -5,9 +5,9 @@ import {
requireCompanyContextGuard, requireCompanyContextGuard,
} from "@erp/core/api"; } from "@erp/core/api";
import type { CreateProformaRequestDTO } from "../../../../common/dto/index.ts"; import type { CreateProformaRequestDTO } from "../../../../../common/dto";
import type { CreateProformaUseCase } from "../../../application/index.ts"; import type { CreateProformaUseCase } from "../../../../application";
import { customerInvoicesApiErrorMapper } from "../../express/proformas/proformas-api-error-mapper.ts"; import { customerInvoicesApiErrorMapper } from "../proformas-api-error-mapper.ts";
export class CreateProformaController extends ExpressController { export class CreateProformaController extends ExpressController {
public constructor(private readonly useCase: CreateProformaUseCase) { public constructor(private readonly useCase: CreateProformaUseCase) {

View File

@ -5,8 +5,8 @@ import {
requireCompanyContextGuard, requireCompanyContextGuard,
} from "@erp/core/api"; } from "@erp/core/api";
import type { DeleteProformaUseCase } from "../../../application/index.ts"; import type { DeleteProformaUseCase } from "../../../../application";
import { customerInvoicesApiErrorMapper } from "../../express/proformas/proformas-api-error-mapper.ts"; import { customerInvoicesApiErrorMapper } from "../proformas-api-error-mapper.ts";
export class DeleteProformaController extends ExpressController { export class DeleteProformaController extends ExpressController {
public constructor(private readonly useCase: DeleteProformaUseCase) { public constructor(private readonly useCase: DeleteProformaUseCase) {

View File

@ -5,8 +5,8 @@ import {
requireCompanyContextGuard, requireCompanyContextGuard,
} from "@erp/core/api"; } from "@erp/core/api";
import type { GetProformaUseCase } from "../../../application/index.ts"; import type { GetProformaUseCase } from "../../../../application";
import { customerInvoicesApiErrorMapper } from "../../express/proformas/proformas-api-error-mapper.ts"; import { customerInvoicesApiErrorMapper } from "../proformas-api-error-mapper.ts";
export class GetProformaController extends ExpressController { export class GetProformaController extends ExpressController {
public constructor(private readonly useCase: GetProformaUseCase) { public constructor(private readonly useCase: GetProformaUseCase) {

View File

@ -0,0 +1,8 @@
export * from "./change-status-proforma.controller";
export * from "./create-proforma.controller";
export * from "./delete-proforma.controller";
export * from "./get-proforma.controller";
export * from "./issue-proforma.controller";
export * from "./list-proformas.controller";
export * from "./report-proforma.controller";
export * from "./update-proforma.controller";

View File

@ -5,8 +5,8 @@ import {
requireCompanyContextGuard, requireCompanyContextGuard,
} from "@erp/core/api"; } from "@erp/core/api";
import type { IssueProformaUseCase } from "../../../application/index.ts"; import type { IssueProformaUseCase } from "../../../../application";
import { customerInvoicesApiErrorMapper } from "../../express/proformas/proformas-api-error-mapper.ts"; import { customerInvoicesApiErrorMapper } 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) {

View File

@ -6,8 +6,8 @@ import {
} from "@erp/core/api"; } from "@erp/core/api";
import { Criteria } from "@repo/rdx-criteria/server"; import { Criteria } from "@repo/rdx-criteria/server";
import type { ListProformasUseCase } from "../../../application/index.ts"; import type { ListProformasUseCase } from "../../../../application";
import { customerInvoicesApiErrorMapper } from "../../express/proformas/proformas-api-error-mapper.ts"; import { customerInvoicesApiErrorMapper } from "../proformas-api-error-mapper.ts";
export class ListProformasController extends ExpressController { export class ListProformasController extends ExpressController {
public constructor(private readonly useCase: ListProformasUseCase) { public constructor(private readonly useCase: ListProformasUseCase) {

View File

@ -5,8 +5,8 @@ import {
requireCompanyContextGuard, requireCompanyContextGuard,
} from "@erp/core/api"; } from "@erp/core/api";
import type { ReportProformaUseCase } from "../../../application/index.ts"; import type { ReportProformaUseCase } from "../../../../application";
import { customerInvoicesApiErrorMapper } from "../../express/proformas/proformas-api-error-mapper.ts"; import { customerInvoicesApiErrorMapper } from "../proformas-api-error-mapper.ts";
export class ReportProformaController extends ExpressController { export class ReportProformaController extends ExpressController {
public constructor(private readonly useCase: ReportProformaUseCase) { public constructor(private readonly useCase: ReportProformaUseCase) {

View File

@ -5,9 +5,9 @@ import {
requireCompanyContextGuard, requireCompanyContextGuard,
} from "@erp/core/api"; } from "@erp/core/api";
import type { UpdateProformaByIdRequestDTO } from "../../../../common/dto/index.ts"; import type { UpdateProformaByIdRequestDTO } from "../../../../../common/dto";
import type { UpdateProformaUseCase } from "../../../application/index.ts"; import type { UpdateProformaUseCase } from "../../../../application";
import { customerInvoicesApiErrorMapper } from "../../express/proformas/proformas-api-error-mapper.ts"; import { customerInvoicesApiErrorMapper } from "../proformas-api-error-mapper.ts";
export class UpdateProformaController extends ExpressController { export class UpdateProformaController extends ExpressController {
public constructor(private readonly useCase: UpdateProformaUseCase) { public constructor(private readonly useCase: UpdateProformaUseCase) {

View File

@ -1,4 +1,3 @@
export * from "../../proformas/express"; export * from "./controllers";
export * from "./proformas.routes"; export * from "./proformas.routes";
export * from "./proformas-api-error-mapper"; export * from "./proformas-api-error-mapper";

View File

@ -16,6 +16,7 @@ import {
UpdateProformaByIdRequestSchema, UpdateProformaByIdRequestSchema,
} from "../../../../common"; } from "../../../../common";
import type { IssuedInvoicesInternalDeps } from "../../issued-invoices/di"; import type { IssuedInvoicesInternalDeps } from "../../issued-invoices/di";
import { import {
ChangeStatusProformaController, ChangeStatusProformaController,
CreateProformaController, CreateProformaController,
@ -25,7 +26,7 @@ import {
ListProformasController, ListProformasController,
ReportProformaController, ReportProformaController,
UpdateProformaController, UpdateProformaController,
} from "../../proformas/express"; } from "./controllers";
export const proformasRouter = (params: ModuleParams, deps: IssuedInvoicesInternalDeps) => { export const proformasRouter = (params: ModuleParams, deps: IssuedInvoicesInternalDeps) => {
const { app, config } = params; const { app, config } = params;

View File

@ -1,19 +1,29 @@
import { SpainTaxCatalogProvider } from "@erp/core"; import { SpainTaxCatalogProvider } from "@erp/core";
import { InMemoryMapperRegistry } from "@erp/core/api";
import type { Sequelize } from "sequelize"; import type { Sequelize } from "sequelize";
import { import {
IssuedInvoiceRepository, CustomerInvoiceDomainMapper,
SequelizeIssuedInvoiceDomainMapper, CustomerInvoiceListMapper,
SequelizeIssuedInvoiceListMapper, CustomerInvoiceRepository,
} from "../persistence"; } from "../../common/persistence";
export const buildIssuedInvoiceRepository = (database: Sequelize) => { export const buildIssuedInvoiceRepository = (database: Sequelize) => {
const mapperRegistry = new InMemoryMapperRegistry();
const taxCatalog = SpainTaxCatalogProvider(); const taxCatalog = SpainTaxCatalogProvider();
const domainMapper = new SequelizeIssuedInvoiceDomainMapper({ mapperRegistry
taxCatalog, .registerDomainMapper(
}); { resource: "customer-invoice" },
const listMapper = new SequelizeIssuedInvoiceListMapper(); new CustomerInvoiceDomainMapper({ taxCatalog })
)
.registerQueryMappers([
{
key: { resource: "customer-invoice", query: "LIST" },
mapper: new CustomerInvoiceListMapper(),
},
]);
return new IssuedInvoiceRepository(domainMapper, listMapper, database); return new CustomerInvoiceRepository({ mapperRegistry, database });
}; };

View File

@ -1,3 +0,0 @@
export * from "./get-issued-invoice-by-id.controller";
export * from "./list-issued-invoices.controller";
export * from "./report-issued-invoice.controller";

View File

@ -1,4 +1,3 @@
export * from "./di"; export * from "./di";
export * from "./documents"; export * from "./documents";
export * from "./express";
export * from "./persistence"; export * from "./persistence";

View File

@ -1,2 +1,3 @@
export * from "./mappers"; export * from "./mappers";
export * from "./repositories"; export * from "./repositories";
export * from "./services";

View File

@ -16,13 +16,13 @@ import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
import type { IIssuedInvoiceDomainMapper } from "../../../../../../application"; import type { IIssuedInvoiceDomainMapper } from "../../../../../../application";
import { import {
CustomerInvoiceItems,
type IIssuedInvoiceProps,
InvoiceNumber, InvoiceNumber,
InvoicePaymentMethod, InvoicePaymentMethod,
InvoiceSerie, InvoiceSerie,
InvoiceStatus, InvoiceStatus,
IssuedInvoice, IssuedInvoice,
IssuedInvoiceItems,
type IssuedInvoiceProps,
} from "../../../../../../domain"; } from "../../../../../../domain";
import type { import type {
CustomerInvoiceCreationAttributes, CustomerInvoiceCreationAttributes,
@ -52,7 +52,7 @@ export class SequelizeIssuedInvoiceDomainMapper
this._itemsMapper = new SequelizeIssuedInvoiceItemDomainMapper(params); // Instanciar el mapper de items this._itemsMapper = new SequelizeIssuedInvoiceItemDomainMapper(params); // Instanciar el mapper de items
this._recipientMapper = new SequelizeIssuedInvoiceRecipientDomainMapper(); this._recipientMapper = new SequelizeIssuedInvoiceRecipientDomainMapper();
this._taxesMapper = new SequelizeIssuedInvoiceTaxesDomainMapper(); this._taxesMapper = new SequelizeIssuedInvoiceTaxesDomainMapper(params);
this._verifactuMapper = new SequelizeIssuedInvoiceVerifactuDomainMapper(); this._verifactuMapper = new SequelizeIssuedInvoiceVerifactuDomainMapper();
} }
@ -236,16 +236,17 @@ export class SequelizeIssuedInvoiceDomainMapper
// 6) Construcción del agregado (Dominio) // 6) Construcción del agregado (Dominio)
const items = IssuedInvoiceItems.create({ const items = CustomerInvoiceItems.create({
languageCode: attributes.languageCode!, languageCode: attributes.languageCode!,
currencyCode: attributes.currencyCode!, currencyCode: attributes.currencyCode!,
globalDiscountPercentage: attributes.discountPercentage!, globalDiscountPercentage: attributes.discountPercentage!,
items: itemsResults.data.getAll(), items: itemsResults.data.getAll(),
}); });
const invoiceProps: IssuedInvoiceProps = { const invoiceProps: IIssuedInvoiceProps = {
companyId: attributes.companyId!, companyId: attributes.companyId!,
isIssuedInvoice: attributes.isIssuedInvoice,
proformaId: attributes.proformaId!, proformaId: attributes.proformaId!,
status: attributes.status!, status: attributes.status!,
series: attributes.series!, series: attributes.series!,
@ -263,7 +264,7 @@ export class SequelizeIssuedInvoiceDomainMapper
languageCode: attributes.languageCode!, languageCode: attributes.languageCode!,
currencyCode: attributes.currencyCode!, currencyCode: attributes.currencyCode!,
globalDiscountPercentage: attributes.discountPercentage!, discountPercentage: attributes.discountPercentage!,
paymentMethod: attributes.paymentMethod!, paymentMethod: attributes.paymentMethod!,
@ -308,7 +309,7 @@ export class SequelizeIssuedInvoiceDomainMapper
} }
// 2) Taxes // 2) Taxes
const taxesResult = this._taxesMapper.mapToPersistenceArray(source.taxes, { const taxesResult = this._taxesMapper.mapToPersistenceArray(source.getTaxes(), {
errors, errors,
parent: source, parent: source,
...params, ...params,
@ -346,6 +347,8 @@ export class SequelizeIssuedInvoiceDomainMapper
const taxes = taxesResult.data; const taxes = taxesResult.data;
const verifactu = verifactuResult.data; const verifactu = verifactuResult.data;
const allAmounts = source.calculateAllAmounts(); // Da los totales ya calculados
const invoiceValues: Partial<CustomerInvoiceCreationAttributes> = { const invoiceValues: Partial<CustomerInvoiceCreationAttributes> = {
// Identificación // Identificación
id: source.id.toPrimitive(), id: source.id.toPrimitive(),
@ -353,11 +356,11 @@ export class SequelizeIssuedInvoiceDomainMapper
// Flags / estado / serie / número // Flags / estado / serie / número
is_proforma: false, is_proforma: false,
status: source.status.toPrimitive(),
proforma_id: toNullable(source.proformaId, (v) => v.toPrimitive()), proforma_id: toNullable(source.proformaId, (v) => v.toPrimitive()),
status: source.status.toPrimitive(),
series: toNullable(source.series, (v) => v.toPrimitive()), series: toNullable(source.series, (v) => v.toPrimitive()),
invoice_number: source.invoiceNumber.toPrimitive(), invoice_number: source.invoiceNumber.toPrimitive(),
invoice_date: source.invoiceDate.toPrimitive(), invoice_date: source.invoiceDate.toPrimitive(),
operation_date: toNullable(source.operationDate, (v) => v.toPrimitive()), operation_date: toNullable(source.operationDate, (v) => v.toPrimitive()),
language_code: source.languageCode.toPrimitive(), language_code: source.languageCode.toPrimitive(),
@ -365,20 +368,13 @@ export class SequelizeIssuedInvoiceDomainMapper
reference: toNullable(source.reference, (reference) => reference), reference: toNullable(source.reference, (reference) => reference),
description: toNullable(source.description, (description) => description), description: toNullable(source.description, (description) => description),
notes: toNullable(source.notes, (v) => v.toPrimitive()), notes: toNullable(source.notes, (v) => v.toPrimitive()),
payment_method_id: toNullable(source.paymentMethod, (payment) => payment.toObjectString().id),
payment_method_description: toNullable(
source.paymentMethod,
(payment) => payment.toObjectString().payment_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,
discount_percentage_value: source.globalDiscountPercentage.toPrimitive().value, discount_percentage_value: source.discountPercentage.toPrimitive().value,
discount_percentage_scale: source.globalDiscountPercentage.toPrimitive().scale, discount_percentage_scale: source.discountPercentage.toPrimitive().scale,
discount_amount_value: source.globalDiscountAmount.value, discount_amount_value: source.globalDiscountAmount.value,
discount_amount_scale: source.globalDiscountAmount.scale, discount_amount_scale: source.globalDiscountAmount.scale,
@ -392,6 +388,12 @@ export class SequelizeIssuedInvoiceDomainMapper
total_amount_value: source.totalAmount.value, total_amount_value: source.totalAmount.value,
total_amount_scale: source.totalAmount.scale, total_amount_scale: source.totalAmount.scale,
payment_method_id: toNullable(source.paymentMethod, (payment) => payment.toObjectString().id),
payment_method_description: toNullable(
source.paymentMethod,
(payment) => payment.toObjectString().payment_description
),
customer_id: source.customerId.toPrimitive(), customer_id: source.customerId.toPrimitive(),
...recipient, ...recipient,

View File

@ -14,10 +14,11 @@ import {
} from "@repo/rdx-ddd"; } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import type { IssuedInvoiceListDTO } from "../../../../../../application";
import { InvoiceRecipient } from "../../../../../../domain"; import { InvoiceRecipient } from "../../../../../../domain";
import type { CustomerInvoiceModel } from "../../../../../common"; import type { CustomerInvoiceModel } from "../../../../../common";
import type { CustomerInvoiceListDTO } from "./sequelize-issued-invoice.list.mapper";
export class SequelizeIssuedInvoiceRecipientListMapper extends SequelizeQueryMapper< export class SequelizeIssuedInvoiceRecipientListMapper extends SequelizeQueryMapper<
CustomerInvoiceModel, CustomerInvoiceModel,
InvoiceRecipient InvoiceRecipient
@ -32,7 +33,7 @@ export class SequelizeIssuedInvoiceRecipientListMapper extends SequelizeQueryMap
const { errors, attributes } = params as { const { errors, attributes } = params as {
errors: ValidationErrorDetail[]; errors: ValidationErrorDetail[];
attributes: Partial<IssuedInvoiceListDTO>; attributes: Partial<CustomerInvoiceListDTO>;
}; };
const { isProforma } = attributes; const { isProforma } = attributes;

View File

@ -2,6 +2,7 @@ import { type MapperParamsType, SequelizeQueryMapper } from "@erp/core/api";
import { import {
CurrencyCode, CurrencyCode,
LanguageCode, LanguageCode,
Percentage,
UniqueID, UniqueID,
UtcDate, UtcDate,
ValidationErrorCollection, ValidationErrorCollection,
@ -93,7 +94,7 @@ export class SequelizeIssuedInvoiceListMapper
operationDate: attributes.operationDate!, operationDate: attributes.operationDate!,
description: attributes.description!, description: attributes.description!,
reference: attributes.reference!, reference: attributes.description!,
customerId: attributes.customerId!, customerId: attributes.customerId!,
recipient: recipientResult.data, recipient: recipientResult.data,
@ -101,8 +102,9 @@ export class SequelizeIssuedInvoiceListMapper
languageCode: attributes.languageCode!, languageCode: attributes.languageCode!,
currencyCode: attributes.currencyCode!, currencyCode: attributes.currencyCode!,
discountPercentage: attributes.discountPercentage!,
subtotalAmount: attributes.subtotalAmount!, subtotalAmount: attributes.subtotalAmount!,
totalDiscountAmount: attributes.totalDiscountAmount!, discountAmount: attributes.discountAmount!,
taxableAmount: attributes.taxableAmount!, taxableAmount: attributes.taxableAmount!,
taxesAmount: attributes.taxesAmount!, taxesAmount: attributes.taxesAmount!,
totalAmount: attributes.totalAmount!, totalAmount: attributes.totalAmount!,
@ -173,6 +175,15 @@ export class SequelizeIssuedInvoiceListMapper
errors errors
); );
const discountPercentage = extractOrPushError(
Percentage.create({
value: raw.discount_percentage_value,
scale: raw.discount_percentage_scale,
}),
"discount_percentage_value",
errors
);
const subtotalAmount = extractOrPushError( const subtotalAmount = extractOrPushError(
InvoiceAmount.create({ InvoiceAmount.create({
value: raw.subtotal_amount_value, value: raw.subtotal_amount_value,
@ -182,12 +193,12 @@ export class SequelizeIssuedInvoiceListMapper
errors errors
); );
const totalDiscountAmount = extractOrPushError( const discountAmount = extractOrPushError(
InvoiceAmount.create({ InvoiceAmount.create({
value: raw.total_discount_amount_value, value: raw.discount_amount_value,
currency_code: currencyCode?.code, currency_code: currencyCode?.code,
}), }),
"total_discount_amount_value", "discount_amount_value",
errors errors
); );
@ -232,9 +243,9 @@ export class SequelizeIssuedInvoiceListMapper
description, description,
languageCode, languageCode,
currencyCode, currencyCode,
discountPercentage,
subtotalAmount, subtotalAmount,
totalDiscountAmount, discountAmount,
taxableAmount, taxableAmount,
taxesAmount, taxesAmount,
totalAmount, totalAmount,

View File

@ -12,10 +12,6 @@ import {
CustomerInvoiceTaxModel, CustomerInvoiceTaxModel,
VerifactuRecordModel, VerifactuRecordModel,
} from "../../../../common"; } from "../../../../common";
import type {
SequelizeIssuedInvoiceDomainMapper,
SequelizeIssuedInvoiceListMapper,
} from "../mappers";
export class IssuedInvoiceRepository export class IssuedInvoiceRepository
extends SequelizeRepository<IssuedInvoice> extends SequelizeRepository<IssuedInvoice>
@ -80,38 +76,6 @@ export class IssuedInvoiceRepository
} }
} }
/**
* Comprueba si existe una factura con un `id` dentro de una `company`.
*
* @param companyId - Identificador UUID de la empresa a la que pertenece la factura.
* @param id - Identificador UUID de la factura.
* @param transaction - Transacción activa para la operación.
* @param options - Opciones adicionales para la consulta (Sequelize FindOptions)
* @returns Result<boolean, Error>
*/
async existsByIdInCompany(
companyId: UniqueID,
id: UniqueID,
transaction: Transaction,
options: FindOptions<InferAttributes<CustomerInvoiceModel>> = {}
): Promise<Result<boolean, Error>> {
try {
const count = await CustomerInvoiceModel.count({
...options,
where: {
id: id.toString(),
company_id: companyId.toString(),
is_proforma: false,
...(options.where ?? {}),
},
transaction,
});
return Result.ok(Boolean(count > 0));
} catch (error: unknown) {
return Result.fail(translateSequelizeError(error));
}
}
/** /**
* *
* Busca una factura por su identificador único. * Busca una factura por su identificador único.
@ -122,7 +86,7 @@ export class IssuedInvoiceRepository
* @param options - Opciones adicionales para la consulta (Sequelize FindOptions) * @param options - Opciones adicionales para la consulta (Sequelize FindOptions)
* @returns Result<CustomerInvoice, Error> * @returns Result<CustomerInvoice, Error>
*/ */
async getByIdInCompany( async getIssuedInvoiceByIdInCompany(
companyId: UniqueID, companyId: UniqueID,
id: UniqueID, id: UniqueID,
transaction: Transaction, transaction: Transaction,

View File

@ -1,17 +1,45 @@
import { type ModuleParams, buildTransactionManager } from "@erp/core/api"; import {
InMemoryMapperRegistry,
InMemoryPresenterRegistry,
type ModuleParams,
buildTransactionManager,
} from "@erp/core/api";
import { buildProformaCreator } from "@erp/customer-invoices/api/application/proformas/di/proforma-creator.di";
import { import {
type GetProformaByIdUseCase, type GetProformaByIdUseCase,
type ListProformasUseCase,
type ReportProformaUseCase,
buildGetProformaByIdUseCase, buildGetProformaByIdUseCase,
buildListProformasUseCase, buildListProformasUseCase,
buildProformaFinder, buildProformaFinder,
buildProformaSnapshotBuilders,
buildReportProformaUseCase, buildReportProformaUseCase,
} from "../../../application"; } from "../../../application";
import { buildProformaSnapshotBuilders } from "../../../application/issued-invoices";
import {
ChangeStatusProformaUseCase,
CreateProformaUseCase,
CustomerInvoiceApplicationService,
DeleteProformaUseCase,
GetProformaUseCase,
IssueProformaUseCase,
ListProformasUseCase,
ProformaFullPresenter,
ProformaListPresenter,
ReportProformaUseCase,
UpdateProformaUseCase,
} from "../application";
import {
ProformaItemsReportPresenter,
ProformaReportPresenter,
ProformaTaxesReportPresenter,
} from "../application/snapshot-builders/reports";
import { buildproformaDocumentService } from "./proforma-documents.di"; import { SequelizeInvoiceNumberGenerator } from "./persistence/sequelize";
import {
CustomerInvoiceDomainMapper,
CustomerInvoiceListMapper,
} from "./persistence/sequelize/mappers";
import { buildProformaDocumentService } from "./proforma-documents.di";
import { buildProformaNumberGenerator } from "./proforma-number-generator.di";
import { buildProformaRepository } from "./proforma-repositories.di"; import { buildProformaRepository } from "./proforma-repositories.di";
export type ProformasInternalDeps = { export type ProformasInternalDeps = {
@ -34,15 +62,15 @@ export function buildProformasDependencies(params: ModuleParams): ProformasInter
// Infrastructure // Infrastructure
const transactionManager = buildTransactionManager(database); const transactionManager = buildTransactionManager(database);
const repository = buildProformaRepository(database); const repository = buildProformaRepository(database);
//const numberService = buildProformaNumberGenerator(); const numberService = buildProformaNumberGenerator();
// Application helpers // Application helpers
const finder = buildProformaFinder(repository); const finder = buildProformaFinder(repository);
//const creator = buildProformaCreator(numberService, repository); const creator = buildProformaCreator(numberService, repository);
const snapshotBuilders = buildProformaSnapshotBuilders(); const snapshotBuilders = buildProformaSnapshotBuilders();
const documentGeneratorPipeline = buildproformaDocumentService(params); const documentGeneratorPipeline = buildProformaDocumentService(params);
// Internal use cases (factories) // Internal use cases (factories)
return { return {
@ -80,7 +108,7 @@ export function buildProformasDependencies(params: ModuleParams): ProformasInter
}; };
} }
/*const mapperRegistry = new InMemoryMapperRegistry(); const mapperRegistry = new InMemoryMapperRegistry();
mapperRegistry mapperRegistry
.registerDomainMapper( .registerDomainMapper(
{ resource: "customer-invoice" }, { resource: "customer-invoice" },
@ -96,7 +124,7 @@ mapperRegistry
// Repository & Services // Repository & Services
const numberGenerator = new SequelizeInvoiceNumberGenerator(); const numberGenerator = new SequelizeInvoiceNumberGenerator();
/** Aplicación */
const appService = new CustomerInvoiceApplicationService(repository, numberGenerator); const appService = new CustomerInvoiceApplicationService(repository, numberGenerator);
// Presenter Registry // Presenter Registry
@ -150,5 +178,4 @@ const useCases: ProformasDeps["useCases"] = {
new ReportProformaUseCase(appService, transactionManager, presenterRegistry), new ReportProformaUseCase(appService, transactionManager, presenterRegistry),
issue_proforma: () => new IssueProformaUseCase(appService, transactionManager, presenterRegistry), issue_proforma: () => new IssueProformaUseCase(appService, transactionManager, presenterRegistry),
changeStatus_proforma: () => new ChangeStatusProformaUseCase(appService, transactionManager), changeStatus_proforma: () => new ChangeStatusProformaUseCase(appService, transactionManager),
};
*/

View File

@ -16,6 +16,7 @@ import {
ProformaDocumentPropertiesFactory, ProformaDocumentPropertiesFactory,
type ProformaReportSnapshot, type ProformaReportSnapshot,
} from "../../../../application"; } from "../../../../application";
import { DigitalSignaturePostProcessor } from "../post-processors";
import { ProformaSignedDocumentCachePreProcessor } from "../pre-processors"; import { ProformaSignedDocumentCachePreProcessor } from "../pre-processors";
import { ProformaDocumentRenderer } from "../renderers"; import { ProformaDocumentRenderer } from "../renderers";
import { PersistProformaDocumentSideEffect } from "../side-effects"; import { PersistProformaDocumentSideEffect } from "../side-effects";
@ -50,7 +51,7 @@ export class ProformaDocumentPipelineFactory {
// 3) Firma real (Core / Infra) // 3) Firma real (Core / Infra)
const postProcessor: IDocumentPostProcessor = new DocumentPostProcessorChain([ const postProcessor: IDocumentPostProcessor = new DocumentPostProcessorChain([
// Aquí podrían ir más post-procesadores, como uno de validación o similar new DigitalSignaturePostProcessor(deps.signingContextResolver, deps.documentSigningService),
]); ]);
// 4. Side-effects (persistencia best-effort) // 4. Side-effects (persistencia best-effort)

View File

@ -0,0 +1 @@
export * from "./digital-signature-post-processor";

View File

@ -1,8 +0,0 @@
//export * from "./change-status-proforma.controller";
//export * from "./create-proforma.controller";
//export * from "./delete-proforma.controller";
export * from "./get-proforma.controller";
//export * from "./issue-proforma.controller";
export * from "./list-proformas.controller";
export * from "./report-proforma.controller";
//export * from "./update-proforma.controller";

View File

@ -1,4 +1,3 @@
export * from "./di"; export * from "./di";
export * from "./documents"; export * from "./documents";
export * from "./express";
export * from "./persistence"; export * from "./persistence";

View File

@ -249,7 +249,7 @@ export class SequelizeProformaDomainMapper
languageCode: attributes.languageCode!, languageCode: attributes.languageCode!,
currencyCode: attributes.currencyCode!, currencyCode: attributes.currencyCode!,
globalDiscountPercentage: attributes.discountPercentage!, discountPercentage: attributes.discountPercentage!,
paymentMethod: attributes.paymentMethod!, paymentMethod: attributes.paymentMethod!,
@ -348,17 +348,11 @@ export class SequelizeProformaDomainMapper
subtotal_amount_value: allAmounts.subtotalAmount.value, subtotal_amount_value: allAmounts.subtotalAmount.value,
subtotal_amount_scale: allAmounts.subtotalAmount.scale, subtotal_amount_scale: allAmounts.subtotalAmount.scale,
items_discount_amount_value: allAmounts.itemDiscountAmount.value, discount_percentage_value: source.discountPercentage.toPrimitive().value,
items_discount_amount_scale: allAmounts.itemDiscountAmount.scale, discount_percentage_scale: source.discountPercentage.toPrimitive().scale,
global_discount_percentage_value: source.globalDiscountPercentage.toPrimitive().value, discount_amount_value: allAmounts.globalDiscountAmount.value,
global_discount_percentage_scale: source.globalDiscountPercentage.toPrimitive().scale, discount_amount_scale: allAmounts.globalDiscountAmount.scale,
global_discount_amount_value: allAmounts.globalDiscountAmount.value,
global_discount_amount_scale: allAmounts.globalDiscountAmount.scale,
total_discount_amount_value: allAmounts.totalDiscountAmount.value,
total_discount_amount_scale: allAmounts.totalDiscountAmount.scale,
taxable_amount_value: allAmounts.taxableAmount.value, taxable_amount_value: allAmounts.taxableAmount.value,
taxable_amount_scale: allAmounts.taxableAmount.scale, taxable_amount_scale: allAmounts.taxableAmount.scale,

View File

@ -42,9 +42,9 @@ export class SequelizeProformaTaxesDomainMapper extends SequelizeDomainMapper<
}; };
try { try {
const { ivaAmount, recAmount, retentionAmount, totalAmount } = source.calculateAmounts(); const { ivaAmount, recAmount, retentionAmount } = source.calculateAmounts();
const totalTaxes = totalAmount; const totalTaxes = ivaAmount.add(recAmount).add(retentionAmount);
const dto: CustomerInvoiceTaxCreationAttributes = { const dto: CustomerInvoiceTaxCreationAttributes = {
tax_id: UniqueID.generateNewID().toPrimitive(), tax_id: UniqueID.generateNewID().toPrimitive(),

View File

@ -2,6 +2,7 @@ import { type MapperParamsType, SequelizeQueryMapper } from "@erp/core/api";
import { import {
CurrencyCode, CurrencyCode,
LanguageCode, LanguageCode,
Percentage,
UniqueID, UniqueID,
UtcDate, UtcDate,
ValidationErrorCollection, ValidationErrorCollection,
@ -82,8 +83,9 @@ export class SequelizeProformaListMapper
languageCode: attributes.languageCode!, languageCode: attributes.languageCode!,
currencyCode: attributes.currencyCode!, currencyCode: attributes.currencyCode!,
discountPercentage: attributes.discountPercentage!,
subtotalAmount: attributes.subtotalAmount!, subtotalAmount: attributes.subtotalAmount!,
totalDiscountAmount: attributes.totalDiscountAmount!, discountAmount: attributes.discountAmount!,
taxableAmount: attributes.taxableAmount!, taxableAmount: attributes.taxableAmount!,
taxesAmount: attributes.taxesAmount!, taxesAmount: attributes.taxesAmount!,
totalAmount: attributes.totalAmount!, totalAmount: attributes.totalAmount!,
@ -152,6 +154,15 @@ export class SequelizeProformaListMapper
errors errors
); );
const discountPercentage = extractOrPushError(
Percentage.create({
value: raw.discount_percentage_value,
scale: raw.discount_percentage_scale,
}),
"discount_percentage_value",
errors
);
const subtotalAmount = extractOrPushError( const subtotalAmount = extractOrPushError(
InvoiceAmount.create({ InvoiceAmount.create({
value: raw.subtotal_amount_value, value: raw.subtotal_amount_value,
@ -161,12 +172,12 @@ export class SequelizeProformaListMapper
errors errors
); );
const totalDiscountAmount = extractOrPushError( const discountAmount = extractOrPushError(
InvoiceAmount.create({ InvoiceAmount.create({
value: raw.total_discount_amount_value, value: raw.discount_amount_value,
currency_code: currencyCode?.code, currency_code: currencyCode?.code,
}), }),
"total_discount_amount_value", "discount_amount_value",
errors errors
); );
@ -211,9 +222,9 @@ export class SequelizeProformaListMapper
description, description,
languageCode, languageCode,
currencyCode, currencyCode,
discountPercentage,
subtotalAmount, subtotalAmount,
totalDiscountAmount, discountAmount,
taxableAmount, taxableAmount,
taxesAmount, taxesAmount,
totalAmount, totalAmount,

View File

@ -1,4 +1,9 @@
import { MetadataSchema, MoneySchema, createPaginatedListSchema } from "@erp/core"; import {
MetadataSchema,
MoneySchema,
PercentageSchema,
createPaginatedListSchema,
} from "@erp/core";
import { z } from "zod/v4"; import { z } from "zod/v4";
export const ListIssuedInvoicesResponseSchema = createPaginatedListSchema( export const ListIssuedInvoicesResponseSchema = createPaginatedListSchema(
@ -34,7 +39,8 @@ export const ListIssuedInvoicesResponseSchema = createPaginatedListSchema(
}), }),
subtotal_amount: MoneySchema, subtotal_amount: MoneySchema,
total_discount_amount: MoneySchema, discount_percentage: PercentageSchema,
discount_amount: MoneySchema,
taxable_amount: MoneySchema, taxable_amount: MoneySchema,
taxes_amount: MoneySchema, taxes_amount: MoneySchema,
total_amount: MoneySchema, total_amount: MoneySchema,