import type { DiscountPercentage } from "@erp/core/api"; import { AggregateRoot, type CurrencyCode, DomainValidationError, type LanguageCode, type Percentage, type TextValue, type UniqueID, type UtcDate, } from "@repo/rdx-ddd"; import { type Maybe, Result } from "@repo/rdx-utils"; import type { InvoiceAmount, InvoiceNumber, InvoicePaymentMethod, InvoiceRecipient, InvoiceSerie, InvoiceStatus, } from "../../common"; import { type IIssuedInvoiceItemCreateProps, IssuedInvoiceItem, IssuedInvoiceItems, type IssuedInvoiceTaxes, type VerifactuRecord, } from "../entities"; import { IssuedInvoiceItemMismatch } from "../errors"; export interface IIssuedInvoiceCreateProps { companyId: UniqueID; status: InvoiceStatus; proformaId: UniqueID; // <- id de la proforma padre en caso de issue series: Maybe; invoiceNumber: InvoiceNumber; invoiceDate: UtcDate; operationDate: Maybe; customerId: UniqueID; recipient: Maybe; reference: Maybe; description: Maybe; notes: Maybe; languageCode: LanguageCode; currencyCode: CurrencyCode; paymentMethod: Maybe; items: IIssuedInvoiceItemCreateProps[]; taxes: IssuedInvoiceTaxes; subtotalAmount: InvoiceAmount; itemsDiscountAmount: InvoiceAmount; globalDiscountPercentage: DiscountPercentage; globalDiscountAmount: InvoiceAmount; totalDiscountAmount: InvoiceAmount; taxableAmount: InvoiceAmount; ivaAmount: InvoiceAmount; recAmount: InvoiceAmount; retentionAmount: InvoiceAmount; taxesAmount: InvoiceAmount; totalAmount: InvoiceAmount; verifactu: Maybe; } export interface IIssuedInvoice { companyId: UniqueID; } export type InternalIssuedInvoiceProps = Omit; export class IssuedInvoice extends AggregateRoot implements IIssuedInvoice { private readonly _items!: IssuedInvoiceItems; protected constructor( props: InternalIssuedInvoiceProps, items: IssuedInvoiceItems, id?: UniqueID ) { super(props, id); this._items = items; } static create(props: IIssuedInvoiceCreateProps, id?: UniqueID): Result { const validationResult = IssuedInvoice.validateCreateProps(props); if (validationResult.isFailure) { return Result.fail(validationResult.error); } const internalItems = IssuedInvoiceItems.create({ items: [], languageCode: props.languageCode, currencyCode: props.currencyCode, globalDiscountPercentage: props.globalDiscountPercentage, }); const { items, ...internalProps } = props; const issuedInvoice = new IssuedInvoice(internalProps, internalItems, id); const initializeResult = issuedInvoice.initializeItems(items); if (initializeResult.isFailure) { return Result.fail(initializeResult.error); } // Reglas de negocio / validaciones // ... // 🔹 Disparar evento de dominio "IssuedInvoiceAuthenticatedEvent" //const { customerInvoice } = props; //user.addDomainEvent(new IssuedInvoiceAuthenticatedEvent(id, customerInvoice.toString())); return Result.ok(issuedInvoice); } private static validateCreateProps(props: IIssuedInvoiceCreateProps): Result { if (!props.recipient) { return Result.fail( new DomainValidationError( "MISSING_RECIPIENT", "recipient", "Issued invoice requires recipient" ) ); } return Result.ok(); } // Rehidratación desde persistencia static rehydrate( props: InternalIssuedInvoiceProps, items: IssuedInvoiceItems, id: UniqueID ): IssuedInvoice { return new IssuedInvoice(props, items, id); } private initializeItems(itemsProps: IIssuedInvoiceItemCreateProps[]): Result { for (const [index, itemProps] of itemsProps.entries()) { const itemResult = IssuedInvoiceItem.create(itemProps); if (itemResult.isFailure) { return Result.fail(itemResult.error); } const added = this._items.add(itemResult.data); if (!added) { return Result.fail(new IssuedInvoiceItemMismatch(index)); } } return Result.ok(); } // Getters public get companyId(): UniqueID { return this.props.companyId; } public get customerId(): UniqueID { return this.props.customerId; } public get proformaId(): UniqueID { return this.props.proformaId; } public get status(): InvoiceStatus { 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 reference(): Maybe { return this.props.reference; } public get description(): Maybe { return this.props.description; } public get notes(): Maybe { return this.props.notes; } public get recipient(): Maybe { return this.props.recipient; } public get paymentMethod(): Maybe { return this.props.paymentMethod; } public get languageCode(): LanguageCode { return this.props.languageCode; } public get currencyCode(): CurrencyCode { return this.props.currencyCode; } public get verifactu(): Maybe { return this.props.verifactu; } public get subtotalAmount(): InvoiceAmount { return this.props.subtotalAmount; } public get itemsDiscountAmount(): InvoiceAmount { return this.props.itemsDiscountAmount; } public get globalDiscountPercentage(): Percentage { return this.props.globalDiscountPercentage; } public get globalDiscountAmount(): InvoiceAmount { return this.props.globalDiscountAmount; } public get totalDiscountAmount(): InvoiceAmount { return this.props.totalDiscountAmount; } public get taxableAmount(): InvoiceAmount { return this.props.taxableAmount; } public get ivaAmount(): InvoiceAmount { return this.props.ivaAmount; } public get recAmount(): InvoiceAmount { return this.props.recAmount; } public get retentionAmount(): InvoiceAmount { return this.props.retentionAmount; } public get taxesAmount(): InvoiceAmount { return this.props.taxesAmount; } public get totalAmount(): InvoiceAmount { return this.props.totalAmount; } public get taxes(): IssuedInvoiceTaxes { return this.props.taxes; } // Method to get the complete list of line items public get items(): IssuedInvoiceItems { return this._items; } public get hasRecipient() { return this.recipient.isSome(); } public get hasPaymentMethod() { return this.paymentMethod.isSome(); } }