Facturas de cliente
This commit is contained in:
parent
b7a58ebad5
commit
d40c2279bd
@ -5,7 +5,7 @@ import { Invoice } from "../aggregates";
|
||||
export interface IInvoiceRepository {
|
||||
findAll(transaction?: any): Promise<Result<Collection<Invoice>, Error>>;
|
||||
getById(id: UniqueID, transaction?: any): Promise<Result<Invoice, Error>>;
|
||||
deleteById(id: UniqueID, transaction?: any): Promise<Result<boolean, Error>>;
|
||||
deleteByIdInCompany(id: UniqueID, transaction?: any): Promise<Result<boolean, Error>>;
|
||||
|
||||
create(invoice: Invoice, transaction?: any): Promise<void>;
|
||||
update(invoice: Invoice, transaction?: any): Promise<void>;
|
||||
|
||||
@ -7,6 +7,9 @@ import { IInvoiceService } from "./invoice-service.interface";
|
||||
|
||||
export class InvoiceService implements IInvoiceService {
|
||||
constructor(private readonly repo: IInvoiceRepository) {}
|
||||
deleteInvoiceById(invoiceId: UniqueID, transaction?: any): Promise<Result<boolean, Error>> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
async findInvoices(transaction?: Transaction): Promise<Result<Collection<Invoice>, Error>> {
|
||||
const invoicesOrError = await this.repo.findAll(transaction);
|
||||
@ -71,10 +74,11 @@ export class InvoiceService implements IInvoiceService {
|
||||
return Result.ok(newInvoice);
|
||||
}
|
||||
|
||||
async deleteInvoiceById(
|
||||
async deleteInvoiceByIdInCompnay(
|
||||
companyId: UniqueID,
|
||||
invoiceId: UniqueID,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<boolean, Error>> {
|
||||
return this.repo.deleteById(invoiceId, transaction);
|
||||
return this.repo.deleteByIdInCompany(companyId, invoiceId, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,35 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import { ILogger } from "../../logger";
|
||||
import { ITransactionManager } from "./transaction-manager.interface";
|
||||
|
||||
export abstract class TransactionManager implements ITransactionManager {
|
||||
protected _transaction: any | null = null;
|
||||
protected _isCompleted = false;
|
||||
protected readonly logger!: ILogger;
|
||||
|
||||
constructor(logger?: ILogger) {
|
||||
// Si no hay logger, usa console adaptado
|
||||
this.logger = logger ?? {
|
||||
info: (msg, meta) => console.info(msg, meta),
|
||||
warn: (msg, meta) => console.warn(msg, meta),
|
||||
error: (msg, err) => console.error(msg, err),
|
||||
debug: (msg, meta) => console.debug(msg, meta),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔹 Inicia una transacción si no hay una activa
|
||||
*/
|
||||
async start(): Promise<void> {
|
||||
if (!this._transaction) {
|
||||
this._transaction = await this._startTransaction();
|
||||
if (this._transaction) {
|
||||
this.logger.error("❌ Transaction already started. Nested transactions are not allowed.", {
|
||||
label: "TransactionManager.start",
|
||||
});
|
||||
throw new Error("A transaction is already active. Nested transactions are not allowed.");
|
||||
}
|
||||
this._transaction = await this._startTransaction();
|
||||
this._isCompleted = false;
|
||||
this.logger.debug("Transaction started", { label: "TransactionManager.start" });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -17,8 +37,16 @@ export abstract class TransactionManager implements ITransactionManager {
|
||||
*/
|
||||
getTransaction(): any {
|
||||
if (!this._transaction) {
|
||||
this.logger.error("❌ No active transaction. Call start() first.", {
|
||||
label: "TransactionManager.getTransaction",
|
||||
});
|
||||
throw new Error("No active transaction. Call start() first.");
|
||||
}
|
||||
if (this._isCompleted) {
|
||||
this.logger.error("❌ Transaction already completed.");
|
||||
throw new Error("Transaction already completed.");
|
||||
}
|
||||
|
||||
return this._transaction;
|
||||
}
|
||||
|
||||
@ -26,13 +54,30 @@ export abstract class TransactionManager implements ITransactionManager {
|
||||
* 🔹 Ejecuta una función dentro de una transacción
|
||||
*/
|
||||
async complete<T>(work: (transaction: any) => Promise<T>): Promise<T> {
|
||||
if (this._transaction) {
|
||||
this.logger.error(
|
||||
"❌ Cannot start a new transaction inside another. Nested transactions are not allowed.",
|
||||
{ label: "TransactionManager.complete" }
|
||||
);
|
||||
|
||||
throw new Error("A transaction is already active. Nested transactions are not allowed.");
|
||||
}
|
||||
|
||||
await this.start();
|
||||
try {
|
||||
const result = await work(this.getTransaction());
|
||||
if (result instanceof Result && result.isFailure) {
|
||||
throw result.error;
|
||||
}
|
||||
await this.commit();
|
||||
return result;
|
||||
} catch (error) {
|
||||
} catch (err) {
|
||||
await this.rollback();
|
||||
const error = err as Error;
|
||||
this.logger.error(`❌ Transaction rolled back due to error: ${error.message}`, {
|
||||
stack: error.stack,
|
||||
label: "TransactionManager.start",
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@ -45,16 +90,59 @@ export abstract class TransactionManager implements ITransactionManager {
|
||||
protected abstract _rollbackTransaction(): Promise<void>;
|
||||
|
||||
async commit(): Promise<void> {
|
||||
if (this._transaction) {
|
||||
if (!this._transaction) {
|
||||
this.logger.error("❌ No transaction to commit.", { label: "TransactionManager.commit" });
|
||||
throw new Error("No transaction to commit.");
|
||||
}
|
||||
if (this._isCompleted) {
|
||||
this.logger.error("❌ Transaction already completed. Cannot commit again.", {
|
||||
label: "TransactionManager.commit",
|
||||
});
|
||||
throw new Error("Transaction already completed.");
|
||||
}
|
||||
|
||||
try {
|
||||
await this._commitTransaction();
|
||||
this.logger.info("Transaction committed.", { label: "TransactionManager.commit" });
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
this.logger.error(`❌ Error during commit: ${error.message}`, {
|
||||
stack: error.stack,
|
||||
label: "TransactionManager.commit",
|
||||
});
|
||||
throw error;
|
||||
} finally {
|
||||
// Siempre limpia el estado interno
|
||||
this._transaction = null;
|
||||
this._isCompleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
async rollback(): Promise<void> {
|
||||
if (this._transaction) {
|
||||
if (!this._transaction) {
|
||||
this.logger.error("❌ No transaction to rollback.", { label: "TransactionManager.rollback" });
|
||||
throw new Error("No transaction to rollback.");
|
||||
}
|
||||
if (this._isCompleted) {
|
||||
this.logger.error("❌ Transaction already completed. Cannot rollback again.", {
|
||||
label: "TransactionManager.rollback",
|
||||
});
|
||||
throw new Error("Transaction already completed.");
|
||||
}
|
||||
|
||||
try {
|
||||
await this._rollbackTransaction();
|
||||
this.logger.info("Transaction rolled back.");
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
this.logger.error(`❌ Error during rollback: ${error.message}`, {
|
||||
stack: error.stack,
|
||||
label: "TransactionManager.rollback",
|
||||
});
|
||||
throw error;
|
||||
} finally {
|
||||
this._transaction = null;
|
||||
this._isCompleted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,17 @@
|
||||
import { DatabaseError } from "sequelize";
|
||||
import { InfrastructureError } from "./infrastructure-errors";
|
||||
|
||||
export class InfrastructureRepositoryError extends InfrastructureError {
|
||||
public readonly code = "REPOSITORY_ERROR" as const;
|
||||
constructor(message = "Repository operation failed", options?: ErrorOptions) {
|
||||
super(message, options);
|
||||
const { cause } = options || {};
|
||||
let _message = message;
|
||||
|
||||
if (cause && cause instanceof DatabaseError) {
|
||||
_message = `${message}: ${(cause as DatabaseError).message}`;
|
||||
}
|
||||
|
||||
super(_message, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,14 +17,14 @@ import { Result } from "@repo/rdx-utils";
|
||||
import { CreateCustomerInvoiceRequestDTO } from "../../../common/dto";
|
||||
import {
|
||||
CustomerInvoiceItemDescription,
|
||||
CustomerInvoiceItemDiscount,
|
||||
CustomerInvoiceItemProps,
|
||||
CustomerInvoiceItemQuantity,
|
||||
CustomerInvoiceItemUnitAmount,
|
||||
CustomerInvoiceNumber,
|
||||
CustomerInvoiceProps,
|
||||
CustomerInvoiceSerie,
|
||||
CustomerInvoiceStatus,
|
||||
ItemAmount,
|
||||
ItemDiscount,
|
||||
} from "../../domain";
|
||||
|
||||
/**
|
||||
@ -162,15 +162,13 @@ function mapDTOToCreateCustomerInvoiceItemsProps(
|
||||
);
|
||||
|
||||
const unitAmount = extractOrPushError(
|
||||
maybeFromNullableVO(item.unit_amount, (value) => CustomerInvoiceItemUnitAmount.create(value)),
|
||||
maybeFromNullableVO(item.unit_amount, (value) => ItemAmount.create(value)),
|
||||
"unit_amount",
|
||||
errors
|
||||
);
|
||||
|
||||
const discountPercentage = extractOrPushError(
|
||||
maybeFromNullableVO(item.discount_percentage, (value) =>
|
||||
CustomerInvoiceItemDiscount.create(value)
|
||||
),
|
||||
maybeFromNullableVO(item.discount_percentage, (value) => ItemDiscount.create(value)),
|
||||
"discount_percentage",
|
||||
errors
|
||||
);
|
||||
|
||||
@ -1,22 +1,23 @@
|
||||
import { EntityNotFoundError, ITransactionManager } from "@erp/core/api";
|
||||
import { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import { ICustomerInvoiceService } from "../../domain";
|
||||
import { CustomerInvoiceService } from "../../domain";
|
||||
|
||||
type DeleteCustomerInvoiceUseCaseInput = {
|
||||
tenantId: string;
|
||||
id: string;
|
||||
companyId: UniqueID;
|
||||
invoice_id: string;
|
||||
};
|
||||
|
||||
export class DeleteCustomerInvoiceUseCase {
|
||||
constructor(
|
||||
private readonly service: ICustomerInvoiceService,
|
||||
private readonly service: CustomerInvoiceService,
|
||||
private readonly transactionManager: ITransactionManager
|
||||
) {}
|
||||
|
||||
public execute(params: DeleteCustomerInvoiceUseCaseInput) {
|
||||
const { id, tenantId } = params;
|
||||
const idOrError = UniqueID.create(id);
|
||||
const { invoice_id, companyId } = params;
|
||||
|
||||
const idOrError = UniqueID.create(invoice_id);
|
||||
|
||||
if (idOrError.isFailure) {
|
||||
return Result.fail(idOrError.error);
|
||||
@ -26,17 +27,25 @@ export class DeleteCustomerInvoiceUseCase {
|
||||
|
||||
return this.transactionManager.complete(async (transaction) => {
|
||||
try {
|
||||
const existsCheck = await this.service.existsById(invoiceId, transaction);
|
||||
const existsCheck = await this.service.existsByIdInCompany(
|
||||
companyId,
|
||||
invoiceId,
|
||||
transaction
|
||||
);
|
||||
|
||||
if (existsCheck.isFailure) {
|
||||
return Result.fail(existsCheck.error);
|
||||
}
|
||||
|
||||
if (!existsCheck.data) {
|
||||
return Result.fail(new EntityNotFoundError("CustomerInvoice", "id", id.toString()));
|
||||
const invoiceExists = existsCheck.data;
|
||||
|
||||
if (!invoiceExists) {
|
||||
return Result.fail(
|
||||
new EntityNotFoundError("Customer invoice", "id", invoiceId.toString())
|
||||
);
|
||||
}
|
||||
|
||||
return await this.service.deleteById(invoiceId, transaction);
|
||||
return await this.service.deleteInvoiceByIdInCompany(companyId, invoiceId, transaction);
|
||||
} catch (error: unknown) {
|
||||
return Result.fail(error as Error);
|
||||
}
|
||||
|
||||
@ -4,9 +4,9 @@ import { Result } from "@repo/rdx-utils";
|
||||
import {
|
||||
CustomerInvoiceItem,
|
||||
CustomerInvoiceItemDescription,
|
||||
CustomerInvoiceItemDiscount,
|
||||
CustomerInvoiceItemQuantity,
|
||||
CustomerInvoiceItemUnitAmount,
|
||||
ItemAmount,
|
||||
ItemDiscount,
|
||||
} from "../../domain";
|
||||
import { extractOrPushError } from "./extract-or-push-error";
|
||||
import { hasNoUndefinedFields } from "./has-no-undefined-fields";
|
||||
@ -36,7 +36,7 @@ export function mapDTOToCustomerInvoiceItemsProps(
|
||||
);
|
||||
|
||||
const unitPrice = extractOrPushError(
|
||||
CustomerInvoiceItemUnitAmount.create({
|
||||
ItemAmount.create({
|
||||
value: item.unitPrice.amount,
|
||||
scale: item.unitPrice.scale,
|
||||
currency_code: item.unitPrice.currency,
|
||||
@ -46,7 +46,7 @@ export function mapDTOToCustomerInvoiceItemsProps(
|
||||
);
|
||||
|
||||
const discount = extractOrPushError(
|
||||
CustomerInvoiceItemDiscount.create({
|
||||
ItemDiscount.create({
|
||||
value: item.discount.amount,
|
||||
scale: item.discount.scale,
|
||||
}),
|
||||
|
||||
@ -9,26 +9,50 @@ import {
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
import {
|
||||
CustomerInvoiceItemDescription,
|
||||
CustomerInvoiceItemDiscount,
|
||||
CustomerInvoiceItemQuantity,
|
||||
CustomerInvoiceItemSubtotalAmount,
|
||||
CustomerInvoiceItemTotalAmount,
|
||||
CustomerInvoiceItemUnitAmount,
|
||||
ItemAmount,
|
||||
ItemDiscount,
|
||||
ItemQuantity,
|
||||
} from "../../value-objects";
|
||||
|
||||
export interface CustomerInvoiceItemProps {
|
||||
description: Maybe<CustomerInvoiceItemDescription>;
|
||||
quantity: Maybe<CustomerInvoiceItemQuantity>; // Cantidad de unidades
|
||||
unitAmount: Maybe<CustomerInvoiceItemUnitAmount>; // Precio unitario en la moneda de la factura
|
||||
discountPercentage: Maybe<CustomerInvoiceItemDiscount>; // % descuento
|
||||
quantity: Maybe<ItemQuantity>; // Cantidad de unidades
|
||||
unitAmount: Maybe<ItemAmount>; // Precio unitario en la moneda de la factura
|
||||
discountPercentage: Maybe<ItemDiscount>; // % descuento
|
||||
|
||||
languageCode: LanguageCode;
|
||||
currencyCode: CurrencyCode;
|
||||
}
|
||||
|
||||
export class CustomerInvoiceItem extends DomainEntity<CustomerInvoiceItemProps> {
|
||||
private _subtotalAmount!: CustomerInvoiceItemSubtotalAmount;
|
||||
private _totalAmount!: CustomerInvoiceItemTotalAmount;
|
||||
export interface ICustomerInvoiceItem {
|
||||
description: Maybe<CustomerInvoiceItemDescription>;
|
||||
|
||||
quantity: Maybe<ItemQuantity>; // Cantidad de unidades
|
||||
unitAmount: Maybe<ItemAmount>; // Precio unitario en la moneda de la factura
|
||||
subtotalAmount: ItemAmount;
|
||||
|
||||
discountPercentage: Maybe<ItemDiscount>; // % descuento
|
||||
discountAmount: Maybe<ItemAmount>;
|
||||
|
||||
taxableAmount: ItemAmount;
|
||||
taxesAmount: ItemAmount;
|
||||
|
||||
totalAmount: ItemAmount;
|
||||
|
||||
languageCode: LanguageCode;
|
||||
currencyCode: CurrencyCode;
|
||||
|
||||
calculateSubtotal(): ItemAmount;
|
||||
|
||||
calculateTotal(): ItemAmount;
|
||||
}
|
||||
|
||||
export class CustomerInvoiceItem
|
||||
extends DomainEntity<CustomerInvoiceItemProps>
|
||||
implements ICustomerInvoiceItem
|
||||
{
|
||||
private _subtotalAmount!: ItemAmount;
|
||||
private _totalAmount!: ItemAmount;
|
||||
|
||||
public static create(
|
||||
props: CustomerInvoiceItemProps,
|
||||
@ -51,19 +75,16 @@ export class CustomerInvoiceItem extends DomainEntity<CustomerInvoiceItemProps>
|
||||
return this.props.description;
|
||||
}
|
||||
|
||||
get quantity(): Maybe<CustomerInvoiceItemQuantity> {
|
||||
get quantity(): Maybe<ItemQuantity> {
|
||||
return this.props.quantity;
|
||||
}
|
||||
|
||||
get unitAmount(): Maybe<CustomerInvoiceItemUnitAmount> {
|
||||
get unitAmount(): Maybe<ItemAmount> {
|
||||
return this.props.unitAmount;
|
||||
}
|
||||
|
||||
get subtotalAmount(): CustomerInvoiceItemSubtotalAmount {
|
||||
if (!this._subtotalAmount) {
|
||||
this._subtotalAmount = this.calculateSubtotal();
|
||||
}
|
||||
return this._subtotalAmount;
|
||||
get subtotalAmount(): ItemAmount {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
get discountPercentage(): Maybe<Percentage> {
|
||||
@ -74,11 +95,16 @@ export class CustomerInvoiceItem extends DomainEntity<CustomerInvoiceItemProps>
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
get totalAmount(): CustomerInvoiceItemTotalAmount {
|
||||
if (!this._totalAmount) {
|
||||
this._totalAmount = this.calculateTotal();
|
||||
}
|
||||
return this._totalAmount;
|
||||
get taxableAmount(): ItemAmount {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
get taxesAmount(): ItemAmount {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
get totalAmount(): ItemAmount {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
public get languageCode(): LanguageCode {
|
||||
@ -89,15 +115,15 @@ export class CustomerInvoiceItem extends DomainEntity<CustomerInvoiceItemProps>
|
||||
return this.props.currencyCode;
|
||||
}
|
||||
|
||||
getValue(): CustomerInvoiceItemProps {
|
||||
getProps(): CustomerInvoiceItemProps {
|
||||
return this.props;
|
||||
}
|
||||
|
||||
toPrimitive() {
|
||||
return this.getValue();
|
||||
return this.getProps();
|
||||
}
|
||||
|
||||
calculateSubtotal(): CustomerInvoiceItemSubtotalAmount {
|
||||
calculateSubtotal(): ItemAmount {
|
||||
throw new Error("Not implemented");
|
||||
|
||||
/*const unitPrice = this.unitPrice.isSome()
|
||||
@ -106,7 +132,7 @@ export class CustomerInvoiceItem extends DomainEntity<CustomerInvoiceItemProps>
|
||||
return this.unitPrice.multiply(this.quantity.toNumber()); // Precio unitario * Cantidad*/
|
||||
}
|
||||
|
||||
calculateTotal(): CustomerInvoiceItemTotalAmount {
|
||||
calculateTotal(): ItemAmount {
|
||||
throw new Error("Not implemented");
|
||||
//return this.subtotalPrice.subtract(this.subtotalPrice.percentage(this.discount.toNumber()));
|
||||
}
|
||||
|
||||
@ -128,7 +128,7 @@ export class CustomerInvoiceService {
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<boolean, Error> - Resultado de la operación.
|
||||
*/
|
||||
async deleteById(
|
||||
async deleteInvoiceByIdInCompany(
|
||||
companyId: UniqueID,
|
||||
invoiceId: UniqueID,
|
||||
transaction?: Transaction
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
import { Percentage } from "@repo/rdx-ddd";
|
||||
|
||||
export class CustomerInvoiceItemDiscount extends Percentage {}
|
||||
@ -1,14 +0,0 @@
|
||||
import { MoneyValue, MoneyValueProps } from "@repo/rdx-ddd";
|
||||
|
||||
export class CustomerInvoiceItemSubtotalAmount extends MoneyValue {
|
||||
public static DEFAULT_SCALE = 4;
|
||||
|
||||
static create({ value, currency_code }: MoneyValueProps) {
|
||||
const props = {
|
||||
value: Number(value),
|
||||
scale: CustomerInvoiceItemSubtotalAmount.DEFAULT_SCALE,
|
||||
currency_code,
|
||||
};
|
||||
return MoneyValue.create(props);
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
import { MoneyValue, MoneyValueProps } from "@repo/rdx-ddd";
|
||||
|
||||
export class ItemTotalAmount extends MoneyValue {
|
||||
public static DEFAULT_SCALE = 4;
|
||||
|
||||
static create({ value, currency_code, scale }: MoneyValueProps) {
|
||||
const props = {
|
||||
value: Number(value),
|
||||
scale: scale ?? MoneyValue.DEFAULT_SCALE,
|
||||
currency_code,
|
||||
};
|
||||
return MoneyValue.create(props);
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
import { MoneyValue, MoneyValueProps } from "@repo/rdx-ddd";
|
||||
|
||||
export class CustomerInvoiceItemUnitAmount extends MoneyValue {
|
||||
public static DEFAULT_SCALE = 4;
|
||||
|
||||
static create({ value: amount, currency_code, scale }: MoneyValueProps) {
|
||||
const props = {
|
||||
amount: Number(amount),
|
||||
scale: scale ?? MoneyValue.DEFAULT_SCALE,
|
||||
currency_code,
|
||||
};
|
||||
return MoneyValue.create(props);
|
||||
}
|
||||
|
||||
static zero(currency_code: string, scale: number = CustomerInvoiceItemUnitAmount.DEFAULT_SCALE) {
|
||||
const props: MoneyValueProps = {
|
||||
value: 0,
|
||||
scale,
|
||||
currency_code,
|
||||
};
|
||||
return MoneyValue.create(props);
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,8 @@
|
||||
export * from "./customer-invoice-address-type";
|
||||
export * from "./customer-invoice-item-description";
|
||||
export * from "./customer-invoice-item-discount";
|
||||
export * from "./customer-invoice-item-subtotal-amount";
|
||||
export * from "./customer-invoice-item-total-amount";
|
||||
export * from "./customer-invoice-item-unit-amount";
|
||||
export * from "./customer-invoice-number";
|
||||
export * from "./customer-invoice-serie";
|
||||
export * from "./customer-invoice-status";
|
||||
export * from "./item-amount";
|
||||
export * from "./item-discount";
|
||||
export * from "./item-quantity";
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
import { MoneyValue, MoneyValueProps } from "@repo/rdx-ddd";
|
||||
|
||||
type ItemAmountProps = Pick<MoneyValueProps, "value" | "currency_code">;
|
||||
|
||||
export class ItemAmount extends MoneyValue {
|
||||
public static DEFAULT_SCALE = 4;
|
||||
|
||||
static create({ value, currency_code }: ItemAmountProps) {
|
||||
const props = {
|
||||
value: Number(value),
|
||||
scale: ItemAmount.DEFAULT_SCALE,
|
||||
currency_code,
|
||||
};
|
||||
return MoneyValue.create(props);
|
||||
}
|
||||
|
||||
static zero(currency_code: string) {
|
||||
const props = {
|
||||
value: 0,
|
||||
currency_code,
|
||||
};
|
||||
return ItemAmount.create(props);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
import { Percentage, PercentageProps } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
|
||||
type ItemDiscountProps = Pick<PercentageProps, "value">;
|
||||
|
||||
export class ItemDiscount extends Percentage {
|
||||
static DEFAULT_SCALE = 2;
|
||||
|
||||
static create({ value }: ItemDiscountProps): Result<Percentage> {
|
||||
return Percentage.create({
|
||||
value,
|
||||
scale: ItemDiscount.DEFAULT_SCALE,
|
||||
});
|
||||
}
|
||||
|
||||
static zero() {
|
||||
return ItemDiscount.create({ value: 0 });
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,18 @@
|
||||
import { Quantity, QuantityProps } from "@repo/rdx-ddd";
|
||||
|
||||
type ItemQuantityProps = Pick<QuantityProps, "value">;
|
||||
|
||||
export class ItemQuantity extends Quantity {
|
||||
public static DEFAULT_SCALE = 2;
|
||||
|
||||
static create({ value }: QuantityProps) {
|
||||
const props = {
|
||||
value: Number(value),
|
||||
static create({ value }: ItemQuantityProps) {
|
||||
return Quantity.create({
|
||||
value,
|
||||
scale: ItemQuantity.DEFAULT_SCALE,
|
||||
};
|
||||
return Quantity.create(props);
|
||||
});
|
||||
}
|
||||
|
||||
static zero() {
|
||||
return ItemQuantity.create({ value: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,16 +5,22 @@ import {
|
||||
ValidationErrorDetail,
|
||||
extractOrPushError,
|
||||
} from "@erp/core/api";
|
||||
import { CurrencyCode, LanguageCode, Quantity, UniqueID, maybeFromNullableVO, toNullable } from "@repo/rdx-ddd";
|
||||
import {
|
||||
CurrencyCode,
|
||||
LanguageCode,
|
||||
UniqueID,
|
||||
maybeFromNullableVO,
|
||||
toNullable,
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import { InferCreationAttributes } from "sequelize";
|
||||
import {
|
||||
CustomerInvoice,
|
||||
CustomerInvoiceItem,
|
||||
CustomerInvoiceItemDescription,
|
||||
CustomerInvoiceItemDiscount,
|
||||
CustomerInvoiceItemQuantity,
|
||||
CustomerInvoiceItemUnitAmount,
|
||||
ItemAmount,
|
||||
ItemDiscount,
|
||||
ItemQuantity,
|
||||
} from "../../domain";
|
||||
import {
|
||||
CustomerInvoiceItemCreationAttributes,
|
||||
@ -48,17 +54,17 @@ export class CustomerInvoiceItemMapper
|
||||
|
||||
const itemId = extractOrPushError(UniqueID.create(source.item_id), "item_id", errors);
|
||||
|
||||
const languageCode = extractOrPushError(
|
||||
LanguageCode.create(sourceParent.language_code),
|
||||
"language_code",
|
||||
errors
|
||||
);
|
||||
const languageCode = extractOrPushError(
|
||||
LanguageCode.create(sourceParent.language_code),
|
||||
"language_code",
|
||||
errors
|
||||
);
|
||||
|
||||
const currencyCode = extractOrPushError(
|
||||
CurrencyCode.create(sourceParent.currency_code),
|
||||
"currency_code",
|
||||
errors
|
||||
);
|
||||
const currencyCode = extractOrPushError(
|
||||
CurrencyCode.create(sourceParent.currency_code),
|
||||
"currency_code",
|
||||
errors
|
||||
);
|
||||
|
||||
const description = extractOrPushError(
|
||||
maybeFromNullableVO(source.description, (value) =>
|
||||
@ -69,36 +75,27 @@ export class CustomerInvoiceItemMapper
|
||||
);
|
||||
|
||||
const quantity = extractOrPushError(
|
||||
maybeFromNullableVO(source.quantity_value, (value) =>
|
||||
Quantity.create({ value, })
|
||||
|
||||
CustomerInvoiceItemQuantity.create({
|
||||
value: source.quantity_amouwnt,
|
||||
scale: source.quantity_scale,
|
||||
}),
|
||||
maybeFromNullableVO(source.quantity_value, (value) => ItemQuantity.create({ value })),
|
||||
"discount_percentage",
|
||||
errors
|
||||
);
|
||||
|
||||
const unitAmount = extractOrPushError(
|
||||
CustomerInvoiceItemUnitAmount.create({
|
||||
value: source.unit_price_amount,
|
||||
scale: source.unit_price_scale,
|
||||
}),
|
||||
"discount_percentage",
|
||||
maybeFromNullableVO(source.unit_amount_value, (value) =>
|
||||
ItemAmount.create({ value, currency_code: currencyCode!.code })
|
||||
),
|
||||
"unit_amount",
|
||||
errors
|
||||
);
|
||||
|
||||
const discountPercentage = extractOrPushError(
|
||||
CustomerInvoiceItemDiscount.create({
|
||||
value: source.discount_amount,
|
||||
scale: source.discount_scale,
|
||||
}),
|
||||
maybeFromNullableVO(source.discount_percentage_value, (value) =>
|
||||
ItemDiscount.create({ value })
|
||||
),
|
||||
"discount_percentage",
|
||||
errors
|
||||
);
|
||||
|
||||
|
||||
// Creación del objeto de dominio
|
||||
return CustomerInvoiceItem.create(
|
||||
{
|
||||
@ -108,8 +105,8 @@ export class CustomerInvoiceItemMapper
|
||||
quantity: quantity!,
|
||||
unitAmount: unitAmount!,
|
||||
discountPercentage: discountPercentage!,
|
||||
},º
|
||||
id
|
||||
},
|
||||
itemId
|
||||
);
|
||||
}
|
||||
|
||||
@ -117,14 +114,12 @@ export class CustomerInvoiceItemMapper
|
||||
source: CustomerInvoiceItem,
|
||||
params?: MapperParamsType
|
||||
): InferCreationAttributes<CustomerInvoiceItemModel, {}> {
|
||||
1
|
||||
1;
|
||||
const { index, sourceParent } = params as {
|
||||
index: number;
|
||||
sourceParent: CustomerInvoice;
|
||||
};
|
||||
|
||||
|
||||
|
||||
return {
|
||||
item_id: source.id.toPrimitive(),
|
||||
invoice_id: sourceParent.id.toPrimitive(),
|
||||
@ -135,23 +130,41 @@ export class CustomerInvoiceItemMapper
|
||||
quantity_value: toNullable(source.quantity, (value) => value.toPrimitive().value),
|
||||
quantity_scale: source.quantity.match(
|
||||
(value) => value.toPrimitive().scale,
|
||||
() => CustomerInvoiceItemQuantity.DEFAULT_SCALE),
|
||||
() => ItemQuantity.DEFAULT_SCALE
|
||||
),
|
||||
|
||||
unit_amount_value: toNullable(source.unitAmount, (value) => value.toPrimitive().value),
|
||||
unit_amount_scale: source.unitAmount.match(
|
||||
(value) => value.toPrimitive().scale,
|
||||
() => CustomerInvoiceItemUnitAmount.DEFAULT_SCALE),
|
||||
() => ItemAmount.DEFAULT_SCALE
|
||||
),
|
||||
|
||||
subtotal_amount_value: source.subtotalAmount.toPrimitive().value,
|
||||
subtotal_amount_scale: source.subtotalAmount.toPrimitive().scale,
|
||||
|
||||
discount_percentage_value: toNullable(source.discountPercentage, (value) => value.toPrimitive().value),
|
||||
discount_percentage_value: toNullable(
|
||||
source.discountPercentage,
|
||||
(value) => value.toPrimitive().value
|
||||
),
|
||||
discount_percentage_scale: source.discountPercentage.match(
|
||||
(value) => value.toPrimitive().scale,
|
||||
() => CustomerInvoiceItemUnitAmount.DEFAULT_SCALE),
|
||||
() => ItemAmount.DEFAULT_SCALE
|
||||
),
|
||||
|
||||
discount_amount_value: source.subtotalAmount.toPrimitive().value,
|
||||
discount_amount_scale: source.subtotalAmount.toPrimitive().scale,
|
||||
discount_amount_value: toNullable(
|
||||
source.discountAmount,
|
||||
(value) => value.toPrimitive().value
|
||||
),
|
||||
discount_amount_scale: source.discountAmount.match(
|
||||
(value) => value.toPrimitive().scale,
|
||||
() => ItemDiscount.DEFAULT_SCALE
|
||||
),
|
||||
|
||||
taxable_amount_value: source.taxableAmount.toPrimitive().value,
|
||||
taxable_amount_scale: source.taxableAmount.toPrimitive().value,
|
||||
|
||||
taxes_amount_value: source.taxesAmount.toPrimitive().value,
|
||||
taxes_amount_scale: source.taxesAmount.toPrimitive().value,
|
||||
|
||||
total_amount_value: source.totalAmount.toPrimitive().value,
|
||||
total_amount_scale: source.totalAmount.toPrimitive().scale,
|
||||
|
||||
@ -14,10 +14,12 @@ import {
|
||||
UniqueID,
|
||||
UtcDate,
|
||||
maybeFromNullableVO,
|
||||
toNullable,
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import {
|
||||
CustomerInvoice,
|
||||
CustomerInvoiceItems,
|
||||
CustomerInvoiceNumber,
|
||||
CustomerInvoiceProps,
|
||||
CustomerInvoiceSerie,
|
||||
@ -149,7 +151,10 @@ export class CustomerInvoiceMapper
|
||||
|
||||
discountPercentage: discountPercentage!,
|
||||
|
||||
items: items!,
|
||||
items: CustomerInvoiceItems.create({
|
||||
languageCode: languageCode!,
|
||||
currencyCode: currencyCode!,
|
||||
}),
|
||||
};
|
||||
|
||||
return CustomerInvoice.create(invoiceProps, invoiceId);
|
||||
@ -162,26 +167,41 @@ export class CustomerInvoiceMapper
|
||||
source: CustomerInvoice,
|
||||
params?: MapperParamsType
|
||||
): CustomerInvoiceCreationAttributes {
|
||||
const subtotal = source.calculateSubtotal();
|
||||
const total = source.calculateTotal();
|
||||
//const subtotal = source.calculateSubtotal();
|
||||
//const total = source.calculateTotal();
|
||||
|
||||
const items = this._itemsMapper.mapCollectionToPersistence(source.items, params);
|
||||
|
||||
return {
|
||||
id: source.id.toString(),
|
||||
invoice_status: source.status.toPrimitive(),
|
||||
invoice_series: source.invoiceSeries.toPrimitive(),
|
||||
id: source.id.toPrimitive(),
|
||||
company_id: source.companyId.toPrimitive(),
|
||||
|
||||
status: source.status.toPrimitive(),
|
||||
series: toNullable(source.series, (series) => series.toPrimitive()),
|
||||
|
||||
invoice_number: source.invoiceNumber.toPrimitive(),
|
||||
invoice_date: source.invoiceDate.toPrimitive(),
|
||||
operation_date: source.operationDate.toPrimitive(),
|
||||
invoice_language: "es",
|
||||
invoice_currency: source.currency || "EUR",
|
||||
|
||||
subtotal_amount: subtotal.amount,
|
||||
subtotal_scale: subtotal.scale,
|
||||
operation_date: toNullable(source.operationDate, (operationDate) =>
|
||||
operationDate.toPrimitive()
|
||||
),
|
||||
|
||||
total_amount: total.amount,
|
||||
total_scale: total.scale,
|
||||
notes: toNullable(source.notes, (notes) => notes.toPrimitive()),
|
||||
|
||||
language_code: source.languageCode.code,
|
||||
currency_code: source.currencyCode.code,
|
||||
|
||||
subtotal_amount_value: 0, //subtotal.amount,
|
||||
subtotal_amount_scale: 2, //subtotal.scale,
|
||||
|
||||
/*discount_percentage_value: source.discountPercentage.value,
|
||||
discount_percentage_scale: source.discountPercentage.scale,
|
||||
|
||||
discount_amount_value: source.discountAmount.value,
|
||||
discount_amount_scale: source.discountAmount.scale,*/
|
||||
|
||||
total_amount_value: 0, //total.amount,
|
||||
total_amount_scale: 2, //total.scale,
|
||||
|
||||
items,
|
||||
};
|
||||
|
||||
@ -17,7 +17,7 @@ export class CustomerInvoiceItemTaxesModel extends Model<
|
||||
InferAttributes<CustomerInvoiceItemTaxesModel>,
|
||||
InferCreationAttributes<CustomerInvoiceItemTaxesModel>
|
||||
> {
|
||||
declare id: string;
|
||||
declare tax_id: string;
|
||||
declare item_id: string;
|
||||
|
||||
declare tax_code: string; //"iva_21"
|
||||
@ -38,7 +38,7 @@ export class CustomerInvoiceItemTaxesModel extends Model<
|
||||
|
||||
CustomerInvoiceItemTaxesModel.belongsTo(CustomerInvoiceItemModel, {
|
||||
as: "item",
|
||||
targetKey: "id",
|
||||
targetKey: "item_id",
|
||||
foreignKey: "item_id",
|
||||
onDelete: "CASCADE",
|
||||
});
|
||||
@ -50,7 +50,7 @@ export class CustomerInvoiceItemTaxesModel extends Model<
|
||||
export default (database: Sequelize) => {
|
||||
CustomerInvoiceItemTaxesModel.init(
|
||||
{
|
||||
id: {
|
||||
tax_id: {
|
||||
type: new DataTypes.UUID(),
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
@ -195,10 +195,7 @@ export default (database: Sequelize) => {
|
||||
|
||||
underscored: true,
|
||||
|
||||
indexes: [
|
||||
{ name: "invoice_idx", fields: ["invoice_id"], unique: false },
|
||||
{ name: "parent_idx", fields: ["parent_id"], unique: false },
|
||||
],
|
||||
indexes: [{ name: "invoice_idx", fields: ["invoice_id"], unique: false }],
|
||||
|
||||
whereMergeStrategy: "and", // <- cómo tratar el merge de un scope
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ export class CustomerInvoiceTaxesModel extends Model<
|
||||
InferAttributes<CustomerInvoiceTaxesModel>,
|
||||
InferCreationAttributes<CustomerInvoiceTaxesModel>
|
||||
> {
|
||||
declare id: string;
|
||||
declare tax_id: string;
|
||||
declare invoice_id: string;
|
||||
|
||||
declare tax_code: string; //"iva_21"
|
||||
@ -50,7 +50,7 @@ export class CustomerInvoiceTaxesModel extends Model<
|
||||
export default (database: Sequelize) => {
|
||||
CustomerInvoiceTaxesModel.init(
|
||||
{
|
||||
id: {
|
||||
tax_id: {
|
||||
type: new DataTypes.UUID(),
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
@ -1,9 +1,19 @@
|
||||
import customerInvoiceItemTaxesModelInit from "./customer-invoice-item-taxes.model";
|
||||
import customerInvoiceItemModelInit from "./customer-invoice-item.model";
|
||||
import customerInvoiceTaxesModelInit from "./customer-invoice-taxes.model";
|
||||
import customerInvoiceModelInit from "./customer-invoice.model";
|
||||
|
||||
export * from "./customer-invoice-item.model"; // exporta las clases, tipos
|
||||
export * from "./customer-invoice-item-taxes.model";
|
||||
export * from "./customer-invoice-item.model";
|
||||
export * from "./customer-invoice-taxes.model";
|
||||
export * from "./customer-invoice.model";
|
||||
export * from "./customer-invoice.repository";
|
||||
|
||||
// Array de inicializadores para que registerModels() lo use
|
||||
export const models = [customerInvoiceItemModelInit, customerInvoiceModelInit];
|
||||
export const models = [
|
||||
customerInvoiceModelInit,
|
||||
customerInvoiceItemModelInit,
|
||||
|
||||
customerInvoiceTaxesModelInit,
|
||||
customerInvoiceItemTaxesModelInit,
|
||||
];
|
||||
|
||||
@ -38,6 +38,10 @@ export class CurrencyCode extends ValueObject<CurrencyCodeProps> {
|
||||
return Result.ok(new CurrencyCode({ value: valueIsValid.data }));
|
||||
}
|
||||
|
||||
get code(): string {
|
||||
return this.props.value;
|
||||
}
|
||||
|
||||
getProps(): string {
|
||||
return this.props.value;
|
||||
}
|
||||
|
||||
@ -38,6 +38,10 @@ export class LanguageCode extends ValueObject<LanguageCodeProps> {
|
||||
return Result.ok(new LanguageCode({ value: valueIsValid.data }));
|
||||
}
|
||||
|
||||
get code(): string {
|
||||
return this.props.value;
|
||||
}
|
||||
|
||||
getProps(): string {
|
||||
return this.props.value;
|
||||
}
|
||||
|
||||
@ -34,6 +34,6 @@ export class TextValue extends ValueObject<TextValueProps> {
|
||||
}
|
||||
|
||||
toPrimitive(): string {
|
||||
return this.getProps();
|
||||
return String(this.getProps());
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,10 +37,10 @@ export class UniqueID extends ValueObject<string> {
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.getProps();
|
||||
return String(this.getProps());
|
||||
}
|
||||
|
||||
toPrimitive() {
|
||||
return this.getProps();
|
||||
return this.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user