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

294 lines
6.8 KiB
TypeScript
Raw Normal View History

2026-02-21 20:46:27 +00:00
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,
2026-02-17 14:32:50 +00:00
InvoicePaymentMethod,
InvoiceRecipient,
InvoiceSerie,
InvoiceStatus,
2026-02-17 14:32:50 +00:00
} from "../../common";
2026-03-28 21:10:05 +00:00
import {
type IIssuedInvoiceItemCreateProps,
IssuedInvoiceItem,
IssuedInvoiceItems,
type IssuedInvoiceTaxes,
type VerifactuRecord,
} from "../entities";
import { IssuedInvoiceItemMismatch } from "../errors";
export interface IIssuedInvoiceCreateProps {
companyId: UniqueID;
status: InvoiceStatus;
2026-04-20 17:20:42 +00:00
linkedProformaId: Maybe<UniqueID>; // <- id de la proforma padre en caso de issue
2026-04-20 17:20:42 +00:00
series: InvoiceSerie;
invoiceNumber: InvoiceNumber;
invoiceDate: UtcDate;
operationDate: Maybe<UtcDate>;
customerId: UniqueID;
2026-04-20 17:20:42 +00:00
recipient: InvoiceRecipient;
reference: Maybe<string>;
2026-04-20 17:20:42 +00:00
description: string;
notes: Maybe<TextValue>;
languageCode: LanguageCode;
currencyCode: CurrencyCode;
paymentMethod: Maybe<InvoicePaymentMethod>;
2026-03-28 21:10:05 +00:00
items: IIssuedInvoiceItemCreateProps[];
taxes: IssuedInvoiceTaxes;
subtotalAmount: InvoiceAmount;
2026-02-17 18:13:59 +00:00
itemsDiscountAmount: InvoiceAmount;
2026-02-21 20:46:27 +00:00
globalDiscountPercentage: DiscountPercentage;
globalDiscountAmount: InvoiceAmount;
totalDiscountAmount: InvoiceAmount;
taxableAmount: InvoiceAmount;
2026-02-19 14:51:30 +00:00
ivaAmount: InvoiceAmount;
recAmount: InvoiceAmount;
retentionAmount: InvoiceAmount;
taxesAmount: InvoiceAmount;
totalAmount: InvoiceAmount;
verifactu: Maybe<VerifactuRecord>;
2026-03-03 11:05:09 +00:00
}
2026-03-28 21:10:05 +00:00
export interface IIssuedInvoice {
companyId: UniqueID;
}
export type InternalIssuedInvoiceProps = Omit<IIssuedInvoiceCreateProps, "items">;
2026-03-28 21:10:05 +00:00
export class IssuedInvoice
extends AggregateRoot<InternalIssuedInvoiceProps>
implements IIssuedInvoice
{
private readonly _items!: IssuedInvoiceItems;
protected constructor(
props: InternalIssuedInvoiceProps,
items: IssuedInvoiceItems,
id?: UniqueID
) {
super(props, id);
2026-03-28 21:10:05 +00:00
this._items = items;
}
2026-03-28 21:10:05 +00:00
static create(props: IIssuedInvoiceCreateProps, id?: UniqueID): Result<IssuedInvoice, Error> {
2026-03-29 19:00:07 +00:00
const validationResult = IssuedInvoice.validateCreateProps(props);
if (validationResult.isFailure) {
return Result.fail(validationResult.error);
}
2026-03-28 21:10:05 +00:00
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);
}
2026-03-29 19:00:07 +00:00
private static validateCreateProps(props: IIssuedInvoiceCreateProps): Result<void, Error> {
if (!props.recipient) {
return Result.fail(
new DomainValidationError(
"MISSING_RECIPIENT",
"recipient",
"Issued invoice requires recipient"
)
);
}
return Result.ok();
}
2026-03-28 21:10:05 +00:00
// Rehidratación desde persistencia
static rehydrate(
props: InternalIssuedInvoiceProps,
items: IssuedInvoiceItems,
id: UniqueID
): IssuedInvoice {
return new IssuedInvoice(props, items, id);
}
private initializeItems(itemsProps: IIssuedInvoiceItemCreateProps[]): Result<void, Error> {
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;
}
2026-04-20 17:20:42 +00:00
public get linkedProformaId(): Maybe<UniqueID> {
return this.props.linkedProformaId;
}
public get status(): InvoiceStatus {
return this.props.status;
}
2026-04-20 17:20:42 +00:00
public get series(): InvoiceSerie {
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;
}
2026-04-20 17:20:42 +00:00
public get description(): string {
return this.props.description;
}
public get notes(): Maybe<TextValue> {
return this.props.notes;
}
2026-04-20 17:20:42 +00:00
public get recipient(): 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 verifactu(): Maybe<VerifactuRecord> {
return this.props.verifactu;
}
2026-02-17 10:35:07 +00:00
public get subtotalAmount(): InvoiceAmount {
return this.props.subtotalAmount;
}
2026-02-17 18:13:59 +00:00
public get itemsDiscountAmount(): InvoiceAmount {
return this.props.itemsDiscountAmount;
2026-02-17 10:35:07 +00:00
}
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;
}
2026-02-19 14:51:30 +00:00
public get ivaAmount(): InvoiceAmount {
return this.props.ivaAmount;
}
public get recAmount(): InvoiceAmount {
return this.props.recAmount;
}
public get retentionAmount(): InvoiceAmount {
return this.props.retentionAmount;
}
2026-02-17 10:35:07 +00:00
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 hasPaymentMethod() {
return this.paymentMethod.isSome();
}
}