349 lines
8.9 KiB
TypeScript
349 lines
8.9 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, InvoiceTaxTotal } from "../entities";
|
|
import {
|
|
CustomerInvoiceNumber,
|
|
CustomerInvoiceSerie,
|
|
CustomerInvoiceStatus,
|
|
InvoiceAmount,
|
|
InvoiceRecipient,
|
|
} from "../value-objects";
|
|
|
|
export interface CustomerInvoiceProps {
|
|
companyId: UniqueID;
|
|
|
|
isProforma: boolean;
|
|
status: CustomerInvoiceStatus;
|
|
|
|
proformaId: Maybe<UniqueID>;
|
|
|
|
series: Maybe<CustomerInvoiceSerie>;
|
|
invoiceNumber: 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 {
|
|
canTransitionTo(nextStatus: string): boolean;
|
|
|
|
hasRecipient: boolean;
|
|
hasPaymentMethod: boolean;
|
|
|
|
getTaxes(): InvoiceTaxTotal[];
|
|
getProps(): CustomerInvoiceProps;
|
|
}
|
|
|
|
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,
|
|
});
|
|
}
|
|
|
|
getHeaderDiscountAmount(): InvoiceAmount {
|
|
throw new Error("Method not implemented.");
|
|
}
|
|
|
|
getTaxableAmount(): InvoiceAmount {
|
|
throw new Error("Method not implemented.");
|
|
}
|
|
|
|
getTaxesAmount(): InvoiceAmount {
|
|
throw new Error("Method not implemented.");
|
|
}
|
|
|
|
getTotalAmount(): InvoiceAmount {
|
|
throw new Error("Method not implemented.");
|
|
}
|
|
|
|
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 proformaId(): Maybe<UniqueID> {
|
|
return this.props.proformaId;
|
|
}
|
|
|
|
public get status(): CustomerInvoiceStatus {
|
|
return this.props.status;
|
|
}
|
|
|
|
canTransitionTo(nextStatus: string): boolean {
|
|
return this.props.status.canTransitionTo(nextStatus);
|
|
}
|
|
|
|
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 hasRecipient() {
|
|
return this.recipient.isSome();
|
|
}
|
|
|
|
public get hasPaymentMethod() {
|
|
return this.paymentMethod.isSome();
|
|
}
|
|
|
|
private _getHeaderDiscountAmount(
|
|
subtotalAmount: InvoiceAmount,
|
|
itemsDiscountAmount: InvoiceAmount
|
|
): InvoiceAmount {
|
|
return subtotalAmount.subtract(itemsDiscountAmount).percentage(this.discountPercentage);
|
|
}
|
|
|
|
private _getTaxableAmount(
|
|
subtotalAmount: InvoiceAmount,
|
|
itemsDiscountAmount: InvoiceAmount,
|
|
headerDiscountAmount: InvoiceAmount
|
|
): InvoiceAmount {
|
|
return subtotalAmount.subtract(itemsDiscountAmount).subtract(headerDiscountAmount);
|
|
}
|
|
|
|
private _getTaxesAmount(taxableAmount: InvoiceAmount): InvoiceAmount {
|
|
let amount = InvoiceAmount.zero(this.currencyCode.code);
|
|
|
|
const itemTaxes = this.items.getTaxesAmountByTaxes();
|
|
|
|
for (const taxItem of itemTaxes) {
|
|
amount = amount.add(
|
|
InvoiceAmount.create({
|
|
value: taxItem.taxesAmount.convertScale(2).value,
|
|
currency_code: this.currencyCode.code,
|
|
}).data
|
|
);
|
|
}
|
|
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 _getItemsDiscountAmount(): InvoiceAmount {
|
|
const itemsDiscountAmount = this.items.getDiscountAmount().convertScale(2);
|
|
|
|
return InvoiceAmount.create({
|
|
value: itemsDiscountAmount.value,
|
|
currency_code: this.currencyCode.code,
|
|
}).data as InvoiceAmount;
|
|
}
|
|
|
|
/*public getHeaderDiscountAmount(): InvoiceAmount {
|
|
return this._getHeaderDiscountAmount(this.getSubtotalAmount());
|
|
}
|
|
|
|
public getTaxableAmount(): InvoiceAmount {
|
|
return this._getTaxableAmount(this.getSubtotalAmount(), this.getHeaderDiscountAmount());
|
|
}
|
|
|
|
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 getTaxes(): InvoiceTaxTotal[] {
|
|
const itemTaxes = this.items.getTaxesAmountByTaxes();
|
|
|
|
return itemTaxes.map((item) => {
|
|
return {
|
|
tax: item.tax,
|
|
taxableAmount: InvoiceAmount.create({
|
|
value: item.taxableAmount.convertScale(2).value,
|
|
currency_code: this.currencyCode.code,
|
|
}).data,
|
|
taxesAmount: InvoiceAmount.create({
|
|
value: item.taxesAmount.convertScale(2).value,
|
|
currency_code: this.currencyCode.code,
|
|
}).data,
|
|
};
|
|
});
|
|
}
|
|
|
|
public getAllAmounts() {
|
|
const subtotalAmount = this._getSubtotalAmount(); // Sin IVA ni dtos de línea
|
|
const itemDiscountAmount = this._getItemsDiscountAmount(); // Suma de los Importes de descuentos de linea
|
|
const headerDiscountAmount = this._getHeaderDiscountAmount(subtotalAmount, itemDiscountAmount); // Importe de descuento de cabecera
|
|
|
|
const taxableAmount = this._getTaxableAmount(
|
|
subtotalAmount,
|
|
itemDiscountAmount,
|
|
headerDiscountAmount
|
|
); //
|
|
|
|
const taxesAmount = this._getTaxesAmount(taxableAmount);
|
|
const totalAmount = this._getTotalAmount(taxableAmount, taxesAmount);
|
|
|
|
return {
|
|
subtotalAmount,
|
|
itemDiscountAmount,
|
|
headerDiscountAmount,
|
|
taxableAmount,
|
|
taxesAmount,
|
|
totalAmount,
|
|
};
|
|
}
|
|
|
|
public getProps(): CustomerInvoiceProps {
|
|
return this.props;
|
|
}
|
|
}
|