From a0f75a4a8f2e8f756265631a71dcfb500972a32a Mon Sep 17 00:00:00 2001 From: david Date: Tue, 2 Sep 2025 10:57:41 +0200 Subject: [PATCH] Clientes --- .../create-customer.use-case.ts | 10 ++-- .../map-dto-to-create-customer-props.ts | 16 ++--- .../delete-customer.use-case.ts | 18 +++--- .../get-customer/get-customer.use-case.ts | 10 ++-- .../map-dto-to-update-customer-props.ts | 2 +- .../update-customer.use-case.ts | 6 +- .../api/domain/services/customer.service.ts | 2 +- .../controllers/delete-customer.controller.ts | 4 +- .../controllers/get-customer.controller.ts | 6 +- .../controllers/update-customer.controller.ts | 4 +- .../express/customers.routes.ts | 13 +++-- .../infrastructure/mappers/customer.mapper.ts | 39 +++++++------ .../sequelize/customer.model.ts | 58 +++++++++---------- .../delete-customer-by-id.request.dto.ts | 2 +- .../request/get-customer-by-id.request.dto.ts | 4 +- .../request/update-customer.request.dto.ts | 4 ++ 16 files changed, 104 insertions(+), 94 deletions(-) diff --git a/modules/customers/src/api/application/create-customer/create-customer.use-case.ts b/modules/customers/src/api/application/create-customer/create-customer.use-case.ts index b828aaac..ea1b9177 100644 --- a/modules/customers/src/api/application/create-customer/create-customer.use-case.ts +++ b/modules/customers/src/api/application/create-customer/create-customer.use-case.ts @@ -30,8 +30,6 @@ export class CreateCustomerUseCase { const { props, id } = dtoResult.data; - console.debug("Creating customer with props:", props); - // 3) Construir entidad de dominio const buildResult = this.service.buildCustomerInCompany(companyId, props, id); if (buildResult.isFailure) { @@ -40,18 +38,18 @@ export class CreateCustomerUseCase { const newCustomer = buildResult.data; - console.debug("Built new customer entity:", newCustomer); + console.debug("Built new customer entity:", id, newCustomer); // 4) Ejecutar bajo transacción: verificar duplicado → persistir → ensamblar vista - return this.transactionManager.complete(async (tx: Transaction) => { - const existsGuard = await this.ensureNotExists(companyId, id, tx); + return this.transactionManager.complete(async (transaction: Transaction) => { + const existsGuard = await this.ensureNotExists(companyId, id, transaction); if (existsGuard.isFailure) { return Result.fail(existsGuard.error); } console.debug("No existing customer with same ID found, proceeding to save."); - const saveResult = await this.service.saveCustomer(newCustomer, tx); + const saveResult = await this.service.saveCustomer(newCustomer, transaction); if (saveResult.isFailure) { return Result.fail(saveResult.error); } diff --git a/modules/customers/src/api/application/create-customer/map-dto-to-create-customer-props.ts b/modules/customers/src/api/application/create-customer/map-dto-to-create-customer-props.ts index 74b394bf..06d2f38c 100644 --- a/modules/customers/src/api/application/create-customer/map-dto-to-create-customer-props.ts +++ b/modules/customers/src/api/application/create-customer/map-dto-to-create-customer-props.ts @@ -23,7 +23,7 @@ import { UniqueID, maybeFromNullableVO, } from "@repo/rdx-ddd"; -import { Collection, Result } from "@repo/rdx-utils"; +import { Collection, Result, isNullishOrEmpty } from "@repo/rdx-utils"; import { CreateCustomerRequestDTO } from "../../../common/dto"; import { CustomerProps, CustomerStatus } from "../../domain"; @@ -146,12 +146,14 @@ export function mapDTOToCreateCustomerProps(dto: CreateCustomerRequestDTO) { const defaultTaxes = new Collection(); - dto.default_taxes.split(",").map((taxCode, index) => { - const tax = extractOrPushError(TaxCode.create(taxCode), `default_taxes.${index}`, errors); - if (tax) { - defaultTaxes.add(tax!); - } - }); + if (!isNullishOrEmpty(dto.default_taxes)) { + dto.default_taxes.split(",").map((taxCode, index) => { + const tax = extractOrPushError(TaxCode.create(taxCode), `default_taxes.${index}`, errors); + if (tax) { + defaultTaxes.add(tax!); + } + }); + } if (errors.length > 0) { console.error(errors); diff --git a/modules/customers/src/api/application/delete-customer/delete-customer.use-case.ts b/modules/customers/src/api/application/delete-customer/delete-customer.use-case.ts index 577f5026..2853d586 100644 --- a/modules/customers/src/api/application/delete-customer/delete-customer.use-case.ts +++ b/modules/customers/src/api/application/delete-customer/delete-customer.use-case.ts @@ -5,7 +5,7 @@ import { CustomerService } from "../../domain"; type DeleteCustomerUseCaseInput = { companyId: UniqueID; - id: string; + customer_id: string; }; export class DeleteCustomerUseCase { @@ -15,19 +15,23 @@ export class DeleteCustomerUseCase { ) {} public execute(params: DeleteCustomerUseCaseInput) { - const { companyId, id } = params; + const { companyId, customer_id } = params; - const idOrError = UniqueID.create(id); + const idOrError = UniqueID.create(customer_id); if (idOrError.isFailure) { return Result.fail(idOrError.error); } - const validId = idOrError.data; + const customerId = idOrError.data; return this.transactionManager.complete(async (transaction) => { try { - const existsCheck = await this.service.existsByIdInCompany(companyId, validId, transaction); + const existsCheck = await this.service.existsByIdInCompany( + companyId, + customerId, + transaction + ); if (existsCheck.isFailure) { return Result.fail(existsCheck.error); @@ -36,10 +40,10 @@ export class DeleteCustomerUseCase { const customerExists = existsCheck.data; if (!customerExists) { - return Result.fail(new EntityNotFoundError("Customer", "id", validId.toString())); + return Result.fail(new EntityNotFoundError("Customer", "id", customerId.toString())); } - return await this.service.deleteCustomerByIdInCompany(validId, companyId, transaction); + return await this.service.deleteCustomerByIdInCompany(customerId, companyId, transaction); } catch (error: unknown) { return Result.fail(error as Error); } diff --git a/modules/customers/src/api/application/get-customer/get-customer.use-case.ts b/modules/customers/src/api/application/get-customer/get-customer.use-case.ts index 45212f6d..f364d2bb 100644 --- a/modules/customers/src/api/application/get-customer/get-customer.use-case.ts +++ b/modules/customers/src/api/application/get-customer/get-customer.use-case.ts @@ -6,7 +6,7 @@ import { GetCustomerAssembler } from "./assembler"; type GetCustomerUseCaseInput = { companyId: UniqueID; - id: string; + customer_id: string; }; export class GetCustomerUseCase { @@ -18,19 +18,21 @@ export class GetCustomerUseCase { public execute(params: GetCustomerUseCaseInput) { console.log(params); - const { id, companyId } = params; + const { customer_id, companyId } = params; - const idOrError = UniqueID.create(id); + const idOrError = UniqueID.create(customer_id); if (idOrError.isFailure) { return Result.fail(idOrError.error); } + const customerId = idOrError.data; + return this.transactionManager.complete(async (transaction) => { try { const customerOrError = await this.service.getCustomerByIdInCompany( companyId, - idOrError.data, + customerId, transaction ); diff --git a/modules/customers/src/api/application/update-customer/map-dto-to-update-customer-props.ts b/modules/customers/src/api/application/update-customer/map-dto-to-update-customer-props.ts index 7de09ef1..2e46b542 100644 --- a/modules/customers/src/api/application/update-customer/map-dto-to-update-customer-props.ts +++ b/modules/customers/src/api/application/update-customer/map-dto-to-update-customer-props.ts @@ -164,7 +164,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerRequestDTO) return; } - defaultTaxes!.map((taxCode, index) => { + defaultTaxes!.split(",").forEach((taxCode, index) => { const tax = extractOrPushError(TaxCode.create(taxCode), `default_taxes.${index}`, errors); if (tax && customerPatchProps.defaultTaxes) { customerPatchProps.defaultTaxes.add(tax); diff --git a/modules/customers/src/api/application/update-customer/update-customer.use-case.ts b/modules/customers/src/api/application/update-customer/update-customer.use-case.ts index 1fefb509..028cdadc 100644 --- a/modules/customers/src/api/application/update-customer/update-customer.use-case.ts +++ b/modules/customers/src/api/application/update-customer/update-customer.use-case.ts @@ -8,7 +8,7 @@ import { mapDTOToUpdateCustomerPatchProps } from "./map-dto-to-update-customer-p type UpdateCustomerUseCaseInput = { companyId: UniqueID; - id: string; + customer_id: string; dto: UpdateCustomerRequestDTO; }; @@ -20,9 +20,9 @@ export class UpdateCustomerUseCase { ) {} public execute(params: UpdateCustomerUseCaseInput) { - const { companyId, id, dto } = params; + const { companyId, customer_id, dto } = params; - const idOrError = UniqueID.create(id); + const idOrError = UniqueID.create(customer_id); if (idOrError.isFailure) { return Result.fail(idOrError.error); } diff --git a/modules/customers/src/api/domain/services/customer.service.ts b/modules/customers/src/api/domain/services/customer.service.ts index eefda4fc..03f83c2e 100644 --- a/modules/customers/src/api/domain/services/customer.service.ts +++ b/modules/customers/src/api/domain/services/customer.service.ts @@ -26,7 +26,7 @@ export class CustomerService { /** * Guarda una instancia de Customer en persistencia. * - * @param customer - El agregado a guardar. + * @param customer - El agregado a guardar (con el companyId ya asignado) * @param transaction - Transacción activa para la operación. * @returns Result - El agregado guardado o un error si falla la operación. */ diff --git a/modules/customers/src/api/infrastructure/express/controllers/delete-customer.controller.ts b/modules/customers/src/api/infrastructure/express/controllers/delete-customer.controller.ts index 99c99796..e246adc3 100644 --- a/modules/customers/src/api/infrastructure/express/controllers/delete-customer.controller.ts +++ b/modules/customers/src/api/infrastructure/express/controllers/delete-customer.controller.ts @@ -10,9 +10,9 @@ export class DeleteCustomerController extends ExpressController { async executeImpl(): Promise { const companyId = this.getTenantId()!; // garantizado por tenantGuard - const { id } = this.req.params; + const { customer_id } = this.req.params; - const result = await this.useCase.execute({ id, companyId }); + const result = await this.useCase.execute({ customer_id, companyId }); return result.match( (data) => this.ok(data), diff --git a/modules/customers/src/api/infrastructure/express/controllers/get-customer.controller.ts b/modules/customers/src/api/infrastructure/express/controllers/get-customer.controller.ts index f008cfbb..ba923181 100644 --- a/modules/customers/src/api/infrastructure/express/controllers/get-customer.controller.ts +++ b/modules/customers/src/api/infrastructure/express/controllers/get-customer.controller.ts @@ -10,11 +10,9 @@ export class GetCustomerController extends ExpressController { protected async executeImpl() { const companyId = this.getTenantId()!; // garantizado por tenantGuard - const { id } = this.req.params; + const { customer_id } = this.req.params; - console.log(id); - - const result = await this.useCase.execute({ id, companyId }); + const result = await this.useCase.execute({ customer_id, companyId }); return result.match( (data) => this.ok(data), diff --git a/modules/customers/src/api/infrastructure/express/controllers/update-customer.controller.ts b/modules/customers/src/api/infrastructure/express/controllers/update-customer.controller.ts index fde7ebe9..af03b408 100644 --- a/modules/customers/src/api/infrastructure/express/controllers/update-customer.controller.ts +++ b/modules/customers/src/api/infrastructure/express/controllers/update-customer.controller.ts @@ -11,10 +11,10 @@ export class UpdateCustomerController extends ExpressController { protected async executeImpl() { const companyId = this.getTenantId()!; // garantizado por tenantGuard - const { id } = this.req.params; + const { customer_id } = this.req.params; const dto = this.req.body as UpdateCustomerRequestDTO; - const result = await this.useCase.execute({ id, companyId, dto }); + const result = await this.useCase.execute({ customer_id, companyId, dto }); return result.match( (data) => this.created(data), diff --git a/modules/customers/src/api/infrastructure/express/customers.routes.ts b/modules/customers/src/api/infrastructure/express/customers.routes.ts index 4a8bcd38..18bce96b 100644 --- a/modules/customers/src/api/infrastructure/express/customers.routes.ts +++ b/modules/customers/src/api/infrastructure/express/customers.routes.ts @@ -7,6 +7,7 @@ import { CustomerListRequestSchema, DeleteCustomerByIdRequestSchema, GetCustomerByIdRequestSchema, + UpdateCustomerParamsRequestSchema, UpdateCustomerRequestSchema, } from "../../../common/dto"; import { getCustomerDependencies } from "../dependencies"; @@ -51,7 +52,7 @@ export const customersRouter = (params: ModuleParams) => { ); router.get( - "/:id", + "/:customer_id", //checkTabContext, validateRequest(GetCustomerByIdRequestSchema, "params"), @@ -66,7 +67,7 @@ export const customersRouter = (params: ModuleParams) => { "/", //checkTabContext, - validateRequest(CreateCustomerRequestSchema), + validateRequest(CreateCustomerRequestSchema, "body"), (req: Request, res: Response, next: NextFunction) => { const useCase = deps.build.create(); const controller = new CreateCustomerController(useCase); @@ -75,10 +76,10 @@ export const customersRouter = (params: ModuleParams) => { ); router.put( - "/:customerId", + "/:customer_id", //checkTabContext, - - validateRequest(UpdateCustomerRequestSchema), + validateRequest(UpdateCustomerParamsRequestSchema, "params"), + validateRequest(UpdateCustomerRequestSchema, "body"), (req: Request, res: Response, next: NextFunction) => { const useCase = deps.build.update(); const controller = new UpdateCustomerController(useCase); @@ -87,7 +88,7 @@ export const customersRouter = (params: ModuleParams) => { ); router.delete( - "/:id", + "/:customer_id", //checkTabContext, validateRequest(DeleteCustomerByIdRequestSchema, "params"), diff --git a/modules/customers/src/api/infrastructure/mappers/customer.mapper.ts b/modules/customers/src/api/infrastructure/mappers/customer.mapper.ts index df219db3..c845dd95 100644 --- a/modules/customers/src/api/infrastructure/mappers/customer.mapper.ts +++ b/modules/customers/src/api/infrastructure/mappers/customer.mapper.ts @@ -211,41 +211,42 @@ export class CustomerMapper } public mapToPersistence(source: Customer, params?: MapperParamsType): CustomerCreationAttributes { - return { + const customerValues: Partial = { id: source.id.toPrimitive(), company_id: source.companyId.toPrimitive(), - reference: source.reference.match( - (value) => value.toPrimitive(), - () => "" - ), - + reference: toNullable(source.reference, (reference) => reference.toPrimitive()), is_company: source.isCompany, name: source.name.toPrimitive(), - trade_name: toNullable(source.tradeName, (trade_name) => trade_name.toPrimitive()), + trade_name: toNullable(source.tradeName, (tradeName) => tradeName.toPrimitive()), tin: toNullable(source.tin, (tin) => tin.toPrimitive()), - street: toNullable(source.address.street, (street) => street.toPrimitive()), - street2: toNullable(source.address.street2, (street2) => street2.toPrimitive()), - city: toNullable(source.address.city, (city) => city.toPrimitive()), - province: toNullable(source.address.province, (province) => province.toPrimitive()), - postal_code: toNullable(source.address.postalCode, (postal_code) => - postal_code.toPrimitive() - ), - country: toNullable(source.address.country, (country) => country.toPrimitive()), - email: toNullable(source.email, (email) => email.toPrimitive()), phone: toNullable(source.phone, (phone) => phone.toPrimitive()), fax: toNullable(source.fax, (fax) => fax.toPrimitive()), website: toNullable(source.website, (website) => website.toPrimitive()), - legal_record: toNullable(source.legalRecord, (legal_record) => legal_record.toPrimitive()), - - default_taxes: source.defaultTaxes.map((item) => item.toPrimitive()).join(", "), + legal_record: toNullable(source.legalRecord, (legalRecord) => legalRecord.toPrimitive()), + default_taxes: source.defaultTaxes.map((taxItem) => taxItem.toPrimitive()).join(", "), status: source.isActive ? "active" : "inactive", language_code: source.languageCode.toPrimitive(), currency_code: source.currencyCode.toPrimitive(), }; + + if (source.address) { + Object.assign(customerValues, { + street: toNullable(source.address.street, (street) => street.toPrimitive()), + street2: toNullable(source.address.street2, (street2) => street2.toPrimitive()), + city: toNullable(source.address.city, (city) => city.toPrimitive()), + province: toNullable(source.address.province, (province) => province.toPrimitive()), + postal_code: toNullable(source.address.postalCode, (postalCode) => + postalCode.toPrimitive() + ), + country: toNullable(source.address.country, (country) => country.toPrimitive()), + }); + } + + return customerValues as CustomerCreationAttributes; } } diff --git a/modules/customers/src/api/infrastructure/sequelize/customer.model.ts b/modules/customers/src/api/infrastructure/sequelize/customer.model.ts index 59a0e435..0f8b5603 100644 --- a/modules/customers/src/api/infrastructure/sequelize/customer.model.ts +++ b/modules/customers/src/api/infrastructure/sequelize/customer.model.ts @@ -57,8 +57,8 @@ export default (database: Sequelize) => { }, reference: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, }, is_company: { type: DataTypes.BOOLEAN, @@ -71,82 +71,82 @@ export default (database: Sequelize) => { }, trade_name: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, }, tin: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, }, street: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, }, street2: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, }, city: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, }, province: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, }, postal_code: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, }, country: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, }, email: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, validate: { isEmail: true, }, }, phone: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, }, fax: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, }, website: { type: DataTypes.STRING, - allowNull: false, - defaultValue: "", + allowNull: true, + defaultValue: null, validate: { isUrl: true, }, }, legal_record: { type: DataTypes.TEXT, - allowNull: false, - defaultValue: "", + defaultValue: null, + allowNull: true, }, default_taxes: { type: DataTypes.STRING, - allowNull: true, defaultValue: null, + allowNull: true, }, language_code: { diff --git a/modules/customers/src/common/dto/request/delete-customer-by-id.request.dto.ts b/modules/customers/src/common/dto/request/delete-customer-by-id.request.dto.ts index 70bb4b9c..d8afc88d 100644 --- a/modules/customers/src/common/dto/request/delete-customer-by-id.request.dto.ts +++ b/modules/customers/src/common/dto/request/delete-customer-by-id.request.dto.ts @@ -7,7 +7,7 @@ import * as z from "zod/v4"; */ export const DeleteCustomerByIdRequestSchema = z.object({ - id: z.string(), + customer_id: z.string(), }); export type DeleteCustomerByIdRequestDTO = z.infer; diff --git a/modules/customers/src/common/dto/request/get-customer-by-id.request.dto.ts b/modules/customers/src/common/dto/request/get-customer-by-id.request.dto.ts index 9f868337..07f04ac8 100644 --- a/modules/customers/src/common/dto/request/get-customer-by-id.request.dto.ts +++ b/modules/customers/src/common/dto/request/get-customer-by-id.request.dto.ts @@ -2,12 +2,12 @@ import * as z from "zod/v4"; /** * Este DTO es utilizado por el endpoint: - * `GET /customers/:id` (consultar una factura por ID). + * `GET /customers/:customer_id` (consultar una factura por ID). * */ export const GetCustomerByIdRequestSchema = z.object({ - id: z.string(), + customer_id: z.string(), }); export type GetCustomerByIdRequestDTO = z.infer; diff --git a/modules/customers/src/common/dto/request/update-customer.request.dto.ts b/modules/customers/src/common/dto/request/update-customer.request.dto.ts index 5d292a92..239e6b4b 100644 --- a/modules/customers/src/common/dto/request/update-customer.request.dto.ts +++ b/modules/customers/src/common/dto/request/update-customer.request.dto.ts @@ -1,5 +1,9 @@ import * as z from "zod/v4"; +export const UpdateCustomerParamsRequestSchema = z.object({ + customer_id: z.string(), +}); + export const UpdateCustomerRequestSchema = z.object({ reference: z.string().optional(),