FactuGES_ERP/modules/customer-invoices/src/api/domain/aggregates/customer-invoice.ts

349 lines
8.9 KiB
TypeScript
Raw Normal View History

2025-09-03 10:41:12 +00:00
import {
AggregateRoot,
CurrencyCode,
2025-09-17 17:37:41 +00:00
DomainValidationError,
2025-09-03 10:41:12 +00:00
LanguageCode,
2025-09-03 18:04:09 +00:00
Percentage,
2025-09-03 10:41:12 +00:00
TextValue,
UniqueID,
UtcDate,
} from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
2025-10-12 09:14:33 +00:00
import { CustomerInvoiceItems, InvoicePaymentMethod, InvoiceTaxTotal } from "../entities";
2025-06-12 06:55:17 +00:00
import {
CustomerInvoiceNumber,
CustomerInvoiceSerie,
CustomerInvoiceStatus,
2025-09-10 18:14:19 +00:00
InvoiceAmount,
2025-09-10 16:06:29 +00:00
InvoiceRecipient,
2025-06-12 06:55:17 +00:00
} from "../value-objects";
2025-03-18 08:05:00 +00:00
2025-06-24 18:38:57 +00:00
export interface CustomerInvoiceProps {
2025-09-03 10:41:12 +00:00
companyId: UniqueID;
2025-09-03 18:04:09 +00:00
2025-09-09 15:48:12 +00:00
isProforma: boolean;
2025-06-11 15:13:44 +00:00
status: CustomerInvoiceStatus;
2025-09-26 18:09:14 +00:00
proformaId: Maybe<UniqueID>;
2025-09-03 10:41:12 +00:00
series: Maybe<CustomerInvoiceSerie>;
invoiceNumber: CustomerInvoiceNumber;
2025-03-18 08:05:00 +00:00
2025-09-04 17:57:04 +00:00
invoiceDate: UtcDate;
2025-09-03 10:41:12 +00:00
operationDate: Maybe<UtcDate>;
2025-09-08 17:24:38 +00:00
customerId: UniqueID;
recipient: Maybe<InvoiceRecipient>;
2025-09-26 18:09:14 +00:00
reference: Maybe<string>;
2025-09-30 11:57:21 +00:00
description: Maybe<string>;
2025-09-03 10:41:12 +00:00
notes: Maybe<TextValue>;
2025-03-18 08:05:00 +00:00
2025-09-03 10:41:12 +00:00
languageCode: LanguageCode;
currencyCode: CurrencyCode;
2025-09-10 18:14:19 +00:00
items: CustomerInvoiceItems;
2025-09-03 18:04:09 +00:00
2025-09-22 17:31:49 +00:00
paymentMethod: Maybe<InvoicePaymentMethod>;
2025-09-03 18:04:09 +00:00
discountPercentage: Percentage;
2025-09-26 15:00:11 +00:00
2025-09-26 18:09:14 +00:00
/*verifactu_qr: string;
2025-09-26 15:00:11 +00:00
verifactu_url: string;
2025-09-26 18:09:14 +00:00
verifactu_status: string;*/
2025-09-10 18:14:19 +00:00
}
2025-09-03 18:04:09 +00:00
2025-10-03 19:01:38 +00:00
export type CustomerInvoicePatchProps = Partial<
Omit<CustomerInvoiceProps, "companyId" | "items">
> & {
items?: CustomerInvoiceItems;
};
2025-09-10 18:14:19 +00:00
export interface ICustomerInvoice {
canTransitionTo(nextStatus: string): boolean;
2025-09-22 17:31:49 +00:00
hasRecipient: boolean;
hasPaymentMethod: boolean;
2025-10-12 09:14:33 +00:00
getTaxes(): InvoiceTaxTotal[];
2025-11-07 17:51:18 +00:00
getProps(): CustomerInvoiceProps;
2025-03-18 08:05:00 +00:00
}
2025-09-10 18:14:19 +00:00
export class CustomerInvoice
extends AggregateRoot<CustomerInvoiceProps>
implements ICustomerInvoice
{
2025-09-03 10:41:12 +00:00
private _items!: CustomerInvoiceItems;
2025-04-01 14:26:15 +00:00
2025-06-24 18:38:57 +00:00
protected constructor(props: CustomerInvoiceProps, id?: UniqueID) {
2025-04-01 14:26:15 +00:00
super(props, id);
2025-09-03 10:41:12 +00:00
this._items =
props.items ||
CustomerInvoiceItems.create({
languageCode: props.languageCode,
currencyCode: props.currencyCode,
});
2025-04-01 14:26:15 +00:00
}
2025-11-07 17:51:18 +00:00
getHeaderDiscountAmount(): InvoiceAmount {
throw new Error("Method not implemented.");
}
2025-11-07 17:51:18 +00:00
getTaxableAmount(): InvoiceAmount {
throw new Error("Method not implemented.");
}
2025-11-07 17:51:18 +00:00
getTaxesAmount(): InvoiceAmount {
throw new Error("Method not implemented.");
}
2025-11-07 17:51:18 +00:00
getTotalAmount(): InvoiceAmount {
throw new Error("Method not implemented.");
}
2025-03-18 08:05:00 +00:00
2025-06-24 18:38:57 +00:00
static create(props: CustomerInvoiceProps, id?: UniqueID): Result<CustomerInvoice, Error> {
2025-06-12 06:55:17 +00:00
const customerInvoice = new CustomerInvoice(props, id);
2025-03-18 08:05:00 +00:00
// Reglas de negocio / validaciones
2025-09-09 15:48:12 +00:00
if (!customerInvoice.isProforma && !customerInvoice.hasRecipient) {
return Result.fail(
new DomainValidationError(
"MISSING_CUSTOMER_DATA",
"recipient",
"Customer data must be provided for non-proforma invoices"
)
);
}
2025-06-11 15:13:44 +00:00
// 🔹 Disparar evento de dominio "CustomerInvoiceAuthenticatedEvent"
2025-06-12 06:55:17 +00:00
//const { customerInvoice } = props;
//user.addDomainEvent(new CustomerInvoiceAuthenticatedEvent(id, customerInvoice.toString()));
2025-03-18 08:05:00 +00:00
2025-06-12 06:55:17 +00:00
return Result.ok(customerInvoice);
2025-03-18 08:05:00 +00:00
}
2025-09-03 10:41:12 +00:00
public update(partialInvoice: CustomerInvoicePatchProps): Result<CustomerInvoice, Error> {
2025-10-03 19:01:38 +00:00
const { items, ...rest } = partialInvoice;
const updatedProps = {
...this.props,
...rest,
} as CustomerInvoiceProps;
/*if (partialAddress) {
const updatedAddressOrError = this.address.update(partialAddress);
if (updatedAddressOrError.isFailure) {
return Result.fail(updatedAddressOrError.error);
}
updatedProps.address = updatedAddressOrError.data;
}*/
return CustomerInvoice.create(updatedProps, this.id);
2025-03-18 08:05:00 +00:00
}
2025-09-03 10:41:12 +00:00
public get companyId(): UniqueID {
return this.props.companyId;
2025-03-18 08:05:00 +00:00
}
2025-09-08 17:24:38 +00:00
public get customerId(): UniqueID {
return this.props.customerId;
}
2025-09-09 15:48:12 +00:00
public get isProforma(): boolean {
return this.props.isProforma;
}
public get proformaId(): Maybe<UniqueID> {
return this.props.proformaId;
}
2025-09-03 18:04:09 +00:00
public get status(): CustomerInvoiceStatus {
return this.props.status;
}
canTransitionTo(nextStatus: string): boolean {
return this.props.status.canTransitionTo(nextStatus);
}
2025-09-03 10:41:12 +00:00
public get series(): Maybe<CustomerInvoiceSerie> {
return this.props.series;
2025-03-18 08:05:00 +00:00
}
2025-09-03 10:41:12 +00:00
public get invoiceNumber() {
return this.props.invoiceNumber;
}
2025-03-18 08:05:00 +00:00
2025-09-04 17:57:04 +00:00
public get invoiceDate(): UtcDate {
return this.props.invoiceDate;
2025-03-18 08:05:00 +00:00
}
2025-09-03 10:41:12 +00:00
public get operationDate(): Maybe<UtcDate> {
2025-03-18 08:05:00 +00:00
return this.props.operationDate;
}
2025-09-26 18:09:14 +00:00
public get reference(): Maybe<string> {
return this.props.reference;
}
2025-09-30 11:57:21 +00:00
public get description(): Maybe<string> {
return this.props.description;
}
2025-09-03 10:41:12 +00:00
public get notes(): Maybe<TextValue> {
return this.props.notes;
2025-03-18 08:05:00 +00:00
}
2025-09-08 17:24:38 +00:00
public get recipient(): Maybe<InvoiceRecipient> {
return this.props.recipient;
}
2025-09-22 17:31:49 +00:00
public get paymentMethod(): Maybe<InvoicePaymentMethod> {
return this.props.paymentMethod;
}
2025-09-03 10:41:12 +00:00
public get languageCode(): LanguageCode {
return this.props.languageCode;
2025-03-18 08:05:00 +00:00
}
2025-09-03 10:41:12 +00:00
public get currencyCode(): CurrencyCode {
return this.props.currencyCode;
2025-03-18 08:05:00 +00:00
}
2025-09-03 18:04:09 +00:00
public get discountPercentage(): Percentage {
return this.props.discountPercentage;
}
2025-09-10 18:14:19 +00:00
// Method to get the complete list of line items
public get items(): CustomerInvoiceItems {
return this._items;
2025-09-03 18:04:09 +00:00
}
2025-09-10 18:14:19 +00:00
public get hasRecipient() {
return this.recipient.isSome();
2025-09-03 18:04:09 +00:00
}
2025-09-22 17:31:49 +00:00
public get hasPaymentMethod() {
return this.paymentMethod.isSome();
}
private _getHeaderDiscountAmount(
subtotalAmount: InvoiceAmount,
itemsDiscountAmount: InvoiceAmount
): InvoiceAmount {
return subtotalAmount.subtract(itemsDiscountAmount).percentage(this.discountPercentage);
2025-09-17 17:37:41 +00:00
}
private _getTaxableAmount(
subtotalAmount: InvoiceAmount,
itemsDiscountAmount: InvoiceAmount,
headerDiscountAmount: InvoiceAmount
2025-09-17 17:37:41 +00:00
): InvoiceAmount {
return subtotalAmount.subtract(itemsDiscountAmount).subtract(headerDiscountAmount);
2025-09-17 17:37:41 +00:00
}
private _getTaxesAmount(taxableAmount: InvoiceAmount): InvoiceAmount {
2025-09-26 18:09:14 +00:00
let amount = InvoiceAmount.zero(this.currencyCode.code);
2025-10-12 09:14:33 +00:00
const itemTaxes = this.items.getTaxesAmountByTaxes();
for (const taxItem of itemTaxes) {
2025-10-12 10:43:06 +00:00
amount = amount.add(
InvoiceAmount.create({
value: taxItem.taxesAmount.convertScale(2).value,
currency_code: this.currencyCode.code,
}).data
);
2025-09-26 18:09:14 +00:00
}
return amount;
2025-09-17 17:37:41 +00:00
}
private _getTotalAmount(taxableAmount: InvoiceAmount, taxesAmount: InvoiceAmount): InvoiceAmount {
return taxableAmount.add(taxesAmount);
}
public _getSubtotalAmount(): InvoiceAmount {
2025-09-17 17:37:41 +00:00
const itemsSubtotal = this.items.getSubtotalAmount().convertScale(2);
2025-09-03 18:04:09 +00:00
2025-09-10 18:14:19 +00:00
return InvoiceAmount.create({
value: itemsSubtotal.value,
currency_code: this.currencyCode.code,
}).data as InvoiceAmount;
2025-03-18 08:05:00 +00:00
}
public _getItemsDiscountAmount(): InvoiceAmount {
const itemsDiscountAmount = this.items.getDiscountAmount().convertScale(2);
return InvoiceAmount.create({
value: itemsDiscountAmount.value,
currency_code: this.currencyCode.code,
}).data as InvoiceAmount;
}
/*public getHeaderDiscountAmount(): InvoiceAmount {
return this._getHeaderDiscountAmount(this.getSubtotalAmount());
2025-09-08 17:24:38 +00:00
}
2025-09-10 18:14:19 +00:00
public getTaxableAmount(): InvoiceAmount {
return this._getTaxableAmount(this.getSubtotalAmount(), this.getHeaderDiscountAmount());
2025-03-18 08:05:00 +00:00
}
2025-09-10 18:14:19 +00:00
public getTaxesAmount(): InvoiceAmount {
return this._getTaxesAmount(this.getTaxableAmount());
2025-03-18 08:05:00 +00:00
}
2025-09-10 18:14:19 +00:00
public getTotalAmount(): InvoiceAmount {
const taxableAmount = this.getTaxableAmount();
2025-09-17 17:37:41 +00:00
const taxesAmount = this._getTaxesAmount(taxableAmount);
return this._getTotalAmount(taxableAmount, taxesAmount);
}*/
2025-03-18 08:05:00 +00:00
2025-10-12 09:14:33 +00:00
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,
};
});
}
2025-09-10 18:14:19 +00:00
public getAllAmounts() {
const subtotalAmount = this._getSubtotalAmount(); // Sin IVA ni dtos de línea
const itemDiscountAmount = this._getItemsDiscountAmount(); // Suma de los Importes de descuentos de linea
const headerDiscountAmount = this._getHeaderDiscountAmount(subtotalAmount, itemDiscountAmount); // Importe de descuento de cabecera
const taxableAmount = this._getTaxableAmount(
subtotalAmount,
itemDiscountAmount,
headerDiscountAmount
); //
2025-09-17 17:37:41 +00:00
const taxesAmount = this._getTaxesAmount(taxableAmount);
const totalAmount = this._getTotalAmount(taxableAmount, taxesAmount);
2025-09-10 18:14:19 +00:00
return {
2025-09-17 17:37:41 +00:00
subtotalAmount,
itemDiscountAmount,
headerDiscountAmount,
2025-09-17 17:37:41 +00:00
taxableAmount,
taxesAmount,
totalAmount,
2025-09-10 18:14:19 +00:00
};
2025-03-18 08:05:00 +00:00
}
2025-09-26 15:00:11 +00:00
2025-11-07 17:51:18 +00:00
public getProps(): CustomerInvoiceProps {
return this.props;
2025-09-26 15:00:11 +00:00
}
2025-03-18 08:05:00 +00:00
}