Facturas de cliente
This commit is contained in:
parent
700ace3b76
commit
efcae31500
@ -17,7 +17,7 @@ export class CustomerInvoiceItemsFullPresenter extends Presenter {
|
||||
|
||||
return {
|
||||
id: invoiceItem.id.toPrimitive(),
|
||||
isNonValued: String(invoiceItem.isNonValued),
|
||||
is_non_valued: String(invoiceItem.isNonValued),
|
||||
position: String(index),
|
||||
description: toEmptyString(invoiceItem.description, (value) => value.toPrimitive()),
|
||||
|
||||
|
||||
@ -35,6 +35,17 @@ export class CustomerInvoiceFullPresenter extends Presenter<
|
||||
() => undefined
|
||||
);
|
||||
|
||||
const invoiceTaxes = invoice.getTaxes().map((taxItem) => {
|
||||
console.log(taxItem);
|
||||
return {
|
||||
tax_code: taxItem.tax.code,
|
||||
taxable_amount: taxItem.taxableAmount.toObjectString(),
|
||||
taxes_amount: taxItem.taxesAmount.toObjectString(),
|
||||
};
|
||||
});
|
||||
|
||||
console.log(invoiceTaxes);
|
||||
|
||||
return {
|
||||
id: invoice.id.toString(),
|
||||
company_id: invoice.companyId.toString(),
|
||||
@ -56,13 +67,7 @@ export class CustomerInvoiceFullPresenter extends Presenter<
|
||||
customer_id: invoice.customerId.toString(),
|
||||
recipient,
|
||||
|
||||
taxes: invoice.taxes.map((taxItem) => {
|
||||
return {
|
||||
tax_code: taxItem.tax.code,
|
||||
taxable_amount: taxItem.taxableAmount.convertScale(2).toObjectString(),
|
||||
taxes_amount: taxItem.taxesAmount.convertScale(2).toObjectString(),
|
||||
};
|
||||
}),
|
||||
taxes: invoiceTaxes,
|
||||
|
||||
payment_method: payment,
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
UtcDate,
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
import { CustomerInvoiceItems, InvoicePaymentMethod } from "../entities";
|
||||
import { CustomerInvoiceItems, InvoicePaymentMethod, InvoiceTaxTotal } from "../entities";
|
||||
import {
|
||||
CustomerInvoiceNumber,
|
||||
CustomerInvoiceSerie,
|
||||
@ -68,6 +68,8 @@ export interface ICustomerInvoice {
|
||||
getTaxesAmount(): InvoiceAmount;
|
||||
getTotalAmount(): InvoiceAmount;
|
||||
|
||||
getTaxes(): InvoiceTaxTotal[];
|
||||
|
||||
issueInvoice(newInvoiceNumber: CustomerInvoiceNumber): Result<CustomerInvoice, Error>;
|
||||
}
|
||||
|
||||
@ -198,10 +200,6 @@ export class CustomerInvoice
|
||||
return this._items;
|
||||
}
|
||||
|
||||
public get taxes() {
|
||||
return this.items.getTaxesAmountByTaxes();
|
||||
}
|
||||
|
||||
public get hasRecipient() {
|
||||
return this.recipient.isSome();
|
||||
}
|
||||
@ -224,7 +222,9 @@ export class CustomerInvoice
|
||||
private _getTaxesAmount(taxableAmount: InvoiceAmount): InvoiceAmount {
|
||||
let amount = InvoiceAmount.zero(this.currencyCode.code);
|
||||
|
||||
for (const taxItem of this.taxes) {
|
||||
const itemTaxes = this.items.getTaxesAmountByTaxes();
|
||||
|
||||
for (const taxItem of itemTaxes) {
|
||||
amount = amount.add(taxItem.taxesAmount);
|
||||
}
|
||||
return amount;
|
||||
@ -262,6 +262,24 @@ export class CustomerInvoice
|
||||
return this._getTotalAmount(taxableAmount, taxesAmount);
|
||||
}
|
||||
|
||||
public getTaxes(): InvoiceTaxTotal[] {
|
||||
const itemTaxes = this.items.getTaxesAmountByTaxes();
|
||||
|
||||
return itemTaxes.map((item) => {
|
||||
return {
|
||||
tax: item.tax,
|
||||
taxableAmount: InvoiceAmount.create({
|
||||
value: item.taxableAmount.convertScale(2).value,
|
||||
currency_code: this.currencyCode.code,
|
||||
}).data,
|
||||
taxesAmount: InvoiceAmount.create({
|
||||
value: item.taxesAmount.convertScale(2).value,
|
||||
currency_code: this.currencyCode.code,
|
||||
}).data,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public getAllAmounts() {
|
||||
const subtotalAmount = this.getSubtotalAmount();
|
||||
const discountAmount = this._getDiscountAmount(subtotalAmount);
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
ItemDiscount,
|
||||
ItemQuantity,
|
||||
} from "../../value-objects";
|
||||
import { ItemTaxes } from "../item-taxes";
|
||||
import { ItemTaxTotal, ItemTaxes } from "../item-taxes";
|
||||
|
||||
export interface CustomerInvoiceItemProps {
|
||||
description: Maybe<CustomerInvoiceItemDescription>;
|
||||
@ -109,6 +109,12 @@ export class CustomerInvoiceItem
|
||||
return this.getProps();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @summary Calcula el importe de descuento a partir del subtotal y el porcentaje.
|
||||
* @param subtotalAmount - Importe subtotal.
|
||||
* @returns El importe de descuento calculado.
|
||||
*/
|
||||
private _getDiscountAmount(subtotalAmount: ItemAmount): ItemAmount {
|
||||
const discount = this.discountPercentage.match(
|
||||
(percentage) => percentage,
|
||||
@ -117,18 +123,44 @@ export class CustomerInvoiceItem
|
||||
return subtotalAmount.percentage(discount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @summary Calcula el importe imponible restando el descuento al subtotal.
|
||||
* @param subtotalAmount - Importe subtotal.
|
||||
* @param discountAmount - Importe de descuento.
|
||||
* @returns El importe imponible resultante.
|
||||
*/
|
||||
private _getTaxableAmount(subtotalAmount: ItemAmount, discountAmount: ItemAmount): ItemAmount {
|
||||
return subtotalAmount.subtract(discountAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @summary Calcula el importe total de impuestos sobre la base imponible.
|
||||
* @param taxableAmount - Importe imponible.
|
||||
* @returns El importe de impuestos calculado.
|
||||
*/
|
||||
private _getTaxesAmount(taxableAmount: ItemAmount): ItemAmount {
|
||||
return this.props.taxes.getTaxesAmount(taxableAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @summary Calcula el importe total sumando base imponible e impuestos.
|
||||
* @param taxableAmount - Importe imponible.
|
||||
* @param taxesAmount - Importe de impuestos.
|
||||
* @returns El importe total del ítem.
|
||||
*/
|
||||
private _getTotalAmount(taxableAmount: ItemAmount, taxesAmount: ItemAmount): ItemAmount {
|
||||
return taxableAmount.add(taxesAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Calcula el subtotal del ítem (cantidad × importe unitario).
|
||||
* @returns Un `ItemAmount` con el subtotal del ítem.
|
||||
* @remarks
|
||||
* Si la cantidad o el importe unitario no están definidos, se asumen valores cero.
|
||||
*/
|
||||
public getSubtotalAmount(): ItemAmount {
|
||||
const curCode = this.currencyCode.code;
|
||||
const quantity = this.quantity.match(
|
||||
@ -143,18 +175,34 @@ export class CustomerInvoiceItem
|
||||
return unitAmount.multiply(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Calcula el importe total de descuento del ítem.
|
||||
* @returns Un `ItemAmount` con el importe descontado.
|
||||
*/
|
||||
public getDiscountAmount(): ItemAmount {
|
||||
return this._getDiscountAmount(this.getSubtotalAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Calcula el importe imponible (subtotal − descuento).
|
||||
* @returns Un `ItemAmount` con la base imponible del ítem.
|
||||
*/
|
||||
public getTaxableAmount(): ItemAmount {
|
||||
return this._getTaxableAmount(this.getSubtotalAmount(), this.getDiscountAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Calcula el importe total de impuestos aplicados al ítem.
|
||||
* @returns Un `ItemAmount` con el total de impuestos.
|
||||
*/
|
||||
public getTaxesAmount(): ItemAmount {
|
||||
return this._getTaxesAmount(this.getTaxableAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Calcula el importe total final del ítem (base imponible + impuestos).
|
||||
* @returns Un `ItemAmount` con el importe total.
|
||||
*/
|
||||
public getTotalAmount(): ItemAmount {
|
||||
const taxableAmount = this.getTaxableAmount();
|
||||
const taxesAmount = this._getTaxesAmount(taxableAmount);
|
||||
@ -162,10 +210,26 @@ export class CustomerInvoiceItem
|
||||
return this._getTotalAmount(taxableAmount, taxesAmount);
|
||||
}
|
||||
|
||||
public getTaxesAmountByTaxes() {
|
||||
/**
|
||||
* @summary Obtiene los importes de impuestos agrupados por tipo de impuesto.
|
||||
* @returns Una colección con la base imponible y el importe de impuestos por cada tipo.
|
||||
*/
|
||||
public getTaxesAmountByTaxes(): ItemTaxTotal[] {
|
||||
return this.taxes.getTaxesAmountByTaxes(this.getTaxableAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Devuelve todos los importes calculados del ítem en un único objeto.
|
||||
* @returns Un objeto con las propiedades:
|
||||
* - `subtotalAmount`
|
||||
* - `discountAmount`
|
||||
* - `taxableAmount`
|
||||
* - `taxesAmount`
|
||||
* - `totalAmount`
|
||||
* @remarks
|
||||
* Este método es útil para mostrar todos los cálculos en la interfaz de usuario
|
||||
* o serializar el ítem con sus valores calculados.
|
||||
*/
|
||||
public getAllAmounts() {
|
||||
const subtotalAmount = this.getSubtotalAmount();
|
||||
const discountAmount = this._getDiscountAmount(subtotalAmount);
|
||||
|
||||
@ -2,6 +2,7 @@ import { Tax } from "@erp/core/api";
|
||||
import { CurrencyCode, LanguageCode } from "@repo/rdx-ddd";
|
||||
import { Collection } from "@repo/rdx-utils";
|
||||
import { ItemAmount } from "../../value-objects";
|
||||
import { ItemTaxes } from "../item-taxes";
|
||||
import { CustomerInvoiceItem } from "./customer-invoice-item";
|
||||
|
||||
export interface CustomerInvoiceItemsProps {
|
||||
@ -25,6 +26,15 @@ export class CustomerInvoiceItems extends Collection<CustomerInvoiceItem> {
|
||||
return new CustomerInvoiceItems(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Añade un nuevo ítem a la colección.
|
||||
* @param item - El ítem de factura a añadir.
|
||||
* @returns `true` si el ítem fue añadido correctamente; `false` si fue rechazado.
|
||||
* @remarks
|
||||
* Sólo se aceptan ítems cuyo `LanguageCode` y `CurrencyCode` coincidan con
|
||||
* los de la colección. Si no coinciden, el método devuelve `false` sin modificar
|
||||
* la colección.
|
||||
*/
|
||||
add(item: CustomerInvoiceItem): boolean {
|
||||
// Antes de añadir un nuevo item, debo comprobar que el item a añadir
|
||||
// tiene el mismo "currencyCode" y "languageCode" que la colección de items.
|
||||
@ -37,6 +47,10 @@ export class CustomerInvoiceItems extends Collection<CustomerInvoiceItem> {
|
||||
return super.add(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Calcula el subtotal de todos los ítems sin descuentos ni impuestos.
|
||||
* @returns Un `ItemAmount` con el subtotal total de la colección en su moneda.
|
||||
*/
|
||||
public getSubtotalAmount(): ItemAmount {
|
||||
return this.getAll().reduce(
|
||||
(total, tax) => total.add(tax.getSubtotalAmount()),
|
||||
@ -44,6 +58,10 @@ export class CustomerInvoiceItems extends Collection<CustomerInvoiceItem> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Calcula el importe total de descuentos aplicados a todos los ítems.
|
||||
* @returns Un `ItemAmount` con el importe total de descuentos.
|
||||
*/
|
||||
public getDiscountAmount(): ItemAmount {
|
||||
return this.getAll().reduce(
|
||||
(total, item) => total.add(item.getDiscountAmount()),
|
||||
@ -51,6 +69,10 @@ export class CustomerInvoiceItems extends Collection<CustomerInvoiceItem> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Calcula el importe imponible total (base antes de impuestos).
|
||||
* @returns Un `ItemAmount` con el total imponible.
|
||||
*/
|
||||
public getTaxableAmount(): ItemAmount {
|
||||
return this.getAll().reduce(
|
||||
(total, item) => total.add(item.getTaxableAmount()),
|
||||
@ -58,6 +80,10 @@ export class CustomerInvoiceItems extends Collection<CustomerInvoiceItem> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Calcula el importe total de impuestos de todos los ítems.
|
||||
* @returns Un `ItemAmount` con la suma de todos los impuestos aplicados.
|
||||
*/
|
||||
public getTaxesAmount(): ItemAmount {
|
||||
return this.getAll().reduce(
|
||||
(total, item) => total.add(item.getTaxesAmount()),
|
||||
@ -65,6 +91,10 @@ export class CustomerInvoiceItems extends Collection<CustomerInvoiceItem> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Calcula el importe total final de todos los ítems (subtotal -descuentos + impuestos).
|
||||
* @returns Un `ItemAmount` con el total global de la colección.
|
||||
*/
|
||||
public getTotalAmount(): ItemAmount {
|
||||
return this.getAll().reduce(
|
||||
(total, item) => total.add(item.getTotalAmount()),
|
||||
@ -72,6 +102,18 @@ export class CustomerInvoiceItems extends Collection<CustomerInvoiceItem> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Agrupa los importes imponibles e impuestos por tipo de impuesto.
|
||||
* @returns Un array con objetos que contienen:
|
||||
* - `tax`: El tipo de impuesto.
|
||||
* - `taxableAmount`: El total de base imponible asociada a ese impuesto.
|
||||
* - `taxesAmount`: El importe total de impuestos calculado.
|
||||
* @remarks
|
||||
* Los impuestos se agrupan por su `tax.code` (código fiscal).
|
||||
* Si existen varios impuestos con el mismo código, sus importes se agregan.
|
||||
* En caso de conflicto de datos en impuestos con mismo código, prevalece
|
||||
* el primer `Tax` encontrado.
|
||||
*/
|
||||
public getTaxesAmountByTaxes(): Array<{
|
||||
tax: Tax;
|
||||
taxableAmount: ItemAmount;
|
||||
@ -105,4 +147,20 @@ export class CustomerInvoiceItems extends Collection<CustomerInvoiceItem> {
|
||||
|
||||
return Array.from(resultMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Obtiene la lista de impuestos únicos aplicados en todos los ítems.
|
||||
* @returns Un objeto `ItemTaxes` que contiene todos los impuestos distintos.
|
||||
* @remarks
|
||||
* Los impuestos se deduplican por su código (`tax.code`).
|
||||
* Si existen varios impuestos con el mismo código, el último encontrado
|
||||
* sobrescribirá los anteriores sin generar error.
|
||||
*/
|
||||
public getTaxes(): ItemTaxes {
|
||||
const taxes = this.getAll()
|
||||
.flatMap((item) => item.taxes.getAll())
|
||||
.reduce((map, tax) => map.set(tax.code, tax), new Map<string, Tax>());
|
||||
|
||||
return ItemTaxes.create([...taxes.values()]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,31 +1,43 @@
|
||||
import { Collection } from "@repo/rdx-utils";
|
||||
import { Tax, Taxes } from "@erp/core/api";
|
||||
import { InvoiceAmount } from "../../value-objects";
|
||||
import { InvoiceTax } from "./invoice-tax";
|
||||
|
||||
export interface InvoiceTaxesProps {
|
||||
items?: InvoiceTax[];
|
||||
}
|
||||
export type InvoiceTaxTotal = {
|
||||
tax: Tax;
|
||||
taxableAmount: InvoiceAmount;
|
||||
taxesAmount: InvoiceAmount;
|
||||
};
|
||||
|
||||
export class InvoiceTaxes extends Collection<InvoiceTax> {
|
||||
constructor(props: InvoiceTaxesProps) {
|
||||
const { items = [] } = props;
|
||||
super(items);
|
||||
}
|
||||
|
||||
public static create(props: InvoiceTaxesProps): InvoiceTaxes {
|
||||
return new InvoiceTaxes(props);
|
||||
export class InvoiceTaxes extends Taxes {
|
||||
constructor(items: Tax[] = [], totalItems: number | null = null) {
|
||||
super(items, totalItems);
|
||||
}
|
||||
|
||||
public getTaxesAmount(taxableAmount: InvoiceAmount): InvoiceAmount {
|
||||
return this.getAll().reduce(
|
||||
(total, tax) => total.add(tax.getTaxAmount(taxableAmount)),
|
||||
(total, tax) => total.add(taxableAmount.percentage(tax.percentage)),
|
||||
InvoiceAmount.zero(taxableAmount.currencyCode)
|
||||
) as InvoiceAmount;
|
||||
);
|
||||
}
|
||||
|
||||
public getTaxesAmountByTaxCode(taxCode: string, taxableAmount: InvoiceAmount): InvoiceAmount {
|
||||
const currencyCode = taxableAmount.currencyCode;
|
||||
|
||||
return this.filter((itemTax) => itemTax.code === taxCode).reduce((totalAmount, itemTax) => {
|
||||
return taxableAmount.percentage(itemTax.percentage).add(totalAmount);
|
||||
}, InvoiceAmount.zero(currencyCode));
|
||||
}
|
||||
|
||||
public getTaxesAmountByTaxes(taxableAmount: InvoiceAmount): InvoiceTaxTotal[] {
|
||||
return this.getAll().map((taxItem) => ({
|
||||
taxableAmount,
|
||||
tax: taxItem,
|
||||
taxesAmount: this.getTaxesAmountByTaxCode(taxItem.code, taxableAmount),
|
||||
}));
|
||||
}
|
||||
|
||||
public getCodesToString(): string {
|
||||
return this.getAll()
|
||||
.map((taxItem) => taxItem.tax.code)
|
||||
.map((taxItem) => taxItem.code)
|
||||
.join(", ");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
import { Tax, Taxes } from "@erp/core/api";
|
||||
import { ItemAmount } from "../../value-objects";
|
||||
|
||||
export type ItemTaxTotal = {
|
||||
tax: Tax;
|
||||
taxableAmount: ItemAmount;
|
||||
taxesAmount: ItemAmount;
|
||||
};
|
||||
|
||||
export class ItemTaxes extends Taxes {
|
||||
constructor(items: Tax[] = [], totalItems: number | null = null) {
|
||||
super(items, totalItems);
|
||||
@ -21,7 +27,7 @@ export class ItemTaxes extends Taxes {
|
||||
}, ItemAmount.zero(currencyCode));
|
||||
}
|
||||
|
||||
public getTaxesAmountByTaxes(taxableAmount: ItemAmount) {
|
||||
public getTaxesAmountByTaxes(taxableAmount: ItemAmount): ItemTaxTotal[] {
|
||||
return this.getAll().map((taxItem) => ({
|
||||
taxableAmount,
|
||||
tax: taxItem,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user