Clientes y facturas de cliente
This commit is contained in:
parent
d6b2370cf1
commit
6e5b14305e
@ -3,7 +3,7 @@ import { ILogger } from "../../logger";
|
|||||||
import { ITransactionManager } from "./transaction-manager.interface";
|
import { ITransactionManager } from "./transaction-manager.interface";
|
||||||
|
|
||||||
export abstract class TransactionManager implements ITransactionManager {
|
export abstract class TransactionManager implements ITransactionManager {
|
||||||
protected _transaction: any | null = null;
|
protected _transaction: unknown | null = null;
|
||||||
protected _isCompleted = false;
|
protected _isCompleted = false;
|
||||||
protected readonly logger!: ILogger;
|
protected readonly logger!: ILogger;
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ export abstract class TransactionManager implements ITransactionManager {
|
|||||||
/**
|
/**
|
||||||
* 🔹 Ejecuta una función dentro de una transacción
|
* 🔹 Ejecuta una función dentro de una transacción
|
||||||
*/
|
*/
|
||||||
async complete<T>(work: (transaction: any) => Promise<T>): Promise<T> {
|
async complete<T>(work: (transaction: unknown) => Promise<T>): Promise<T> {
|
||||||
if (this._transaction) {
|
if (this._transaction) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
"❌ Cannot start a new transaction inside another. Nested transactions are not allowed.",
|
"❌ Cannot start a new transaction inside another. Nested transactions are not allowed.",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
export * from "./create";
|
export * from "./create";
|
||||||
export * from "./get-customer-invoice.use-case";
|
export * from "./get-customer-invoice.use-case";
|
||||||
|
export * from "./issue-customer-invoice.use-case";
|
||||||
export * from "./list-customer-invoices.use-case";
|
export * from "./list-customer-invoices.use-case";
|
||||||
export * from "./report";
|
export * from "./report";
|
||||||
//export * from "./update-customer-invoice.use-case";
|
export * from "./update";
|
||||||
export * from "./issue-customer-invoice.use-case";
|
|
||||||
|
|||||||
@ -1,401 +0,0 @@
|
|||||||
import { UniqueID } from "@/core/common/domain";
|
|
||||||
import { ITransactionManager } from "@/core/common/infrastructure/database";
|
|
||||||
import { Result } from "@repo/rdx-utils";
|
|
||||||
import { IUpdateCustomerInvoiceRequestDTO } from "../../common/dto";
|
|
||||||
import { CustomerInvoice, ICustomerInvoiceService } from "../domain";
|
|
||||||
|
|
||||||
export class CreateCustomerInvoiceUseCase {
|
|
||||||
constructor(
|
|
||||||
private readonly customerInvoiceService: ICustomerInvoiceService,
|
|
||||||
private readonly transactionManager: ITransactionManager
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public execute(
|
|
||||||
customerInvoiceID: UniqueID,
|
|
||||||
dto: Partial<IUpdateCustomerInvoiceRequestDTO>
|
|
||||||
): Promise<Result<CustomerInvoice, Error>> {
|
|
||||||
return this.transactionManager.complete(async (transaction) => {
|
|
||||||
return Result.fail(new Error("No implementado"));
|
|
||||||
/*
|
|
||||||
try {
|
|
||||||
const validOrErrors = this.validateCustomerInvoiceData(dto);
|
|
||||||
if (validOrErrors.isFailure) {
|
|
||||||
return Result.fail(validOrErrors.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = validOrErrors.data;
|
|
||||||
|
|
||||||
// Update customerInvoice with dto
|
|
||||||
return await this.customerInvoiceService.updateCustomerInvoiceById(customerInvoiceID, data, transaction);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
logger.error(error as Error);
|
|
||||||
return Result.fail(error as Error);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* private validateCustomerInvoiceData(
|
|
||||||
dto: Partial<IUpdateCustomerInvoiceRequestDTO>
|
|
||||||
): Result<Partial<ICustomerInvoiceProps>, Error> {
|
|
||||||
const errors: Error[] = [];
|
|
||||||
const validatedData: Partial<ICustomerInvoiceProps> = {};
|
|
||||||
|
|
||||||
// Create customerInvoice
|
|
||||||
let customerInvoice_status = CustomerInvoiceStatus.create(customerInvoiceDTO.status).object;
|
|
||||||
if (customerInvoice_status.isEmpty()) {
|
|
||||||
customerInvoice_status = CustomerInvoiceStatus.createDraft();
|
|
||||||
}
|
|
||||||
|
|
||||||
let customerInvoice_series = CustomerInvoiceSeries.create(customerInvoiceDTO.customerInvoice_series).object;
|
|
||||||
if (customerInvoice_series.isEmpty()) {
|
|
||||||
customerInvoice_series = CustomerInvoiceSeries.create(customerInvoiceDTO.customerInvoice_series).object;
|
|
||||||
}
|
|
||||||
|
|
||||||
let invoice_date = CustomerInvoiceDate.create(customerInvoiceDTO.invoice_date).object;
|
|
||||||
if (invoice_date.isEmpty()) {
|
|
||||||
invoice_date = CustomerInvoiceDate.createCurrentDate().object;
|
|
||||||
}
|
|
||||||
|
|
||||||
let operation_date = CustomerInvoiceDate.create(customerInvoiceDTO.operation_date).object;
|
|
||||||
if (operation_date.isEmpty()) {
|
|
||||||
operation_date = CustomerInvoiceDate.createCurrentDate().object;
|
|
||||||
}
|
|
||||||
|
|
||||||
let customerInvoiceCurrency = Currency.createFromCode(customerInvoiceDTO.currency).object;
|
|
||||||
|
|
||||||
if (customerInvoiceCurrency.isEmpty()) {
|
|
||||||
customerInvoiceCurrency = Currency.createDefaultCode().object;
|
|
||||||
}
|
|
||||||
|
|
||||||
let customerInvoiceLanguage = Language.createFromCode(customerInvoiceDTO.language_code).object;
|
|
||||||
|
|
||||||
if (customerInvoiceLanguage.isEmpty()) {
|
|
||||||
customerInvoiceLanguage = Language.createDefaultCode().object;
|
|
||||||
}
|
|
||||||
|
|
||||||
const items = new Collection<CustomerInvoiceItem>(
|
|
||||||
customerInvoiceDTO.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,
|
|
||||||
invoiceDate: invoice_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
|
|
||||||
);
|
|
||||||
} */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* export type UpdateCustomerInvoiceResponseOrError =
|
|
||||||
| Result<never, IUseCaseError> // Misc errors (value objects)
|
|
||||||
| Result<CustomerInvoice, never>; // Success!
|
|
||||||
|
|
||||||
export class UpdateCustomerInvoiceUseCase2
|
|
||||||
implements
|
|
||||||
IUseCase<{ id: UniqueID; data: IUpdateCustomerInvoice_DTO }, Promise<UpdateCustomerInvoiceResponseOrError>>
|
|
||||||
{
|
|
||||||
private _context: IInvoicingContext;
|
|
||||||
private _adapter: ISequelizeAdapter;
|
|
||||||
private _repositoryManager: IRepositoryManager;
|
|
||||||
|
|
||||||
constructor(context: IInvoicingContext) {
|
|
||||||
this._context = context;
|
|
||||||
this._adapter = context.adapter;
|
|
||||||
this._repositoryManager = context.repositoryManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getRepository<T>(name: string) {
|
|
||||||
return this._repositoryManager.getRepository<T>(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleValidationFailure(
|
|
||||||
validationError: Error,
|
|
||||||
message?: string
|
|
||||||
): Result<never, IUseCaseError> {
|
|
||||||
return Result.fail<IUseCaseError>(
|
|
||||||
UseCaseError.create(
|
|
||||||
UseCaseError.INVALID_INPUT_DATA,
|
|
||||||
message ? message : validationError.message,
|
|
||||||
validationError
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async execute(request: {
|
|
||||||
id: UniqueID;
|
|
||||||
data: IUpdateCustomerInvoice_DTO;
|
|
||||||
}): Promise<UpdateCustomerInvoiceResponseOrError> {
|
|
||||||
const { id, data: customerInvoiceDTO } = request;
|
|
||||||
|
|
||||||
// Validaciones
|
|
||||||
const customerInvoiceDTOOrError = ensureUpdateCustomerInvoice_DTOIsValid(customerInvoiceDTO);
|
|
||||||
if (customerInvoiceDTOOrError.isFailure) {
|
|
||||||
return this.handleValidationFailure(customerInvoiceDTOOrError.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
const transaction = this._adapter.startTransaction();
|
|
||||||
|
|
||||||
const customerInvoiceRepoBuilder = this.getRepository<ICustomerInvoiceRepository>("CustomerInvoice");
|
|
||||||
|
|
||||||
let customerInvoice: CustomerInvoice | null = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await transaction.complete(async (t) => {
|
|
||||||
customerInvoice = await customerInvoiceRepoBuilder({ transaction: t }).getById(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (customerInvoice === null) {
|
|
||||||
return Result.fail<IUseCaseError>(
|
|
||||||
UseCaseError.create(UseCaseError.NOT_FOUND_ERROR, `CustomerInvoice not found`, {
|
|
||||||
id: request.id.toString(),
|
|
||||||
entity: "customerInvoice",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.ok<CustomerInvoice>(customerInvoice);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
const _error = error as Error;
|
|
||||||
if (customerInvoiceRepoBuilder().isRepositoryError(_error)) {
|
|
||||||
return this.handleRepositoryError(error as BaseError, customerInvoiceRepoBuilder());
|
|
||||||
} else {
|
|
||||||
return this.handleUnexceptedError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recipient validations
|
|
||||||
const recipientIdOrError = ensureParticipantIdIsValid(
|
|
||||||
customerInvoiceDTO?.recipient?.id,
|
|
||||||
);
|
|
||||||
if (recipientIdOrError.isFailure) {
|
|
||||||
return this.handleValidationFailure(
|
|
||||||
recipientIdOrError.error,
|
|
||||||
"Recipient ID not valid",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const recipientId = recipientIdOrError.object;
|
|
||||||
|
|
||||||
const recipientBillingIdOrError = ensureParticipantAddressIdIsValid(
|
|
||||||
customerInvoiceDTO?.recipient?.billing_address_id,
|
|
||||||
);
|
|
||||||
if (recipientBillingIdOrError.isFailure) {
|
|
||||||
return this.handleValidationFailure(
|
|
||||||
recipientBillingIdOrError.error,
|
|
||||||
"Recipient billing address ID not valid",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const recipientBillingId = recipientBillingIdOrError.object;
|
|
||||||
|
|
||||||
const recipientShippingIdOrError = ensureParticipantAddressIdIsValid(
|
|
||||||
customerInvoiceDTO?.recipient?.shipping_address_id,
|
|
||||||
);
|
|
||||||
if (recipientShippingIdOrError.isFailure) {
|
|
||||||
return this.handleValidationFailure(
|
|
||||||
recipientShippingIdOrError.error,
|
|
||||||
"Recipient shipping address ID not valid",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const recipientShippingId = recipientShippingIdOrError.object;
|
|
||||||
|
|
||||||
const recipientContact = await this.findContact(
|
|
||||||
recipientId,
|
|
||||||
recipientBillingId,
|
|
||||||
recipientShippingId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!recipientContact) {
|
|
||||||
return this.handleValidationFailure(
|
|
||||||
new Error(`Recipient with ID ${recipientId.toString()} does not exist`),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crear customerInvoice
|
|
||||||
const customerInvoiceOrError = await this.tryUpdateCustomerInvoiceInstance(
|
|
||||||
customerInvoiceDTO,
|
|
||||||
customerInvoiceIdOrError.object,
|
|
||||||
//senderId,
|
|
||||||
//senderBillingId,
|
|
||||||
//senderShippingId,
|
|
||||||
recipientContact,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (customerInvoiceOrError.isFailure) {
|
|
||||||
const { error: domainError } = customerInvoiceOrError;
|
|
||||||
let errorCode = "";
|
|
||||||
let message = "";
|
|
||||||
|
|
||||||
switch (domainError.code) {
|
|
||||||
case CustomerInvoice.ERROR_CUSTOMER_WITHOUT_NAME:
|
|
||||||
errorCode = UseCaseError.INVALID_INPUT_DATA;
|
|
||||||
message =
|
|
||||||
"El cliente debe ser una compañía o tener nombre y apellidos.";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
errorCode = UseCaseError.UNEXCEPTED_ERROR;
|
|
||||||
message = "";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.fail<IUseCaseError>(
|
|
||||||
UseCaseError.create(errorCode, message, domainError),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.saveCustomerInvoice(customerInvoiceOrError.object);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private async tryUpdateCustomerInvoiceInstance(customerInvoiceDTO, customerInvoiceId, recipient) {
|
|
||||||
// Create customerInvoice
|
|
||||||
let customerInvoice_status = CustomerInvoiceStatus.create(customerInvoiceDTO.status).object;
|
|
||||||
if (customerInvoice_status.isEmpty()) {
|
|
||||||
customerInvoice_status = CustomerInvoiceStatus.createDraft();
|
|
||||||
}
|
|
||||||
|
|
||||||
let customerInvoice_series = CustomerInvoiceSeries.create(customerInvoiceDTO.customerInvoice_series).object;
|
|
||||||
if (customerInvoice_series.isEmpty()) {
|
|
||||||
customerInvoice_series = CustomerInvoiceSeries.create(customerInvoiceDTO.customerInvoice_series).object;
|
|
||||||
}
|
|
||||||
|
|
||||||
let invoice_date = CustomerInvoiceDate.create(customerInvoiceDTO.invoice_date).object;
|
|
||||||
if (invoice_date.isEmpty()) {
|
|
||||||
invoice_date = CustomerInvoiceDate.createCurrentDate().object;
|
|
||||||
}
|
|
||||||
|
|
||||||
let operation_date = CustomerInvoiceDate.create(customerInvoiceDTO.operation_date).object;
|
|
||||||
if (operation_date.isEmpty()) {
|
|
||||||
operation_date = CustomerInvoiceDate.createCurrentDate().object;
|
|
||||||
}
|
|
||||||
|
|
||||||
let customerInvoiceCurrency = Currency.createFromCode(customerInvoiceDTO.currency).object;
|
|
||||||
|
|
||||||
if (customerInvoiceCurrency.isEmpty()) {
|
|
||||||
customerInvoiceCurrency = Currency.createDefaultCode().object;
|
|
||||||
}
|
|
||||||
|
|
||||||
let customerInvoiceLanguage = Language.createFromCode(customerInvoiceDTO.language_code).object;
|
|
||||||
|
|
||||||
if (customerInvoiceLanguage.isEmpty()) {
|
|
||||||
customerInvoiceLanguage = Language.createDefaultCode().object;
|
|
||||||
}
|
|
||||||
|
|
||||||
const items = new Collection<CustomerInvoiceItem>(
|
|
||||||
customerInvoiceDTO.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,
|
|
||||||
invoiceDate: invoice_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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async findContact(
|
|
||||||
contactId: UniqueID,
|
|
||||||
billingAddressId: UniqueID,
|
|
||||||
shippingAddressId: UniqueID
|
|
||||||
) {
|
|
||||||
const contactRepoBuilder = this.getRepository<IContactRepository>("Contact");
|
|
||||||
|
|
||||||
const contact = await contactRepoBuilder().getById2(
|
|
||||||
contactId,
|
|
||||||
billingAddressId,
|
|
||||||
shippingAddressId
|
|
||||||
);
|
|
||||||
|
|
||||||
return contact;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async saveCustomerInvoice(customerInvoice: DraftCustomerInvoice) {
|
|
||||||
const transaction = this._adapter.startTransaction();
|
|
||||||
const customerInvoiceRepoBuilder = this.getRepository<ICustomerInvoiceRepository>("CustomerInvoice");
|
|
||||||
|
|
||||||
try {
|
|
||||||
await transaction.complete(async (t) => {
|
|
||||||
const customerInvoiceRepo = customerInvoiceRepoBuilder({ transaction: t });
|
|
||||||
await customerInvoiceRepo.save(customerInvoice);
|
|
||||||
});
|
|
||||||
|
|
||||||
return Result.ok<DraftCustomerInvoice>(customerInvoice);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
const _error = error as Error;
|
|
||||||
if (customerInvoiceRepoBuilder().isRepositoryError(_error)) {
|
|
||||||
return this.handleRepositoryError(error as BaseError, customerInvoiceRepoBuilder());
|
|
||||||
} else {
|
|
||||||
return this.handleUnexceptedError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleUnexceptedError(error): Result<never, IUseCaseError> {
|
|
||||||
return Result.fail<IUseCaseError>(
|
|
||||||
UseCaseError.create(UseCaseError.UNEXCEPTED_ERROR, error.message, error)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRepositoryError(
|
|
||||||
error: BaseError,
|
|
||||||
repository: ICustomerInvoiceRepository
|
|
||||||
): Result<never, IUseCaseError> {
|
|
||||||
const { message, details } = repository.handleRepositoryError(error);
|
|
||||||
return Result.fail<IUseCaseError>(
|
|
||||||
UseCaseError.create(UseCaseError.REPOSITORY_ERROR, message, details)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./update-customer-invoice.use-case";
|
||||||
@ -0,0 +1,133 @@
|
|||||||
|
import {
|
||||||
|
CurrencyCode,
|
||||||
|
DomainError,
|
||||||
|
LanguageCode,
|
||||||
|
TextValue,
|
||||||
|
UniqueID,
|
||||||
|
UtcDate,
|
||||||
|
ValidationErrorCollection,
|
||||||
|
ValidationErrorDetail,
|
||||||
|
extractOrPushError,
|
||||||
|
maybeFromNullableVO,
|
||||||
|
} from "@repo/rdx-ddd";
|
||||||
|
import { Result, isNullishOrEmpty, toPatchField } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import { UpdateCustomerInvoiceByIdRequestDTO } from "../../../../common/dto";
|
||||||
|
import { CustomerInvoicePatchProps, CustomerInvoiceSerie } from "../../../domain";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mapDTOToUpdateCustomerInvoicePatchProps
|
||||||
|
* Convierte el DTO a las props validadas (CustomerInvoiceProps).
|
||||||
|
* No construye directamente el agregado.
|
||||||
|
* Tri-estado:
|
||||||
|
* - campo omitido → no se cambia
|
||||||
|
* - campo con valor null/"" → se quita el valor -> set(None()),
|
||||||
|
* - campo con valor no-vacío → se pone el nuevo valor -> set(Some(VO)).
|
||||||
|
*
|
||||||
|
* @param dto - DTO con los datos a cambiar en la factura de cliente
|
||||||
|
* @returns Cambios en las propiedades de la factura de cliente
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function mapDTOToUpdateCustomerInvoicePatchProps(dto: UpdateCustomerInvoiceByIdRequestDTO) {
|
||||||
|
try {
|
||||||
|
const errors: ValidationErrorDetail[] = [];
|
||||||
|
const props: CustomerInvoicePatchProps = {};
|
||||||
|
|
||||||
|
toPatchField(dto.series).ifSet((series) => {
|
||||||
|
props.series = extractOrPushError(
|
||||||
|
maybeFromNullableVO(series, (value) => CustomerInvoiceSerie.create(value)),
|
||||||
|
"reference",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
toPatchField(dto.invoice_date).ifSet((invoice_date) => {
|
||||||
|
if (isNullishOrEmpty(invoice_date)) {
|
||||||
|
errors.push({ path: "invoice_date", message: "Invoice date cannot be empty" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
props.invoiceDate = extractOrPushError(
|
||||||
|
UtcDate.createFromISO(invoice_date!),
|
||||||
|
"invoice_date",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
toPatchField(dto.operation_date).ifSet((operation_date) => {
|
||||||
|
props.operationDate = extractOrPushError(
|
||||||
|
maybeFromNullableVO(operation_date, (value) => UtcDate.createFromISO(value)),
|
||||||
|
"operation_date",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
toPatchField(dto.customer_id).ifSet((customer_id) => {
|
||||||
|
if (isNullishOrEmpty(customer_id)) {
|
||||||
|
errors.push({ path: "customer_id", message: "Customer cannot be empty" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
props.customerId = extractOrPushError(UniqueID.create(customer_id!), "customer_id", errors);
|
||||||
|
});
|
||||||
|
|
||||||
|
toPatchField(dto.reference).ifSet((reference) => {
|
||||||
|
props.reference = extractOrPushError(
|
||||||
|
maybeFromNullableVO(reference, (value) => Result.ok(String(value))),
|
||||||
|
"reference",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
toPatchField(dto.description).ifSet((description) => {
|
||||||
|
props.description = extractOrPushError(
|
||||||
|
maybeFromNullableVO(description, (value) => Result.ok(String(value))),
|
||||||
|
"description",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
toPatchField(dto.notes).ifSet((notes) => {
|
||||||
|
props.notes = extractOrPushError(
|
||||||
|
maybeFromNullableVO(notes, (value) => TextValue.create(value)),
|
||||||
|
"notes",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
toPatchField(dto.language_code).ifSet((languageCode) => {
|
||||||
|
if (isNullishOrEmpty(languageCode)) {
|
||||||
|
errors.push({ path: "language_code", message: "Language code cannot be empty" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
props.languageCode = extractOrPushError(
|
||||||
|
LanguageCode.create(languageCode!),
|
||||||
|
"language_code",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
toPatchField(dto.currency_code).ifSet((currencyCode) => {
|
||||||
|
if (isNullishOrEmpty(currencyCode)) {
|
||||||
|
errors.push({ path: "currency_code", message: "Currency code cannot be empty" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
props.currencyCode = extractOrPushError(
|
||||||
|
CurrencyCode.create(currencyCode!),
|
||||||
|
"currency_code",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
return Result.fail(
|
||||||
|
new ValidationErrorCollection("Customer invoice props mapping failed (update)", errors)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(props);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
return Result.fail(new DomainError("Customer invoice props mapping failed", { cause: err }));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
|
||||||
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
import { Transaction } from "sequelize";
|
||||||
|
import { UpdateCustomerInvoiceByIdRequestDTO } from "../../../../common";
|
||||||
|
import { CustomerInvoicePatchProps, CustomerInvoiceService } from "../../../domain";
|
||||||
|
import { CustomerInvoiceFullPresenter } from "../../presenters";
|
||||||
|
import { mapDTOToUpdateCustomerInvoicePatchProps } from "./map-dto-to-update-customer-invoice-props";
|
||||||
|
|
||||||
|
type UpdateCustomerInvoiceUseCaseInput = {
|
||||||
|
companyId: UniqueID;
|
||||||
|
invoice_id: string;
|
||||||
|
dto: UpdateCustomerInvoiceByIdRequestDTO;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class UpdateCustomerInvoiceUseCase {
|
||||||
|
constructor(
|
||||||
|
private readonly service: CustomerInvoiceService,
|
||||||
|
private readonly transactionManager: ITransactionManager,
|
||||||
|
private readonly presenterRegistry: IPresenterRegistry
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public execute(params: UpdateCustomerInvoiceUseCaseInput) {
|
||||||
|
const { companyId, invoice_id, dto } = params;
|
||||||
|
|
||||||
|
const idOrError = UniqueID.create(invoice_id);
|
||||||
|
if (idOrError.isFailure) {
|
||||||
|
return Result.fail(idOrError.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const invoiceId = idOrError.data;
|
||||||
|
const presenter = this.presenterRegistry.getPresenter({
|
||||||
|
resource: "customer-invoice",
|
||||||
|
projection: "FULL",
|
||||||
|
}) as CustomerInvoiceFullPresenter;
|
||||||
|
|
||||||
|
// Mapear DTO → props de dominio
|
||||||
|
const patchPropsResult = mapDTOToUpdateCustomerInvoicePatchProps(dto);
|
||||||
|
if (patchPropsResult.isFailure) {
|
||||||
|
return Result.fail(patchPropsResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const patchProps: CustomerInvoicePatchProps = patchPropsResult.data;
|
||||||
|
|
||||||
|
return this.transactionManager.complete(async (transaction: Transaction) => {
|
||||||
|
try {
|
||||||
|
const updatedInvoice = await this.service.updateInvoiceByIdInCompany(
|
||||||
|
companyId,
|
||||||
|
invoiceId,
|
||||||
|
patchProps,
|
||||||
|
transaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (updatedInvoice.isFailure) {
|
||||||
|
return Result.fail(updatedInvoice.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const invoiceOrError = await this.service.updateInvoice(
|
||||||
|
companyId,
|
||||||
|
updatedInvoice.data,
|
||||||
|
transaction
|
||||||
|
);
|
||||||
|
const invoice = invoiceOrError.data;
|
||||||
|
const dto = presenter.toOutput(invoice);
|
||||||
|
return Result.ok(dto);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -51,6 +51,12 @@ export interface CustomerInvoiceProps {
|
|||||||
verifactu_status: string;*/
|
verifactu_status: string;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CustomerInvoicePatchProps = Partial<
|
||||||
|
Omit<CustomerInvoiceProps, "companyId" | "items">
|
||||||
|
> & {
|
||||||
|
items?: CustomerInvoiceItems;
|
||||||
|
};
|
||||||
|
|
||||||
export interface ICustomerInvoice {
|
export interface ICustomerInvoice {
|
||||||
hasRecipient: boolean;
|
hasRecipient: boolean;
|
||||||
hasPaymentMethod: boolean;
|
hasPaymentMethod: boolean;
|
||||||
@ -65,8 +71,6 @@ export interface ICustomerInvoice {
|
|||||||
issueInvoice(newInvoiceNumber: CustomerInvoiceNumber): Result<CustomerInvoice, Error>;
|
issueInvoice(newInvoiceNumber: CustomerInvoiceNumber): Result<CustomerInvoice, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CustomerInvoicePatchProps = Partial<Omit<CustomerInvoiceProps, "companyId">>;
|
|
||||||
|
|
||||||
export class CustomerInvoice
|
export class CustomerInvoice
|
||||||
extends AggregateRoot<CustomerInvoiceProps>
|
extends AggregateRoot<CustomerInvoiceProps>
|
||||||
implements ICustomerInvoice
|
implements ICustomerInvoice
|
||||||
@ -106,7 +110,23 @@ export class CustomerInvoice
|
|||||||
}
|
}
|
||||||
|
|
||||||
public update(partialInvoice: CustomerInvoicePatchProps): Result<CustomerInvoice, Error> {
|
public update(partialInvoice: CustomerInvoicePatchProps): Result<CustomerInvoice, Error> {
|
||||||
throw new Error("Not implemented");
|
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 {
|
public get companyId(): UniqueID {
|
||||||
|
|||||||
@ -11,14 +11,22 @@ import { CustomerInvoice } from "../aggregates";
|
|||||||
export interface ICustomerInvoiceRepository {
|
export interface ICustomerInvoiceRepository {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Persiste una nueva factura o actualiza una existente.
|
* Crea una nueva factura.
|
||||||
* Retorna el objeto actualizado tras la operación.
|
|
||||||
*
|
*
|
||||||
* @param invoice - El agregado a guardar.
|
* @param invoice - El agregado a guardar.
|
||||||
* @param transaction - Transacción activa para la operación.
|
* @param transaction - Transacción activa para la operación.
|
||||||
* @returns Result<CustomerInvoice, Error>
|
* @returns Result<void, Error>
|
||||||
*/
|
*/
|
||||||
save(invoice: CustomerInvoice, transaction: any): Promise<Result<CustomerInvoice, Error>>;
|
create(invoice: CustomerInvoice, transaction?: unknown): Promise<Result<void, Error>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualiza una factura existente.
|
||||||
|
*
|
||||||
|
* @param invoice - El agregado a actualizar.
|
||||||
|
* @param transaction - Transacción activa para la operación.
|
||||||
|
* @returns Result<void, Error>
|
||||||
|
*/
|
||||||
|
update(invoice: CustomerInvoice, transaction?: unknown): Promise<Result<void, Error>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Comprueba si existe una factura con un `id` dentro de una `company`.
|
* Comprueba si existe una factura con un `id` dentro de una `company`.
|
||||||
@ -26,7 +34,7 @@ export interface ICustomerInvoiceRepository {
|
|||||||
existsByIdInCompany(
|
existsByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
id: UniqueID,
|
id: UniqueID,
|
||||||
transaction?: any
|
transaction?: unknown
|
||||||
): Promise<Result<boolean, Error>>;
|
): Promise<Result<boolean, Error>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,7 +44,7 @@ export interface ICustomerInvoiceRepository {
|
|||||||
getByIdInCompany(
|
getByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
id: UniqueID,
|
id: UniqueID,
|
||||||
transaction?: any
|
transaction?: unknown
|
||||||
): Promise<Result<CustomerInvoice, Error>>;
|
): Promise<Result<CustomerInvoice, Error>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,7 +63,7 @@ export interface ICustomerInvoiceRepository {
|
|||||||
findByCriteriaInCompany(
|
findByCriteriaInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
criteria: Criteria,
|
criteria: Criteria,
|
||||||
transaction: any
|
transaction: unknown
|
||||||
): Promise<Result<Collection<CustomerInvoiceListDTO>, Error>>;
|
): Promise<Result<Collection<CustomerInvoiceListDTO>, Error>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,6 +78,6 @@ export interface ICustomerInvoiceRepository {
|
|||||||
deleteByIdInCompany(
|
deleteByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
id: UniqueID,
|
id: UniqueID,
|
||||||
transaction: any
|
transaction: unknown
|
||||||
): Promise<Result<void, Error>>;
|
): Promise<Result<void, Error>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,17 +26,45 @@ export class CustomerInvoiceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Guarda una instancia de CustomerInvoice en persistencia.
|
* Guarda una nueva factura y devuelve la factura guardada.
|
||||||
*
|
*
|
||||||
|
* @param companyId - Identificador UUID de la empresa a la que pertenece el cliente.
|
||||||
* @param invoice - El agregado a guardar.
|
* @param invoice - El agregado a guardar.
|
||||||
* @param transaction - Transacción activa para la operación.
|
* @param transaction - Transacción activa para la operación.
|
||||||
* @returns Result<CustomerInvoice, Error> - El agregado guardado o un error si falla la operación.
|
* @returns Result<CustomerInvoice, Error> - El agregado guardado o un error si falla la operación.
|
||||||
*/
|
*/
|
||||||
async saveInvoice(
|
async createInvoice(
|
||||||
|
companyId: UniqueID,
|
||||||
invoice: CustomerInvoice,
|
invoice: CustomerInvoice,
|
||||||
transaction: any
|
transaction: Transaction
|
||||||
): Promise<Result<CustomerInvoice, Error>> {
|
): Promise<Result<CustomerInvoice, Error>> {
|
||||||
return this.repository.save(invoice, transaction);
|
const result = await this.repository.create(invoice, transaction);
|
||||||
|
if (result.isFailure) {
|
||||||
|
return Result.fail(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getInvoiceByIdInCompany(companyId, invoice.id, transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualiza una nueva factura y devuelve la factura actualizada.
|
||||||
|
*
|
||||||
|
* @param companyId - Identificador UUID de la empresa a la que pertenece el cliente.
|
||||||
|
* @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 updateInvoice(
|
||||||
|
companyId: UniqueID,
|
||||||
|
invoice: CustomerInvoice,
|
||||||
|
transaction: Transaction
|
||||||
|
): Promise<Result<CustomerInvoice, Error>> {
|
||||||
|
const result = await this.repository.update(invoice, transaction);
|
||||||
|
if (result.isFailure) {
|
||||||
|
return Result.fail(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getInvoiceByIdInCompany(companyId, invoice.id, transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +80,7 @@ export class CustomerInvoiceService {
|
|||||||
async existsByIdInCompany(
|
async existsByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
invoiceId: UniqueID,
|
invoiceId: UniqueID,
|
||||||
transaction?: any
|
transaction?: Transaction
|
||||||
): Promise<Result<boolean, Error>> {
|
): Promise<Result<boolean, Error>> {
|
||||||
return this.repository.existsByIdInCompany(companyId, invoiceId, transaction);
|
return this.repository.existsByIdInCompany(companyId, invoiceId, transaction);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import {
|
|||||||
ListCustomerInvoicesUseCase,
|
ListCustomerInvoicesUseCase,
|
||||||
RecipientInvoiceFullPresenter,
|
RecipientInvoiceFullPresenter,
|
||||||
ReportCustomerInvoiceUseCase,
|
ReportCustomerInvoiceUseCase,
|
||||||
|
UpdateCustomerInvoiceUseCase,
|
||||||
} from "../application";
|
} from "../application";
|
||||||
|
|
||||||
import { JsonTaxCatalogProvider, spainTaxCatalogProvider } from "@erp/core";
|
import { JsonTaxCatalogProvider, spainTaxCatalogProvider } from "@erp/core";
|
||||||
@ -41,7 +42,7 @@ export type CustomerInvoiceDeps = {
|
|||||||
list: () => ListCustomerInvoicesUseCase;
|
list: () => ListCustomerInvoicesUseCase;
|
||||||
get: () => GetCustomerInvoiceUseCase;
|
get: () => GetCustomerInvoiceUseCase;
|
||||||
create: () => CreateCustomerInvoiceUseCase;
|
create: () => CreateCustomerInvoiceUseCase;
|
||||||
//update: () => UpdateCustomerInvoiceUseCase;
|
update: () => UpdateCustomerInvoiceUseCase;
|
||||||
//delete: () => DeleteCustomerInvoiceUseCase;
|
//delete: () => DeleteCustomerInvoiceUseCase;
|
||||||
report: () => ReportCustomerInvoiceUseCase;
|
report: () => ReportCustomerInvoiceUseCase;
|
||||||
};
|
};
|
||||||
@ -154,7 +155,8 @@ export function buildCustomerInvoiceDependencies(params: ModuleParams): Customer
|
|||||||
presenterRegistry,
|
presenterRegistry,
|
||||||
catalogs.taxes
|
catalogs.taxes
|
||||||
),
|
),
|
||||||
// update: () => new UpdateCustomerInvoiceUseCase(service, transactionManager),
|
update: () =>
|
||||||
|
new UpdateCustomerInvoiceUseCase(service, transactionManager, presenterRegistry),
|
||||||
// delete: () => new DeleteCustomerInvoiceUseCase(service, transactionManager),
|
// delete: () => new DeleteCustomerInvoiceUseCase(service, transactionManager),
|
||||||
report: () =>
|
report: () =>
|
||||||
new ReportCustomerInvoiceUseCase(service, transactionManager, presenterRegistry),
|
new ReportCustomerInvoiceUseCase(service, transactionManager, presenterRegistry),
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
export * from "./create-customer-invoice.controller";
|
export * from "./create-customer-invoice.controller";
|
||||||
export * from "./delete-customer-invoice.controller";
|
export * from "./delete-customer-invoice.controller";
|
||||||
export * from "./get-customer-invoice.controller";
|
export * from "./get-customer-invoice.controller";
|
||||||
export * from "./list-customer-invoices.controller";
|
|
||||||
///export * from "./update-customer-invoice.controller";
|
|
||||||
export * from "./issue-customer-invoice.controller";
|
export * from "./issue-customer-invoice.controller";
|
||||||
|
export * from "./list-customer-invoices.controller";
|
||||||
export * from "./report-customer-invoice.controller";
|
export * from "./report-customer-invoice.controller";
|
||||||
|
export * from "./update-customer-invoice.controller";
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api";
|
||||||
|
import { UpdateCustomerInvoiceByIdRequestDTO } from "../../../../common/dto";
|
||||||
|
import { UpdateCustomerInvoiceUseCase } from "../../../application";
|
||||||
|
|
||||||
|
export class UpdateCustomerInvoiceController extends ExpressController {
|
||||||
|
public constructor(private readonly useCase: UpdateCustomerInvoiceUseCase) {
|
||||||
|
super();
|
||||||
|
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||||
|
this.useGuards(authGuard(), tenantGuard(), forbidQueryFieldGuard("companyId"));
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeImpl(): Promise<any> {
|
||||||
|
const companyId = this.getTenantId();
|
||||||
|
if (!companyId) {
|
||||||
|
return this.forbiddenError("Tenant ID not found");
|
||||||
|
}
|
||||||
|
const { invoice_id } = this.req.params;
|
||||||
|
const dto = this.req.body as UpdateCustomerInvoiceByIdRequestDTO;
|
||||||
|
|
||||||
|
const result = await this.useCase.execute({ invoice_id, companyId, dto });
|
||||||
|
|
||||||
|
return result.match(
|
||||||
|
(data) => this.ok(data),
|
||||||
|
(err) => this.handleError(err)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,72 +0,0 @@
|
|||||||
import { IInvoicingContext } from "#/server/intrastructure";
|
|
||||||
import { ExpressController } from "@rdx/core";
|
|
||||||
import { IUpdateCustomerInvoicePresenter } from "./presenter";
|
|
||||||
|
|
||||||
export class UpdateCustomerInvoiceController extends ExpressController {
|
|
||||||
private useCase: UpdateCustomerInvoiceUseCase2;
|
|
||||||
private presenter: IUpdateCustomerInvoicePresenter;
|
|
||||||
private context: IInvoicingContext;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
props: {
|
|
||||||
useCase: UpdateCustomerInvoiceUseCase;
|
|
||||||
presenter: IUpdateCustomerInvoicePresenter;
|
|
||||||
},
|
|
||||||
context: IInvoicingContext
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
const { useCase, presenter } = props;
|
|
||||||
this.useCase = useCase;
|
|
||||||
this.presenter = presenter;
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
async executeImpl(): Promise<any> {
|
|
||||||
const { customerInvoiceId } = this.req.params;
|
|
||||||
const request: IUpdateCustomerInvoice_DTO = this.req.body;
|
|
||||||
|
|
||||||
if (RuleValidator.validate(RuleValidator.RULE_NOT_NULL_OR_UNDEFINED, customerInvoiceId).isFailure) {
|
|
||||||
return this.invalidInputError("CustomerInvoice Id param is required!");
|
|
||||||
}
|
|
||||||
|
|
||||||
const idOrError = UniqueID.create(customerInvoiceId);
|
|
||||||
if (idOrError.isFailure) {
|
|
||||||
return this.invalidInputError("Invalid customerInvoice Id param!");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await this.useCase.execute({
|
|
||||||
id: idOrError.object,
|
|
||||||
data: request,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.isFailure) {
|
|
||||||
const { error } = result;
|
|
||||||
|
|
||||||
switch (error.code) {
|
|
||||||
case UseCaseError.NOT_FOUND_ERROR:
|
|
||||||
return this.notFoundError("CustomerInvoice not found", error);
|
|
||||||
|
|
||||||
case UseCaseError.INVALID_INPUT_DATA:
|
|
||||||
return this.invalidInputError(error.message);
|
|
||||||
|
|
||||||
case UseCaseError.UNEXCEPTED_ERROR:
|
|
||||||
return this.internalServerError(result.error.message, result.error);
|
|
||||||
|
|
||||||
case UseCaseError.REPOSITORY_ERROR:
|
|
||||||
return this.conflictError(result.error, result.error.details);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return this.clientError(result.error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const customerInvoice = <CustomerInvoice>result.object;
|
|
||||||
|
|
||||||
return this.ok<IUpdateCustomerInvoice_Response_DTO>(this.presenter.map(customerInvoice, this.context));
|
|
||||||
} catch (e: unknown) {
|
|
||||||
return this.fail(e as IServerError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -8,6 +8,8 @@ import {
|
|||||||
CustomerInvoiceListRequestSchema,
|
CustomerInvoiceListRequestSchema,
|
||||||
GetCustomerInvoiceByIdRequestSchema,
|
GetCustomerInvoiceByIdRequestSchema,
|
||||||
ReportCustomerInvoiceByIdRequestSchema,
|
ReportCustomerInvoiceByIdRequestSchema,
|
||||||
|
UpdateCustomerInvoiceByIdParamsRequestSchema,
|
||||||
|
UpdateCustomerInvoiceByIdRequestSchema,
|
||||||
} from "../../../common/dto";
|
} from "../../../common/dto";
|
||||||
import { buildCustomerInvoiceDependencies } from "../dependencies";
|
import { buildCustomerInvoiceDependencies } from "../dependencies";
|
||||||
import {
|
import {
|
||||||
@ -15,6 +17,7 @@ import {
|
|||||||
GetCustomerInvoiceController,
|
GetCustomerInvoiceController,
|
||||||
ListCustomerInvoicesController,
|
ListCustomerInvoicesController,
|
||||||
ReportCustomerInvoiceController,
|
ReportCustomerInvoiceController,
|
||||||
|
UpdateCustomerInvoiceController,
|
||||||
} from "./controllers";
|
} from "./controllers";
|
||||||
|
|
||||||
export const customerInvoicesRouter = (params: ModuleParams) => {
|
export const customerInvoicesRouter = (params: ModuleParams) => {
|
||||||
@ -81,9 +84,10 @@ export const customerInvoicesRouter = (params: ModuleParams) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
/*router.put(
|
router.put(
|
||||||
"/:invoice_id",
|
"/:invoice_id",
|
||||||
//checkTabContext,
|
//checkTabContext,
|
||||||
|
|
||||||
validateRequest(UpdateCustomerInvoiceByIdParamsRequestSchema, "params"),
|
validateRequest(UpdateCustomerInvoiceByIdParamsRequestSchema, "params"),
|
||||||
validateRequest(UpdateCustomerInvoiceByIdRequestSchema, "body"),
|
validateRequest(UpdateCustomerInvoiceByIdRequestSchema, "body"),
|
||||||
(req: Request, res: Response, next: NextFunction) => {
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
@ -91,7 +95,7 @@ export const customerInvoicesRouter = (params: ModuleParams) => {
|
|||||||
const controller = new UpdateCustomerInvoiceController(useCase);
|
const controller = new UpdateCustomerInvoiceController(useCase);
|
||||||
return controller.execute(req, res, next);
|
return controller.execute(req, res, next);
|
||||||
}
|
}
|
||||||
);*/
|
);
|
||||||
|
|
||||||
/*router.delete(
|
/*router.delete(
|
||||||
"/:invoice_id",
|
"/:invoice_id",
|
||||||
|
|||||||
@ -374,7 +374,11 @@ export class CustomerInvoiceDomainMapper
|
|||||||
const allAmounts = source.getAllAmounts();
|
const allAmounts = source.getAllAmounts();
|
||||||
|
|
||||||
// 4) Cliente
|
// 4) Cliente
|
||||||
const recipient = this._mapRecipientToPersistence(source);
|
const recipient = this._mapRecipientToPersistence(source, {
|
||||||
|
errors,
|
||||||
|
parent: source,
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
|
||||||
// 7) Si hubo errores de mapeo, devolvemos colección de validación
|
// 7) Si hubo errores de mapeo, devolvemos colección de validación
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
|
|||||||
@ -41,21 +41,21 @@ export class InvoiceRecipientDomainMapper {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const _name = isProforma ? source.current_customer.name : source.customer_name;
|
const _name = isProforma ? source.current_customer.name : source.customer_name!;
|
||||||
const _tin = isProforma ? source.current_customer.tin : source.customer_tin;
|
const _tin = isProforma ? source.current_customer.tin : source.customer_tin!;
|
||||||
const _street = isProforma ? source.current_customer.street : source.customer_street;
|
const _street = isProforma ? source.current_customer.street : source.customer_street!;
|
||||||
const _street2 = isProforma ? source.current_customer.street2 : source.customer_street2;
|
const _street2 = isProforma ? source.current_customer.street2 : source.customer_street2!;
|
||||||
const _city = isProforma ? source.current_customer.city : source.customer_city;
|
const _city = isProforma ? source.current_customer.city : source.customer_city!;
|
||||||
const _postal_code = isProforma
|
const _postal_code = isProforma
|
||||||
? source.current_customer.postal_code
|
? source.current_customer.postal_code
|
||||||
: source.customer_postal_code;
|
: source.customer_postal_code!;
|
||||||
const _province = isProforma ? source.current_customer.province : source.customer_province;
|
const _province = isProforma ? source.current_customer.province : source.customer_province!;
|
||||||
const _country = isProforma ? source.current_customer.country : source.customer_country;
|
const _country = isProforma ? source.current_customer.country : source.customer_country!;
|
||||||
|
|
||||||
// Customer (snapshot)
|
// Customer (snapshot)
|
||||||
const customerName = extractOrPushError(Name.create(_name), "customer_name", errors);
|
const customerName = extractOrPushError(Name.create(_name!), "customer_name", errors);
|
||||||
|
|
||||||
const customerTin = extractOrPushError(TINNumber.create(_tin), "customer_tin", errors);
|
const customerTin = extractOrPushError(TINNumber.create(_tin!), "customer_tin", errors);
|
||||||
|
|
||||||
const customerStreet = extractOrPushError(
|
const customerStreet = extractOrPushError(
|
||||||
maybeFromNullableVO(_street, (value) => Street.create(value)),
|
maybeFromNullableVO(_street, (value) => Street.create(value)),
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
import { EntityNotFoundError, SequelizeRepository, translateSequelizeError } from "@erp/core/api";
|
import {
|
||||||
|
EntityNotFoundError,
|
||||||
|
InfrastructureRepositoryError,
|
||||||
|
SequelizeRepository,
|
||||||
|
translateSequelizeError,
|
||||||
|
} from "@erp/core/api";
|
||||||
import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server";
|
import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server";
|
||||||
import { UniqueID } from "@repo/rdx-ddd";
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
import { Collection, Result } from "@repo/rdx-utils";
|
import { Collection, Result } from "@repo/rdx-utils";
|
||||||
@ -53,31 +58,67 @@ export class CustomerInvoiceRepository
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Persiste una nueva factura o actualiza una existente.
|
* Crea una nueva factura.
|
||||||
*
|
*
|
||||||
* @param invoice - El agregado a guardar.
|
* @param invoice - El agregado a guardar.
|
||||||
* @param transaction - Transacción activa para la operación.
|
* @param transaction - Transacción activa para la operación.
|
||||||
* @returns Result<CustomerInvoice, Error>
|
* @returns Result<void, Error>
|
||||||
*/
|
*/
|
||||||
async save(
|
async create(invoice: CustomerInvoice, transaction?: Transaction): Promise<Result<void, Error>> {
|
||||||
invoice: CustomerInvoice,
|
|
||||||
transaction: Transaction
|
|
||||||
): Promise<Result<CustomerInvoice, Error>> {
|
|
||||||
try {
|
try {
|
||||||
const mapper: ICustomerInvoiceDomainMapper = this._registry.getDomainMapper({
|
const mapper: ICustomerInvoiceDomainMapper = this._registry.getDomainMapper({
|
||||||
resource: "customer-invoice",
|
resource: "customer-invoice",
|
||||||
});
|
});
|
||||||
const mapperData = mapper.mapToPersistence(invoice);
|
const dto = mapper.mapToPersistence(invoice);
|
||||||
|
|
||||||
if (mapperData.isFailure) {
|
if (dto.isFailure) {
|
||||||
return Result.fail(mapperData.error);
|
return Result.fail(dto.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = mapperData;
|
const { data } = dto;
|
||||||
|
|
||||||
const [instance] = await CustomerInvoiceModel.upsert(data, { transaction, returning: true });
|
await CustomerInvoiceModel.create(data, {
|
||||||
const savedInvoice = mapper.mapToDomain(instance);
|
include: [{ all: true }],
|
||||||
return savedInvoice;
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Result.ok();
|
||||||
|
} catch (err: unknown) {
|
||||||
|
return Result.fail(translateSequelizeError(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualiza una factura existente.
|
||||||
|
*
|
||||||
|
* @param invoice - El agregado a actualizar.
|
||||||
|
* @param transaction - Transacción activa para la operación.
|
||||||
|
* @returns Result<void, Error>
|
||||||
|
*/
|
||||||
|
async update(invoice: CustomerInvoice, transaction?: Transaction): Promise<Result<void, Error>> {
|
||||||
|
try {
|
||||||
|
const mapper: ICustomerInvoiceDomainMapper = this._registry.getDomainMapper({
|
||||||
|
resource: "customer-invoice",
|
||||||
|
});
|
||||||
|
const dto = mapper.mapToPersistence(invoice);
|
||||||
|
|
||||||
|
const { id, ...updatePayload } = dto.data;
|
||||||
|
const [affected] = await CustomerInvoiceModel.update(updatePayload, {
|
||||||
|
where: { id /*, version */ },
|
||||||
|
//fields: Object.keys(updatePayload),
|
||||||
|
transaction,
|
||||||
|
individualHooks: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (affected === 0) {
|
||||||
|
return Result.fail(
|
||||||
|
new InfrastructureRepositoryError(
|
||||||
|
"Concurrency conflict or not found update customer invoice"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok();
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
return Result.fail(translateSequelizeError(err));
|
return Result.fail(translateSequelizeError(err));
|
||||||
}
|
}
|
||||||
@ -236,7 +277,7 @@ export class CustomerInvoiceRepository
|
|||||||
async deleteByIdInCompany(
|
async deleteByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
id: UniqueID,
|
id: UniqueID,
|
||||||
transaction: any
|
transaction: Transaction
|
||||||
): Promise<Result<void, Error>> {
|
): Promise<Result<void, Error>> {
|
||||||
try {
|
try {
|
||||||
const deleted = await CustomerInvoiceModel.destroy({
|
const deleted = await CustomerInvoiceModel.destroy({
|
||||||
|
|||||||
@ -30,6 +30,9 @@ export class CustomerInvoiceModel extends Model<
|
|||||||
InferAttributes<CustomerInvoiceModel>,
|
InferAttributes<CustomerInvoiceModel>,
|
||||||
InferCreationAttributes<CustomerInvoiceModel, { omit: "items" | "taxes" }>
|
InferCreationAttributes<CustomerInvoiceModel, { omit: "items" | "taxes" }>
|
||||||
> {
|
> {
|
||||||
|
// Version
|
||||||
|
// declare version: CreationOptional<number>;
|
||||||
|
|
||||||
declare id: string;
|
declare id: string;
|
||||||
declare company_id: string;
|
declare company_id: string;
|
||||||
|
|
||||||
@ -130,6 +133,12 @@ export class CustomerInvoiceModel extends Model<
|
|||||||
export default (database: Sequelize) => {
|
export default (database: Sequelize) => {
|
||||||
CustomerInvoiceModel.init(
|
CustomerInvoiceModel.init(
|
||||||
{
|
{
|
||||||
|
/*version: {
|
||||||
|
type: DataTypes.INTEGER.UNSIGNED,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 0,
|
||||||
|
},*/
|
||||||
|
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
@ -359,6 +368,14 @@ export default (database: Sequelize) => {
|
|||||||
defaultScope: {},
|
defaultScope: {},
|
||||||
|
|
||||||
scopes: {},
|
scopes: {},
|
||||||
|
|
||||||
|
/*hooks: {
|
||||||
|
// Incrementa la versión en cada update exitoso (OCC).
|
||||||
|
beforeUpdate: (instance) => {
|
||||||
|
const current = instance.get("version") as number;
|
||||||
|
instance.set("version", current + 1);
|
||||||
|
},
|
||||||
|
},*/
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -5,18 +5,19 @@ export const UpdateCustomerInvoiceByIdParamsRequestSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const UpdateCustomerInvoiceByIdRequestSchema = z.object({
|
export const UpdateCustomerInvoiceByIdRequestSchema = z.object({
|
||||||
invoice_number: z.string(),
|
series: z.string().optional(),
|
||||||
series: z.string().default(""),
|
|
||||||
|
|
||||||
invoice_date: z.string(),
|
invoice_date: z.string().optional(),
|
||||||
operation_date: z.string().default(""),
|
operation_date: z.string().optional(),
|
||||||
|
|
||||||
customer_id: z.uuid(),
|
customer_id: z.uuid().optional(),
|
||||||
|
|
||||||
notes: z.string().default(""),
|
reference: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
notes: z.string().optional(),
|
||||||
|
|
||||||
language_code: z.string().toLowerCase().default("es"),
|
language_code: z.string().optional(),
|
||||||
currency_code: z.string().toUpperCase().default("EUR"),
|
currency_code: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type UpdateCustomerInvoiceByIdRequestDTO = Partial<
|
export type UpdateCustomerInvoiceByIdRequestDTO = Partial<
|
||||||
|
|||||||
@ -12,6 +12,7 @@ export const GetCustomerInvoiceByIdResponseSchema = z.object({
|
|||||||
invoice_date: z.string(),
|
invoice_date: z.string(),
|
||||||
operation_date: z.string(),
|
operation_date: z.string(),
|
||||||
|
|
||||||
|
description: z.string(),
|
||||||
notes: z.string(),
|
notes: z.string(),
|
||||||
|
|
||||||
language_code: z.string(),
|
language_code: z.string(),
|
||||||
|
|||||||
@ -38,6 +38,8 @@ export function RecipientModalSelectorField<TFormValues extends FieldValues>({
|
|||||||
<FormItem className={className}>
|
<FormItem className={className}>
|
||||||
<CustomerModalSelector
|
<CustomerModalSelector
|
||||||
value={value}
|
value={value}
|
||||||
|
disabled={isDisabled}
|
||||||
|
readOnly={isReadOnly}
|
||||||
onValueChange={onChange}
|
onValueChange={onChange}
|
||||||
className='bg-fuchsia-200'
|
className='bg-fuchsia-200'
|
||||||
initialCustomer={{
|
initialCustomer={{
|
||||||
|
|||||||
@ -32,12 +32,17 @@ export function useUpdateCustomerInvoice() {
|
|||||||
|
|
||||||
mutationFn: async (payload) => {
|
mutationFn: async (payload) => {
|
||||||
const { id: invoiceId, data } = payload;
|
const { id: invoiceId, data } = payload;
|
||||||
|
|
||||||
|
console.log(payload);
|
||||||
|
|
||||||
if (!invoiceId) {
|
if (!invoiceId) {
|
||||||
throw new Error("customerInvoiceId is required");
|
throw new Error("customerInvoiceId is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = schema.safeParse(data);
|
const result = schema.safeParse(data);
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
|
console.log(result);
|
||||||
|
|
||||||
// Construye errores detallados
|
// Construye errores detallados
|
||||||
const validationErrors = result.error.issues.map((err) => ({
|
const validationErrors = result.error.issues.map((err) => ({
|
||||||
field: err.path.join("."),
|
field: err.path.join("."),
|
||||||
|
|||||||
@ -63,6 +63,8 @@ export const CustomerInvoiceUpdatePage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const patchData = pickFormDirtyValues(formData, dirtyFields);
|
const patchData = pickFormDirtyValues(formData, dirtyFields);
|
||||||
|
console.log(patchData);
|
||||||
|
|
||||||
mutate(
|
mutate(
|
||||||
{ id: invoiceId!, data: patchData },
|
{ id: invoiceId!, data: patchData },
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { MoneySchema, PercentageSchema, QuantitySchema } from "@erp/core";
|
|
||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
export const CustomerInvoiceFormSchema = z.object({
|
export const CustomerInvoiceFormSchema = z.object({
|
||||||
@ -14,7 +13,7 @@ export const CustomerInvoiceFormSchema = z.object({
|
|||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
notes: z.string().optional(),
|
notes: z.string().optional(),
|
||||||
|
|
||||||
language_code: z
|
/*language_code: z
|
||||||
.string({
|
.string({
|
||||||
error: "El idioma es obligatorio",
|
error: "El idioma es obligatorio",
|
||||||
})
|
})
|
||||||
@ -65,7 +64,7 @@ export const CustomerInvoiceFormSchema = z.object({
|
|||||||
discount_amount: MoneySchema,
|
discount_amount: MoneySchema,
|
||||||
taxable_amount: MoneySchema,
|
taxable_amount: MoneySchema,
|
||||||
taxes_amount: MoneySchema,
|
taxes_amount: MoneySchema,
|
||||||
total_amount: MoneySchema,
|
total_amount: MoneySchema,*/
|
||||||
});
|
});
|
||||||
|
|
||||||
export type CustomerInvoiceFormData = z.infer<typeof CustomerInvoiceFormSchema>;
|
export type CustomerInvoiceFormData = z.infer<typeof CustomerInvoiceFormSchema>;
|
||||||
|
|||||||
@ -6,7 +6,7 @@ export class CustomerNotExistsInCompanySpecification extends CompositeSpecificat
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly service: CustomerService,
|
private readonly service: CustomerService,
|
||||||
private readonly companyId: UniqueID,
|
private readonly companyId: UniqueID,
|
||||||
private readonly transaction?: any
|
private readonly transaction?: unknown
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
|
import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
|
||||||
import { UniqueID } from "@repo/rdx-ddd";
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
import { Transaction } from "sequelize";
|
||||||
import { UpdateCustomerByIdRequestDTO } from "../../../../common/dto";
|
import { UpdateCustomerByIdRequestDTO } from "../../../../common/dto";
|
||||||
import { CustomerPatchProps, CustomerService } from "../../../domain";
|
import { CustomerPatchProps, CustomerService } from "../../../domain";
|
||||||
import { CustomerFullPresenter } from "../../presenters";
|
import { CustomerFullPresenter } from "../../presenters";
|
||||||
@ -41,7 +42,7 @@ export class UpdateCustomerUseCase {
|
|||||||
|
|
||||||
const patchProps: CustomerPatchProps = patchPropsResult.data;
|
const patchProps: CustomerPatchProps = patchPropsResult.data;
|
||||||
|
|
||||||
return this.transactionManager.complete(async (transaction) => {
|
return this.transactionManager.complete(async (transaction: Transaction) => {
|
||||||
try {
|
try {
|
||||||
const updatedCustomer = await this.service.updateCustomerByIdInCompany(
|
const updatedCustomer = await this.service.updateCustomerByIdInCompany(
|
||||||
companyId,
|
companyId,
|
||||||
|
|||||||
@ -13,7 +13,7 @@ export interface ICustomerRepository {
|
|||||||
* Guarda (crea o actualiza) un Customer en la base de datos.
|
* Guarda (crea o actualiza) un Customer en la base de datos.
|
||||||
* Retorna el objeto actualizado tras la operación.
|
* Retorna el objeto actualizado tras la operación.
|
||||||
*/
|
*/
|
||||||
save(customer: Customer, transaction: any): Promise<Result<Customer, Error>>;
|
save(customer: Customer, transaction: unknown): Promise<Result<Customer, Error>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Comprueba si existe un Customer con un `id` dentro de una `company`.
|
* Comprueba si existe un Customer con un `id` dentro de una `company`.
|
||||||
@ -21,7 +21,7 @@ export interface ICustomerRepository {
|
|||||||
existsByIdInCompany(
|
existsByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
id: UniqueID,
|
id: UniqueID,
|
||||||
transaction?: any
|
transaction?: unknown
|
||||||
): Promise<Result<boolean, Error>>;
|
): Promise<Result<boolean, Error>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,7 +31,7 @@ export interface ICustomerRepository {
|
|||||||
getByIdInCompany(
|
getByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
id: UniqueID,
|
id: UniqueID,
|
||||||
transaction?: any
|
transaction?: unknown
|
||||||
): Promise<Result<Customer, Error>>;
|
): Promise<Result<Customer, Error>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,7 +42,7 @@ export interface ICustomerRepository {
|
|||||||
findByCriteriaInCompany(
|
findByCriteriaInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
criteria: Criteria,
|
criteria: Criteria,
|
||||||
transaction?: any
|
transaction?: unknown
|
||||||
): Promise<Result<Collection<CustomerListDTO>, Error>>;
|
): Promise<Result<Collection<CustomerListDTO>, Error>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,6 +53,6 @@ export interface ICustomerRepository {
|
|||||||
deleteByIdInCompany(
|
deleteByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
id: UniqueID,
|
id: UniqueID,
|
||||||
transaction: any
|
transaction: unknown
|
||||||
): Promise<Result<void, Error>>;
|
): Promise<Result<void, Error>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export class CustomerService {
|
|||||||
* @param transaction - Transacción activa para la operación.
|
* @param transaction - Transacción activa para la operación.
|
||||||
* @returns Result<Customer, Error> - El agregado guardado o un error si falla la operación.
|
* @returns Result<Customer, Error> - El agregado guardado o un error si falla la operación.
|
||||||
*/
|
*/
|
||||||
async saveCustomer(customer: Customer, transaction: any): Promise<Result<Customer, Error>> {
|
async saveCustomer(customer: Customer, transaction: unknown): Promise<Result<Customer, Error>> {
|
||||||
return this.repository.save(customer, transaction);
|
return this.repository.save(customer, transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ export class CustomerService {
|
|||||||
existsByIdInCompany(
|
existsByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
customerId: UniqueID,
|
customerId: UniqueID,
|
||||||
transaction?: any
|
transaction?: unknown
|
||||||
): Promise<Result<boolean, Error>> {
|
): Promise<Result<boolean, Error>> {
|
||||||
return this.repository.existsByIdInCompany(companyId, customerId, transaction);
|
return this.repository.existsByIdInCompany(companyId, customerId, transaction);
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ export class CustomerService {
|
|||||||
async findCustomerByCriteriaInCompany(
|
async findCustomerByCriteriaInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
criteria: Criteria,
|
criteria: Criteria,
|
||||||
transaction?: any
|
transaction?: unknown
|
||||||
): Promise<Result<Collection<CustomerListDTO>, Error>> {
|
): Promise<Result<Collection<CustomerListDTO>, Error>> {
|
||||||
return this.repository.findByCriteriaInCompany(companyId, criteria, transaction);
|
return this.repository.findByCriteriaInCompany(companyId, criteria, transaction);
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ export class CustomerService {
|
|||||||
async getCustomerByIdInCompany(
|
async getCustomerByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
customerId: UniqueID,
|
customerId: UniqueID,
|
||||||
transaction?: any
|
transaction?: unknown
|
||||||
): Promise<Result<Customer>> {
|
): Promise<Result<Customer>> {
|
||||||
return this.repository.getByIdInCompany(companyId, customerId, transaction);
|
return this.repository.getByIdInCompany(companyId, customerId, transaction);
|
||||||
}
|
}
|
||||||
@ -99,7 +99,7 @@ export class CustomerService {
|
|||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
customerId: UniqueID,
|
customerId: UniqueID,
|
||||||
changes: CustomerPatchProps,
|
changes: CustomerPatchProps,
|
||||||
transaction?: any
|
transaction?: unknown
|
||||||
): Promise<Result<Customer, Error>> {
|
): Promise<Result<Customer, Error>> {
|
||||||
const customerResult = await this.getCustomerByIdInCompany(companyId, customerId, transaction);
|
const customerResult = await this.getCustomerByIdInCompany(companyId, customerId, transaction);
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ export class CustomerService {
|
|||||||
async deleteCustomerByIdInCompany(
|
async deleteCustomerByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
customerId: UniqueID,
|
customerId: UniqueID,
|
||||||
transaction?: any
|
transaction?: unknown
|
||||||
): Promise<Result<void>> {
|
): Promise<Result<void>> {
|
||||||
return this.repository.deleteByIdInCompany(companyId, customerId, transaction);
|
return this.repository.deleteByIdInCompany(companyId, customerId, transaction);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,8 @@ interface CustomerModalSelectorProps {
|
|||||||
value?: string;
|
value?: string;
|
||||||
onValueChange?: (id: string) => void;
|
onValueChange?: (id: string) => void;
|
||||||
initialCustomer?: CustomerSummary;
|
initialCustomer?: CustomerSummary;
|
||||||
|
disabled?: boolean;
|
||||||
|
readOnly?: boolean;
|
||||||
className: string;
|
className: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,6 +29,8 @@ export const CustomerModalSelector = ({
|
|||||||
value,
|
value,
|
||||||
onValueChange,
|
onValueChange,
|
||||||
initialCustomer,
|
initialCustomer,
|
||||||
|
disabled = false,
|
||||||
|
readOnly = false,
|
||||||
className,
|
className,
|
||||||
}: CustomerModalSelectorProps) => {
|
}: CustomerModalSelectorProps) => {
|
||||||
// UI state
|
// UI state
|
||||||
|
|||||||
@ -6,7 +6,7 @@ export interface IDocNumberingRepository {
|
|||||||
getByReferenceInCompany(
|
getByReferenceInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
reference: string,
|
reference: string,
|
||||||
transaction?: any
|
transaction?: unknown
|
||||||
): Promise<Result<DocNumber, Error>>;
|
): Promise<Result<DocNumber, Error>>;
|
||||||
save(reference: string, transaction?: any): Promise<Result<DocNumber, Error>>;
|
save(reference: string, transaction?: unknown): Promise<Result<DocNumber, Error>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ export class DocNumberRepository
|
|||||||
async getByReferenceInCompany(
|
async getByReferenceInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
reference: string,
|
reference: string,
|
||||||
transaction?: any
|
transaction?: unknown
|
||||||
): Promise<Result<DocNumber, Error>> {
|
): Promise<Result<DocNumber, Error>> {
|
||||||
try {
|
try {
|
||||||
const mapper: IDocNumberDomainMapper = this._registry.getDomainMapper({
|
const mapper: IDocNumberDomainMapper = this._registry.getDomainMapper({
|
||||||
|
|||||||
@ -16,11 +16,14 @@ export interface IVerifactuRecordRepository {
|
|||||||
* @param transaction - Transacción activa para la operación.
|
* @param transaction - Transacción activa para la operación.
|
||||||
* @returns Result<VerifactuRecord, Error>
|
* @returns Result<VerifactuRecord, Error>
|
||||||
*/
|
*/
|
||||||
save(verifactuRecord: VerifactuRecord, transaction: any): Promise<Result<VerifactuRecord, Error>>;
|
save(
|
||||||
|
verifactuRecord: VerifactuRecord,
|
||||||
|
transaction: unknown
|
||||||
|
): Promise<Result<VerifactuRecord, Error>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recupera un registro por su ID.
|
* Recupera un registro por su ID.
|
||||||
* Devuelve un `NotFoundError` si no se encuentra.
|
* Devuelve un `NotFoundError` si no se encuentra.
|
||||||
*/
|
*/
|
||||||
getById(id: UniqueID, transaction?: any): Promise<Result<VerifactuRecord, Error>>;
|
getById(id: UniqueID, transaction?: unknown): Promise<Result<VerifactuRecord, Error>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export class VerifactuRecordService {
|
|||||||
*/
|
*/
|
||||||
async saveVerifactuRecord(
|
async saveVerifactuRecord(
|
||||||
verifactuRecord: VerifactuRecord,
|
verifactuRecord: VerifactuRecord,
|
||||||
transaction: any
|
transaction: unknown
|
||||||
): Promise<Result<VerifactuRecord, Error>> {
|
): Promise<Result<VerifactuRecord, Error>> {
|
||||||
return this.repository.save(verifactuRecord, transaction);
|
return this.repository.save(verifactuRecord, transaction);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export class VerifactuRecordRepository
|
|||||||
extends SequelizeRepository<VerifactuRecord>
|
extends SequelizeRepository<VerifactuRecord>
|
||||||
implements IVerifactuRecordRepository
|
implements IVerifactuRecordRepository
|
||||||
{
|
{
|
||||||
getById(id: UniqueID, transaction?: any): Promise<Result<VerifactuRecord, Error>> {
|
getById(id: UniqueID, transaction?: unknown): Promise<Result<VerifactuRecord, Error>> {
|
||||||
throw new Error("Method not implemented.");
|
throw new Error("Method not implemented.");
|
||||||
}
|
}
|
||||||
// Listado por tenant con criteria saneada
|
// Listado por tenant con criteria saneada
|
||||||
@ -235,7 +235,7 @@ export class VerifactuRecordRepository
|
|||||||
async deleteByIdInCompany(
|
async deleteByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
id: UniqueID,
|
id: UniqueID,
|
||||||
transaction: any
|
transaction: unknown
|
||||||
): Promise<Result<void, Error>> {
|
): Promise<Result<void, Error>> {
|
||||||
try {
|
try {
|
||||||
const deleted = await VerifactuRecordModel.destroy({
|
const deleted = await VerifactuRecordModel.destroy({
|
||||||
|
|||||||
@ -1,15 +1,29 @@
|
|||||||
import { CompositeSpecification, UniqueID } from "@repo/rdx-ddd";
|
import { CompositeSpecification, UniqueID } from "@repo/rdx-ddd";
|
||||||
import { I{{pascalCase name}}Repository } from "../../domain/repositories";
|
import { I
|
||||||
|
{
|
||||||
|
pascalCase;
|
||||||
|
name;
|
||||||
|
}
|
||||||
|
Repository;
|
||||||
|
} from "../../domain/repositories"
|
||||||
|
|
||||||
export class {{pascalCase name}}NotExistsSpecification extends CompositeSpecification<UniqueID> {
|
export class {
|
||||||
|
{
|
||||||
|
pascalCase;
|
||||||
|
name;
|
||||||
|
}
|
||||||
|
}NotExistsSpecification extends CompositeSpecification<UniqueID>
|
||||||
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private readonly repo: I{{pascalCase name}}Repository,
|
private readonly repo: I{{pascalCase name}}Repository,
|
||||||
private readonly transaction?: any
|
private readonly transaction?: unknown
|
||||||
) {
|
)
|
||||||
super();
|
super();
|
||||||
}
|
|
||||||
|
|
||||||
async isSatisfiedBy(entityId: UniqueID): Promise<boolean> {
|
async;
|
||||||
|
isSatisfiedBy(entityId: UniqueID)
|
||||||
|
: Promise<boolean>
|
||||||
|
{
|
||||||
const existsOrError = await this.repo.existsById(entityId, this.transaction);
|
const existsOrError = await this.repo.existsById(entityId, this.transaction);
|
||||||
if (existsOrError.isFailure) throw existsOrError.error;
|
if (existsOrError.isFailure) throw existsOrError.error;
|
||||||
return existsOrError.data === false;
|
return existsOrError.data === false;
|
||||||
|
|||||||
@ -1,15 +1,29 @@
|
|||||||
import { CompositeSpecification } from "@repo/rdx-ddd";
|
import { CompositeSpecification } from "@repo/rdx-ddd";
|
||||||
import { I{{pascalCase name}}Repository } from "../../domain/repositories";
|
import { I
|
||||||
|
{
|
||||||
|
pascalCase;
|
||||||
|
name;
|
||||||
|
}
|
||||||
|
Repository;
|
||||||
|
} from "../../domain/repositories"
|
||||||
|
|
||||||
export class {{pascalCase name}}UniqueNameSpecification extends CompositeSpecification<string> {
|
export class {
|
||||||
|
{
|
||||||
|
pascalCase;
|
||||||
|
name;
|
||||||
|
}
|
||||||
|
}UniqueNameSpecification extends CompositeSpecification<string>
|
||||||
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private readonly repo: I{{pascalCase name}}Repository,
|
private readonly repo: I{{pascalCase name}}Repository,
|
||||||
private readonly transaction?: any
|
private readonly transaction?: unknown
|
||||||
) {
|
)
|
||||||
super();
|
super();
|
||||||
}
|
|
||||||
|
|
||||||
async isSatisfiedBy(name: string): Promise<boolean> {
|
async;
|
||||||
|
isSatisfiedBy(name: string)
|
||||||
|
: Promise<boolean>
|
||||||
|
{
|
||||||
const criteria = { filters: { name } };
|
const criteria = { filters: { name } };
|
||||||
const resultOrError = await this.repo.findByCriteria(criteria as any, this.transaction);
|
const resultOrError = await this.repo.findByCriteria(criteria as any, this.transaction);
|
||||||
if (resultOrError.isFailure) throw resultOrError.error;
|
if (resultOrError.isFailure) throw resultOrError.error;
|
||||||
|
|||||||
@ -1,12 +1,33 @@
|
|||||||
import { UniqueID } from "@repo/rdx-ddd";
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
import { Result, Collection } from "@repo/rdx-utils";
|
import { Result, Collection } from "@repo/rdx-utils";
|
||||||
import { Criteria } from "@repo/rdx-criteria/server";
|
import { Criteria } from "@repo/rdx-criteria/server";
|
||||||
import { {{pascalCase name}} } from "../aggregates";
|
import {
|
||||||
|
{
|
||||||
export interface I{{pascalCase name}}Repository {
|
pascalCase;
|
||||||
save(entity: {{pascalCase name}}, transaction: any): Promise<Result<{{pascalCase name}}, Error>>;
|
name;
|
||||||
existsById(id: UniqueID, transaction?: any): Promise<Result<boolean, Error>>;
|
}
|
||||||
getById(id: UniqueID, transaction?: any): Promise<Result<{{pascalCase name}}, Error>>;
|
} from "../aggregates"
|
||||||
findByCriteria(criteria: Criteria, transaction?: any): Promise<Result<Collection<{{pascalCase name}}>, Error>>;
|
|
||||||
deleteById(id: UniqueID, transaction: any): Promise<Result<void, Error>>;
|
export interface I{{pascalCase name}
|
||||||
|
}Repository
|
||||||
|
{
|
||||||
|
save(entity: {{pascalCase name}}, transaction: unknown)
|
||||||
|
: Promise<Result<
|
||||||
|
pascalCase;
|
||||||
|
name;
|
||||||
|
, Error>>
|
||||||
|
existsById(id: UniqueID, transaction?: unknown)
|
||||||
|
: Promise<Result<boolean, Error>>
|
||||||
|
getById(id: UniqueID, transaction?: unknown)
|
||||||
|
: Promise<Result<
|
||||||
|
pascalCase;
|
||||||
|
name;
|
||||||
|
, Error>>
|
||||||
|
findByCriteria(criteria: Criteria, transaction?: unknown)
|
||||||
|
: Promise<Result<Collection<
|
||||||
|
pascalCase;
|
||||||
|
name;
|
||||||
|
>, Error>>
|
||||||
|
deleteById(id: UniqueID, transaction: unknown)
|
||||||
|
: Promise<Result<void, Error>>
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user