From 91c00e73445af64bf38b67953d2a8abcdd9b7655 Mon Sep 17 00:00:00 2001 From: david Date: Thu, 26 Jun 2025 20:25:38 +0200 Subject: [PATCH] Facturas de cliente --- .../src/api/errors/entity-not-found-error.ts | 6 ++++ modules/core/src/api/errors/error-mapper.ts | 5 +++ modules/core/src/api/errors/index.ts | 1 + .../delete-customer-invoice.use-case.ts | 36 +++++++++++++------ .../presenter/get-invoice.presenter.ts | 6 ++-- .../src/api/application/index.ts | 2 +- .../delete-invoice.controller.ts | 30 ++++++++++++++++ .../delete-invoice.controller.ts.bak | 12 ------- .../delete-customer-invoice/index.ts | 19 ++++++++++ .../delete-customer-invoice/index.ts.bak | 14 -------- .../src/api/controllers/index.ts | 2 +- .../customer-invoice-service.interface.ts | 2 +- .../services/customer-invoice.service.ts | 2 +- .../express/customer-invoices.routes.ts | 16 +++++---- ...lete-customer-invoice-by-id.command.dto.ts | 15 ++++++++ ...> get-customer-invoice-by-id.query.dto.ts} | 0 .../src/common/dto/request/index.ts | 3 +- ... get-customer-invoice-by-id.result.dto.ts} | 4 +-- .../src/common/dto/response/index.ts | 2 +- 19 files changed, 122 insertions(+), 55 deletions(-) create mode 100644 modules/core/src/api/errors/entity-not-found-error.ts create mode 100644 modules/customer-invoices/src/api/controllers/delete-customer-invoice/delete-invoice.controller.ts delete mode 100644 modules/customer-invoices/src/api/controllers/delete-customer-invoice/delete-invoice.controller.ts.bak create mode 100644 modules/customer-invoices/src/api/controllers/delete-customer-invoice/index.ts delete mode 100644 modules/customer-invoices/src/api/controllers/delete-customer-invoice/index.ts.bak create mode 100644 modules/customer-invoices/src/common/dto/request/delete-customer-invoice-by-id.command.dto.ts rename modules/customer-invoices/src/common/dto/request/{get-customer-invoice.query.dto.ts => get-customer-invoice-by-id.query.dto.ts} (100%) rename modules/customer-invoices/src/common/dto/response/{get-customer-invoice.result.dto.ts => get-customer-invoice-by-id.result.dto.ts} (70%) diff --git a/modules/core/src/api/errors/entity-not-found-error.ts b/modules/core/src/api/errors/entity-not-found-error.ts new file mode 100644 index 00000000..4bcd487a --- /dev/null +++ b/modules/core/src/api/errors/entity-not-found-error.ts @@ -0,0 +1,6 @@ +export class EntityNotFoundError extends Error { + constructor(entity: string, id: string | number) { + super(`Entity '${entity}' with ID '${id}' was not found.`); + this.name = "EntityNotFoundError"; + } +} diff --git a/modules/core/src/api/errors/error-mapper.ts b/modules/core/src/api/errors/error-mapper.ts index 118f3893..7be82c99 100644 --- a/modules/core/src/api/errors/error-mapper.ts +++ b/modules/core/src/api/errors/error-mapper.ts @@ -10,6 +10,7 @@ import { ApiError } from "./api-error"; import { ConflictApiError } from "./conflict-api-error"; import { DomainValidationError } from "./domain-validation-error"; import { DuplicateEntityError } from "./duplicate-entity-error"; +import { EntityNotFoundError } from "./entity-not-found-error"; import { ForbiddenApiError } from "./forbidden-api-error"; import { InternalApiError } from "./internal-api-error"; import { NotFoundApiError } from "./not-found-api-error"; @@ -79,6 +80,10 @@ export const errorMapper = { return new ConflictApiError(error.message); } + if (error instanceof EntityNotFoundError) { + return new NotFoundApiError(error.message); + } + // 3. 🔍 Errores individuales de validación if ( message.includes("invalid") || diff --git a/modules/core/src/api/errors/index.ts b/modules/core/src/api/errors/index.ts index f2d52462..1e3c8a02 100644 --- a/modules/core/src/api/errors/index.ts +++ b/modules/core/src/api/errors/index.ts @@ -2,6 +2,7 @@ export * from "./api-error"; export * from "./conflict-api-error"; export * from "./domain-validation-error"; export * from "./duplicate-entity-error"; +export * from "./entity-not-found-error"; export * from "./error-mapper"; export * from "./forbidden-api-error"; export * from "./internal-api-error"; diff --git a/modules/customer-invoices/src/api/application/delete-customer-invoice/delete-customer-invoice.use-case.ts b/modules/customer-invoices/src/api/application/delete-customer-invoice/delete-customer-invoice.use-case.ts index 0062fd93..fe3b8d9d 100644 --- a/modules/customer-invoices/src/api/application/delete-customer-invoice/delete-customer-invoice.use-case.ts +++ b/modules/customer-invoices/src/api/application/delete-customer-invoice/delete-customer-invoice.use-case.ts @@ -1,24 +1,38 @@ -import { UniqueID } from "@/core/common/domain"; -import { ITransactionManager } from "@/core/common/infrastructure/database"; -import { logger } from "@/core/logger"; +import { EntityNotFoundError, ITransactionManager } from "@erp/core/api"; +import { DeleteCustomerInvoiceByIdQueryDTO } from "@erp/customer-invoices/common/dto"; +import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { ICustomerInvoiceService } from "../domain"; +import { ICustomerInvoiceService } from "../../domain"; export class DeleteCustomerInvoiceUseCase { constructor( - private readonly customerInvoiceService: ICustomerInvoiceService, + private readonly service: ICustomerInvoiceService, private readonly transactionManager: ITransactionManager ) {} - public execute(customerInvoiceID: UniqueID): Promise> { + public execute(dto: DeleteCustomerInvoiceByIdQueryDTO) { + const idOrError = UniqueID.create(dto.id); + + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } + + const id = idOrError.data; + return this.transactionManager.complete(async (transaction) => { try { - return await this.customerInvoiceService.deleteCustomerInvoiceById( - customerInvoiceID, - transaction - ); + const existsCheck = await this.service.existsById(id, transaction); + + if (existsCheck.isFailure) { + return Result.fail(existsCheck.error); + } + + if (!existsCheck.data) { + return Result.fail(new EntityNotFoundError("CustomerInvoice", id.toString())); + } + + return await this.service.deleteById(id, transaction); } catch (error: unknown) { - logger.error(error as Error); return Result.fail(error as Error); } }); diff --git a/modules/customer-invoices/src/api/application/get-customer-invoice/presenter/get-invoice.presenter.ts b/modules/customer-invoices/src/api/application/get-customer-invoice/presenter/get-invoice.presenter.ts index afa03589..0ea9a9d7 100644 --- a/modules/customer-invoices/src/api/application/get-customer-invoice/presenter/get-invoice.presenter.ts +++ b/modules/customer-invoices/src/api/application/get-customer-invoice/presenter/get-invoice.presenter.ts @@ -1,12 +1,12 @@ -import { GetCustomerInvoiceResultDTO } from "../../../../common/dto"; +import { GetCustomerInvoiceByIdResultDTO } from "../../../../common/dto"; import { CustomerInvoice } from "../../../domain"; export interface GetCustomerInvoicePresenter { - toDTO: (customerInvoice: CustomerInvoice) => GetCustomerInvoiceResultDTO; + toDTO: (customerInvoice: CustomerInvoice) => GetCustomerInvoiceByIdResultDTO; } export const getCustomerInvoicePresenter: GetCustomerInvoicePresenter = { - toDTO: (customerInvoice: CustomerInvoice): GetCustomerInvoiceResultDTO => ({ + toDTO: (customerInvoice: CustomerInvoice): GetCustomerInvoiceByIdResultDTO => ({ id: customerInvoice.id.toPrimitive(), invoice_status: customerInvoice.status.toString(), diff --git a/modules/customer-invoices/src/api/application/index.ts b/modules/customer-invoices/src/api/application/index.ts index ec97e0a7..8e2a87ac 100644 --- a/modules/customer-invoices/src/api/application/index.ts +++ b/modules/customer-invoices/src/api/application/index.ts @@ -1,5 +1,5 @@ export * from "./create-customer-invoice"; -//export * from "./delete-customer-invoice"; +export * from "./delete-customer-invoice"; export * from "./get-customer-invoice"; export * from "./list-customer-invoices"; //export * from "./update-customer-invoice"; diff --git a/modules/customer-invoices/src/api/controllers/delete-customer-invoice/delete-invoice.controller.ts b/modules/customer-invoices/src/api/controllers/delete-customer-invoice/delete-invoice.controller.ts new file mode 100644 index 00000000..92cbe23f --- /dev/null +++ b/modules/customer-invoices/src/api/controllers/delete-customer-invoice/delete-invoice.controller.ts @@ -0,0 +1,30 @@ +import { ExpressController, errorMapper } from "@erp/core/api"; +import { DeleteCustomerInvoiceUseCase } from "../../application"; + +export class DeleteCustomerInvoiceController extends ExpressController { + public constructor(private readonly deleteCustomerInvoice: DeleteCustomerInvoiceUseCase) { + super(); + } + + async executeImpl(): Promise { + const { id } = this.req.params; + + /* + const user = this.req.user; // asumimos middleware authenticateJWT inyecta user + + if (!user || !user.companyId) { + this.unauthorized(res, "Unauthorized: user or company not found"); + return; + } + */ + + const result = await this.deleteCustomerInvoice.execute({ id }); + + if (result.isFailure) { + const apiError = errorMapper.toApiError(result.error); + return this.handleApiError(apiError); + } + + return this.ok(result.data); + } +} diff --git a/modules/customer-invoices/src/api/controllers/delete-customer-invoice/delete-invoice.controller.ts.bak b/modules/customer-invoices/src/api/controllers/delete-customer-invoice/delete-invoice.controller.ts.bak deleted file mode 100644 index ca4b152c..00000000 --- a/modules/customer-invoices/src/api/controllers/delete-customer-invoice/delete-invoice.controller.ts.bak +++ /dev/null @@ -1,12 +0,0 @@ -import { DeleteCustomerInvoiceUseCase } from "@/contexts/customer-invoices/application"; -import { ExpressController } from "@/core/common/presentation"; - -export class DeleteCustomerInvoiceController extends ExpressController { - public constructor(private readonly deleteCustomerInvoice: DeleteCustomerInvoiceUseCase) { - super(); - } - - async executeImpl(): Promise { - return this.noContent(); - } -} diff --git a/modules/customer-invoices/src/api/controllers/delete-customer-invoice/index.ts b/modules/customer-invoices/src/api/controllers/delete-customer-invoice/index.ts new file mode 100644 index 00000000..32dc9574 --- /dev/null +++ b/modules/customer-invoices/src/api/controllers/delete-customer-invoice/index.ts @@ -0,0 +1,19 @@ +import { SequelizeTransactionManager } from "@erp/core/api"; +import { Sequelize } from "sequelize"; +import { DeleteCustomerInvoiceUseCase } from "../../application"; +import { CustomerInvoiceService } from "../../domain"; +import { CustomerInvoiceMapper, CustomerInvoiceRepository } from "../../infrastructure"; +import { DeleteCustomerInvoiceController } from "./delete-invoice.controller"; + +export const buildDeleteCustomerInvoiceController = (database: Sequelize) => { + const transactionManager = new SequelizeTransactionManager(database); + const customerInvoiceRepository = new CustomerInvoiceRepository( + database, + new CustomerInvoiceMapper() + ); + const customerInvoiceService = new CustomerInvoiceService(customerInvoiceRepository); + + const useCase = new DeleteCustomerInvoiceUseCase(customerInvoiceService, transactionManager); + + return new DeleteCustomerInvoiceController(useCase); +}; diff --git a/modules/customer-invoices/src/api/controllers/delete-customer-invoice/index.ts.bak b/modules/customer-invoices/src/api/controllers/delete-customer-invoice/index.ts.bak deleted file mode 100644 index 9f3445fb..00000000 --- a/modules/customer-invoices/src/api/controllers/delete-customer-invoice/index.ts.bak +++ /dev/null @@ -1,14 +0,0 @@ -import { DeleteCustomerInvoiceUseCase } from "@/contexts/customer-invoices/application"; -import { CustomerInvoiceService } from "@/contexts/customer-invoices/domain"; -import { customerInvoiceRepository } from "@/contexts/customer-invoices/intrastructure"; -import { SequelizeTransactionManager } from "@/core/common/infrastructure"; -import { DeleteCustomerInvoiceController } from "./delete-customer-invoice.controller"; - -export const buildDeleteCustomerInvoiceController = () => { - const transactionManager = new SequelizeTransactionManager(); - const customerInvoiceService = new CustomerInvoiceService(customerInvoiceRepository); - - const useCase = new DeleteCustomerInvoiceUseCase(customerInvoiceService, transactionManager); - - return new DeleteCustomerInvoiceController(useCase); -}; diff --git a/modules/customer-invoices/src/api/controllers/index.ts b/modules/customer-invoices/src/api/controllers/index.ts index bf49ebc7..fbd5e70d 100644 --- a/modules/customer-invoices/src/api/controllers/index.ts +++ b/modules/customer-invoices/src/api/controllers/index.ts @@ -1,5 +1,5 @@ export * from "./create-customer-invoice"; -//export * from "./delete-customer-invoice"; +export * from "./delete-customer-invoice"; export * from "./get-customer-invoice"; export * from "./list-customer-invoices"; ///export * from "./update-customer-invoice"; diff --git a/modules/customer-invoices/src/api/domain/services/customer-invoice-service.interface.ts b/modules/customer-invoices/src/api/domain/services/customer-invoice-service.interface.ts index ae99347a..8c2c4f49 100644 --- a/modules/customer-invoices/src/api/domain/services/customer-invoice-service.interface.ts +++ b/modules/customer-invoices/src/api/domain/services/customer-invoice-service.interface.ts @@ -29,5 +29,5 @@ export interface ICustomerInvoiceService { transaction?: any ): Promise>; - deleteById(id: UniqueID, transaction?: any): Promise>; + deleteById(id: UniqueID, transaction?: any): Promise>; } diff --git a/modules/customer-invoices/src/api/domain/services/customer-invoice.service.ts b/modules/customer-invoices/src/api/domain/services/customer-invoice.service.ts index 0533ba1f..4694cda0 100644 --- a/modules/customer-invoices/src/api/domain/services/customer-invoice.service.ts +++ b/modules/customer-invoices/src/api/domain/services/customer-invoice.service.ts @@ -144,7 +144,7 @@ export class CustomerInvoiceService implements ICustomerInvoiceService { * @param transaction - Transacción activa para la operación. * @returns Result - Resultado de la operación. */ - async deleteById(id: UniqueID, transaction?: Transaction): Promise> { + async deleteById(id: UniqueID, transaction?: Transaction): Promise> { return this.repository.deleteById(id, transaction); } } diff --git a/modules/customer-invoices/src/api/infrastructure/express/customer-invoices.routes.ts b/modules/customer-invoices/src/api/infrastructure/express/customer-invoices.routes.ts index fa71364f..5a6a0f18 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/customer-invoices.routes.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/customer-invoices.routes.ts @@ -3,11 +3,13 @@ import { Application, NextFunction, Request, Response, Router } from "express"; import { Sequelize } from "sequelize"; import { CreateCustomerInvoiceCommandSchema, - GetCustomerInvoiceByIdQuerySchema, + DeleteCustomerInvoiceByIdQuerySchema, + DeleteCustomerInvoiceByIdQuerySchema as GetCustomerInvoiceByIdQuerySchema, ListCustomerInvoicesQuerySchema, } from "../../../common/dto"; import { buildCreateCustomerInvoicesController, + buildDeleteCustomerInvoiceController, buildGetCustomerInvoiceController, buildListCustomerInvoicesController, } from "../../controllers"; @@ -60,17 +62,17 @@ export const customerInvoicesRouter = (params: ModuleParams) => { (req: Request, res: Response, next: NextFunction) => { buildUpdateCustomerInvoiceController().execute(req, res, next); } - ); + );*/ routes.delete( - "/:customerInvoiceId", - validateAndParseBody(IDeleteCustomerInvoiceRequestSchema), - checkTabContext, + "/:id", + //checkTabContext, //checkUser, + validateRequest(DeleteCustomerInvoiceByIdQuerySchema, "params"), (req: Request, res: Response, next: NextFunction) => { - buildDeleteCustomerInvoiceController().execute(req, res, next); + buildDeleteCustomerInvoiceController(database).execute(req, res, next); } - );*/ + ); app.use(`${baseRoutePath}/customer-invoices`, routes); }; diff --git a/modules/customer-invoices/src/common/dto/request/delete-customer-invoice-by-id.command.dto.ts b/modules/customer-invoices/src/common/dto/request/delete-customer-invoice-by-id.command.dto.ts new file mode 100644 index 00000000..1dbd3aea --- /dev/null +++ b/modules/customer-invoices/src/common/dto/request/delete-customer-invoice-by-id.command.dto.ts @@ -0,0 +1,15 @@ +import * as z from "zod/v4"; + +/** + * Este DTO es utilizado por el endpoint: + * `DELETE /customer-invoices/:id` (eliminar una factura por ID). + * + */ + +export const DeleteCustomerInvoiceByIdQuerySchema = z.object({ + id: z.string(), +}); + +export type DeleteCustomerInvoiceByIdQueryDTO = z.infer< + typeof DeleteCustomerInvoiceByIdQuerySchema +>; diff --git a/modules/customer-invoices/src/common/dto/request/get-customer-invoice.query.dto.ts b/modules/customer-invoices/src/common/dto/request/get-customer-invoice-by-id.query.dto.ts similarity index 100% rename from modules/customer-invoices/src/common/dto/request/get-customer-invoice.query.dto.ts rename to modules/customer-invoices/src/common/dto/request/get-customer-invoice-by-id.query.dto.ts diff --git a/modules/customer-invoices/src/common/dto/request/index.ts b/modules/customer-invoices/src/common/dto/request/index.ts index ef34c462..8f92104a 100644 --- a/modules/customer-invoices/src/common/dto/request/index.ts +++ b/modules/customer-invoices/src/common/dto/request/index.ts @@ -1,3 +1,4 @@ export * from "./create-customer-invoice.command.dto"; -export * from "./get-customer-invoice.query.dto"; +export * from "./delete-customer-invoice-by-id.command.dto"; +export * from "./get-customer-invoice-by-id.query.dto"; export * from "./list-customer-invoices.query.dto"; diff --git a/modules/customer-invoices/src/common/dto/response/get-customer-invoice.result.dto.ts b/modules/customer-invoices/src/common/dto/response/get-customer-invoice-by-id.result.dto.ts similarity index 70% rename from modules/customer-invoices/src/common/dto/response/get-customer-invoice.result.dto.ts rename to modules/customer-invoices/src/common/dto/response/get-customer-invoice-by-id.result.dto.ts index b01eceac..47dfece1 100644 --- a/modules/customer-invoices/src/common/dto/response/get-customer-invoice.result.dto.ts +++ b/modules/customer-invoices/src/common/dto/response/get-customer-invoice-by-id.result.dto.ts @@ -1,7 +1,7 @@ import { MetadataSchema } from "@erp/core"; import * as z from "zod/v4"; -export const GetCustomerInvoiceResultSchema = z.object({ +export const GetCustomerInvoiceByIdResultSchema = z.object({ id: z.uuid(), invoice_status: z.string(), invoice_number: z.string(), @@ -14,4 +14,4 @@ export const GetCustomerInvoiceResultSchema = z.object({ metadata: MetadataSchema.optional(), }); -export type GetCustomerInvoiceResultDTO = z.infer; +export type GetCustomerInvoiceByIdResultDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/response/index.ts b/modules/customer-invoices/src/common/dto/response/index.ts index 422c8aa5..39bd0969 100644 --- a/modules/customer-invoices/src/common/dto/response/index.ts +++ b/modules/customer-invoices/src/common/dto/response/index.ts @@ -1,3 +1,3 @@ export * from "./customer-invoice-creation.result.dto"; -export * from "./get-customer-invoice.result.dto"; +export * from "./get-customer-invoice-by-id.result.dto"; export * from "./list-customer-invoices.result.dto";