294 lines
6.7 KiB
TypeScript
294 lines
6.7 KiB
TypeScript
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;
|
|
|
|
linkedProformaId: UniqueID; // <- id de la proforma padre en caso de issue
|
|
|
|
series: InvoiceSerie;
|
|
invoiceNumber: InvoiceNumber;
|
|
|
|
invoiceDate: UtcDate;
|
|
operationDate: Maybe<UtcDate>;
|
|
|
|
customerId: UniqueID;
|
|
recipient: InvoiceRecipient;
|
|
|
|
reference: Maybe<string>;
|
|
description: string;
|
|
notes: Maybe<TextValue>;
|
|
|
|
languageCode: LanguageCode;
|
|
currencyCode: CurrencyCode;
|
|
|
|
paymentMethod: Maybe<InvoicePaymentMethod>;
|
|
|
|
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<VerifactuRecord>;
|
|
}
|
|
|
|
export interface IIssuedInvoice {
|
|
companyId: UniqueID;
|
|
}
|
|
|
|
export type InternalIssuedInvoiceProps = Omit<IIssuedInvoiceCreateProps, "items">;
|
|
|
|
export class IssuedInvoice
|
|
extends AggregateRoot<InternalIssuedInvoiceProps>
|
|
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<IssuedInvoice, Error> {
|
|
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<void, Error> {
|
|
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<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;
|
|
}
|
|
|
|
public get linkedProformaId(): UniqueID {
|
|
return this.props.linkedProformaId;
|
|
}
|
|
|
|
public get status(): InvoiceStatus {
|
|
return this.props.status;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public get description(): string {
|
|
return this.props.description;
|
|
}
|
|
|
|
public get notes(): Maybe<TextValue> {
|
|
return this.props.notes;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 hasPaymentMethod() {
|
|
return this.paymentMethod.isSome();
|
|
}
|
|
}
|