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

293 lines
7.2 KiB
TypeScript

import {
AggregateRoot,
CurrencyCode,
DomainValidationError,
LanguageCode,
Percentage,
TextValue,
UniqueID,
UtcDate,
} from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
import { CustomerInvoiceItems, InvoicePaymentMethod } from "../entities";
import {
CustomerInvoiceNumber,
CustomerInvoiceSerie,
CustomerInvoiceStatus,
InvoiceAmount,
InvoiceRecipient,
} from "../value-objects";
export interface CustomerInvoiceProps {
companyId: UniqueID;
isProforma: boolean;
status: CustomerInvoiceStatus;
series: Maybe<CustomerInvoiceSerie>;
invoiceNumber: Maybe<CustomerInvoiceNumber>;
invoiceDate: UtcDate;
operationDate: Maybe<UtcDate>;
customerId: UniqueID;
recipient: Maybe<InvoiceRecipient>;
reference: Maybe<string>;
description: Maybe<string>;
notes: Maybe<TextValue>;
languageCode: LanguageCode;
currencyCode: CurrencyCode;
items: CustomerInvoiceItems;
paymentMethod: Maybe<InvoicePaymentMethod>;
discountPercentage: Percentage;
/*verifactu_qr: string;
verifactu_url: string;
verifactu_status: string;*/
}
export type CustomerInvoicePatchProps = Partial<
Omit<CustomerInvoiceProps, "companyId" | "items">
> & {
items?: CustomerInvoiceItems;
};
export interface ICustomerInvoice {
hasRecipient: boolean;
hasPaymentMethod: boolean;
getSubtotalAmount(): InvoiceAmount;
getDiscountAmount(): InvoiceAmount;
getTaxableAmount(): InvoiceAmount;
getTaxesAmount(): InvoiceAmount;
getTotalAmount(): InvoiceAmount;
issueInvoice(newInvoiceNumber: CustomerInvoiceNumber): Result<CustomerInvoice, Error>;
}
export class CustomerInvoice
extends AggregateRoot<CustomerInvoiceProps>
implements ICustomerInvoice
{
private _items!: CustomerInvoiceItems;
protected constructor(props: CustomerInvoiceProps, id?: UniqueID) {
super(props, id);
this._items =
props.items ||
CustomerInvoiceItems.create({
languageCode: props.languageCode,
currencyCode: props.currencyCode,
});
}
static create(props: CustomerInvoiceProps, id?: UniqueID): Result<CustomerInvoice, Error> {
const customerInvoice = new CustomerInvoice(props, id);
// Reglas de negocio / validaciones
if (!customerInvoice.isProforma && !customerInvoice.hasRecipient) {
return Result.fail(
new DomainValidationError(
"MISSING_CUSTOMER_DATA",
"recipient",
"Customer data must be provided for non-proforma invoices"
)
);
}
// 🔹 Disparar evento de dominio "CustomerInvoiceAuthenticatedEvent"
//const { customerInvoice } = props;
//user.addDomainEvent(new CustomerInvoiceAuthenticatedEvent(id, customerInvoice.toString()));
return Result.ok(customerInvoice);
}
public update(partialInvoice: CustomerInvoicePatchProps): Result<CustomerInvoice, Error> {
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);
}
public get companyId(): UniqueID {
return this.props.companyId;
}
public get customerId(): UniqueID {
return this.props.customerId;
}
public get isProforma(): boolean {
return this.props.isProforma;
}
public get status(): CustomerInvoiceStatus {
return this.props.status;
}
public get series(): Maybe<CustomerInvoiceSerie> {
return this.props.series;
}
public get invoiceNumber() {
return this.props.invoiceNumber;
}
public get invoiceDate(): UtcDate {
return this.props.invoiceDate;
}
public get operationDate(): Maybe<UtcDate> {
return this.props.operationDate;
}
public get reference(): Maybe<string> {
return this.props.reference;
}
public get description(): Maybe<string> {
return this.props.description;
}
public get notes(): Maybe<TextValue> {
return this.props.notes;
}
public get recipient(): Maybe<InvoiceRecipient> {
return this.props.recipient;
}
public get paymentMethod(): Maybe<InvoicePaymentMethod> {
return this.props.paymentMethod;
}
public get languageCode(): LanguageCode {
return this.props.languageCode;
}
public get currencyCode(): CurrencyCode {
return this.props.currencyCode;
}
public get discountPercentage(): Percentage {
return this.props.discountPercentage;
}
// Method to get the complete list of line items
public get items(): CustomerInvoiceItems {
return this._items;
}
public get taxes() {
return this.items.getTaxesAmountByTaxes();
}
public get hasRecipient() {
return this.recipient.isSome();
}
public get hasPaymentMethod() {
return this.paymentMethod.isSome();
}
private _getDiscountAmount(subtotalAmount: InvoiceAmount): InvoiceAmount {
return subtotalAmount.percentage(this.discountPercentage);
}
private _getTaxableAmount(
subtotalAmount: InvoiceAmount,
discountAmount: InvoiceAmount
): InvoiceAmount {
return subtotalAmount.subtract(discountAmount);
}
private _getTaxesAmount(taxableAmount: InvoiceAmount): InvoiceAmount {
let amount = InvoiceAmount.zero(this.currencyCode.code);
for (const tax of this.taxes) {
amount = amount.add(tax.taxesAmount);
}
return amount;
}
private _getTotalAmount(taxableAmount: InvoiceAmount, taxesAmount: InvoiceAmount): InvoiceAmount {
return taxableAmount.add(taxesAmount);
}
public getSubtotalAmount(): InvoiceAmount {
const itemsSubtotal = this.items.getSubtotalAmount().convertScale(2);
return InvoiceAmount.create({
value: itemsSubtotal.value,
currency_code: this.currencyCode.code,
}).data as InvoiceAmount;
}
public getDiscountAmount(): InvoiceAmount {
return this._getDiscountAmount(this.getSubtotalAmount());
}
public getTaxableAmount(): InvoiceAmount {
return this._getTaxableAmount(this.getSubtotalAmount(), this.getDiscountAmount());
}
public getTaxesAmount(): InvoiceAmount {
return this._getTaxesAmount(this.getTaxableAmount());
}
public getTotalAmount(): InvoiceAmount {
const taxableAmount = this.getTaxableAmount();
const taxesAmount = this._getTaxesAmount(taxableAmount);
return this._getTotalAmount(taxableAmount, taxesAmount);
}
public getAllAmounts() {
const subtotalAmount = this.getSubtotalAmount();
const discountAmount = this._getDiscountAmount(subtotalAmount);
const taxableAmount = this._getTaxableAmount(subtotalAmount, discountAmount);
const taxesAmount = this._getTaxesAmount(taxableAmount);
const totalAmount = this._getTotalAmount(taxableAmount, taxesAmount);
return {
subtotalAmount,
discountAmount,
taxableAmount,
taxesAmount,
totalAmount,
};
}
public issueInvoice(newInvoiceNumber: CustomerInvoiceNumber) {
return CustomerInvoice.create(
{
...this.props,
status: CustomerInvoiceStatus.createEmitted(),
isProforma: false,
invoiceNumber: Maybe.some(newInvoiceNumber),
},
this.id
);
}
}