Facturas de cliente
This commit is contained in:
parent
4a73456c46
commit
dba421e9c5
@ -32,7 +32,8 @@
|
||||
"noExplicitAny": "info"
|
||||
},
|
||||
"style": {
|
||||
"useImportType": "off"
|
||||
"useImportType": "off",
|
||||
"noNonNullAssertion": "info"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { ConnectionError, UniqueConstraintError } from "sequelize";
|
||||
import {
|
||||
ConnectionError,
|
||||
DatabaseError,
|
||||
ForeignKeyConstraintError,
|
||||
UniqueConstraintError,
|
||||
ValidationError,
|
||||
} from "sequelize";
|
||||
|
||||
import { ApiError } from "./api-error";
|
||||
import { ConflictApiError } from "./conflict-api-error";
|
||||
@ -11,26 +17,50 @@ import { UnavailableApiError } from "./unavailable-api-error";
|
||||
import { ValidationApiError } from "./validation-api-error";
|
||||
import { ValidationErrorCollection } from "./validation-error-collection";
|
||||
|
||||
/**
|
||||
* Mapea errores de la aplicación a errores de la API.
|
||||
*
|
||||
* Esta función toma un error de la aplicación y lo convierte en un objeto ApiError
|
||||
* adecuado para enviar como respuesta HTTP. Maneja errores comunes como validación,
|
||||
* conflictos, no encontrados, autenticación y errores de infraestructura.
|
||||
*
|
||||
* @param error - El error de la aplicación a mapear.
|
||||
* @returns Un objeto ApiError que representa el error mapeado.
|
||||
* @example
|
||||
* const error = new Error("Invalid input");
|
||||
* const apiError = errorMapper.toApiError(error);
|
||||
* console.log(apiError);
|
||||
* // Output: ValidationApiError { status: 422, title: 'Validation Failed', detail: 'Invalid input', type: 'https://httpstatuses.com/422' }
|
||||
* @throws {ApiError} Si el error no puede ser mapeado a un tipo conocido.
|
||||
* @see ApiError
|
||||
* @see ValidationApiError
|
||||
*/
|
||||
|
||||
export const errorMapper = {
|
||||
toDomainError(error: unknown): Error {
|
||||
if (error instanceof UniqueConstraintError) {
|
||||
const field = error.errors[0]?.path || "unknown_field";
|
||||
return new Error(`A record with this ${field} already exists.`);
|
||||
}
|
||||
|
||||
if (error instanceof ForeignKeyConstraintError) {
|
||||
return new Error("A referenced entity was not found or is invalid.");
|
||||
}
|
||||
|
||||
if (error instanceof ValidationError) {
|
||||
return new Error(`Invalid data provided: ${error.message}`);
|
||||
}
|
||||
|
||||
if (error instanceof DatabaseError) {
|
||||
return new Error("Database error occurred.");
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
return error; // Fallback a error estándar
|
||||
}
|
||||
|
||||
return new Error("Unknown persistence error.");
|
||||
},
|
||||
|
||||
/**
|
||||
* Mapea errores de la aplicación a errores de la API.
|
||||
*
|
||||
* Esta función toma un error de la aplicación y lo convierte en un objeto ApiError
|
||||
* adecuado para enviar como respuesta HTTP. Maneja errores comunes como validación,
|
||||
* conflictos, no encontrados, autenticación y errores de infraestructura.
|
||||
*
|
||||
* @param error - El error de la aplicación a mapear.
|
||||
* @returns Un objeto ApiError que representa el error mapeado.
|
||||
* @example
|
||||
* const error = new Error("Invalid input");
|
||||
* const apiError = errorMapper.toApiError(error);
|
||||
* console.log(apiError);
|
||||
* // Output: ValidationApiError { status: 422, title: 'Validation Failed', detail: 'Invalid input', type: 'https://httpstatuses.com/422' }
|
||||
* @throws {ApiError} Si el error no puede ser mapeado a un tipo conocido.
|
||||
* @see ApiError
|
||||
* @see ValidationApiError
|
||||
*/
|
||||
toApiError: (error: Error): ApiError => {
|
||||
const message = error.message || "An unexpected error occurred";
|
||||
|
||||
|
||||
@ -1,149 +1,41 @@
|
||||
import { ITransactionManager } from "@erp/core/api";
|
||||
import { UniqueID } from "@repo/rdx-ddd";
|
||||
import { CreateCustomerInvoiceCommandDTO } from "@erp/customer-invoices/common/dto";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import { Transaction } from "sequelize";
|
||||
import { ICreateCustomerInvoiceRequestDTO } from "../../common/dto";
|
||||
import { ICustomerInvoiceProps, ICustomerInvoiceService } from "../domain";
|
||||
import { ICustomerInvoiceService } from "../../domain";
|
||||
import { mapDTOToCustomerInvoiceProps } from "../helpers";
|
||||
import { CreateCustomerInvoicesPresenter } from "./presenter";
|
||||
|
||||
export class CreateCustomerInvoiceUseCase {
|
||||
constructor(
|
||||
private readonly customerInvoiceService: ICustomerInvoiceService,
|
||||
private readonly transactionManager: ITransactionManager
|
||||
private readonly transactionManager: ITransactionManager,
|
||||
private readonly presenter: CreateCustomerInvoicesPresenter
|
||||
) {}
|
||||
|
||||
public execute(customerInvoiceID: UniqueID, data: ICreateCustomerInvoiceRequestDTO) {
|
||||
public execute(dto: CreateCustomerInvoiceCommandDTO) {
|
||||
const invoicePropOrError = mapDTOToCustomerInvoiceProps(dto);
|
||||
|
||||
if (invoicePropOrError.isFailure) {
|
||||
return Result.fail(invoicePropOrError.error);
|
||||
}
|
||||
|
||||
const invoiceOrError = this.customerInvoiceService.build(invoicePropOrError.data);
|
||||
|
||||
if (invoiceOrError.isFailure) {
|
||||
return Result.fail(invoiceOrError.error);
|
||||
}
|
||||
|
||||
const newInvoice = invoiceOrError.data;
|
||||
|
||||
return this.transactionManager.complete(async (transaction: Transaction) => {
|
||||
try {
|
||||
/*const validOrErrors = this.validateCustomerInvoiceData(dto);
|
||||
if (validOrErrors.isFailure) {
|
||||
return Result.fail(validOrErrors.error);
|
||||
}
|
||||
|
||||
const data = validOrErrors.data;*/
|
||||
|
||||
const invoiceProps: ICustomerInvoiceProps = {
|
||||
customerInvoiceNumber: data.customerInvoice_number,
|
||||
customerInvoiceSeries: data.customerInvoice_series,
|
||||
issueDate: data.issue_date,
|
||||
operationDate: data.operation_date,
|
||||
customerInvoiceCurrency: data.currency,
|
||||
};
|
||||
|
||||
// Update customerInvoice with dto
|
||||
return await this.customerInvoiceService.createCustomerInvoice(
|
||||
customerInvoiceID,
|
||||
invoiceProps,
|
||||
transaction
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
logger.error(error as Error);
|
||||
return Result.fail(error as Error);
|
||||
const result = await this.customerInvoiceService.save(newInvoice, transaction);
|
||||
if (result.isFailure) {
|
||||
return Result.fail(result.error);
|
||||
}
|
||||
|
||||
const viewDTO = this.presenter.toDTO(newInvoice);
|
||||
return Result.ok(viewDTO);
|
||||
});
|
||||
}
|
||||
|
||||
private validateCustomerInvoiceData(
|
||||
dto: ICreateCustomerInvoiceRequestDTO
|
||||
): Result<ICustomerInvoiceProps, Error> {
|
||||
const errors: Error[] = [];
|
||||
|
||||
const customerInvoiceNumerOrError = CustomerInvoiceNumber.create(dto.customerInvoice_number);
|
||||
const customerInvoiceSeriesOrError = CustomerInvoiceSerie.create(dto.customerInvoice_series);
|
||||
const issueDateOrError = UtcDate.create(dto.issue_date);
|
||||
const operationDateOrError = UtcDate.create(dto.operation_date);
|
||||
|
||||
const result = Result.combine([
|
||||
customerInvoiceNumerOrError,
|
||||
customerInvoiceSeriesOrError,
|
||||
issueDateOrError,
|
||||
operationDateOrError,
|
||||
]);
|
||||
|
||||
if (result.isFailure) {
|
||||
return Result.fail(result.error);
|
||||
}
|
||||
|
||||
const validatedData: ICustomerInvoiceProps = {
|
||||
status: CustomerInvoiceStatus.createDraft(),
|
||||
customerInvoiceNumber: customerInvoiceNumerOrError.data,
|
||||
customerInvoiceSeries: customerInvoiceSeriesOrError.data,
|
||||
issueDate: issueDateOrError.data,
|
||||
operationDate: operationDateOrError.data,
|
||||
customerInvoiceCurrency: dto.currency,
|
||||
};
|
||||
|
||||
/*if (errors.length > 0) {
|
||||
const message = errors.map((err) => err.message).toString();
|
||||
return Result.fail(new Error(message));
|
||||
}*/
|
||||
return Result.ok(validatedData);
|
||||
|
||||
/*let customerInvoice_status = CustomerInvoiceStatus.create(dto.status).object;
|
||||
if (customerInvoice_status.isEmpty()) {
|
||||
customerInvoice_status = CustomerInvoiceStatus.createDraft();
|
||||
}
|
||||
|
||||
let customerInvoice_series = CustomerInvoiceSeries.create(dto.customerInvoice_series).object;
|
||||
if (customerInvoice_series.isEmpty()) {
|
||||
customerInvoice_series = CustomerInvoiceSeries.create(dto.customerInvoice_series).object;
|
||||
}
|
||||
|
||||
let issue_date = CustomerInvoiceDate.create(dto.issue_date).object;
|
||||
if (issue_date.isEmpty()) {
|
||||
issue_date = CustomerInvoiceDate.createCurrentDate().object;
|
||||
}
|
||||
|
||||
let operation_date = CustomerInvoiceDate.create(dto.operation_date).object;
|
||||
if (operation_date.isEmpty()) {
|
||||
operation_date = CustomerInvoiceDate.createCurrentDate().object;
|
||||
}
|
||||
|
||||
let customerInvoiceCurrency = Currency.createFromCode(dto.currency).object;
|
||||
|
||||
if (customerInvoiceCurrency.isEmpty()) {
|
||||
customerInvoiceCurrency = Currency.createDefaultCode().object;
|
||||
}
|
||||
|
||||
let customerInvoiceLanguage = Language.createFromCode(dto.language_code).object;
|
||||
|
||||
if (customerInvoiceLanguage.isEmpty()) {
|
||||
customerInvoiceLanguage = Language.createDefaultCode().object;
|
||||
}
|
||||
|
||||
const items = new Collection<CustomerInvoiceItem>(
|
||||
dto.items?.map(
|
||||
(item) =>
|
||||
CustomerInvoiceSimpleItem.create({
|
||||
description: Description.create(item.description).object,
|
||||
quantity: Quantity.create(item.quantity).object,
|
||||
unitPrice: UnitPrice.create({
|
||||
amount: item.unit_price.amount,
|
||||
currencyCode: item.unit_price.currency,
|
||||
precision: item.unit_price.precision,
|
||||
}).object,
|
||||
}).object
|
||||
)
|
||||
);
|
||||
|
||||
if (!customerInvoice_status.isDraft()) {
|
||||
throw Error("Error al crear una factura que no es borrador");
|
||||
}
|
||||
|
||||
return DraftCustomerInvoice.create(
|
||||
{
|
||||
customerInvoiceSeries: customerInvoice_series,
|
||||
issueDate: issue_date,
|
||||
operationDate: operation_date,
|
||||
customerInvoiceCurrency,
|
||||
language: customerInvoiceLanguage,
|
||||
customerInvoiceNumber: CustomerInvoiceNumber.create(undefined).object,
|
||||
//notes: Note.create(customerInvoiceDTO.notes).object,
|
||||
|
||||
//senderId: UniqueID.create(null).object,
|
||||
recipient,
|
||||
|
||||
items,
|
||||
},
|
||||
customerInvoiceId
|
||||
);*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1,2 @@
|
||||
export * from "./create-customer-invoice.use-case";
|
||||
export * from "./presenter";
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
import { CustomerInvoice } from "@erp/customer-invoices/api/domain";
|
||||
import { CustomerInvoicesCreationResultDTO } from "@erp/customer-invoices/common/dto";
|
||||
|
||||
export class CreateCustomerInvoicesPresenter {
|
||||
public toDTO(invoice: CustomerInvoice): CustomerInvoicesCreationResultDTO {
|
||||
return {
|
||||
id: invoice.id.toPrimitive(),
|
||||
|
||||
invoice_status: invoice.status.toString(),
|
||||
invoice_number: invoice.invoiceNumber.toString(),
|
||||
invoice_series: invoice.invoiceSeries.toString(),
|
||||
issue_date: invoice.issueDate.toISOString(),
|
||||
operation_date: invoice.operationDate.toISOString(),
|
||||
language_code: "ES",
|
||||
currency: "EUR",
|
||||
|
||||
//subtotal_price: invoice.calculateSubtotal().toPrimitive(),
|
||||
//total_price: invoice.calculateTotal().toPrimitive(),
|
||||
|
||||
//recipient: CustomerInvoiceParticipantPresenter(customerInvoice.recipient),
|
||||
|
||||
metadata: {
|
||||
entity: "customer-invoice",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
export * from "./create-customer-invoices.presenter";
|
||||
@ -1,54 +0,0 @@
|
||||
import { ValidationErrorCollection, ValidationErrorDetail } from "@erp/core/api";
|
||||
import { UniqueID, UtcDate } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import { CreateCustomerInvoiceCommandDTO } from "../../../common/dto";
|
||||
import { CustomerInvoiceNumber, CustomerInvoiceProps, CustomerInvoiceSerie } from "../../domain";
|
||||
import { buildInvoiceItemsFromDTO } from "./build-customer-invoice-items-from-dto";
|
||||
import { extractOrPushError } from "./extract-or-push-error";
|
||||
|
||||
export async function buildInvoiceFromDTO(
|
||||
dto: CreateCustomerInvoiceCommandDTO
|
||||
): Promise<Result<CustomerInvoiceProps, Error>> {
|
||||
const errors: ValidationErrorDetail[] = [];
|
||||
|
||||
const invoiceNumber = extractOrPushError(
|
||||
CustomerInvoiceNumber.create(dto.invoice_number),
|
||||
"invoice_number",
|
||||
errors
|
||||
);
|
||||
const invoiceSeries = extractOrPushError(
|
||||
CustomerInvoiceSerie.create(dto.invoice_series),
|
||||
"invoice_series",
|
||||
errors
|
||||
);
|
||||
const issueDate = extractOrPushError(UtcDate.createFromISO(dto.issue_date), "issue_date", errors);
|
||||
const operationDate = extractOrPushError(
|
||||
UtcDate.createFromISO(dto.operation_date),
|
||||
"operation_date",
|
||||
errors
|
||||
);
|
||||
|
||||
// 🔄 Validar y construir los items de factura con helper especializado
|
||||
const itemsResult = await buildInvoiceItemsFromDTO(dto.items);
|
||||
if (itemsResult.isFailure) {
|
||||
return Result.fail(itemsResult.error);
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
return Result.fail(new ValidationErrorCollection(errors));
|
||||
}
|
||||
|
||||
return Result.ok({
|
||||
id: UniqueID.create(),
|
||||
customerId: customerId.data,
|
||||
invoiceNumber: invoiceNumber.data,
|
||||
invoiceSeries: invoiceSeries.data,
|
||||
issueDate: issueDate.data,
|
||||
operationDate: operationDate.data,
|
||||
subtotalPrice: subtotalPrice.data,
|
||||
discount: discount.data,
|
||||
tax: tax.data,
|
||||
totalAmount: totalAmount.data,
|
||||
lines: itemsResult.data,
|
||||
});
|
||||
}
|
||||
@ -24,3 +24,27 @@ export function hasNoUndefinedFields<T extends Record<string, any>>(
|
||||
): obj is { [K in keyof T]-?: Exclude<T[K], undefined> } {
|
||||
return Object.values(obj).every((value) => value !== undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @description Verifica si un objeto tiene campos con valor undefined.
|
||||
* Esta función es el complemento de `hasNoUndefinedFields`.
|
||||
*
|
||||
* @example
|
||||
* const obj = { a: 1, b: 'test', c: null };
|
||||
* console.log(hasUndefinedFields(obj)); // false
|
||||
*
|
||||
* const objWithUndefined = { a: 1, b: undefined, c: null };
|
||||
* console.log(hasUndefinedFields(objWithUndefined)); // true
|
||||
*
|
||||
* @template T - El tipo del objeto.
|
||||
* @param obj - El objeto a evaluar.
|
||||
* @returns true si el objeto tiene al menos un campo undefined, false en caso contrario.
|
||||
*
|
||||
*/
|
||||
|
||||
export function hasUndefinedFields<T extends Record<string, any>>(
|
||||
obj: T
|
||||
): obj is { [K in keyof T]-?: Exclude<T[K], undefined> } {
|
||||
return !hasNoUndefinedFields(obj);
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "./build-customer-invoice-from-dto";
|
||||
export * from "./map-dto-to-customer-invoice-props";
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
import { extractOrPushError } from "./extract-or-push-error";
|
||||
import { hasNoUndefinedFields } from "./has-no-undefined-fields";
|
||||
|
||||
export function buildInvoiceItemsFromDTO(
|
||||
export function mapDTOToCustomerInvoiceItemsProps(
|
||||
dtoItems: Pick<CreateCustomerInvoiceCommandDTO, "items">["items"]
|
||||
): Result<CustomerInvoiceItem[], ValidationErrorCollection> {
|
||||
const errors: ValidationErrorDetail[] = [];
|
||||
@ -0,0 +1,84 @@
|
||||
import { ValidationErrorCollection, ValidationErrorDetail } from "@erp/core/api";
|
||||
import { UtcDate } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import { CreateCustomerInvoiceCommandDTO } from "../../../common/dto";
|
||||
import {
|
||||
CustomerInvoiceNumber,
|
||||
CustomerInvoiceProps,
|
||||
CustomerInvoiceSerie,
|
||||
CustomerInvoiceStatus,
|
||||
} from "../../domain";
|
||||
import { extractOrPushError } from "./extract-or-push-error";
|
||||
import { mapDTOToCustomerInvoiceItemsProps } from "./map-dto-to-customer-invoice-items-props";
|
||||
|
||||
/**
|
||||
* Convierte el DTO a las props validadas (CustomerInvoiceProps).
|
||||
* No construye directamente el agregado.
|
||||
*
|
||||
* @param dto - DTO con los datos de la factura de cliente
|
||||
* @returns CustomerInvoiceProps - Las propiedades para crear una factura de cliente o error
|
||||
*
|
||||
*/
|
||||
|
||||
export function mapDTOToCustomerInvoiceProps(
|
||||
dto: CreateCustomerInvoiceCommandDTO
|
||||
): Result<CustomerInvoiceProps, Error> {
|
||||
const errors: ValidationErrorDetail[] = [];
|
||||
|
||||
//const invoiceId = extractOrPushError(UniqueID.create(dto.id), "invoice_id", errors);
|
||||
|
||||
const invoiceNumber = extractOrPushError(
|
||||
CustomerInvoiceNumber.create(dto.invoice_number),
|
||||
"invoice_number",
|
||||
errors
|
||||
);
|
||||
const invoiceSeries = extractOrPushError(
|
||||
CustomerInvoiceSerie.create(dto.invoice_series),
|
||||
"invoice_series",
|
||||
errors
|
||||
);
|
||||
const issueDate = extractOrPushError(UtcDate.createFromISO(dto.issue_date), "issue_date", errors);
|
||||
const operationDate = extractOrPushError(
|
||||
UtcDate.createFromISO(dto.operation_date),
|
||||
"operation_date",
|
||||
errors
|
||||
);
|
||||
|
||||
//const currency = extractOrPushError(Currency.(dto.currency), "currency", errors);
|
||||
const currency = dto.currency;
|
||||
|
||||
// 🔄 Validar y construir los items de factura con helper especializado
|
||||
const itemsResult = mapDTOToCustomerInvoiceItemsProps(dto.items);
|
||||
if (itemsResult.isFailure) {
|
||||
return Result.fail(itemsResult.error);
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
return Result.fail(new ValidationErrorCollection(errors));
|
||||
}
|
||||
|
||||
const invoiceProps: CustomerInvoiceProps = {
|
||||
invoiceNumber: invoiceNumber!,
|
||||
invoiceSeries: invoiceSeries!,
|
||||
issueDate: issueDate!,
|
||||
operationDate: operationDate!,
|
||||
status: CustomerInvoiceStatus.createDraft(),
|
||||
currency,
|
||||
};
|
||||
|
||||
return Result.ok(invoiceProps);
|
||||
|
||||
/*if (hasNoUndefinedFields(invoiceProps)) {
|
||||
const invoiceOrError = CustomerInvoice.create(invoiceProps, invoiceId);
|
||||
if (invoiceOrError.isFailure) {
|
||||
return Result.fail(invoiceOrError.error);
|
||||
}
|
||||
return Result.ok(invoiceOrError.data);
|
||||
}
|
||||
|
||||
return Result.fail(
|
||||
new ValidationErrorCollection([
|
||||
{ path: "", message: "Error building from DTO: Some fields are undefined" },
|
||||
])
|
||||
);*/
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
//export * from "./create-customer-invoice";
|
||||
export * from "./create-customer-invoice";
|
||||
//export * from "./delete-customer-invoice";
|
||||
//export * from "./get-customer-invoice";
|
||||
export * from "./list-customer-invoices";
|
||||
|
||||
@ -20,8 +20,8 @@ export const listCustomerInvoicesPresenter: ListCustomerInvoicesPresenter = {
|
||||
id: invoice.id.toPrimitive(),
|
||||
|
||||
invoice_status: invoice.status.toString(),
|
||||
invoice_number: invoice.customerInvoiceNumber.toString(),
|
||||
invoice_series: invoice.customerInvoiceSeries.toString(),
|
||||
invoice_number: invoice.invoiceNumber.toString(),
|
||||
invoice_series: invoice.invoiceSeries.toString(),
|
||||
issue_date: invoice.issueDate.toISOString(),
|
||||
operation_date: invoice.operationDate.toISOString(),
|
||||
language_code: "ES",
|
||||
|
||||
@ -3,8 +3,7 @@ import { CreateCustomerInvoiceCommandDTO } from "../../../common/dto";
|
||||
|
||||
export class CreateCustomerInvoiceController extends ExpressController {
|
||||
public constructor(
|
||||
private readonly createCustomerInvoice: any, // Replace with actual type
|
||||
private readonly presenter: any // Replace with actual type
|
||||
private readonly createCustomerInvoice: any // Replace with actual type
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@ -26,14 +25,10 @@ export class CreateCustomerInvoiceController extends ExpressController {
|
||||
const result = await this.createCustomerInvoice.execute(dto);
|
||||
|
||||
if (result.isFailure) {
|
||||
/*if (error instanceof AggregatedValidationError) {
|
||||
return this.invalidInputError(error.message, error.details);
|
||||
}*/
|
||||
|
||||
const apiError = errorMapper.toApiError(result.error);
|
||||
return this.handleApiError(apiError);
|
||||
}
|
||||
|
||||
return this.created(this.presenter.toDTO(result.data));
|
||||
return this.created(result.data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1,21 @@
|
||||
export * from "./create-customer-invoice";
|
||||
import { SequelizeTransactionManager } from "@erp/core/api";
|
||||
import { Sequelize } from "sequelize";
|
||||
import { CreateCustomerInvoiceUseCase, CreateCustomerInvoicesPresenter } from "../../application/";
|
||||
import { CustomerInvoiceService } from "../../domain";
|
||||
import { CustomerInvoiceRepository, customerInvoiceMapper } from "../../infrastructure";
|
||||
import { CreateCustomerInvoiceController } from "./create-customer-invoice";
|
||||
|
||||
export const buildCreateCustomerInvoicesController = (database: Sequelize) => {
|
||||
const transactionManager = new SequelizeTransactionManager(database);
|
||||
const customerInvoiceRepository = new CustomerInvoiceRepository(database, customerInvoiceMapper);
|
||||
const customerInvoiceService = new CustomerInvoiceService(customerInvoiceRepository);
|
||||
const presenter = new CreateCustomerInvoicesPresenter();
|
||||
|
||||
const useCase = new CreateCustomerInvoiceUseCase(
|
||||
customerInvoiceService,
|
||||
transactionManager,
|
||||
presenter
|
||||
);
|
||||
|
||||
return new CreateCustomerInvoiceController(useCase);
|
||||
};
|
||||
|
||||
@ -10,8 +10,8 @@ export const getCustomerInvoicePresenter: IGetCustomerInvoicePresenter = {
|
||||
id: customerInvoice.id.toPrimitive(),
|
||||
|
||||
customerInvoice_status: customerInvoice.status.toString(),
|
||||
customerInvoice_number: customerInvoice.customerInvoiceNumber.toString(),
|
||||
customerInvoice_series: customerInvoice.customerInvoiceSeries.toString(),
|
||||
customerInvoice_number: customerInvoice.invoiceNumber.toString(),
|
||||
customerInvoice_series: customerInvoice.invoiceSeries.toString(),
|
||||
issue_date: customerInvoice.issueDate.toDateString(),
|
||||
operation_date: customerInvoice.operationDate.toDateString(),
|
||||
language_code: "ES",
|
||||
|
||||
@ -24,10 +24,6 @@ export class ListCustomerInvoicesController extends ExpressController {
|
||||
const result = await this.listCustomerInvoices.execute(criteria);
|
||||
|
||||
if (result.isFailure) {
|
||||
/*if (error instanceof AggregatedValidationError) {
|
||||
return this.invalidInputError(error.message, error.details);
|
||||
}*/
|
||||
|
||||
const apiError = errorMapper.toApiError(result.error);
|
||||
return this.handleApiError(apiError);
|
||||
}
|
||||
|
||||
@ -8,8 +8,8 @@ import {
|
||||
} from "../value-objects";
|
||||
|
||||
export interface CustomerInvoiceProps {
|
||||
customerInvoiceNumber: CustomerInvoiceNumber;
|
||||
customerInvoiceSeries: CustomerInvoiceSerie;
|
||||
invoiceNumber: CustomerInvoiceNumber;
|
||||
invoiceSeries: CustomerInvoiceSerie;
|
||||
|
||||
status: CustomerInvoiceStatus;
|
||||
|
||||
@ -19,7 +19,7 @@ export interface CustomerInvoiceProps {
|
||||
//dueDate: UtcDate; // ? --> depende de la forma de pago
|
||||
|
||||
//tax: Tax; // ? --> detalles?
|
||||
customerInvoiceCurrency: string;
|
||||
currency: string;
|
||||
|
||||
//language: Language;
|
||||
|
||||
@ -37,8 +37,8 @@ export interface CustomerInvoiceProps {
|
||||
|
||||
export interface ICustomerInvoice {
|
||||
id: UniqueID;
|
||||
customerInvoiceNumber: CustomerInvoiceNumber;
|
||||
customerInvoiceSeries: CustomerInvoiceSerie;
|
||||
invoiceNumber: CustomerInvoiceNumber;
|
||||
invoiceSeries: CustomerInvoiceSerie;
|
||||
|
||||
status: CustomerInvoiceStatus;
|
||||
|
||||
@ -53,7 +53,7 @@ export interface ICustomerInvoice {
|
||||
|
||||
//tax: Tax;
|
||||
//language: Language;
|
||||
customerInvoiceCurrency: string;
|
||||
currency: string;
|
||||
|
||||
//purchareOrderNumber: string;
|
||||
//notes: Note;
|
||||
@ -77,7 +77,6 @@ export class CustomerInvoice
|
||||
|
||||
protected constructor(props: CustomerInvoiceProps, id?: UniqueID) {
|
||||
super(props, id);
|
||||
|
||||
this._items = props.items || CustomerInvoiceItems.create();
|
||||
}
|
||||
|
||||
@ -95,12 +94,12 @@ export class CustomerInvoice
|
||||
return Result.ok(customerInvoice);
|
||||
}
|
||||
|
||||
get customerInvoiceNumber() {
|
||||
return this.props.customerInvoiceNumber;
|
||||
get invoiceNumber() {
|
||||
return this.props.invoiceNumber;
|
||||
}
|
||||
|
||||
get customerInvoiceSeries() {
|
||||
return this.props.customerInvoiceSeries;
|
||||
get invoiceSeries() {
|
||||
return this.props.invoiceSeries;
|
||||
}
|
||||
|
||||
get issueDate() {
|
||||
@ -159,8 +158,8 @@ export class CustomerInvoice
|
||||
return this.props.shipTo;
|
||||
}*/
|
||||
|
||||
get customerInvoiceCurrency() {
|
||||
return this.props.customerInvoiceCurrency;
|
||||
get currency() {
|
||||
return this.props.currency;
|
||||
}
|
||||
|
||||
/*get notes() {
|
||||
@ -183,7 +182,7 @@ export class CustomerInvoice
|
||||
calculateSubtotal(): MoneyValue {
|
||||
const customerInvoiceSubtotal = MoneyValue.create({
|
||||
amount: 0,
|
||||
currency_code: this.props.customerInvoiceCurrency,
|
||||
currency_code: this.props.currency,
|
||||
scale: 2,
|
||||
}).data;
|
||||
|
||||
@ -196,7 +195,7 @@ export class CustomerInvoice
|
||||
calculateTaxTotal(): MoneyValue {
|
||||
const taxTotal = MoneyValue.create({
|
||||
amount: 0,
|
||||
currency_code: this.props.customerInvoiceCurrency,
|
||||
currency_code: this.props.currency,
|
||||
scale: 2,
|
||||
}).data;
|
||||
|
||||
|
||||
@ -4,13 +4,46 @@ import { Collection, Result } from "@repo/rdx-utils";
|
||||
import { CustomerInvoice } from "../aggregates";
|
||||
|
||||
export interface ICustomerInvoiceRepository {
|
||||
findAll(
|
||||
criteria: Criteria,
|
||||
transaction?: any
|
||||
): Promise<Result<Collection<CustomerInvoice>, Error>>;
|
||||
getById(id: UniqueID, transaction?: any): Promise<Result<CustomerInvoice, Error>>;
|
||||
deleteById(id: UniqueID, transaction?: any): Promise<Result<boolean, Error>>;
|
||||
/**
|
||||
*
|
||||
* Persiste una nueva factura o actualiza una existente.
|
||||
*
|
||||
* @param invoice - El agregado a guardar.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<CustomerInvoice, Error>
|
||||
*/
|
||||
save(invoice: CustomerInvoice, transaction: any): Promise<Result<CustomerInvoice, Error>>;
|
||||
|
||||
create(customerInvoice: CustomerInvoice, transaction?: any): Promise<void>;
|
||||
update(customerInvoice: CustomerInvoice, transaction?: any): Promise<void>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Busca una factura por su identificador único.
|
||||
* @param id - UUID de la factura.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<CustomerInvoice, Error>
|
||||
*/
|
||||
findById(id: UniqueID, transaction: any): Promise<Result<CustomerInvoice, Error>>;
|
||||
|
||||
/**
|
||||
*
|
||||
* Consulta facturas usando un objeto Criteria (filtros, orden, paginación).
|
||||
* @param criteria - Criterios de búsqueda.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<CustomerInvoice[], Error>
|
||||
*
|
||||
* @see Criteria
|
||||
*/
|
||||
findByCriteria(
|
||||
criteria: Criteria,
|
||||
transaction: any
|
||||
): Promise<Result<Collection<CustomerInvoice>, Error>>;
|
||||
|
||||
/**
|
||||
*
|
||||
* Elimina o marca como eliminada una factura.
|
||||
* @param id - UUID de la factura a eliminar.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<void, Error>
|
||||
*/
|
||||
deleteById(id: UniqueID, transaction: any): Promise<Result<void, Error>>;
|
||||
|
||||
|
||||
@ -4,29 +4,28 @@ import { Collection, Result } from "@repo/rdx-utils";
|
||||
import { CustomerInvoice, CustomerInvoiceProps } from "../aggregates";
|
||||
|
||||
export interface ICustomerInvoiceService {
|
||||
findCustomerInvoices(
|
||||
build(props: CustomerInvoiceProps): Result<CustomerInvoice, Error>;
|
||||
|
||||
save(invoice: CustomerInvoice, transaction: any): Promise<Result<CustomerInvoice, Error>>;
|
||||
|
||||
findByCriteria(
|
||||
criteria: Criteria,
|
||||
transaction?: any
|
||||
): Promise<Result<Collection<CustomerInvoice>, Error>>;
|
||||
findCustomerInvoiceById(
|
||||
customerInvoiceId: UniqueID,
|
||||
transaction?: any
|
||||
): Promise<Result<CustomerInvoice>>;
|
||||
|
||||
updateCustomerInvoiceById(
|
||||
customerInvoiceId: UniqueID,
|
||||
getById(id: UniqueID, transaction?: any): Promise<Result<CustomerInvoice>>;
|
||||
|
||||
updateById(
|
||||
id: UniqueID,
|
||||
data: Partial<CustomerInvoiceProps>,
|
||||
transaction?: any
|
||||
): Promise<Result<CustomerInvoice, Error>>;
|
||||
|
||||
createCustomerInvoice(
|
||||
customerInvoiceId: UniqueID,
|
||||
id: UniqueID,
|
||||
data: CustomerInvoiceProps,
|
||||
transaction?: any
|
||||
): Promise<Result<CustomerInvoice, Error>>;
|
||||
|
||||
deleteCustomerInvoiceById(
|
||||
customerInvoiceId: UniqueID,
|
||||
transaction?: any
|
||||
): Promise<Result<boolean, Error>>;
|
||||
deleteById(id: UniqueID, transaction?: any): Promise<Result<boolean, Error>>;
|
||||
}
|
||||
|
||||
@ -7,13 +7,42 @@ import { ICustomerInvoiceRepository } from "../repositories";
|
||||
import { ICustomerInvoiceService } from "./customer-invoice-service.interface";
|
||||
|
||||
export class CustomerInvoiceService implements ICustomerInvoiceService {
|
||||
constructor(private readonly repo: ICustomerInvoiceRepository) {}
|
||||
constructor(private readonly repository: ICustomerInvoiceRepository) {}
|
||||
|
||||
async findCustomerInvoices(
|
||||
/**
|
||||
* Construye un nuevo agregado CustomerInvoice a partir de props validadas.
|
||||
*
|
||||
* @param props - Las propiedades ya validadas para crear la factura.
|
||||
* @returns Result<CustomerInvoice, Error> - El agregado construido o un error si falla la creación.
|
||||
*/
|
||||
build(props: CustomerInvoiceProps): Result<CustomerInvoice, Error> {
|
||||
return CustomerInvoice.create(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guarda una instancia de CustomerInvoice en persistencia.
|
||||
*
|
||||
* @param invoice - El agregado a guardar.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<CustomerInvoice, Error> - El agregado guardado o un error si falla la operación.
|
||||
*/
|
||||
async save(invoice: CustomerInvoice, transaction: any): Promise<Result<CustomerInvoice, Error>> {
|
||||
const saved = await this.repository.save(invoice, transaction);
|
||||
return saved.isSuccess ? Result.ok(invoice) : Result.fail(saved.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene una colección de facturas que cumplen con los filtros definidos en un objeto Criteria.
|
||||
*
|
||||
* @param criteria - Objeto con condiciones de filtro, paginación y orden.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<Collection<CustomerInvoice>, Error> - Colección de facturas o error.
|
||||
*/
|
||||
async findByCriteria(
|
||||
criteria: Criteria,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<Collection<CustomerInvoice>, Error>> {
|
||||
const customerInvoicesOrError = await this.repo.findAll(criteria, transaction);
|
||||
const customerInvoicesOrError = await this.repository.findAll(criteria, transaction);
|
||||
if (customerInvoicesOrError.isFailure) {
|
||||
return Result.fail(customerInvoicesOrError.error);
|
||||
}
|
||||
@ -25,20 +54,32 @@ export class CustomerInvoiceService implements ICustomerInvoiceService {
|
||||
return customerInvoicesOrError;
|
||||
}
|
||||
|
||||
async findCustomerInvoiceById(
|
||||
customerInvoiceId: UniqueID,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<CustomerInvoice>> {
|
||||
return await this.repo.getById(customerInvoiceId, transaction);
|
||||
/**
|
||||
* Recupera una factura por su identificador único.
|
||||
*
|
||||
* @param id - Identificador UUID de la factura.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<CustomerInvoice, Error> - Factura encontrada o error.
|
||||
*/
|
||||
async getById(id: UniqueID, transaction?: Transaction): Promise<Result<CustomerInvoice>> {
|
||||
return await this.repository.getById(id, transaction);
|
||||
}
|
||||
|
||||
async updateCustomerInvoiceById(
|
||||
/**
|
||||
* Actualiza parcialmente una factura existente con nuevos datos.
|
||||
*
|
||||
* @param id - Identificador de la factura a actualizar.
|
||||
* @param changes - Subconjunto de props válidas para aplicar.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<CustomerInvoice, Error> - Factura actualizada o error.
|
||||
*/
|
||||
async updateById(
|
||||
customerInvoiceId: UniqueID,
|
||||
data: Partial<CustomerInvoiceProps>,
|
||||
changes: Partial<CustomerInvoiceProps>,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<CustomerInvoice, Error>> {
|
||||
// Verificar si la factura existe
|
||||
const customerInvoiceOrError = await this.repo.getById(customerInvoiceId, transaction);
|
||||
const customerInvoiceOrError = await this.repository.getById(customerInvoiceId, transaction);
|
||||
if (customerInvoiceOrError.isFailure) {
|
||||
return Result.fail(new Error("CustomerInvoice not found"));
|
||||
}
|
||||
@ -64,7 +105,7 @@ export class CustomerInvoiceService implements ICustomerInvoiceService {
|
||||
transaction?: Transaction
|
||||
): Promise<Result<CustomerInvoice, Error>> {
|
||||
// Verificar si la factura existe
|
||||
const customerInvoiceOrError = await this.repo.getById(customerInvoiceId, transaction);
|
||||
const customerInvoiceOrError = await this.repository.getById(customerInvoiceId, transaction);
|
||||
if (customerInvoiceOrError.isSuccess) {
|
||||
return Result.fail(new Error("CustomerInvoice exists"));
|
||||
}
|
||||
@ -78,14 +119,18 @@ export class CustomerInvoiceService implements ICustomerInvoiceService {
|
||||
|
||||
const newCustomerInvoice = newCustomerInvoiceOrError.data;
|
||||
|
||||
await this.repo.create(newCustomerInvoice, transaction);
|
||||
await this.repository.create(newCustomerInvoice, transaction);
|
||||
return Result.ok(newCustomerInvoice);
|
||||
}
|
||||
|
||||
async deleteCustomerInvoiceById(
|
||||
customerInvoiceId: UniqueID,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<boolean, Error>> {
|
||||
return this.repo.deleteById(customerInvoiceId, transaction);
|
||||
/**
|
||||
* Elimina (o marca como eliminada) una factura según su ID.
|
||||
*
|
||||
* @param id - Identificador UUID de la factura.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<boolean, Error> - Resultado de la operación.
|
||||
*/
|
||||
async deleteById(id: UniqueID, transaction?: Transaction): Promise<Result<boolean, Error>> {
|
||||
return this.repository.deleteById(id, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,8 +90,8 @@ export class CustomerInvoiceMapper
|
||||
return {
|
||||
id: source.id.toString(),
|
||||
invoice_status: source.status.toPrimitive(),
|
||||
invoice_series: source.customerInvoiceSeries.toPrimitive(),
|
||||
invoice_number: source.customerInvoiceNumber.toPrimitive(),
|
||||
invoice_series: source.invoiceSeries.toPrimitive(),
|
||||
invoice_number: source.invoiceNumber.toPrimitive(),
|
||||
issue_date: source.issueDate.toPrimitive(),
|
||||
operation_date: source.operationDate.toPrimitive(),
|
||||
invoice_language: "es",
|
||||
|
||||
@ -1,123 +1,109 @@
|
||||
import { SequelizeRepository } from "@erp/core/api";
|
||||
import { Criteria } from "@repo/rdx-criteria/server";
|
||||
import { SequelizeRepository, errorMapper } from "@erp/core/api";
|
||||
import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server";
|
||||
import { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Collection, Result } from "@repo/rdx-utils";
|
||||
import { Sequelize, Transaction } from "sequelize";
|
||||
import { CustomerInvoice, ICustomerInvoiceRepository } from "../../domain";
|
||||
import { ICustomerInvoiceMapper } from "../mappers/customer-invoice.mapper";
|
||||
import { CustomerInvoiceItemModel } from "./customer-invoice-item.model";
|
||||
import { CustomerInvoiceModel } from "./customer-invoice.model";
|
||||
|
||||
export class CustomerInvoiceRepository
|
||||
extends SequelizeRepository<CustomerInvoice>
|
||||
implements ICustomerInvoiceRepository
|
||||
{
|
||||
private readonly _mapper!: ICustomerInvoiceMapper;
|
||||
|
||||
/**
|
||||
* 🔹 Función personalizada para mapear errores de unicidad en autenticación
|
||||
*/
|
||||
private _customErrorMapper(error: Error): string | null {
|
||||
if (error.name === "SequelizeUniqueConstraintError") {
|
||||
return "CustomerInvoice with this email already exists";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private readonly model: typeof CustomerInvoiceModel;
|
||||
private readonly mapper!: ICustomerInvoiceMapper;
|
||||
|
||||
constructor(database: Sequelize, mapper: ICustomerInvoiceMapper) {
|
||||
super(database);
|
||||
this._mapper = mapper;
|
||||
|
||||
this.model = database.model("CustomerInvoice") as typeof CustomerInvoiceModel;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
async customerInvoiceExists(
|
||||
id: UniqueID,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<boolean, Error>> {
|
||||
/**
|
||||
*
|
||||
* Persiste una nueva factura o actualiza una existente.
|
||||
*
|
||||
* @param invoice - El agregado a guardar.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<CustomerInvoice, Error>
|
||||
*/
|
||||
async save(
|
||||
invoice: CustomerInvoice,
|
||||
transaction: Transaction
|
||||
): Promise<Result<CustomerInvoice, Error>> {
|
||||
try {
|
||||
const _customerInvoice = await this._getById(CustomerInvoiceModel, id, {}, transaction);
|
||||
|
||||
return Result.ok(Boolean(id.equals(_customerInvoice.id)));
|
||||
} catch (error: any) {
|
||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
||||
const data = this.mapper.mapToPersistence(invoice);
|
||||
await this.model.upsert(data, { transaction });
|
||||
return Result.ok(invoice);
|
||||
} catch (err: unknown) {
|
||||
return Result.fail(errorMapper.toDomainError(err));
|
||||
}
|
||||
}
|
||||
|
||||
async findAll(
|
||||
/**
|
||||
*
|
||||
* Busca una factura por su identificador único.
|
||||
* @param id - UUID de la factura.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<CustomerInvoice, Error>
|
||||
*/
|
||||
async findById(id: UniqueID, transaction: Transaction): Promise<Result<CustomerInvoice, Error>> {
|
||||
try {
|
||||
const rawData = await this._findById(this.model, id.toString(), { transaction });
|
||||
|
||||
if (!rawData) {
|
||||
return Result.fail(new Error(`Invoice with id ${id} not found.`));
|
||||
}
|
||||
|
||||
return this.mapper.mapToDomain(rawData);
|
||||
} catch (err: unknown) {
|
||||
return Result.fail(errorMapper.toDomainError(err));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Consulta facturas usando un objeto Criteria (filtros, orden, paginación).
|
||||
* @param criteria - Criterios de búsqueda.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<CustomerInvoice[], Error>
|
||||
*
|
||||
* @see Criteria
|
||||
*/
|
||||
public async findByCriteria(
|
||||
criteria: Criteria,
|
||||
transaction?: Transaction
|
||||
transaction: Transaction
|
||||
): Promise<Result<Collection<CustomerInvoice>, Error>> {
|
||||
try {
|
||||
const rawCustomerInvoices = await CustomerInvoiceModel.findAll({
|
||||
include: [
|
||||
{
|
||||
model: CustomerInvoiceItemModel,
|
||||
as: "items",
|
||||
},
|
||||
],
|
||||
const converter = new CriteriaToSequelizeConverter();
|
||||
const query = converter.convert(criteria);
|
||||
|
||||
const instances = await this.model.findAll({
|
||||
...query,
|
||||
transaction,
|
||||
...this.convertCriteria(criteria),
|
||||
});
|
||||
|
||||
return this._mapper.mapArrayToDomain(rawCustomerInvoices);
|
||||
} catch (error: unknown) {
|
||||
console.error("Error in findAll", error);
|
||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
||||
return this.mapper.mapArrayToDomain(instances);
|
||||
} catch (err: unknown) {
|
||||
return Result.fail(errorMapper.toDomainError(err));
|
||||
}
|
||||
}
|
||||
|
||||
async getById(id: UniqueID, transaction?: Transaction): Promise<Result<CustomerInvoice, Error>> {
|
||||
/**
|
||||
*
|
||||
* Elimina o marca como eliminada una factura.
|
||||
* @param id - UUID de la factura a eliminar.
|
||||
* @param transaction - Transacción activa para la operación.
|
||||
* @returns Result<void, Error>
|
||||
*/
|
||||
async deleteById(id: UniqueID, transaction: any): Promise<Result<void, Error>> {
|
||||
try {
|
||||
const rawCustomerInvoice: any = await this._getById(
|
||||
CustomerInvoiceModel,
|
||||
id,
|
||||
{
|
||||
include: [
|
||||
{
|
||||
model: CustomerInvoiceItemModel,
|
||||
as: "items",
|
||||
},
|
||||
],
|
||||
},
|
||||
transaction
|
||||
);
|
||||
|
||||
if (!rawCustomerInvoice === true) {
|
||||
return Result.fail(new Error(`CustomerInvoice with id ${id.toString()} not exists`));
|
||||
}
|
||||
} catch (error: any) {
|
||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
||||
await this._deleteById(this.model, id, false, transaction);
|
||||
return Result.ok<void>();
|
||||
} catch (err: unknown) {
|
||||
return Result.fail(errorMapper.toDomainError(err));
|
||||
}
|
||||
}
|
||||
|
||||
async deleteById(id: UniqueID, transaction?: Transaction): Promise<Result<boolean, Error>> {
|
||||
try {
|
||||
this._deleteById(CustomerInvoiceModel, id, false, transaction);
|
||||
return Result.ok<boolean>(true);
|
||||
} catch (error: any) {
|
||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
||||
}
|
||||
}
|
||||
|
||||
async create(customerInvoice: CustomerInvoice, transaction?: Transaction): Promise<void> {
|
||||
const customerInvoiceData = this._mapper.mapToPersistence(customerInvoice);
|
||||
await this._save(
|
||||
CustomerInvoiceModel,
|
||||
customerInvoice.id,
|
||||
customerInvoiceData,
|
||||
{},
|
||||
transaction
|
||||
);
|
||||
}
|
||||
|
||||
async update(customerInvoice: CustomerInvoice, transaction?: Transaction): Promise<void> {
|
||||
const customerInvoiceData = this._mapper.mapToPersistence(customerInvoice);
|
||||
await this._save(
|
||||
CustomerInvoiceModel,
|
||||
customerInvoice.id,
|
||||
customerInvoiceData,
|
||||
{},
|
||||
transaction
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import * as z from "zod/v4";
|
||||
|
||||
export const CreateCustomerInvoiceCommandSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
status: z.string(),
|
||||
invoice_number: z.string().min(1, "Customer invoice number is required"),
|
||||
invoice_series: z.string().min(1, "Customer invoice series is required"),
|
||||
issue_date: z.string().datetime({ offset: true, message: "Invalid issue date format" }),
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
import { MetadataSchema } from "@erp/core";
|
||||
import * as z from "zod/v4";
|
||||
|
||||
export const CustomerInvoicesCreationResultSchema = z.object({
|
||||
id: z.uuid(),
|
||||
invoice_status: z.string(),
|
||||
invoice_number: z.string(),
|
||||
invoice_series: z.string(),
|
||||
issue_date: z.iso.datetime({ offset: true }),
|
||||
operation_date: z.iso.datetime({ offset: true }),
|
||||
language_code: z.string(),
|
||||
currency: z.string(),
|
||||
|
||||
metadata: MetadataSchema.optional(),
|
||||
});
|
||||
|
||||
export type CustomerInvoicesCreationResultDTO = z.infer<
|
||||
typeof CustomerInvoicesCreationResultSchema
|
||||
>;
|
||||
@ -1 +1,2 @@
|
||||
export * from "./customer-invoice-creation.result.dto";
|
||||
export * from "./list-customer-invoices.result.dto";
|
||||
|
||||
@ -639,7 +639,7 @@ importers:
|
||||
version: 4.1.10
|
||||
tsup:
|
||||
specifier: ^8.4.0
|
||||
version: 8.5.0(jiti@2.4.2)(postcss@8.5.6)(tsx@4.19.4)(typescript@5.8.3)
|
||||
version: 8.4.0(jiti@2.4.2)(postcss@8.5.6)(tsx@4.19.4)(typescript@5.8.3)
|
||||
tw-animate-css:
|
||||
specifier: ^1.2.9
|
||||
version: 1.3.4
|
||||
@ -2855,11 +2855,6 @@ packages:
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
acorn@8.15.0:
|
||||
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
add@2.0.6:
|
||||
resolution: {integrity: sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q==}
|
||||
|
||||
@ -3269,9 +3264,6 @@ packages:
|
||||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
confbox@0.1.8:
|
||||
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
|
||||
|
||||
connect-history-api-fallback@1.6.0:
|
||||
resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==}
|
||||
engines: {node: '>=0.8'}
|
||||
@ -3765,9 +3757,6 @@ packages:
|
||||
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
fix-dts-default-cjs-exports@1.0.1:
|
||||
resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==}
|
||||
|
||||
fn.name@1.1.0:
|
||||
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
|
||||
|
||||
@ -4721,9 +4710,6 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
mlly@1.7.4:
|
||||
resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
|
||||
|
||||
module-alias@2.2.3:
|
||||
resolution: {integrity: sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==}
|
||||
|
||||
@ -5012,9 +4998,6 @@ packages:
|
||||
pathe@0.2.0:
|
||||
resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==}
|
||||
|
||||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
pause@0.0.1:
|
||||
resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==}
|
||||
|
||||
@ -5047,9 +5030,6 @@ packages:
|
||||
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
pkg-types@1.3.1:
|
||||
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
|
||||
|
||||
pnpm@10.12.2:
|
||||
resolution: {integrity: sha512-oyVAGFuWTuMLtOl55AWtxq9ZImtDjuTMGfnodzZnpm0wL1v+5go508rGnjXkuW5winHdACt+k1nEESoXIqwyPw==}
|
||||
engines: {node: '>=18.12'}
|
||||
@ -5899,25 +5879,6 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
tsup@8.5.0:
|
||||
resolution: {integrity: sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@microsoft/api-extractor': ^7.36.0
|
||||
'@swc/core': ^1
|
||||
postcss: ^8.4.12
|
||||
typescript: '>=4.5.0'
|
||||
peerDependenciesMeta:
|
||||
'@microsoft/api-extractor':
|
||||
optional: true
|
||||
'@swc/core':
|
||||
optional: true
|
||||
postcss:
|
||||
optional: true
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
tsutils@3.21.0:
|
||||
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
||||
engines: {node: '>= 6'}
|
||||
@ -5992,9 +5953,6 @@ packages:
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
ufo@1.6.1:
|
||||
resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
|
||||
|
||||
uglify-js@3.19.3:
|
||||
resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
@ -8412,8 +8370,6 @@ snapshots:
|
||||
|
||||
acorn@8.14.1: {}
|
||||
|
||||
acorn@8.15.0: {}
|
||||
|
||||
add@2.0.6: {}
|
||||
|
||||
ag-charts-types@11.3.2: {}
|
||||
@ -8864,8 +8820,6 @@ snapshots:
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
confbox@0.1.8: {}
|
||||
|
||||
connect-history-api-fallback@1.6.0: {}
|
||||
|
||||
consola@2.15.3: {}
|
||||
@ -9376,12 +9330,6 @@ snapshots:
|
||||
locate-path: 5.0.0
|
||||
path-exists: 4.0.0
|
||||
|
||||
fix-dts-default-cjs-exports@1.0.1:
|
||||
dependencies:
|
||||
magic-string: 0.30.17
|
||||
mlly: 1.7.4
|
||||
rollup: 4.41.1
|
||||
|
||||
fn.name@1.1.0: {}
|
||||
|
||||
follow-redirects@1.15.9: {}
|
||||
@ -10485,13 +10433,6 @@ snapshots:
|
||||
|
||||
mkdirp@3.0.1: {}
|
||||
|
||||
mlly@1.7.4:
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
pathe: 2.0.3
|
||||
pkg-types: 1.3.1
|
||||
ufo: 1.6.1
|
||||
|
||||
module-alias@2.2.3: {}
|
||||
|
||||
moment-timezone@0.5.48:
|
||||
@ -10792,8 +10733,6 @@ snapshots:
|
||||
|
||||
pathe@0.2.0: {}
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
pause@0.0.1: {}
|
||||
|
||||
pg-connection-string@2.9.0: {}
|
||||
@ -10815,12 +10754,6 @@ snapshots:
|
||||
dependencies:
|
||||
find-up: 4.1.0
|
||||
|
||||
pkg-types@1.3.1:
|
||||
dependencies:
|
||||
confbox: 0.1.8
|
||||
mlly: 1.7.4
|
||||
pathe: 2.0.3
|
||||
|
||||
pnpm@10.12.2: {}
|
||||
|
||||
postcss-load-config@3.1.4(postcss@8.5.6)(ts-node@10.9.2(@types/node@22.15.32)(typescript@5.8.3)):
|
||||
@ -11707,34 +11640,6 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
tsup@8.5.0(jiti@2.4.2)(postcss@8.5.6)(tsx@4.19.4)(typescript@5.8.3):
|
||||
dependencies:
|
||||
bundle-require: 5.1.0(esbuild@0.25.5)
|
||||
cac: 6.7.14
|
||||
chokidar: 4.0.3
|
||||
consola: 3.4.2
|
||||
debug: 4.4.1(supports-color@8.1.1)
|
||||
esbuild: 0.25.5
|
||||
fix-dts-default-cjs-exports: 1.0.1
|
||||
joycon: 3.1.1
|
||||
picocolors: 1.1.1
|
||||
postcss-load-config: 6.0.1(jiti@2.4.2)(postcss@8.5.6)(tsx@4.19.4)
|
||||
resolve-from: 5.0.0
|
||||
rollup: 4.41.1
|
||||
source-map: 0.8.0-beta.0
|
||||
sucrase: 3.35.0
|
||||
tinyexec: 0.3.2
|
||||
tinyglobby: 0.2.14
|
||||
tree-kill: 1.2.2
|
||||
optionalDependencies:
|
||||
postcss: 8.5.6
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
- jiti
|
||||
- supports-color
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
tsutils@3.21.0(typescript@5.8.3):
|
||||
dependencies:
|
||||
tslib: 1.14.1
|
||||
@ -11812,8 +11717,6 @@ snapshots:
|
||||
|
||||
typescript@5.8.3: {}
|
||||
|
||||
ufo@1.6.1: {}
|
||||
|
||||
uglify-js@3.19.3:
|
||||
optional: true
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user