This commit is contained in:
David Arranz 2026-02-21 22:11:18 +01:00
parent cc38faed94
commit 167eaff171
7 changed files with 96 additions and 65 deletions

View File

@ -21,7 +21,7 @@ import {
type ItemAmount,
} from "../../common/value-objects";
import { ProformaItems } from "../entities/proforma-items";
import { type IProformaTaxTotals, ProformaTaxCalculator } from "../services";
import { type IProformaTaxTotals, ProformaTaxesCalculator } from "../services";
export type ProformaProps = {
companyId: UniqueID;
@ -255,7 +255,7 @@ export class Proforma extends AggregateRoot<ProformaProps> implements IProforma
}
public taxes(): Collection<IProformaTaxTotals> {
return new ProformaTaxCalculator(this.items).calculate();
return new ProformaTaxesCalculator(this.items).calculate();
}
public getProps(): ProformaProps {

View File

@ -2,7 +2,7 @@ import type { DiscountPercentage } from "@erp/core/api";
import type { CurrencyCode, LanguageCode } from "@repo/rdx-ddd";
import { Collection } from "@repo/rdx-utils";
import { ItemAmount } from "../../../common";
import { ProformaItemsTotalsCalculator } from "../../services/proforma-items-totals-calculator";
import type { IProformaItem, IProformaItemTotals, ProformaItem } from "./proforma-item.entity";
@ -77,63 +77,7 @@ export class ProformaItems extends Collection<ProformaItem> implements IProforma
* Delega en los ítems individuales (DDD correcto) pero evita múltiples recorridos.
*/
public totals(): IProformaItemTotals {
let subtotalAmount = ItemAmount.zero(this.currencyCode.code);
let itemDiscountAmount = ItemAmount.zero(this.currencyCode.code);
let globalDiscountAmount = ItemAmount.zero(this.currencyCode.code);
let totalDiscountAmount = ItemAmount.zero(this.currencyCode.code);
let taxableAmount = ItemAmount.zero(this.currencyCode.code);
let ivaAmount = ItemAmount.zero(this.currencyCode.code);
let recAmount = ItemAmount.zero(this.currencyCode.code);
let retentionAmount = ItemAmount.zero(this.currencyCode.code);
let taxesAmount = ItemAmount.zero(this.currencyCode.code);
let totalAmount = ItemAmount.zero(this.currencyCode.code);
for (const item of this.getAll()) {
const amounts = item.totals();
// Subtotales
subtotalAmount = subtotalAmount.add(amounts.subtotalAmount);
// Descuentos
itemDiscountAmount = itemDiscountAmount.add(amounts.itemDiscountAmount);
globalDiscountAmount = globalDiscountAmount.add(amounts.globalDiscountAmount);
totalDiscountAmount = totalDiscountAmount.add(amounts.totalDiscountAmount);
// Base imponible
taxableAmount = taxableAmount.add(amounts.taxableAmount);
// Impuestos individuales
ivaAmount = ivaAmount.add(amounts.ivaAmount);
recAmount = recAmount.add(amounts.recAmount);
retentionAmount = retentionAmount.add(amounts.retentionAmount);
// Total impuestos del ítem
taxesAmount = taxesAmount.add(amounts.taxesAmount);
// Total final del ítem
totalAmount = totalAmount.add(amounts.totalAmount);
}
return {
subtotalAmount,
itemDiscountAmount,
globalDiscountAmount,
totalDiscountAmount,
taxableAmount,
ivaAmount,
recAmount,
retentionAmount,
taxesAmount,
totalAmount,
} as const;
return new ProformaItemsTotalsCalculator(this).calculate();
}
private ensureSameCurrencyAndLanguage(items: IProformaItem[]): void {

View File

@ -1 +1 @@
export * from "./proforma-tax-calculator";
export * from "./proforma-taxes-calculator";

View File

@ -1,7 +1,7 @@
import type { TaxPercentage } from "@erp/core/api";
import type { Maybe } from "@repo/rdx-utils";
import type { IProformaTaxTotals } from "./proforma-tax-calculator";
import type { IProformaTaxTotals } from "./proforma-taxes-calculator";
/**
* Orden determinista:

View File

@ -0,0 +1,89 @@
import { ItemAmount } from "../../common";
import type { IProformaItems } from "../entities";
export interface IProformaItemsTotals {
subtotalAmount: ItemAmount;
itemDiscountAmount: ItemAmount;
globalDiscountAmount: ItemAmount;
totalDiscountAmount: ItemAmount;
taxableAmount: ItemAmount;
ivaAmount: ItemAmount;
recAmount: ItemAmount;
retentionAmount: ItemAmount;
taxesAmount: ItemAmount;
totalAmount: ItemAmount;
}
/**
* Calcula los totales (scale 4) a partir de las líneas valoradas.
* La lógica fiscal está en ProformaItem; aquí solo se agregan resultados.
*/
export class ProformaItemsTotalsCalculator {
constructor(private readonly items: IProformaItems) {}
public calculate(): IProformaItemsTotals {
const zero = ItemAmount.zero(this.items.currencyCode.code);
let subtotalAmount = zero;
let itemDiscountAmount = zero;
let globalDiscountAmount = zero;
let totalDiscountAmount = zero;
let taxableAmount = zero;
let ivaAmount = zero;
let recAmount = zero;
let retentionAmount = zero;
let taxesAmount = zero;
let totalAmount = zero;
for (const item of this.items.getAll()) {
const amounts = item.totals();
// Subtotales
subtotalAmount = subtotalAmount.add(amounts.subtotalAmount);
// Descuentos
itemDiscountAmount = itemDiscountAmount.add(amounts.itemDiscountAmount);
globalDiscountAmount = globalDiscountAmount.add(amounts.globalDiscountAmount);
totalDiscountAmount = totalDiscountAmount.add(amounts.totalDiscountAmount);
// Base imponible
taxableAmount = taxableAmount.add(amounts.taxableAmount);
// Impuestos individuales
ivaAmount = ivaAmount.add(amounts.ivaAmount);
recAmount = recAmount.add(amounts.recAmount);
retentionAmount = retentionAmount.add(amounts.retentionAmount);
// Total impuestos del ítem
taxesAmount = taxesAmount.add(amounts.taxesAmount);
// Total final del ítem
totalAmount = totalAmount.add(amounts.totalAmount);
}
return {
subtotalAmount,
itemDiscountAmount,
globalDiscountAmount,
totalDiscountAmount,
taxableAmount,
ivaAmount,
recAmount,
retentionAmount,
taxesAmount,
totalAmount,
} as const;
}
}

View File

@ -25,7 +25,7 @@ export interface IProformaTaxTotals {
taxesAmount: InvoiceAmount;
}
export class ProformaTaxCalculator {
export class ProformaTaxesCalculator {
constructor(private readonly items: IProformaItems) {}
public calculate(): Collection<IProformaTaxTotals> {

View File

@ -8,8 +8,6 @@ import { ValueObject } from "./value-object";
const DEFAULT_SCALE = 2;
const DEFAULT_CURRENCY_CODE = "EUR";
type CurrencyData = Currency;
export type RoundingMode =
| "HALF_ODD"
| "HALF_EVEN"