import { DomainValidationError } from "@erp/core/api"; import { AggregateRoot, CurrencyCode, LanguageCode, Percentage, TextValue, UniqueID, UtcDate, } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; import { CustomerInvoiceItems } from "../entities"; import { InvoiceTaxes } from "../entities/invoice-taxes"; import { CustomerInvoiceNumber, CustomerInvoiceSerie, CustomerInvoiceStatus, InvoiceAmount, InvoiceRecipient, } from "../value-objects"; export interface CustomerInvoiceProps { companyId: UniqueID; isProforma: boolean; invoiceNumber: CustomerInvoiceNumber; status: CustomerInvoiceStatus; series: Maybe; invoiceDate: UtcDate; operationDate: Maybe; customerId: UniqueID; recipient: Maybe; notes: Maybe; languageCode: LanguageCode; currencyCode: CurrencyCode; taxes: Maybe; items: CustomerInvoiceItems; discountPercentage: Percentage; } export interface ICustomerInvoice { getSubtotalAmount(): InvoiceAmount; getDiscountAmount(): InvoiceAmount; getTaxableAmount(): InvoiceAmount; getTaxesAmount(): InvoiceAmount; getTotalAmount(): InvoiceAmount; } export type CustomerInvoicePatchProps = Partial>; export class CustomerInvoice extends AggregateRoot implements ICustomerInvoice { private _items!: CustomerInvoiceItems; private _taxes!: InvoiceTaxes; protected constructor(props: CustomerInvoiceProps, id?: UniqueID) { super(props, id); this._items = props.items || CustomerInvoiceItems.create({ languageCode: props.languageCode, currencyCode: props.currencyCode, }); this._taxes = props.taxes.match( (taxes) => taxes, () => InvoiceTaxes.create({}) ); } static create(props: CustomerInvoiceProps, id?: UniqueID): Result { 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 { throw new Error("Not implemented"); } 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 { return this.props.series; } public get invoiceNumber() { return this.props.invoiceNumber; } public get invoiceDate(): UtcDate { return this.props.invoiceDate; } public get operationDate(): Maybe { return this.props.operationDate; } public get notes(): Maybe { return this.props.notes; } public get recipient(): Maybe { return this.props.recipient; } 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(): InvoiceTaxes { return this._taxes; } public get hasRecipient() { return this.recipient.isSome(); } public getSubtotalAmount(): InvoiceAmount { const itemsSubtotal = this.items.getTotalAmount().convertScale(2); return InvoiceAmount.create({ value: itemsSubtotal.value, currency_code: this.currencyCode.code, }).data as InvoiceAmount; } public getDiscountAmount(): InvoiceAmount { return this.getSubtotalAmount().percentage(this.discountPercentage) as InvoiceAmount; } public getTaxableAmount(): InvoiceAmount { return this.getSubtotalAmount().subtract(this.getDiscountAmount()) as InvoiceAmount; } public getTaxesAmount(): InvoiceAmount { return this._getTaxesAmount(this.getTaxableAmount()); } public getTotalAmount(): InvoiceAmount { const taxableAmount = this.getTaxableAmount(); return taxableAmount.add(this._getTaxesAmount(taxableAmount)) as InvoiceAmount; } public getAllAmounts() { return { subtotalAmount: this.getSubtotalAmount(), discountAmount: this.getDiscountAmount(), taxableAmount: this.getTaxableAmount(), taxesAmount: this.getTaxesAmount(), totalAmount: this.getTotalAmount(), }; } private _getTaxesAmount(_taxableAmount: InvoiceAmount): InvoiceAmount { return this._taxes.getTaxesAmount(_taxableAmount); } }