This commit is contained in:
David Arranz 2026-02-19 18:54:59 +01:00
parent acd61cf6eb
commit e9824ecf80
48 changed files with 647 additions and 380 deletions

View File

@ -1,8 +1,8 @@
import type { ICustomerInvoiceRepository } from "../../../domain"; import type { IIssuedInvoiceRepository } from "../repositories";
import { type IIssuedInvoiceFinder, IssuedInvoiceFinder } from "../services/issued-invoice-finder"; import { type IIssuedInvoiceFinder, IssuedInvoiceFinder } from "../services/issued-invoice-finder";
export function buildIssuedInvoiceFinder( export function buildIssuedInvoiceFinder(
repository: ICustomerInvoiceRepository repository: IIssuedInvoiceRepository
): IIssuedInvoiceFinder { ): IIssuedInvoiceFinder {
return new IssuedInvoiceFinder(repository); return new IssuedInvoiceFinder(repository);
} }

View File

@ -9,9 +9,9 @@ import {
IssuedInvoiceVerifactuFullSnapshotBuilder, IssuedInvoiceVerifactuFullSnapshotBuilder,
} from "../snapshot-builders/full"; } from "../snapshot-builders/full";
import { import {
IssuedInvoiceItemReportSnapshotBuilder, IssuedInvoiceReportItemSnapshotBuilder,
IssuedInvoiceReportSnapshotBuilder, IssuedInvoiceReportSnapshotBuilder,
IssuedInvoiceTaxReportSnapshotBuilder, IssuedInvoiceReportTaxSnapshotBuilder,
} from "../snapshot-builders/report"; } from "../snapshot-builders/report";
export function buildIssuedInvoiceSnapshotBuilders() { export function buildIssuedInvoiceSnapshotBuilders() {
@ -32,8 +32,8 @@ export function buildIssuedInvoiceSnapshotBuilders() {
const listSnapshotBuilder = new IssuedInvoiceListItemSnapshotBuilder(); const listSnapshotBuilder = new IssuedInvoiceListItemSnapshotBuilder();
const itemsReportBuilder = new IssuedInvoiceItemReportSnapshotBuilder(); const itemsReportBuilder = new IssuedInvoiceReportItemSnapshotBuilder();
const taxesReportBuilder = new IssuedInvoiceTaxReportSnapshotBuilder(); const taxesReportBuilder = new IssuedInvoiceReportTaxSnapshotBuilder();
const reportSnapshotBuilder = new IssuedInvoiceReportSnapshotBuilder( const reportSnapshotBuilder = new IssuedInvoiceReportSnapshotBuilder(
itemsReportBuilder, itemsReportBuilder,
taxesReportBuilder taxesReportBuilder

View File

@ -17,8 +17,7 @@ export interface IIssuedInvoiceRepository {
existsByIdInCompany( existsByIdInCompany(
companyId: UniqueID, companyId: UniqueID,
id: UniqueID, id: UniqueID,
transaction: unknown, transaction: unknown
options: unknown
): Promise<Result<boolean, Error>>; ): Promise<Result<boolean, Error>>;
findByCriteriaInCompany( findByCriteriaInCompany(

View File

@ -43,9 +43,7 @@ export class IssuedInvoiceFinder implements IIssuedInvoiceFinder {
invoiceId: UniqueID, invoiceId: UniqueID,
transaction?: Transaction transaction?: Transaction
): Promise<Result<boolean, Error>> { ): Promise<Result<boolean, Error>> {
return this.repository.existsByIdInCompany(companyId, invoiceId, transaction, { return this.repository.existsByIdInCompany(companyId, invoiceId, transaction);
is_proforma: false,
});
} }
async findIssuedInvoicesByCriteria( async findIssuedInvoicesByCriteria(

View File

@ -41,7 +41,6 @@ export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnaps
id: invoice.id.toString(), id: invoice.id.toString(),
company_id: invoice.companyId.toString(), company_id: invoice.companyId.toString(),
is_proforma: "false",
invoice_number: invoice.invoiceNumber.toString(), invoice_number: invoice.invoiceNumber.toString(),
status: invoice.status.toPrimitive(), status: invoice.status.toPrimitive(),
series: maybeToEmptyString(invoice.series, (value) => value.toString()), series: maybeToEmptyString(invoice.series, (value) => value.toString()),

View File

@ -7,7 +7,6 @@ export interface IIssuedInvoiceFullSnapshot {
id: string; id: string;
company_id: string; company_id: string;
is_proforma: "true" | "false";
invoice_number: string; invoice_number: string;
status: string; status: string;
series: string; series: string;

View File

@ -15,6 +15,8 @@ export interface IIssuedInvoiceItemFullSnapshot {
global_discount_percentage: { value: string; scale: string }; global_discount_percentage: { value: string; scale: string };
global_discount_amount: { value: string; scale: string; currency_code: string }; global_discount_amount: { value: string; scale: string; currency_code: string };
total_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 };
iva_code: string; iva_code: string;

View File

@ -6,7 +6,7 @@ import {
maybeToEmptyString, maybeToEmptyString,
} from "@repo/rdx-ddd"; } from "@repo/rdx-ddd";
import type { IssuedInvoiceItem, IssuedInvoiceItems } from "../../../../domain"; import { type IssuedInvoiceItem, type IssuedInvoiceItems, ItemAmount } from "../../../../domain";
import type { IIssuedInvoiceItemFullSnapshot } from "./issued-invoice-item-full-snapshot.interface"; import type { IIssuedInvoiceItemFullSnapshot } from "./issued-invoice-item-full-snapshot.interface";
@ -17,9 +17,10 @@ export class IssuedInvoiceItemsFullSnapshotBuilder
implements IIssuedInvoiceItemsFullSnapshotBuilder implements IIssuedInvoiceItemsFullSnapshotBuilder
{ {
private mapItem(invoiceItem: IssuedInvoiceItem, index: number): IIssuedInvoiceItemFullSnapshot { private mapItem(invoiceItem: IssuedInvoiceItem, index: number): IIssuedInvoiceItemFullSnapshot {
const isValued = invoiceItem.isValued;
return { return {
id: invoiceItem.id.toPrimitive(), id: invoiceItem.id.toPrimitive(),
is_valued: String(invoiceItem.isValued), is_valued: String(isValued),
position: String(index), position: String(index),
description: maybeToEmptyString(invoiceItem.description, (value) => value.toString()), description: maybeToEmptyString(invoiceItem.description, (value) => value.toString()),
@ -27,32 +28,50 @@ export class IssuedInvoiceItemsFullSnapshotBuilder
quantity: maybeToEmptyQuantityObjectString(invoiceItem.quantity), quantity: maybeToEmptyQuantityObjectString(invoiceItem.quantity),
unit_amount: maybeToEmptyMoneyObjectString(invoiceItem.unitAmount), unit_amount: maybeToEmptyMoneyObjectString(invoiceItem.unitAmount),
subtotal_amount: maybeToEmptyMoneyObjectString(invoiceItem.subtotalAmount), subtotal_amount: isValued
? invoiceItem.subtotalAmount.toObjectString()
: ItemAmount.EMPTY_MONEY_OBJECT,
discount_percentage: maybeToEmptyPercentageObjectString(invoiceItem.itemDiscountPercentage), discount_percentage: maybeToEmptyPercentageObjectString(invoiceItem.itemDiscountPercentage),
discount_amount: maybeToEmptyMoneyObjectString(invoiceItem.itemDiscountAmount), discount_amount: isValued
? invoiceItem.itemDiscountAmount.toObjectString()
: ItemAmount.EMPTY_MONEY_OBJECT,
global_discount_percentage: maybeToEmptyPercentageObjectString( global_discount_percentage: maybeToEmptyPercentageObjectString(
invoiceItem.globalDiscountPercentage invoiceItem.globalDiscountPercentage
), ),
global_discount_amount: maybeToEmptyMoneyObjectString(invoiceItem.globalDiscountAmount), global_discount_amount: isValued
? invoiceItem.globalDiscountAmount.toObjectString()
: ItemAmount.EMPTY_MONEY_OBJECT,
taxable_amount: maybeToEmptyMoneyObjectString(invoiceItem.taxableAmount), total_discount_amount: isValued
? invoiceItem.totalDiscountAmount.toObjectString()
: ItemAmount.EMPTY_MONEY_OBJECT,
taxable_amount: isValued
? invoiceItem.taxableAmount.toObjectString()
: ItemAmount.EMPTY_MONEY_OBJECT,
iva_code: maybeToEmptyString(invoiceItem.ivaCode), iva_code: maybeToEmptyString(invoiceItem.ivaCode),
iva_percentage: maybeToEmptyPercentageObjectString(invoiceItem.ivaPercentage), iva_percentage: maybeToEmptyPercentageObjectString(invoiceItem.ivaPercentage),
iva_amount: maybeToEmptyMoneyObjectString(invoiceItem.ivaAmount), iva_amount: isValued ? invoiceItem.ivaAmount.toObjectString() : ItemAmount.EMPTY_MONEY_OBJECT,
rec_code: maybeToEmptyString(invoiceItem.recCode), rec_code: maybeToEmptyString(invoiceItem.recCode),
rec_percentage: maybeToEmptyPercentageObjectString(invoiceItem.recPercentage), rec_percentage: maybeToEmptyPercentageObjectString(invoiceItem.recPercentage),
rec_amount: maybeToEmptyMoneyObjectString(invoiceItem.recAmount), rec_amount: isValued ? invoiceItem.recAmount.toObjectString() : ItemAmount.EMPTY_MONEY_OBJECT,
retention_code: maybeToEmptyString(invoiceItem.retentionCode), retention_code: maybeToEmptyString(invoiceItem.retentionCode),
retention_percentage: maybeToEmptyPercentageObjectString(invoiceItem.retentionPercentage), retention_percentage: maybeToEmptyPercentageObjectString(invoiceItem.retentionPercentage),
retention_amount: maybeToEmptyMoneyObjectString(invoiceItem.retentionAmount), retention_amount: isValued
? invoiceItem.retentionAmount.toObjectString()
: ItemAmount.EMPTY_MONEY_OBJECT,
taxes_amount: maybeToEmptyMoneyObjectString(invoiceItem.taxesAmount), taxes_amount: isValued
total_amount: maybeToEmptyMoneyObjectString(invoiceItem.totalAmount), ? invoiceItem.taxesAmount.toObjectString()
: ItemAmount.EMPTY_MONEY_OBJECT,
total_amount: isValued
? invoiceItem.totalAmount.toObjectString()
: ItemAmount.EMPTY_MONEY_OBJECT,
}; };
} }

View File

@ -24,7 +24,6 @@ export class IssuedInvoiceListItemSnapshotBuilder implements IIssuedInvoiceListI
return { return {
id: invoice.id.toString(), id: invoice.id.toString(),
company_id: invoice.companyId.toString(), company_id: invoice.companyId.toString(),
is_proforma: invoice.isProforma,
customer_id: invoice.customerId.toString(), customer_id: invoice.customerId.toString(),

View File

@ -1,7 +1,6 @@
export interface IIssuedInvoiceListItemSnapshot { export interface IIssuedInvoiceListItemSnapshot {
id: string; id: string;
company_id: string; company_id: string;
is_proforma: boolean;
customer_id: string; customer_id: string;

View File

@ -1,6 +1,6 @@
export * from "./issued-invoice-items-report-snapshot-builder";
export * from "./issued-invoice-report-item-snapshot.interface"; export * from "./issued-invoice-report-item-snapshot.interface";
export * from "./issued-invoice-report-item-snapshot-builder";
export * from "./issued-invoice-report-snapshot.interface"; export * from "./issued-invoice-report-snapshot.interface";
export * from "./issued-invoice-report-snapshot-builder"; export * from "./issued-invoice-report-snapshot-builder";
export * from "./issued-invoice-report-tax-snapshot.interface"; export * from "./issued-invoice-report-tax-snapshot.interface";
export * from "./issued-invoice-tax-report-snapshot-builder"; export * from "./issued-invoice-report-tax-snapshot-builder";

View File

@ -1,21 +1,20 @@
import { MoneyDTOHelper, PercentageDTOHelper, QuantityDTOHelper } from "@erp/core"; import { MoneyDTOHelper, PercentageDTOHelper, QuantityDTOHelper } from "@erp/core";
import type { ISnapshotBuilder, ISnapshotBuilderParams } from "@erp/core/api"; import type { ISnapshotBuilder, ISnapshotBuilderParams } from "@erp/core/api";
import type { import type { IIssuedInvoiceItemFullSnapshot } from "../full";
IssuedInvoiceFullSnapshot,
IssuedInvoiceReportItemSnapshot,
} from "../../application-models";
export interface IIssuedInvoiceItemReportSnapshotBuilder import type { IIssuedInvoiceReportItemSnapshot } from "./issued-invoice-report-item-snapshot.interface";
extends ISnapshotBuilder<IssuedInvoiceFullSnapshot["items"], IssuedInvoiceReportItemSnapshot[]> {}
export class IssuedInvoiceItemReportSnapshotBuilder export interface IIssuedInvoiceReportItemSnapshotBuilder
implements IIssuedInvoiceItemReportSnapshotBuilder extends ISnapshotBuilder<IIssuedInvoiceItemFullSnapshot[], IIssuedInvoiceReportItemSnapshot[]> {}
export class IssuedInvoiceReportItemSnapshotBuilder
implements IIssuedInvoiceReportItemSnapshotBuilder
{ {
toOutput( toOutput(
items: IssuedInvoiceFullSnapshot["items"], items: IIssuedInvoiceItemFullSnapshot[],
params?: ISnapshotBuilderParams params?: ISnapshotBuilderParams
): IssuedInvoiceReportItemSnapshot[] { ): IIssuedInvoiceReportItemSnapshot[] {
const locale = params?.locale as string; const locale = params?.locale as string;
const moneyOptions = { const moneyOptions = {

View File

@ -1,4 +1,4 @@
export interface IssuedInvoiceReportItemSnapshot { export interface IIssuedInvoiceReportItemSnapshot {
description: string; description: string;
quantity: string; quantity: string;
unit_amount: string; unit_amount: string;

View File

@ -1,32 +1,25 @@
import { DateHelper, MoneyDTOHelper, PercentageDTOHelper } from "@erp/core"; import { DateHelper, MoneyDTOHelper, PercentageDTOHelper } from "@erp/core";
import type { ISnapshotBuilder, ISnapshotBuilderParams } from "@erp/core/api"; import type { ISnapshotBuilder, ISnapshotBuilderParams } from "@erp/core/api";
import type { import type { IIssuedInvoiceFullSnapshot } from "../full";
IssuedInvoiceFullSnapshot,
IssuedInvoiceReportItemSnapshot, import type { IIssuedInvoiceReportItemSnapshotBuilder } from "./issued-invoice-report-item-snapshot-builder";
IssuedInvoiceReportSnapshot, import type { IIssuedInvoiceReportSnapshot } from "./issued-invoice-report-snapshot.interface";
IssuedInvoiceReportTaxSnapshot, import type { IIssuedInvoiceReportTaxSnapshotBuilder } from "./issued-invoice-report-tax-snapshot-builder";
} from "../../application-models";
export interface IIssuedInvoiceReportSnapshotBuilder export interface IIssuedInvoiceReportSnapshotBuilder
extends ISnapshotBuilder<IssuedInvoiceFullSnapshot, IssuedInvoiceReportSnapshot> {} extends ISnapshotBuilder<IIssuedInvoiceFullSnapshot, IIssuedInvoiceReportSnapshot> {}
export class IssuedInvoiceReportSnapshotBuilder implements IIssuedInvoiceReportSnapshotBuilder { export class IssuedInvoiceReportSnapshotBuilder implements IIssuedInvoiceReportSnapshotBuilder {
constructor( constructor(
private readonly itemsBuilder: ISnapshotBuilder< private readonly itemsBuilder: IIssuedInvoiceReportItemSnapshotBuilder,
IssuedInvoiceFullSnapshot["items"], private readonly taxesBuilder: IIssuedInvoiceReportTaxSnapshotBuilder
IssuedInvoiceReportItemSnapshot[]
>,
private readonly taxesBuilder: ISnapshotBuilder<
IssuedInvoiceFullSnapshot["taxes"],
IssuedInvoiceReportTaxSnapshot[]
>
) {} ) {}
toOutput( toOutput(
snapshot: IssuedInvoiceFullSnapshot, snapshot: IIssuedInvoiceFullSnapshot,
params?: ISnapshotBuilderParams params?: ISnapshotBuilderParams
): IssuedInvoiceReportSnapshot { ): IIssuedInvoiceReportSnapshot {
const locale = params?.locale as string; const locale = params?.locale as string;
const moneyOptions = { const moneyOptions = {
@ -61,10 +54,10 @@ export class IssuedInvoiceReportSnapshotBuilder implements IIssuedInvoiceReportS
taxes: this.taxesBuilder.toOutput(snapshot.taxes, { locale }), taxes: this.taxesBuilder.toOutput(snapshot.taxes, { locale }),
subtotal_amount: MoneyDTOHelper.format(snapshot.subtotal_amount, locale, moneyOptions), subtotal_amount: MoneyDTOHelper.format(snapshot.subtotal_amount, locale, moneyOptions),
discount_percentage: PercentageDTOHelper.format(snapshot.discount_percentage, locale, { discount_percentage: PercentageDTOHelper.format(snapshot.global_discount_percentage, locale, {
hideZeros: true, hideZeros: true,
}), }),
discount_amount: MoneyDTOHelper.format(snapshot.discount_amount, locale, moneyOptions), discount_amount: MoneyDTOHelper.format(snapshot.global_discount_amount, locale, moneyOptions),
taxable_amount: MoneyDTOHelper.format(snapshot.taxable_amount, locale, moneyOptions), taxable_amount: MoneyDTOHelper.format(snapshot.taxable_amount, locale, moneyOptions),
taxes_amount: MoneyDTOHelper.format(snapshot.taxes_amount, locale, moneyOptions), taxes_amount: MoneyDTOHelper.format(snapshot.taxes_amount, locale, moneyOptions),
total_amount: MoneyDTOHelper.format(snapshot.total_amount, locale, moneyOptions), total_amount: MoneyDTOHelper.format(snapshot.total_amount, locale, moneyOptions),
@ -76,7 +69,7 @@ export class IssuedInvoiceReportSnapshotBuilder implements IIssuedInvoiceReportS
}; };
} }
private formatAddress(recipient: IssuedInvoiceFullSnapshot["recipient"]): string { private formatAddress(recipient: IIssuedInvoiceFullSnapshot["recipient"]): string {
const lines: string[] = []; const lines: string[] = [];
if (recipient.street) lines.push(recipient.street); if (recipient.street) lines.push(recipient.street);

View File

@ -1,7 +1,7 @@
import type { IssuedInvoiceReportItemSnapshot } from "./issued-invoice-report-item-snapshot.interface"; import type { IIssuedInvoiceReportItemSnapshot } from "./issued-invoice-report-item-snapshot.interface";
import type { IssuedInvoiceReportTaxSnapshot } from "./issued-invoice-report-tax-snapshot.interface"; import type { IIssuedInvoiceReportTaxSnapshot } from "./issued-invoice-report-tax-snapshot.interface";
export interface IssuedInvoiceReportSnapshot { export interface IIssuedInvoiceReportSnapshot {
id: string; id: string;
company_id: string; company_id: string;
company_slug: string; company_slug: string;
@ -23,8 +23,8 @@ export interface IssuedInvoiceReportSnapshot {
format_address: string; format_address: string;
}; };
items: IssuedInvoiceReportItemSnapshot[]; items: IIssuedInvoiceReportItemSnapshot[];
taxes: IssuedInvoiceReportTaxSnapshot[]; taxes: IIssuedInvoiceReportTaxSnapshot[];
subtotal_amount: string; subtotal_amount: string;
discount_percentage: string; discount_percentage: string;

View File

@ -1,21 +1,20 @@
import { MoneyDTOHelper, PercentageDTOHelper } from "@erp/core"; import { MoneyDTOHelper, PercentageDTOHelper } from "@erp/core";
import type { ISnapshotBuilder, ISnapshotBuilderParams } from "@erp/core/api"; import type { ISnapshotBuilder, ISnapshotBuilderParams } from "@erp/core/api";
import type { import type { IIssuedInvoiceTaxFullSnapshot } from "../full";
IssuedInvoiceFullSnapshot,
IssuedInvoiceReportTaxSnapshot,
} from "../../application-models";
export interface IIssuedInvoiceTaxReportSnapshotBuilder import type { IIssuedInvoiceReportTaxSnapshot } from "./issued-invoice-report-tax-snapshot.interface";
extends ISnapshotBuilder<IssuedInvoiceFullSnapshot["taxes"], IssuedInvoiceReportTaxSnapshot[]> {}
export class IssuedInvoiceTaxReportSnapshotBuilder export interface IIssuedInvoiceReportTaxSnapshotBuilder
implements IIssuedInvoiceTaxReportSnapshotBuilder extends ISnapshotBuilder<IIssuedInvoiceTaxFullSnapshot[], IIssuedInvoiceReportTaxSnapshot[]> {}
export class IssuedInvoiceReportTaxSnapshotBuilder
implements IIssuedInvoiceReportTaxSnapshotBuilder
{ {
toOutput( toOutput(
taxes: IssuedInvoiceFullSnapshot["taxes"], taxes: IIssuedInvoiceTaxFullSnapshot[],
params?: ISnapshotBuilderParams params?: ISnapshotBuilderParams
): IssuedInvoiceReportTaxSnapshot[] { ): IIssuedInvoiceReportTaxSnapshot[] {
const locale = params?.locale as string; const locale = params?.locale as string;
const moneyOptions = { const moneyOptions = {

View File

@ -1,4 +1,4 @@
export interface IssuedInvoiceReportTaxSnapshot { export interface IIssuedInvoiceReportTaxSnapshot {
taxable_amount: string; taxable_amount: string;
iva_code: string; iva_code: string;

View File

@ -1,6 +1,6 @@
import type { ICustomerInvoiceRepository } from "../../../domain"; import type { IProformaRepository } from "../repositories";
import { type IProformaFinder, ProformaFinder } from "../services"; import { type IProformaFinder, ProformaFinder } from "../services";
export function buildProformaFinder(repository: ICustomerInvoiceRepository): IProformaFinder { export function buildProformaFinder(repository: IProformaRepository): IProformaFinder {
return new ProformaFinder(repository); return new ProformaFinder(repository);
} }

View File

@ -13,31 +13,31 @@ export interface IProformaRepository {
existsByIdInCompany( existsByIdInCompany(
companyId: UniqueID, companyId: UniqueID,
id: UniqueID, id: UniqueID,
tx: unknown transaction: unknown
): Promise<Result<boolean, Error>>; ): Promise<Result<boolean, Error>>;
getByIdInCompany( getByIdInCompany(
companyId: UniqueID, companyId: UniqueID,
id: UniqueID, id: UniqueID,
tx: unknown transaction: unknown
): Promise<Result<Proforma, Error>>; ): Promise<Result<Proforma, Error>>;
findByCriteriaInCompany( findByCriteriaInCompany(
companyId: UniqueID, companyId: UniqueID,
criteria: Criteria, criteria: Criteria,
tx: unknown transaction: unknown
): Promise<Result<Collection<ProformaListDTO>, Error>>; ): Promise<Result<Collection<ProformaListDTO>, Error>>;
deleteByIdInCompany( deleteByIdInCompany(
companyId: UniqueID, companyId: UniqueID,
id: UniqueID, id: UniqueID,
tx: unknown transaction: unknown
): Promise<Result<boolean, Error>>; ): Promise<Result<boolean, Error>>;
updateStatusByIdInCompany( updateStatusByIdInCompany(
companyId: UniqueID, companyId: UniqueID,
id: UniqueID, id: UniqueID,
newStatus: InvoiceStatus, newStatus: InvoiceStatus,
tx: unknown transaction: unknown
): Promise<Result<boolean, Error>>; ): Promise<Result<boolean, Error>>;
} }

View File

@ -1,10 +1,11 @@
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 { ICustomerInvoiceRepository, Proforma } from "../../../domain"; import type { Proforma } from "../../../domain";
import type { ProformaListDTO } from "../dtos";
import type { IProformaRepository } from "../repositories";
export interface IProformaFinder { export interface IProformaFinder {
findProformaById( findProformaById(
@ -23,18 +24,18 @@ export interface IProformaFinder {
companyId: UniqueID, companyId: UniqueID,
criteria: Criteria, criteria: Criteria,
transaction?: Transaction transaction?: Transaction
): Promise<Result<Collection<CustomerInvoiceListDTO>, Error>>; ): Promise<Result<Collection<ProformaListDTO>, Error>>;
} }
export class ProformaFinder implements IProformaFinder { export class ProformaFinder implements IProformaFinder {
constructor(private readonly repository: ICustomerInvoiceRepository) {} constructor(private readonly repository: IProformaRepository) {}
async findProformaById( async findProformaById(
companyId: UniqueID, companyId: UniqueID,
proformaId: UniqueID, proformaId: UniqueID,
transaction?: Transaction transaction?: Transaction
): Promise<Result<Proforma, Error>> { ): Promise<Result<Proforma, Error>> {
return this.repository.getProformaByIdInCompany(companyId, proformaId, transaction, {}); return this.repository.getByIdInCompany(companyId, proformaId, transaction, {});
} }
async proformaExists( async proformaExists(
@ -42,16 +43,14 @@ export class ProformaFinder implements IProformaFinder {
proformaId: UniqueID, proformaId: UniqueID,
transaction?: Transaction transaction?: Transaction
): Promise<Result<boolean, Error>> { ): Promise<Result<boolean, Error>> {
return this.repository.existsByIdInCompany(companyId, proformaId, transaction, { return this.repository.existsByIdInCompany(companyId, proformaId, transaction);
is_proforma: true,
});
} }
async findProformasByCriteria( async findProformasByCriteria(
companyId: UniqueID, companyId: UniqueID,
criteria: Criteria, criteria: Criteria,
transaction?: Transaction transaction?: Transaction
): Promise<Result<Collection<CustomerInvoiceListDTO>, Error>> { ): Promise<Result<Collection<ProformaListDTO>, Error>> {
return this.repository.findProformasByCriteriaInCompany(companyId, criteria, transaction, {}); return this.repository.findByCriteriaInCompany(companyId, criteria, transaction, {});
} }
} }

View File

@ -4,3 +4,5 @@ export * from "./proforma-item-full-snapshot.interface";
export * from "./proforma-items-full-snapshot-builder"; export * from "./proforma-items-full-snapshot-builder";
export * from "./proforma-recipient-full-snapshot.interface"; export * from "./proforma-recipient-full-snapshot.interface";
export * from "./proforma-recipient-full-snapshot-builder"; export * from "./proforma-recipient-full-snapshot-builder";
export * from "./proforma-tax-full-snapshot-interface";
export * from "./proforma-taxes-full-snapshot-builder";

View File

@ -83,7 +83,6 @@ export class ProformaFullSnapshotBuilder implements IProformaFullSnapshotBuilder
id: invoice.id.toString(), id: invoice.id.toString(),
company_id: invoice.companyId.toString(), company_id: invoice.companyId.toString(),
is_proforma: invoice.isProforma ? "true" : "false",
invoice_number: invoice.invoiceNumber.toString(), invoice_number: invoice.invoiceNumber.toString(),
status: invoice.status.toPrimitive(), status: invoice.status.toPrimitive(),
series: maybeToEmptyString(invoice.series, (value) => value.toString()), series: maybeToEmptyString(invoice.series, (value) => value.toString()),

View File

@ -5,7 +5,6 @@ export interface IProformaFullSnapshot {
id: string; id: string;
company_id: string; company_id: string;
is_proforma: "true" | "false";
invoice_number: string; invoice_number: string;
status: string; status: string;
series: string; series: string;

View File

@ -1,94 +1,76 @@
import type { ISnapshotBuilder } from "@erp/core/api"; import type { ISnapshotBuilder } from "@erp/core/api";
import { maybeToEmptyString } from "@repo/rdx-ddd"; import {
maybeToEmptyMoneyObjectString,
maybeToEmptyPercentageObjectString,
maybeToEmptyQuantityObjectString,
maybeToEmptyString,
} from "@repo/rdx-ddd";
import { Maybe } from "@repo/rdx-utils";
import type { CustomerInvoiceItems, IssuedInvoiceItem } from "../../../../domain"; import { ItemAmount, type ProformaItem, type ProformaItems } from "../../../../domain";
import type { IProformaItemFullSnapshot } from "./proforma-item-full-snapshot.interface"; import type { IProformaItemFullSnapshot } from "./proforma-item-full-snapshot.interface";
export interface IProformaItemsFullSnapshotBuilder export interface IProformaItemsFullSnapshotBuilder
extends ISnapshotBuilder<CustomerInvoiceItems, IProformaItemFullSnapshot[]> {} extends ISnapshotBuilder<ProformaItems, IProformaItemFullSnapshot[]> {}
export class ProformaItemsFullSnapshotBuilder implements IProformaItemsFullSnapshotBuilder { export class ProformaItemsFullSnapshotBuilder implements IProformaItemsFullSnapshotBuilder {
private mapItem(invoiceItem: IssuedInvoiceItem, index: number): IProformaItemFullSnapshot { private mapItem(proformaItem: ProformaItem, index: number): IProformaItemFullSnapshot {
const allAmounts = invoiceItem.calculateAllAmounts(); const allAmounts = proformaItem.calculateAllAmounts();
const isValued = proformaItem.isValued;
const noneIfNotValued = <T>(value: Maybe<T>): Maybe<T> => (isValued ? value : Maybe.none<T>());
return { return {
id: invoiceItem.id.toPrimitive(), id: proformaItem.id.toPrimitive(),
is_valued: String(invoiceItem.isValued), is_valued: String(isValued),
position: String(index), position: String(index),
description: maybeToEmptyString(invoiceItem.description, (value) => value.toPrimitive()),
quantity: invoiceItem.quantity.match( description: maybeToEmptyString(proformaItem.description, (value) => value.toString()),
(quantity) => quantity.toObjectString(),
() => ({ value: "", scale: "" }) quantity: maybeToEmptyQuantityObjectString(proformaItem.quantity),
unit_amount: maybeToEmptyMoneyObjectString(proformaItem.unitAmount),
subtotal_amount: isValued
? allAmounts.subtotalAmount.toObjectString()
: ItemAmount.EMPTY_MONEY_OBJECT,
discount_percentage: maybeToEmptyPercentageObjectString(proformaItem.itemDiscountPercentage),
discount_amount: isValued
? allAmounts.itemDiscountAmount.toObjectString()
: ItemAmount.EMPTY_MONEY_OBJECT,
global_discount_percentage: maybeToEmptyPercentageObjectString(
proformaItem.globalDiscountPercentage
), ),
global_discount_amount: maybeToEmptyMoneyObjectString(proformaItem.globalDiscountAmount),
unit_amount: invoiceItem.unitAmount.match( total_discount_amount: isValued
(unitAmount) => unitAmount.toObjectString(), ? allAmounts.totalDiscountAmount.toObjectString()
() => ({ value: "", scale: "", currency_code: "" }) : ItemAmount.EMPTY_MONEY_OBJECT,
),
subtotal_amount: allAmounts.subtotalAmount.toObjectString(), taxable_amount: isValued
? allAmounts.taxableAmount.toObjectString()
: ItemAmount.EMPTY_MONEY_OBJECT,
discount_percentage: invoiceItem.itemDiscountPercentage.match( iva_code: maybeToEmptyString(proformaItem.ivaCode),
(discountPercentage) => discountPercentage.toObjectString(), iva_percentage: maybeToEmptyPercentageObjectString(proformaItem.ivaPercentage),
() => ({ value: "", scale: "" }) iva_amount: maybeToEmptyMoneyObjectString(proformaItem.ivaAmount),
),
discount_amount: allAmounts.itemDiscountAmount.toObjectString(), rec_code: maybeToEmptyString(proformaItem.recCode),
rec_percentage: maybeToEmptyPercentageObjectString(proformaItem.recPercentage),
rec_amount: maybeToEmptyMoneyObjectString(proformaItem.recAmount),
global_discount_percentage: invoiceItem.globalDiscountPercentage.match( retention_code: maybeToEmptyString(proformaItem.retentionCode),
(discountPercentage) => discountPercentage.toObjectString(), retention_percentage: maybeToEmptyPercentageObjectString(proformaItem.retentionPercentage),
() => ({ value: "", scale: "" }) retention_amount: maybeToEmptyMoneyObjectString(proformaItem.retentionAmount),
),
global_discount_amount: allAmounts.globalDiscountAmount.toObjectString(), taxes_amount: maybeToEmptyMoneyObjectString(proformaItem.taxesAmount),
total_amount: maybeToEmptyMoneyObjectString(proformaItem.totalAmount),
taxable_amount: allAmounts.taxableAmount.toObjectString(),
iva_code: invoiceItem.taxes.iva.match(
(iva) => iva.code,
() => ""
),
iva_percentage: invoiceItem.taxes.iva.match(
(iva) => iva.percentage.toObjectString(),
() => ({ value: "", scale: "" })
),
iva_amount: allAmounts.ivaAmount.toObjectString(),
rec_code: invoiceItem.taxes.rec.match(
(rec) => rec.code,
() => ""
),
rec_percentage: invoiceItem.taxes.rec.match(
(rec) => rec.percentage.toObjectString(),
() => ({ value: "", scale: "" })
),
rec_amount: allAmounts.recAmount.toObjectString(),
retention_code: invoiceItem.taxes.retention.match(
(retention) => retention.code,
() => ""
),
retention_percentage: invoiceItem.taxes.retention.match(
(retention) => retention.percentage.toObjectString(),
() => ({ value: "", scale: "" })
),
retention_amount: allAmounts.retentionAmount.toObjectString(),
taxes_amount: allAmounts.taxesAmount.toObjectString(),
total_amount: allAmounts.totalAmount.toObjectString(),
}; };
} }
toOutput(invoiceItems: CustomerInvoiceItems): IProformaItemFullSnapshot[] { toOutput(invoiceItems: ProformaItems): IProformaItemFullSnapshot[] {
return invoiceItems.map((item, index) => this.mapItem(item, index)); return invoiceItems.map((item, index) => this.mapItem(item, index));
} }
} }

View File

@ -2,22 +2,23 @@ import type { ISnapshotBuilder } from "@erp/core/api";
import { DomainValidationError, maybeToEmptyString } from "@repo/rdx-ddd"; import { DomainValidationError, maybeToEmptyString } from "@repo/rdx-ddd";
import type { InvoiceRecipient, Proforma } from "../../../../domain"; import type { InvoiceRecipient, Proforma } from "../../../../domain";
import type { ProformaRecipientFullSnapshot } from "../../application-models";
import type { IProformaRecipientFullSnapshot } from "./proforma-recipient-full-snapshot.interface";
export interface IProformaRecipientFullSnapshotBuilder export interface IProformaRecipientFullSnapshotBuilder
extends ISnapshotBuilder<Proforma, ProformaRecipientFullSnapshot> {} extends ISnapshotBuilder<Proforma, IProformaRecipientFullSnapshot> {}
export class ProformaRecipientFullSnapshotBuilder implements IProformaRecipientFullSnapshotBuilder { export class ProformaRecipientFullSnapshotBuilder implements IProformaRecipientFullSnapshotBuilder {
toOutput(invoice: Proforma): ProformaRecipientFullSnapshot { toOutput(proforma: Proforma): IProformaRecipientFullSnapshot {
if (!invoice.recipient) { if (!proforma.recipient) {
throw DomainValidationError.requiredValue("recipient", { throw DomainValidationError.requiredValue("recipient", {
cause: invoice, cause: proforma,
}); });
} }
return invoice.recipient.match( return proforma.recipient.match(
(recipient: InvoiceRecipient) => ({ (recipient: InvoiceRecipient) => ({
id: invoice.customerId.toString(), id: proforma.customerId.toString(),
name: recipient.name.toString(), name: recipient.name.toString(),
tin: recipient.tin.toString(), tin: recipient.tin.toString(),
street: maybeToEmptyString(recipient.street, (v) => v.toString()), street: maybeToEmptyString(recipient.street, (v) => v.toString()),

View File

@ -0,0 +1,17 @@
export interface IProformaTaxFullSnapshot {
taxable_amount: { value: string; scale: string; currency_code: string };
iva_code: string;
iva_percentage: { value: string; scale: string };
iva_amount: { value: string; scale: string; currency_code: string };
rec_code: string;
rec_percentage: { value: string; scale: string };
rec_amount: { value: string; scale: string; currency_code: string };
retention_code: string;
retention_percentage: { value: string; scale: string };
retention_amount: { value: string; scale: string; currency_code: string };
taxes_amount: { value: string; scale: string; currency_code: string };
}

View File

@ -0,0 +1,39 @@
import type { ISnapshotBuilder } from "@erp/core/api";
import {
maybeToEmptyMoneyObjectString,
maybeToEmptyPercentageObjectString,
maybeToEmptyString,
} from "@repo/rdx-ddd";
import type { ProformaTax, ProformaTaxes } from "../../../../domain";
import type { IProformaTaxFullSnapshot } from "./proforma-tax-full-snapshot-interface";
export interface IProformaTaxesFullSnapshotBuilder
extends ISnapshotBuilder<ProformaTaxes, IProformaTaxFullSnapshot[]> {}
export class ProformaTaxesFullSnapshotBuilder implements IProformaTaxesFullSnapshotBuilder {
private mapItem(proformaTax: ProformaTax, index: number): IProformaTaxFullSnapshot {
return {
taxable_amount: proformaTax.taxableAmount.toObjectString(),
iva_code: proformaTax.ivaCode.toString(),
iva_percentage: proformaTax.ivaPercentage.toObjectString(),
iva_amount: proformaTax.ivaAmount.toObjectString(),
rec_code: maybeToEmptyString(proformaTax.recCode),
rec_percentage: maybeToEmptyPercentageObjectString(proformaTax.recPercentage),
rec_amount: maybeToEmptyMoneyObjectString(proformaTax.recAmount),
retention_code: maybeToEmptyString(proformaTax.retentionCode),
retention_percentage: maybeToEmptyPercentageObjectString(proformaTax.retentionPercentage),
retention_amount: maybeToEmptyMoneyObjectString(proformaTax.retentionAmount),
taxes_amount: proformaTax.taxesAmount.toObjectString(),
};
}
toOutput(invoiceTaxes: ProformaTaxes): IProformaTaxFullSnapshot[] {
return invoiceTaxes.map((item, index) => this.mapItem(item, index));
}
}

View File

@ -1,20 +1,20 @@
import type { ISnapshotBuilder } from "@erp/core/api"; import type { ISnapshotBuilder } from "@erp/core/api";
import { maybeToEmptyString } from "@repo/rdx-ddd"; import { maybeToEmptyString } from "@repo/rdx-ddd";
import type { CustomerInvoiceListDTO } from "../../../../infrastructure"; import type { ProformaListDTO } from "../../dtos";
import type { ProformaListItemSnapshot } from "../../application-models";
import type { IProformaListItemSnapshot } from "./proforma-list-item-snapshot.interface";
export interface IProformaListItemSnapshotBuilder export interface IProformaListItemSnapshotBuilder
extends ISnapshotBuilder<CustomerInvoiceListDTO, ProformaListItemSnapshot> {} extends ISnapshotBuilder<ProformaListDTO, IProformaListItemSnapshot> {}
export class ProformaListItemSnapshotBuilder implements IProformaListItemSnapshotBuilder { export class ProformaListItemSnapshotBuilder implements IProformaListItemSnapshotBuilder {
toOutput(proforma: CustomerInvoiceListDTO): ProformaListItemSnapshot { toOutput(proforma: ProformaListDTO): IProformaListItemSnapshot {
const recipient = proforma.recipient.toObjectString(); const recipient = proforma.recipient.toObjectString();
return { return {
id: proforma.id.toString(), id: proforma.id.toString(),
company_id: proforma.companyId.toString(), company_id: proforma.companyId.toString(),
is_proforma: proforma.isProforma,
customer_id: proforma.customerId.toString(), customer_id: proforma.customerId.toString(),
invoice_number: proforma.invoiceNumber.toString(), invoice_number: proforma.invoiceNumber.toString(),
@ -32,8 +32,7 @@ export class ProformaListItemSnapshotBuilder implements IProformaListItemSnapsho
currency_code: proforma.currencyCode.code, currency_code: proforma.currencyCode.code,
subtotal_amount: proforma.subtotalAmount.toObjectString(), subtotal_amount: proforma.subtotalAmount.toObjectString(),
discount_percentage: proforma.discountPercentage.toObjectString(), total_discount_amount: proforma.totalDiscountAmount.toObjectString(),
discount_amount: proforma.discountAmount.toObjectString(),
taxable_amount: proforma.taxableAmount.toObjectString(), taxable_amount: proforma.taxableAmount.toObjectString(),
taxes_amount: proforma.taxesAmount.toObjectString(), taxes_amount: proforma.taxesAmount.toObjectString(),
total_amount: proforma.totalAmount.toObjectString(), total_amount: proforma.totalAmount.toObjectString(),

View File

@ -1,7 +1,6 @@
export interface IProformaListItemSnapshot { export interface IProformaListItemSnapshot {
id: string; id: string;
company_id: string; company_id: string;
is_proforma: boolean;
customer_id: string; customer_id: string;
@ -30,8 +29,7 @@ export interface IProformaListItemSnapshot {
}; };
subtotal_amount: { value: string; scale: string; currency_code: string }; subtotal_amount: { value: string; scale: string; currency_code: string };
discount_percentage: { value: string; scale: string }; total_discount_amount: { value: string; scale: string; currency_code: 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

@ -84,7 +84,6 @@ export class ProformaFullPresenter extends Presenter<Proforma, GetProformaByIdRe
id: proforma.id.toString(), id: proforma.id.toString(),
company_id: proforma.companyId.toString(), company_id: proforma.companyId.toString(),
is_proforma: proforma.isProforma ? "true" : "false",
invoice_number: proforma.invoiceNumber.toString(), invoice_number: proforma.invoiceNumber.toString(),
status: proforma.status.toPrimitive(), status: proforma.status.toPrimitive(),
series: maybeToEmptyString(proforma.series, (value) => value.toString()), series: maybeToEmptyString(proforma.series, (value) => value.toString()),

View File

@ -3,7 +3,6 @@ import { maybeToEmptyString } from "@repo/rdx-ddd";
import type { ArrayElement, Collection } from "@repo/rdx-utils"; import type { ArrayElement, Collection } from "@repo/rdx-utils";
import type { ListIssuedInvoicesResponseDTO } from "../../../../../common/dto"; import type { ListIssuedInvoicesResponseDTO } from "../../../../../common/dto";
import type { CustomerInvoiceListDTO } from "../../../../infrastructure";
export class IssuedInvoiceListPresenter extends Presenter { export class IssuedInvoiceListPresenter extends Presenter {
protected _mapInvoice(invoice: CustomerInvoiceListDTO) { protected _mapInvoice(invoice: CustomerInvoiceListDTO) {
@ -21,7 +20,6 @@ export class IssuedInvoiceListPresenter extends Presenter {
const invoiceDTO: ArrayElement<ListIssuedInvoicesResponseDTO["items"]> = { const invoiceDTO: ArrayElement<ListIssuedInvoicesResponseDTO["items"]> = {
id: invoice.id.toString(), id: invoice.id.toString(),
company_id: invoice.companyId.toString(), company_id: invoice.companyId.toString(),
is_proforma: invoice.isProforma,
customer_id: invoice.customerId.toString(), customer_id: invoice.customerId.toString(),
invoice_number: invoice.invoiceNumber.toString(), invoice_number: invoice.invoiceNumber.toString(),

View File

@ -11,5 +11,4 @@ export * from "./item-amount.vo";
export * from "./item-description.vo"; export * from "./item-description.vo";
export * from "./item-discount-percentage.vo"; export * from "./item-discount-percentage.vo";
export * from "./item-quantity.vo"; export * from "./item-quantity.vo";
export * from "./item-tax-group.vo";
export * from "./item-tax-percentage.vo"; export * from "./item-tax-percentage.vo";

View File

@ -2,8 +2,9 @@ import type { Tax } from "@erp/core/api";
import { ValueObject } from "@repo/rdx-ddd"; import { ValueObject } from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils"; import { type Maybe, Result } from "@repo/rdx-utils";
import type { ProformaItemTaxGroup } from "../../proformas/value-objects/proforma-item-tax-group.vo";
import { InvoiceAmount } from "./invoice-amount.vo"; import { InvoiceAmount } from "./invoice-amount.vo";
import type { ItemTaxGroup } from "./item-tax-group.vo";
export type InvoiceTaxGroupProps = { export type InvoiceTaxGroupProps = {
taxableAmount: InvoiceAmount; taxableAmount: InvoiceAmount;
@ -20,7 +21,7 @@ export class InvoiceTaxGroup extends ValueObject<InvoiceTaxGroupProps> {
/** /**
* Crea un grupo vacío a partir de un ItemTaxGroup (línea) * Crea un grupo vacío a partir de un ItemTaxGroup (línea)
*/ */
static fromItem(lineTaxes: ItemTaxGroup, taxableAmount: InvoiceAmount): InvoiceTaxGroup { static fromItem(lineTaxes: ProformaItemTaxGroup, taxableAmount: InvoiceAmount): InvoiceTaxGroup {
const iva = lineTaxes.iva.unwrap(); // iva siempre obligatorio const iva = lineTaxes.iva.unwrap(); // iva siempre obligatorio
const rec = lineTaxes.rec; const rec = lineTaxes.rec;
const retention = lineTaxes.retention; const retention = lineTaxes.retention;

View File

@ -8,7 +8,6 @@ import {
import { type Maybe, Result } from "@repo/rdx-utils"; import { type Maybe, Result } from "@repo/rdx-utils";
import type { import type {
InvoiceAmount,
ItemAmount, ItemAmount,
ItemDescription, ItemDescription,
ItemDiscountPercentage, ItemDiscountPercentage,
@ -31,40 +30,38 @@ export type IssuedInvoiceItemProps = {
quantity: Maybe<ItemQuantity>; quantity: Maybe<ItemQuantity>;
unitAmount: Maybe<ItemAmount>; unitAmount: Maybe<ItemAmount>;
subtotalAmount: Maybe<InvoiceAmount>; subtotalAmount: ItemAmount;
itemDiscountPercentage: Maybe<ItemDiscountPercentage>; itemDiscountPercentage: Maybe<ItemDiscountPercentage>;
itemDiscountAmount: Maybe<InvoiceAmount>; itemDiscountAmount: ItemAmount;
globalDiscountPercentage: Maybe<ItemDiscountPercentage>; globalDiscountPercentage: Maybe<ItemDiscountPercentage>;
globalDiscountAmount: Maybe<InvoiceAmount>; globalDiscountAmount: ItemAmount;
totalDiscountAmount: Maybe<InvoiceAmount>; totalDiscountAmount: ItemAmount;
taxableAmount: Maybe<InvoiceAmount>; taxableAmount: ItemAmount;
ivaCode: Maybe<string>; ivaCode: Maybe<string>;
ivaPercentage: Maybe<ItemDiscountPercentage>; ivaPercentage: Maybe<ItemDiscountPercentage>;
ivaAmount: Maybe<InvoiceAmount>; ivaAmount: ItemAmount;
recCode: Maybe<string>; recCode: Maybe<string>;
recPercentage: Maybe<ItemDiscountPercentage>; recPercentage: Maybe<ItemDiscountPercentage>;
recAmount: Maybe<InvoiceAmount>; recAmount: ItemAmount;
retentionCode: Maybe<string>; retentionCode: Maybe<string>;
retentionPercentage: Maybe<ItemDiscountPercentage>; retentionPercentage: Maybe<ItemDiscountPercentage>;
retentionAmount: Maybe<InvoiceAmount>; retentionAmount: ItemAmount;
taxesAmount: Maybe<InvoiceAmount>; taxesAmount: ItemAmount;
totalAmount: Maybe<InvoiceAmount>; totalAmount: ItemAmount;
languageCode: LanguageCode; languageCode: LanguageCode;
currencyCode: CurrencyCode; currencyCode: CurrencyCode;
}; };
export class IssuedInvoiceItem extends DomainEntity<IssuedInvoiceItemProps> { export class IssuedInvoiceItem extends DomainEntity<IssuedInvoiceItemProps> {
protected _isValued!: boolean;
public static create( public static create(
props: IssuedInvoiceItemProps, props: IssuedInvoiceItemProps,
id?: UniqueID id?: UniqueID
@ -80,14 +77,12 @@ export class IssuedInvoiceItem extends DomainEntity<IssuedInvoiceItemProps> {
protected constructor(props: IssuedInvoiceItemProps, id?: UniqueID) { protected constructor(props: IssuedInvoiceItemProps, id?: UniqueID) {
super(props, id); super(props, id);
this._isValued = this.quantity.isSome() || this.unitAmount.isSome();
} }
// Getters // Getters
get isValued(): boolean { get isValued(): boolean {
return this._isValued; return this.quantity.isSome() || this.unitAmount.isSome();
} }
get description() { get description() {
@ -144,7 +139,7 @@ export class IssuedInvoiceItem extends DomainEntity<IssuedInvoiceItemProps> {
public get ivaPercentage(): Maybe<Percentage> { public get ivaPercentage(): Maybe<Percentage> {
return this.props.ivaPercentage; return this.props.ivaPercentage;
} }
public get ivaAmount(): Maybe<InvoiceAmount> { public get ivaAmount(): ItemAmount {
return this.props.ivaAmount; return this.props.ivaAmount;
} }
@ -154,7 +149,7 @@ export class IssuedInvoiceItem extends DomainEntity<IssuedInvoiceItemProps> {
public get recPercentage(): Maybe<Percentage> { public get recPercentage(): Maybe<Percentage> {
return this.props.recPercentage; return this.props.recPercentage;
} }
public get recAmount(): Maybe<InvoiceAmount> { public get recAmount(): ItemAmount {
return this.props.recAmount; return this.props.recAmount;
} }
@ -164,7 +159,7 @@ export class IssuedInvoiceItem extends DomainEntity<IssuedInvoiceItemProps> {
public get retentionPercentage(): Maybe<Percentage> { public get retentionPercentage(): Maybe<Percentage> {
return this.props.retentionPercentage; return this.props.retentionPercentage;
} }
public get retentionAmount(): Maybe<InvoiceAmount> { public get retentionAmount(): ItemAmount {
return this.props.retentionAmount; return this.props.retentionAmount;
} }

View File

@ -20,12 +20,11 @@ import {
InvoiceTaxGroup, InvoiceTaxGroup,
type ItemAmount, type ItemAmount,
} from "../../common/value-objects"; } from "../../common/value-objects";
import type { ProformaTaxes } from "../entities";
import { ProformaItems } from "../entities/proforma-items"; import { ProformaItems } from "../entities/proforma-items";
export type ProformaProps = { export type ProformaProps = {
companyId: UniqueID; companyId: UniqueID;
isProforma: boolean;
status: InvoiceStatus; status: InvoiceStatus;
series: Maybe<InvoiceSerie>; series: Maybe<InvoiceSerie>;
@ -44,18 +43,37 @@ export type ProformaProps = {
languageCode: LanguageCode; languageCode: LanguageCode;
currencyCode: CurrencyCode; currencyCode: CurrencyCode;
items: ProformaItems;
paymentMethod: Maybe<InvoicePaymentMethod>; paymentMethod: Maybe<InvoicePaymentMethod>;
items: ProformaItems;
globalDiscountPercentage: Percentage; globalDiscountPercentage: Percentage;
}; };
export interface IProforma extends AggregateRoot<ProformaProps> {
getTaxes: ProformaTaxes;
getSubtotalAmount: InvoiceAmount;
getItemsDiscountAmount: InvoiceAmount;
getGlobalDiscountPercentage: Percentage;
getGlobalDiscountAmount: InvoiceAmount;
getTotalDiscountAmount: InvoiceAmount;
getTaxableAmount: InvoiceAmount;
getIvaAmount: InvoiceAmount;
getRecAmount: InvoiceAmount;
getRetentionAmount: InvoiceAmount;
getTaxesAmount: InvoiceAmount;
getTotalAmount: InvoiceAmount;
}
export type ProformaPatchProps = Partial<Omit<ProformaProps, "companyId" | "items">> & { export type ProformaPatchProps = Partial<Omit<ProformaProps, "companyId" | "items">> & {
items?: ProformaItems; items?: ProformaItems;
}; };
export class Proforma extends AggregateRoot<ProformaProps> { export class Proforma extends AggregateRoot<ProformaProps> implements IProforma {
private _items!: ProformaItems; private _items!: ProformaItems;
protected constructor(props: ProformaProps, id?: UniqueID) { protected constructor(props: ProformaProps, id?: UniqueID) {
@ -120,10 +138,6 @@ export class Proforma extends AggregateRoot<ProformaProps> {
return this.props.customerId; return this.props.customerId;
} }
public get isProforma(): boolean {
return this.props.isProforma;
}
public get status(): InvoiceStatus { public get status(): InvoiceStatus {
return this.props.status; return this.props.status;
} }

View File

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

View File

@ -1,3 +1,4 @@
import type { Tax } from "@erp/core/api";
import { type CurrencyCode, DomainEntity, type LanguageCode, type UniqueID } from "@repo/rdx-ddd"; import { type CurrencyCode, DomainEntity, type LanguageCode, type UniqueID } from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils"; import { type Maybe, Result } from "@repo/rdx-utils";
@ -6,8 +7,8 @@ import {
type ItemDescription, type ItemDescription,
ItemDiscountPercentage, ItemDiscountPercentage,
ItemQuantity, ItemQuantity,
type ItemTaxGroup,
} from "../../../common"; } from "../../../common";
import type { ProformaItemTaxGroup } from "../../value-objects/proforma-item-tax-group.vo";
/** /**
* *
@ -28,21 +29,64 @@ import {
export type ProformaItemProps = { export type ProformaItemProps = {
description: Maybe<ItemDescription>; description: Maybe<ItemDescription>;
quantity: Maybe<ItemQuantity>; // Cantidad de unidades quantity: Maybe<ItemQuantity>; // Cantidad de unidades
unitAmount: Maybe<ItemAmount>; // Precio unitario en la moneda de la factura unitAmount: Maybe<ItemAmount>; // Precio unitario en la moneda de la factura
itemDiscountPercentage: Maybe<ItemDiscountPercentage>; // % descuento itemDiscountPercentage: Maybe<ItemDiscountPercentage>; // % descuento de línea
globalDiscountPercentage: Maybe<ItemDiscountPercentage>; // % descuento de la cabecera
taxes: ItemTaxGroup; taxes: ProformaItemTaxGroup;
// Estos campos vienen de la cabecera,
// pero se necesitan para cálculos y representaciones de la línea.
globalDiscountPercentage: Maybe<ItemDiscountPercentage>; // % descuento de la cabecera
languageCode: LanguageCode; // Para formateos específicos de idioma
currencyCode: CurrencyCode; // Para cálculos y formateos de moneda
};
export interface IProformaItem extends ProformaItemProps {
description: Maybe<ItemDescription>;
isValued: boolean; // Indica si el item tiene cantidad o precio (o ambos) para ser considerado "valorizado"
quantity: Maybe<ItemQuantity>;
unitAmount: Maybe<ItemAmount>;
getSubtotalAmount: Maybe<ItemAmount>;
itemDiscountPercentage: Maybe<ItemDiscountPercentage>; // % descuento de línea
getItemDiscountAmount: Maybe<ItemAmount>;
globalDiscountPercentage: Maybe<ItemDiscountPercentage>; // % descuento de la cabecera
getGlobalDiscountAmount: Maybe<ItemAmount>;
getTotalDiscountAmount: Maybe<ItemAmount>;
getTaxableAmount: Maybe<ItemAmount>;
getIva: Maybe<Tax>;
getIvaCode: Maybe<string>;
getIvaPercentage: Maybe<ItemDiscountPercentage>;
getIvaAmount: ItemAmount;
getRec: Maybe<Tax>;
getRecCode: Maybe<string>;
getRecPercentage: Maybe<ItemDiscountPercentage>;
getRecAmount: ItemAmount;
getRetention: Maybe<Tax>;
getRetentionCode: Maybe<string>;
getRetentionPercentage: Maybe<ItemDiscountPercentage>;
getRetentionAmount: ItemAmount;
getTaxesAmount: ItemAmount;
getTotalAmount: ItemAmount;
languageCode: LanguageCode; languageCode: LanguageCode;
currencyCode: CurrencyCode; currencyCode: CurrencyCode;
}; }
export class ProformaItem extends DomainEntity<ProformaItemProps> { export class ProformaItem extends DomainEntity<ProformaItemProps> {
protected _isValued!: boolean;
public static create(props: ProformaItemProps, id?: UniqueID): Result<ProformaItem, Error> { public static create(props: ProformaItemProps, id?: UniqueID): Result<ProformaItem, Error> {
const item = new ProformaItem(props, id); const item = new ProformaItem(props, id);
@ -55,14 +99,12 @@ export class ProformaItem extends DomainEntity<ProformaItemProps> {
protected constructor(props: ProformaItemProps, id?: UniqueID) { protected constructor(props: ProformaItemProps, id?: UniqueID) {
super(props, id); super(props, id);
this._isValued = this.quantity.isSome() || this.unitAmount.isSome();
} }
// Getters // Getters
get isValued(): boolean { get isValued(): boolean {
return this._isValued; return this.props.quantity.isSome() || this.props.unitAmount.isSome();
} }
get description() { get description() {
@ -105,6 +147,69 @@ export class ProformaItem extends DomainEntity<ProformaItemProps> {
return this.getProps(); return this.getProps();
} }
// Getters específicos para cálculos y representaciones
public getSubtotalAmount(): ItemAmount {
return this.calculateAllAmounts().subtotalAmount;
}
public getItemDiscountAmount(): ItemAmount {
return this.calculateAllAmounts().itemDiscountAmount;
}
public getGlobalDiscountAmount(): ItemAmount {
return this.calculateAllAmounts().globalDiscountAmount;
}
public getTotalDiscountAmount(): ItemAmount {
return this.calculateAllAmounts().totalDiscountAmount;
}
public getTaxableAmount(): ItemAmount {
return this.calculateAllAmounts().taxableAmount;
}
public getIva(): Maybe<Tax> {
return this.taxes.iva;
}
public getIvaCode(): Maybe<string> {
return this.taxes.iva.map((tax) => tax.code);
}
public getIvaPercentage(): Maybe<ItemDiscountPercentage> {
return this.taxes.iva.map((tax) => tax.percentage);
}
public getIvaAmount(): ItemAmount {
return this.calculateAllAmounts().ivaAmount;
}
public getRec(): Maybe<Tax> {
return this.taxes.rec;
}
public getRecCode(): Maybe<string> {
return this.taxes.rec.map((tax) => tax.code);
}
public getRecPercentage(): Maybe<ItemDiscountPercentage> {
return this.taxes.rec.map((tax) => tax.percentage);
}
public getIndividualTaxAmounts() {
const { ivaAmount, recAmount, retentionAmount } = this.calculateAllAmounts();
return { ivaAmount, recAmount, retentionAmount };
}
public getTaxesAmount(): ItemAmount {
return this.calculateAllAmounts().taxesAmount;
}
public getTotalAmount(): ItemAmount {
return this.calculateAllAmounts().totalAmount;
}
// Ayudantes // Ayudantes
/** /**
@ -162,13 +267,6 @@ export class ProformaItem extends DomainEntity<ProformaItemProps> {
return itemDiscountAmount.add(globalDiscountAmount); return itemDiscountAmount.add(globalDiscountAmount);
} }
/**
* @summary Helper puro para calcular impuestos individuales.
*/
private _calculateIndividualTaxes(taxable: ItemAmount) {
return this.taxes.calculateAmounts(taxable);
}
// Cálculos // Cálculos
/** /**
@ -201,7 +299,8 @@ export class ProformaItem extends DomainEntity<ProformaItemProps> {
const taxableAmount = subtotalAmount.subtract(totalDiscountAmount); const taxableAmount = subtotalAmount.subtract(totalDiscountAmount);
const { ivaAmount, recAmount, retentionAmount } = this._calculateIndividualTaxes(taxableAmount); // Calcular impuestos individuales a partir de la base imponible
const { ivaAmount, recAmount, retentionAmount } = this.taxes.calculateAmounts(taxableAmount);
const taxesAmount = ivaAmount.add(recAmount).add(retentionAmount); const taxesAmount = ivaAmount.add(recAmount).add(retentionAmount);
const totalAmount = taxableAmount.add(taxesAmount); const totalAmount = taxableAmount.add(taxesAmount);
@ -223,37 +322,4 @@ export class ProformaItem extends DomainEntity<ProformaItemProps> {
totalAmount, totalAmount,
} as const; } as const;
} }
public getSubtotalAmount(): ItemAmount {
return this.calculateAllAmounts().subtotalAmount;
}
public getItemDiscountAmount(): ItemAmount {
return this.calculateAllAmounts().itemDiscountAmount;
}
public getGlobalDiscountAmount(): ItemAmount {
return this.calculateAllAmounts().globalDiscountAmount;
}
public getTotalDiscountAmount(): ItemAmount {
return this.calculateAllAmounts().totalDiscountAmount;
}
public getTaxableAmount(): ItemAmount {
return this.calculateAllAmounts().taxableAmount;
}
public getIndividualTaxAmounts() {
const { ivaAmount, recAmount, retentionAmount } = this.calculateAllAmounts();
return { ivaAmount, recAmount, retentionAmount };
}
public getTaxesAmount(): ItemAmount {
return this.calculateAllAmounts().taxesAmount;
}
public getTotalAmount(): ItemAmount {
return this.calculateAllAmounts().totalAmount;
}
} }

View File

@ -0,0 +1,2 @@
export * from "./proforma-tax.entity";
export * from "./proforma-taxes.collection";

View File

@ -0,0 +1,70 @@
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 ProformaTaxProps = {
taxableAmount: InvoiceAmount;
ivaCode: string;
ivaPercentage: Percentage;
ivaAmount: InvoiceAmount;
recCode: Maybe<string>;
recPercentage: Maybe<Percentage>;
recAmount: Maybe<InvoiceAmount>;
retentionCode: Maybe<string>;
retentionPercentage: Maybe<Percentage>;
retentionAmount: Maybe<InvoiceAmount>;
taxesAmount: InvoiceAmount;
};
export class ProformaTax extends DomainEntity<ProformaTaxProps> {
public static create(props: ProformaTaxProps, id?: UniqueID): Result<ProformaTax, Error> {
return Result.ok(new ProformaTax(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(): Maybe<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(): Maybe<InvoiceAmount> {
return this.props.retentionAmount;
}
public get taxesAmount(): InvoiceAmount {
return this.props.taxesAmount;
}
public getProps(): ProformaTaxProps {
return this.props;
}
}

View File

@ -0,0 +1,25 @@
import type { CurrencyCode, LanguageCode } from "@repo/rdx-ddd";
import { Collection } from "@repo/rdx-utils";
import type { ProformaTax } from "./proforma-tax.entity";
export type ProformaTaxesProps = {
taxes?: ProformaTax[];
languageCode: LanguageCode;
currencyCode: CurrencyCode;
};
export class ProformaTaxes extends Collection<ProformaTax> {
private _languageCode!: LanguageCode;
private _currencyCode!: CurrencyCode;
constructor(props: ProformaTaxesProps) {
super(props.taxes ?? []);
this._languageCode = props.languageCode;
this._currencyCode = props.currencyCode;
}
public static create(props: ProformaTaxesProps): ProformaTaxes {
return new ProformaTaxes(props);
}
}

View File

@ -0,0 +1,2 @@
export * from "./proforma-item-tax-group.vo";
export * from "./proforma-tax-group.vo";

View File

@ -2,17 +2,17 @@ import type { Tax } from "@erp/core/api";
import { ValueObject } from "@repo/rdx-ddd"; import { ValueObject } from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils"; import { type Maybe, Result } from "@repo/rdx-utils";
import { ItemAmount } from "."; import { ItemAmount } from "../../common/value-objects";
export interface ItemTaxGroupProps { export interface ProformaItemTaxGroupProps {
iva: Maybe<Tax>; // si existe iva: Maybe<Tax>; // si existe
rec: Maybe<Tax>; // si existe rec: Maybe<Tax>; // si existe
retention: Maybe<Tax>; // si existe retention: Maybe<Tax>; // si existe
} }
export class ItemTaxGroup extends ValueObject<ItemTaxGroupProps> { export class ProformaItemTaxGroup extends ValueObject<ProformaItemTaxGroupProps> {
static create(props: ItemTaxGroupProps) { static create(props: ProformaItemTaxGroupProps) {
return Result.ok(new ItemTaxGroup(props)); return Result.ok(new ProformaItemTaxGroup(props));
} }
calculateAmounts(taxableAmount: ItemAmount) { calculateAmounts(taxableAmount: ItemAmount) {

View File

@ -0,0 +1,66 @@
import type { Tax } from "@erp/core/api";
import { ValueObject } from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils";
import type { InvoiceAmount } from "../../common";
export type ProformaTaxGroupProps = {
taxableAmount: InvoiceAmount;
iva: Tax;
ivaAmount: InvoiceAmount;
rec: Maybe<Tax>; // si existe
recAmount: Maybe<InvoiceAmount>;
retention: Maybe<Tax>; // si existe
retentionAmount: Maybe<InvoiceAmount>;
taxesAmount: InvoiceAmount;
};
export class ProformaTaxGroup extends ValueObject<ProformaTaxGroupProps> {
static create(props: ProformaTaxGroupProps) {
return Result.ok(new ProformaTaxGroup(props));
}
get taxableAmount(): InvoiceAmount {
return this.props.taxableAmount;
}
get iva(): Tax {
return this.props.iva;
}
get ivaAmount(): InvoiceAmount {
return this.props.ivaAmount;
}
get rec(): Maybe<Tax> {
return this.props.rec;
}
get recAmount(): Maybe<InvoiceAmount> {
return this.props.recAmount;
}
get retention(): Maybe<Tax> {
return this.props.retention;
}
get retentionAmount(): Maybe<InvoiceAmount> {
return this.props.retentionAmount;
}
get taxesAmount(): InvoiceAmount {
return this.props.taxesAmount;
}
getProps() {
return this.props;
}
toPrimitive() {
return this.getProps();
}
}

View File

@ -33,7 +33,7 @@ export class CustomerInvoiceItemModel extends Model<
declare unit_amount_scale: number; declare unit_amount_scale: number;
// Subtotal (cantidad * importe unitario) // Subtotal (cantidad * importe unitario)
declare subtotal_amount_value: CreationOptional<number | null>; declare subtotal_amount_value: number;
declare subtotal_amount_scale: number; declare subtotal_amount_scale: number;
// Discount percentage // Discount percentage
@ -41,7 +41,7 @@ export class CustomerInvoiceItemModel extends Model<
declare discount_percentage_scale: number; declare discount_percentage_scale: number;
// Discount amount // Discount amount
declare discount_amount_value: CreationOptional<number | null>; declare discount_amount_value: number;
declare discount_amount_scale: number; declare discount_amount_scale: number;
// Porcentaje de descuento global proporcional a esta línea. // Porcentaje de descuento global proporcional a esta línea.
@ -49,15 +49,15 @@ export class CustomerInvoiceItemModel extends Model<
declare global_discount_percentage_scale: number; declare global_discount_percentage_scale: number;
// Importe del descuento global para esta línea // Importe del descuento global para esta línea
declare global_discount_amount_value: CreationOptional<number | null>; declare global_discount_amount_value: number;
declare global_discount_amount_scale: number; declare global_discount_amount_scale: number;
// Suma de los dos descuentos: el de la linea + el global // Suma de los dos descuentos: el de la linea + el global
declare total_discount_amount_value: CreationOptional<number | null>; declare total_discount_amount_value: number;
declare total_discount_amount_scale: number; declare total_discount_amount_scale: number;
// Taxable amount (base imponible tras los dos descuentos) // Taxable amount (base imponible tras los dos descuentos)
declare taxable_amount_value: CreationOptional<number | null>; declare taxable_amount_value: number;
declare taxable_amount_scale: number; declare taxable_amount_scale: number;
// IVA percentage // IVA percentage
@ -67,8 +67,7 @@ export class CustomerInvoiceItemModel extends Model<
declare iva_percentage_scale: number; declare iva_percentage_scale: number;
// IVA amount // IVA amount
declare iva_amount_value: number;
declare iva_amount_value: CreationOptional<number | null>;
declare iva_amount_scale: number; declare iva_amount_scale: number;
// Recargo de equivalencia percentage // Recargo de equivalencia percentage
@ -78,7 +77,7 @@ export class CustomerInvoiceItemModel extends Model<
declare rec_percentage_scale: number; declare rec_percentage_scale: number;
// Recargo de equivalencia amount // Recargo de equivalencia amount
declare rec_amount_value: CreationOptional<number | null>; declare rec_amount_value: number;
declare rec_amount_scale: number; declare rec_amount_scale: number;
// Retention percentage // Retention percentage
@ -88,15 +87,15 @@ export class CustomerInvoiceItemModel extends Model<
declare retention_percentage_scale: number; declare retention_percentage_scale: number;
// Retention amount // Retention amount
declare retention_amount_value: CreationOptional<number | null>; declare retention_amount_value: number;
declare retention_amount_scale: number; declare retention_amount_scale: number;
// Total taxes amount / taxes total // Total taxes amount / taxes total
declare taxes_amount_value: CreationOptional<number | null>; declare taxes_amount_value: number;
declare taxes_amount_scale: number; declare taxes_amount_scale: number;
// Total // Total
declare total_amount_value: CreationOptional<number | null>; declare total_amount_value: number;
declare total_amount_scale: number; declare total_amount_scale: number;
// Relaciones // Relaciones
@ -176,8 +175,8 @@ export default (database: Sequelize) => {
subtotal_amount_value: { subtotal_amount_value: {
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
allowNull: true, allowNull: false,
defaultValue: null, defaultValue: 0,
}, },
subtotal_amount_scale: { subtotal_amount_scale: {
@ -200,8 +199,8 @@ export default (database: Sequelize) => {
discount_amount_value: { discount_amount_value: {
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
allowNull: true, allowNull: false,
defaultValue: null, defaultValue: 0,
}, },
discount_amount_scale: { discount_amount_scale: {
@ -224,8 +223,8 @@ export default (database: Sequelize) => {
global_discount_amount_value: { global_discount_amount_value: {
type: new DataTypes.BIGINT(), type: new DataTypes.BIGINT(),
allowNull: true, allowNull: false,
defaultValue: null, defaultValue: 0,
}, },
global_discount_amount_scale: { global_discount_amount_scale: {
@ -236,8 +235,8 @@ export default (database: Sequelize) => {
total_discount_amount_value: { total_discount_amount_value: {
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
allowNull: true, allowNull: false,
defaultValue: null, defaultValue: 0,
}, },
total_discount_amount_scale: { total_discount_amount_scale: {
@ -248,8 +247,8 @@ export default (database: Sequelize) => {
taxable_amount_value: { taxable_amount_value: {
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
allowNull: true, allowNull: false,
defaultValue: null, defaultValue: 0,
}, },
taxable_amount_scale: { taxable_amount_scale: {
@ -278,8 +277,8 @@ export default (database: Sequelize) => {
iva_amount_value: { iva_amount_value: {
type: DataTypes.BIGINT, type: DataTypes.BIGINT,
allowNull: true, allowNull: false,
defaultValue: null, defaultValue: 0,
}, },
iva_amount_scale: { iva_amount_scale: {
type: DataTypes.SMALLINT, type: DataTypes.SMALLINT,
@ -307,8 +306,8 @@ export default (database: Sequelize) => {
rec_amount_value: { rec_amount_value: {
type: DataTypes.BIGINT, type: DataTypes.BIGINT,
allowNull: true, allowNull: false,
defaultValue: null, defaultValue: 0,
}, },
rec_amount_scale: { rec_amount_scale: {
type: DataTypes.SMALLINT, type: DataTypes.SMALLINT,
@ -336,8 +335,8 @@ export default (database: Sequelize) => {
retention_amount_value: { retention_amount_value: {
type: DataTypes.BIGINT, type: DataTypes.BIGINT,
allowNull: true, allowNull: false,
defaultValue: null, defaultValue: 0,
}, },
retention_amount_scale: { retention_amount_scale: {
type: DataTypes.SMALLINT, type: DataTypes.SMALLINT,
@ -347,8 +346,8 @@ export default (database: Sequelize) => {
taxes_amount_value: { taxes_amount_value: {
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
allowNull: true, allowNull: false,
defaultValue: null, defaultValue: 0,
}, },
taxes_amount_scale: { taxes_amount_scale: {
@ -359,8 +358,8 @@ export default (database: Sequelize) => {
total_amount_value: { total_amount_value: {
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
allowNull: true, allowNull: false,
defaultValue: null, defaultValue: 0,
}, },
total_amount_scale: { total_amount_scale: {

View File

@ -46,7 +46,7 @@ export class CustomerInvoiceTaxModel extends Model<
declare rec_percentage_scale: number; declare rec_percentage_scale: number;
// Recargo de equivalencia amount // Recargo de equivalencia amount
declare rec_amount_value: CreationOptional<number | null>; declare rec_amount_value: number;
declare rec_amount_scale: number; declare rec_amount_scale: number;
// Retention percentage // Retention percentage
@ -56,7 +56,7 @@ export class CustomerInvoiceTaxModel extends Model<
declare retention_percentage_scale: number; declare retention_percentage_scale: number;
// Retention amount // Retention amount
declare retention_amount_value: CreationOptional<number | null>; declare retention_amount_value: number;
declare retention_amount_scale: number; declare retention_amount_scale: number;
// Total taxes amount / taxes total // Total taxes amount / taxes total

View File

@ -85,9 +85,10 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
); );
const subtotalAmount = extractOrPushError( const subtotalAmount = extractOrPushError(
maybeFromNullableResult(raw.subtotal_amount_value, (value) => ItemAmount.create({
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code }) value: raw.subtotal_amount_value,
), currency_code: attributes.currencyCode?.code,
}),
`items[${index}].subtotal_amount_value`, `items[${index}].subtotal_amount_value`,
errors errors
); );
@ -101,9 +102,10 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
); );
const itemDiscountAmount = extractOrPushError( const itemDiscountAmount = extractOrPushError(
maybeFromNullableResult(raw.discount_amount_value, (value) => ItemAmount.create({
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code }) value: raw.discount_amount_value,
), currency_code: attributes.currencyCode?.code,
}),
`items[${index}].discount_amount_value`, `items[${index}].discount_amount_value`,
errors errors
); );
@ -117,21 +119,32 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
); );
const globalDiscountAmount = extractOrPushError( const globalDiscountAmount = extractOrPushError(
maybeFromNullableResult(raw.global_discount_amount_value, (value) => ItemAmount.create({
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code }) value: raw.global_discount_amount_value,
), currency_code: attributes.currencyCode?.code,
}),
`items[${index}].global_discount_amount_value`, `items[${index}].global_discount_amount_value`,
errors errors
); );
const totalDiscountAmount = extractOrPushError( const totalDiscountAmount = extractOrPushError(
maybeFromNullableResult(raw.total_discount_amount_value, (value) => ItemAmount.create({
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code }) value: raw.total_discount_amount_value,
), currency_code: attributes.currencyCode?.code,
}),
`items[${index}].total_discount_amount_value`, `items[${index}].total_discount_amount_value`,
errors errors
); );
const taxableAmount = extractOrPushError(
ItemAmount.create({
value: raw.taxable_amount_value,
currency_code: attributes.currencyCode?.code,
}),
`items[${index}].taxable_amount_value`,
errors
);
const ivaCode = maybeFromNullableOrEmptyString(raw.iva_code); const ivaCode = maybeFromNullableOrEmptyString(raw.iva_code);
const ivaPercentage = extractOrPushError( const ivaPercentage = extractOrPushError(
@ -143,9 +156,10 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
); );
const ivaAmount = extractOrPushError( const ivaAmount = extractOrPushError(
maybeFromNullableResult(raw.iva_amount_value, (value) => ItemAmount.create({
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code }) value: raw.iva_amount_value,
), currency_code: attributes.currencyCode?.code,
}),
`items[${index}].iva_amount_value`, `items[${index}].iva_amount_value`,
errors errors
); );
@ -161,9 +175,10 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
); );
const recAmount = extractOrPushError( const recAmount = extractOrPushError(
maybeFromNullableResult(raw.rec_amount_value, (value) => ItemAmount.create({
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code }) value: raw.rec_amount_value,
), currency_code: attributes.currencyCode?.code,
}),
`items[${index}].rec_amount_value`, `items[${index}].rec_amount_value`,
errors errors
); );
@ -179,25 +194,28 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
); );
const retentionAmount = extractOrPushError( const retentionAmount = extractOrPushError(
maybeFromNullableResult(raw.retention_amount_value, (value) => ItemAmount.create({
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code }) value: raw.retention_amount_value,
), currency_code: attributes.currencyCode?.code,
}),
`items[${index}].retention_amount_value`, `items[${index}].retention_amount_value`,
errors errors
); );
const taxesAmount = extractOrPushError( const taxesAmount = extractOrPushError(
maybeFromNullableResult(raw.taxes_amount_value, (value) => ItemAmount.create({
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code }) value: raw.taxes_amount_value,
), currency_code: attributes.currencyCode?.code,
}),
`items[${index}].taxes_amount_value`, `items[${index}].taxes_amount_value`,
errors errors
); );
const totalAmount = extractOrPushError( const totalAmount = extractOrPushError(
maybeFromNullableResult(raw.total_amount_value, (value) => ItemAmount.create({
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code }) value: raw.total_amount_value,
), currency_code: attributes.currencyCode?.code,
}),
`items[${index}].total_amount_value`, `items[${index}].total_amount_value`,
errors errors
); );
@ -218,6 +236,8 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
globalDiscountAmount, globalDiscountAmount,
totalDiscountAmount, totalDiscountAmount,
taxableAmount,
ivaCode, ivaCode,
ivaPercentage, ivaPercentage,
ivaAmount, ivaAmount,
@ -334,10 +354,8 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
maybeToNullable(source.unitAmount, (v) => v.toPrimitive().scale) ?? maybeToNullable(source.unitAmount, (v) => v.toPrimitive().scale) ??
ItemAmount.DEFAULT_SCALE, ItemAmount.DEFAULT_SCALE,
subtotal_amount_value: maybeToNullable(source.subtotalAmount, (v) => v.toPrimitive().value), subtotal_amount_value: source.subtotalAmount.toPrimitive().value,
subtotal_amount_scale: subtotal_amount_scale: source.subtotalAmount.toPrimitive().scale,
maybeToNullable(source.subtotalAmount, (v) => v.toPrimitive().scale) ??
ItemAmount.DEFAULT_SCALE,
discount_percentage_value: maybeToNullable( discount_percentage_value: maybeToNullable(
source.itemDiscountPercentage, source.itemDiscountPercentage,
@ -347,13 +365,8 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
maybeToNullable(source.itemDiscountPercentage, (v) => v.toPrimitive().scale) ?? maybeToNullable(source.itemDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscountPercentage.DEFAULT_SCALE, ItemDiscountPercentage.DEFAULT_SCALE,
discount_amount_value: maybeToNullable( discount_amount_value: source.itemDiscountAmount.toPrimitive().value,
source.itemDiscountAmount, discount_amount_scale: source.itemDiscountAmount.toPrimitive().scale,
(v) => v.toPrimitive().value
),
discount_amount_scale:
maybeToNullable(source.itemDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscountPercentage.DEFAULT_SCALE,
global_discount_percentage_value: maybeToNullable( global_discount_percentage_value: maybeToNullable(
source.globalDiscountPercentage, source.globalDiscountPercentage,
@ -363,30 +376,14 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
maybeToNullable(source.globalDiscountPercentage, (v) => v.toPrimitive().scale) ?? maybeToNullable(source.globalDiscountPercentage, (v) => v.toPrimitive().scale) ??
ItemDiscountPercentage.DEFAULT_SCALE, ItemDiscountPercentage.DEFAULT_SCALE,
global_discount_amount_value: maybeToNullable( global_discount_amount_value: source.globalDiscountAmount.value,
source.globalDiscountAmount, global_discount_amount_scale: source.globalDiscountAmount.scale,
(v) => v.toPrimitive().value
),
global_discount_amount_scale:
maybeToNullable(source.globalDiscountAmount, (v) => v.toPrimitive().scale) ??
ItemAmount.DEFAULT_SCALE,
total_discount_amount_value: maybeToNullable( total_discount_amount_value: source.totalDiscountAmount.value,
source.totalDiscountAmount, total_discount_amount_scale: source.totalDiscountAmount.scale,
(v) => v.toPrimitive().value
),
total_discount_amount_scale:
maybeToNullable(source.totalDiscountAmount, (v) => v.toPrimitive().scale) ??
ItemAmount.DEFAULT_SCALE,
// Te has quedado aquí --- IGNORE --- taxable_amount_value: source.taxableAmount.value,
// !!!!!!!!!!!!!!!!!!! taxable_amount_scale: source.taxableAmount.scale,
//
taxable_amount_value: maybeToNullable(source.taxableAmount, (v) => v.toPrimitive().value),
taxable_amount_scale:
maybeToNullable(source.taxableAmount, (v) => v.toPrimitive().scale) ??
ItemAmount.DEFAULT_SCALE,
// IVA // IVA
iva_code: maybeToNullableString(source.ivaCode), iva_code: maybeToNullableString(source.ivaCode),
@ -395,8 +392,8 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
iva_percentage_scale: iva_percentage_scale:
maybeToNullable(source.ivaPercentage, (v) => v.toPrimitive().scale) ?? 2, maybeToNullable(source.ivaPercentage, (v) => v.toPrimitive().scale) ?? 2,
iva_amount_value: maybeToNullable(source.ivaAmount, (v) => v.toPrimitive().value), iva_amount_value: source.ivaAmount.value,
iva_amount_scale: maybeToNullable(source.ivaAmount, (v) => v.toPrimitive().scale) ?? 4, iva_amount_scale: source.ivaAmount.scale,
// REC // REC
rec_code: maybeToNullableString(source.recCode), rec_code: maybeToNullableString(source.recCode),
@ -405,8 +402,8 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
rec_percentage_scale: rec_percentage_scale:
maybeToNullable(source.recPercentage, (v) => v.toPrimitive().scale) ?? 2, maybeToNullable(source.recPercentage, (v) => v.toPrimitive().scale) ?? 2,
rec_amount_value: maybeToNullable(source.recAmount, (v) => v.toPrimitive().value), rec_amount_value: source.recAmount.value,
rec_amount_scale: maybeToNullable(source.recAmount, (v) => v.toPrimitive().scale) ?? 4, rec_amount_scale: source.recAmount.scale,
// RET // RET
retention_code: maybeToNullableString(source.retentionCode), retention_code: maybeToNullableString(source.retentionCode),
@ -418,21 +415,16 @@ export class SequelizeIssuedInvoiceItemDomainMapper extends SequelizeDomainMappe
retention_percentage_scale: retention_percentage_scale:
maybeToNullable(source.retentionPercentage, (v) => v.toPrimitive().scale) ?? 2, maybeToNullable(source.retentionPercentage, (v) => v.toPrimitive().scale) ?? 2,
retention_amount_value: maybeToNullable(source.retentionAmount, (v) => v.toPrimitive().value), retention_amount_value: source.retentionAmount.value,
retention_amount_scale: retention_amount_scale: source.retentionAmount.scale,
maybeToNullable(source.retentionAmount, (v) => v.toPrimitive().scale) ?? 4,
// //
taxes_amount_value: maybeToNullable(source.taxesAmount, (v) => v.toPrimitive().value), taxes_amount_value: source.taxesAmount.value,
taxes_amount_scale: taxes_amount_scale: source.taxesAmount.scale,
maybeToNullable(source.taxesAmount, (v) => v.toPrimitive().scale) ??
ItemAmount.DEFAULT_SCALE,
// //
total_amount_value: maybeToNullable(source.totalAmount, (v) => v.toPrimitive().value), total_amount_value: source.totalAmount.value,
total_amount_scale: total_amount_scale: source.totalAmount.scale,
maybeToNullable(source.totalAmount, (v) => v.toPrimitive().scale) ??
ItemAmount.DEFAULT_SCALE,
}); });
} }
} }

View File

@ -5,7 +5,6 @@ export const GetIssuedInvoiceByIdResponseSchema = z.object({
id: z.uuid(), id: z.uuid(),
company_id: z.uuid(), company_id: z.uuid(),
is_proforma: z.string(),
invoice_number: z.string(), invoice_number: z.string(),
status: z.string(), status: z.string(),
series: z.string(), series: z.string(),

View File

@ -5,7 +5,6 @@ export const ListIssuedInvoicesResponseSchema = createPaginatedListSchema(
z.object({ z.object({
id: z.uuid(), id: z.uuid(),
company_id: z.uuid(), company_id: z.uuid(),
is_proforma: z.boolean(),
customer_id: z.string(), customer_id: z.string(),