diff --git a/biome.json b/biome.json index 603080f8..72e95f47 100644 --- a/biome.json +++ b/biome.json @@ -50,7 +50,10 @@ "noInferrableTypes": "error", "noNamespace": "error", "noNegationElse": "warn", - "noNonNullAssertion": "info", + "noNonNullAssertion": { + "level": "info", + "fix": "none" + }, "noParameterAssign": "error", "noUnusedTemplateLiteral": "error", "noUselessElse": "warn", diff --git a/modules/catalogs/src/api/application/di/index.ts b/modules/catalogs/src/api/application/di/index.ts index 81449c81..0421e69d 100644 --- a/modules/catalogs/src/api/application/di/index.ts +++ b/modules/catalogs/src/api/application/di/index.ts @@ -1,5 +1,7 @@ export * from "./payment-method-creator.di"; +export * from "./payment-method-deleter.di"; export * from "./payment-method-finder.di"; export * from "./payment-method-input-mappers.di"; export * from "./payment-method-snapshot-builders.di"; +export * from "./payment-method-status-changer.di"; export * from "./payment-method-updater.di"; diff --git a/modules/catalogs/src/api/application/di/payment-method-deleter.di.ts b/modules/catalogs/src/api/application/di/payment-method-deleter.di.ts new file mode 100644 index 00000000..41cd4d68 --- /dev/null +++ b/modules/catalogs/src/api/application/di/payment-method-deleter.di.ts @@ -0,0 +1,10 @@ +import type { IPaymentMethodRepository } from "../repositories"; +import { type IPaymentMethodDeleter, PaymentMethodDeleter } from "../services"; + +export const buildPaymentMethodDeleter = (params: { + repository: IPaymentMethodRepository; +}): IPaymentMethodDeleter => { + const { repository } = params; + + return new PaymentMethodDeleter(repository); +}; diff --git a/modules/catalogs/src/api/application/di/payment-method-finder.di.ts b/modules/catalogs/src/api/application/di/payment-method-finder.di.ts index fb70baa7..fe07a7eb 100644 --- a/modules/catalogs/src/api/application/di/payment-method-finder.di.ts +++ b/modules/catalogs/src/api/application/di/payment-method-finder.di.ts @@ -1,8 +1,10 @@ import type { IPaymentMethodRepository } from "../repositories"; import { type IPaymentMethodFinder, PaymentMethodFinder } from "../services"; -export function buildPaymentMethodFinder( - repository: IPaymentMethodRepository -): IPaymentMethodFinder { +export function buildPaymentMethodFinder(params: { + repository: IPaymentMethodRepository; +}): IPaymentMethodFinder { + const { repository } = params; + return new PaymentMethodFinder(repository); } diff --git a/modules/catalogs/src/api/application/di/payment-method-status-changer.di.ts b/modules/catalogs/src/api/application/di/payment-method-status-changer.di.ts new file mode 100644 index 00000000..a229b0f7 --- /dev/null +++ b/modules/catalogs/src/api/application/di/payment-method-status-changer.di.ts @@ -0,0 +1,10 @@ +import type { IPaymentMethodRepository } from "../repositories"; +import { type IPaymentMethodStatusChanger, PaymentMethodStatusChanger } from "../services"; + +export const buildPaymentMethodStatusChanger = (params: { + repository: IPaymentMethodRepository; +}): IPaymentMethodStatusChanger => { + const { repository } = params; + + return new PaymentMethodStatusChanger(repository); +}; diff --git a/modules/catalogs/src/api/application/services/index.ts b/modules/catalogs/src/api/application/services/index.ts index 6bb972c0..3bb22624 100644 --- a/modules/catalogs/src/api/application/services/index.ts +++ b/modules/catalogs/src/api/application/services/index.ts @@ -1,5 +1,8 @@ export * from "./payment-method-creator"; +export * from "./payment-method-deleter"; export * from "./payment-method-disabler"; +export * from "./payment-method-enabler"; export * from "./payment-method-finder"; export * from "./payment-method-public-services"; +export * from "./payment-method-status-changer"; export * from "./payment-method-updater"; diff --git a/modules/catalogs/src/api/application/services/payment-method-deleter.ts b/modules/catalogs/src/api/application/services/payment-method-deleter.ts new file mode 100644 index 00000000..9348547d --- /dev/null +++ b/modules/catalogs/src/api/application/services/payment-method-deleter.ts @@ -0,0 +1,41 @@ +import { Result } from "@repo/rdx-utils"; + +import type { PaymentMethod } from "../../domain"; +import { PaymentMethodCannotBeDeletedError } from "../../domain"; +import type { IPaymentMethodRepository } from "../repositories"; + +export interface IPaymentMethodDeleter { + delete(params: { + paymentMethod: PaymentMethod; + transaction?: unknown; + }): Promise>; +} + +export class PaymentMethodDeleter implements IPaymentMethodDeleter { + public constructor(private readonly repository: IPaymentMethodRepository) {} + + public async delete(params: { + paymentMethod: PaymentMethod; + transaction?: unknown; + }): Promise> { + const { paymentMethod, transaction } = params; + + if (paymentMethod.isSystem) { + return Result.fail( + new PaymentMethodCannotBeDeletedError("System payment methods cannot be deleted.") + ); + } + + const deleteResult = await this.repository.deleteByIdInCompany( + paymentMethod.companyId, + paymentMethod.id, + transaction + ); + + if (deleteResult.isFailure) { + return Result.fail(deleteResult.error); + } + + return Result.ok(paymentMethod); + } +} diff --git a/modules/catalogs/src/api/application/services/payment-method-disabler.ts b/modules/catalogs/src/api/application/services/payment-method-disabler.ts index 5182f1f6..8b5a83b2 100644 --- a/modules/catalogs/src/api/application/services/payment-method-disabler.ts +++ b/modules/catalogs/src/api/application/services/payment-method-disabler.ts @@ -1,5 +1,4 @@ import { Result } from "@repo/rdx-utils"; -import type { Transaction } from "sequelize"; import type { PaymentMethod } from "../../domain"; import type { IPaymentMethodRepository } from "../repositories"; @@ -7,7 +6,7 @@ import type { IPaymentMethodRepository } from "../repositories"; export interface IPaymentMethodDisabler { disable(params: { paymentMethod: PaymentMethod; - transaction?: Transaction; + transaction?: unknown; }): Promise>; } @@ -16,16 +15,22 @@ export class PaymentMethodDisabler implements IPaymentMethodDisabler { async disable(params: { paymentMethod: PaymentMethod; - transaction?: Transaction; + transaction?: unknown; }): Promise> { const { paymentMethod, transaction } = params; const disableResult = paymentMethod.disable(); + if (disableResult.isFailure) { return Result.fail(disableResult.error); } + if (!disableResult.data) { + return Result.ok(paymentMethod); + } + const persistenceResult = await this.repository.update(paymentMethod, transaction); + if (persistenceResult.isFailure) { return Result.fail(persistenceResult.error); } diff --git a/modules/catalogs/src/api/application/services/payment-method-enabler.ts b/modules/catalogs/src/api/application/services/payment-method-enabler.ts new file mode 100644 index 00000000..cdda964a --- /dev/null +++ b/modules/catalogs/src/api/application/services/payment-method-enabler.ts @@ -0,0 +1,40 @@ +import { Result } from "@repo/rdx-utils"; + +import type { PaymentMethod } from "../../domain"; +import type { IPaymentMethodRepository } from "../repositories"; + +export interface IPaymentMethodEnabler { + enable(params: { + paymentMethod: PaymentMethod; + transaction?: unknown; + }): Promise>; +} + +export class PaymentMethodEnabler implements IPaymentMethodEnabler { + constructor(private readonly repository: IPaymentMethodRepository) {} + + async enable(params: { + paymentMethod: PaymentMethod; + transaction?: unknown; + }): Promise> { + const { paymentMethod, transaction } = params; + + const enableResult = paymentMethod.enable(); + + if (enableResult.isFailure) { + return Result.fail(enableResult.error); + } + + if (!enableResult.data) { + return Result.ok(paymentMethod); + } + + const persistenceResult = await this.repository.update(paymentMethod, transaction); + + if (persistenceResult.isFailure) { + return Result.fail(persistenceResult.error); + } + + return Result.ok(paymentMethod); + } +} diff --git a/modules/catalogs/src/api/application/services/payment-method-status-changer.ts b/modules/catalogs/src/api/application/services/payment-method-status-changer.ts new file mode 100644 index 00000000..1e1e828a --- /dev/null +++ b/modules/catalogs/src/api/application/services/payment-method-status-changer.ts @@ -0,0 +1,44 @@ +import { Result } from "@repo/rdx-utils"; + +import type { PaymentMethod } from "../../domain"; +import type { IPaymentMethodRepository } from "../repositories"; + +export type PaymentMethodStatusChangeAction = "enable" | "disable"; + +export interface IPaymentMethodStatusChanger { + changeStatus(params: { + paymentMethod: PaymentMethod; + action: PaymentMethodStatusChangeAction; + transaction?: unknown; + }): Promise>; +} + +export class PaymentMethodStatusChanger implements IPaymentMethodStatusChanger { + public constructor(private readonly repository: IPaymentMethodRepository) {} + + public async changeStatus(params: { + paymentMethod: PaymentMethod; + action: PaymentMethodStatusChangeAction; + transaction?: unknown; + }): Promise> { + const { paymentMethod, action, transaction } = params; + + const statusResult = action === "enable" ? paymentMethod.enable() : paymentMethod.disable(); + + if (statusResult.isFailure) { + return Result.fail(statusResult.error); + } + + if (!statusResult.data) { + return Result.ok(paymentMethod); + } + + const persistenceResult = await this.repository.update(paymentMethod, transaction); + + if (persistenceResult.isFailure) { + return Result.fail(persistenceResult.error); + } + + return Result.ok(paymentMethod); + } +} diff --git a/modules/catalogs/src/api/application/services/payment-method-updater.ts b/modules/catalogs/src/api/application/services/payment-method-updater.ts index 33aa3734..f4e31273 100644 --- a/modules/catalogs/src/api/application/services/payment-method-updater.ts +++ b/modules/catalogs/src/api/application/services/payment-method-updater.ts @@ -40,6 +40,13 @@ export class PaymentMethodUpdater implements IPaymentMethodUpdater { return Result.fail(updateResult.error); } + const hasChanges = updateResult.data; + + if (!hasChanges) { + // No hay cambios, retornar el agregado original + return Result.ok(paymentMethod); + } + // Persistir cambios const saveResult = await this.repository.update(paymentMethod, transaction); diff --git a/modules/catalogs/src/api/application/snapshot-builders/full/payment-method-full-snapshot-builder.ts b/modules/catalogs/src/api/application/snapshot-builders/full/payment-method-full-snapshot-builder.ts index 0db00d6c..4d76ca77 100644 --- a/modules/catalogs/src/api/application/snapshot-builders/full/payment-method-full-snapshot-builder.ts +++ b/modules/catalogs/src/api/application/snapshot-builders/full/payment-method-full-snapshot-builder.ts @@ -11,6 +11,7 @@ export class PaymentMethodFullSnapshotBuilder implements IPaymentMethodFullSnaps public toOutput(paymentMethod: PaymentMethod): GetPaymentMethodByIdResponseDTO { return { id: paymentMethod.id.toPrimitive(), + company_id: paymentMethod.companyId.toPrimitive(), name: paymentMethod.name.toPrimitive(), description: toNullable(paymentMethod.description, (value) => value.toPrimitive()), is_active: paymentMethod.isActive, diff --git a/modules/catalogs/src/api/application/use-cases/delete-payment-method-by-id.use-case.ts b/modules/catalogs/src/api/application/use-cases/delete-payment-method-by-id.use-case.ts new file mode 100644 index 00000000..f549d31a --- /dev/null +++ b/modules/catalogs/src/api/application/use-cases/delete-payment-method-by-id.use-case.ts @@ -0,0 +1,57 @@ +import type { ITransactionManager } from "@erp/core/api"; +import { UniqueID } from "@repo/rdx-ddd"; +import { Result } from "@repo/rdx-utils"; + +import type { IPaymentMethodDeleter, IPaymentMethodFinder } from "../services"; +import type { IPaymentMethodFullSnapshotBuilder } from "../snapshot-builders"; + +export type DeletePaymentMethodByIdUseCaseInput = { + companyId: UniqueID; + payment_method_id: string; +}; + +export class DeletePaymentMethodByIdUseCase { + constructor( + private readonly deps: { + finder: IPaymentMethodFinder; + deleter: IPaymentMethodDeleter; + fullSnapshotBuilder: IPaymentMethodFullSnapshotBuilder; + transactionManager: ITransactionManager; + } + ) {} + + public execute(params: DeletePaymentMethodByIdUseCaseInput) { + const { payment_method_id, companyId } = params; + + const idOrError = UniqueID.create(payment_method_id); + if (idOrError.isFailure) return Result.fail(idOrError.error); + + const paymentMethodId = idOrError.data; + + return this.deps.transactionManager.complete(async (transaction: unknown) => { + try { + const findResult = await this.deps.finder.findPaymentMethodById( + companyId, + paymentMethodId, + transaction + ); + if (findResult.isFailure) { + return Result.fail(findResult.error); + } + + const deleteResult = await this.deps.deleter.delete({ + paymentMethod: findResult.data, + transaction, + }); + + if (deleteResult.isFailure) { + return Result.fail(deleteResult.error); + } + + return Result.ok(this.deps.fullSnapshotBuilder.toOutput(deleteResult.data)); + } catch (error: unknown) { + return Result.fail(error as Error); + } + }); + } +} diff --git a/modules/catalogs/src/api/application/use-cases/disable-payment-method-by-id.use-case.ts b/modules/catalogs/src/api/application/use-cases/disable-payment-method-by-id.use-case.ts index 394a13f2..b82f7f35 100644 --- a/modules/catalogs/src/api/application/use-cases/disable-payment-method-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/use-cases/disable-payment-method-by-id.use-case.ts @@ -1,39 +1,50 @@ import type { ITransactionManager } from "@erp/core/api"; +import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import type { Transaction } from "sequelize"; -import type { IPaymentMethodDisabler, IPaymentMethodFinder } from "../services"; +import type { IPaymentMethodFinder, IPaymentMethodStatusChanger } from "../services"; import type { IPaymentMethodFullSnapshotBuilder } from "../snapshot-builders"; export type DisablePaymentMethodByIdUseCaseInput = { - id: string; + companyId: UniqueID; + payment_method_id: string; }; export class DisablePaymentMethodByIdUseCase { constructor( private readonly deps: { finder: IPaymentMethodFinder; - disabler: IPaymentMethodDisabler; + changer: IPaymentMethodStatusChanger; fullSnapshotBuilder: IPaymentMethodFullSnapshotBuilder; transactionManager: ITransactionManager; } ) {} public execute(params: DisablePaymentMethodByIdUseCaseInput) { - const { id } = params; + const { payment_method_id, companyId } = params; + + const idOrError = UniqueID.create(payment_method_id); + if (idOrError.isFailure) return Result.fail(idOrError.error); + + const paymentMethodId = idOrError.data; return this.deps.transactionManager.complete(async (transaction: unknown) => { - const tx = transaction as Transaction; try { - const findResult = await this.deps.finder.getById(id, tx); + const findResult = await this.deps.finder.findPaymentMethodById( + companyId, + paymentMethodId, + transaction + ); if (findResult.isFailure) { return Result.fail(findResult.error); } - const disableResult = await this.deps.disabler.disable({ + const disableResult = await this.deps.changer.changeStatus({ paymentMethod: findResult.data, - transaction: tx, + action: "disable", + transaction, }); + if (disableResult.isFailure) { return Result.fail(disableResult.error); } diff --git a/modules/catalogs/src/api/application/use-cases/enable-payment-method-by-id.use-case.ts b/modules/catalogs/src/api/application/use-cases/enable-payment-method-by-id.use-case.ts new file mode 100644 index 00000000..cfb6aa83 --- /dev/null +++ b/modules/catalogs/src/api/application/use-cases/enable-payment-method-by-id.use-case.ts @@ -0,0 +1,58 @@ +import type { ITransactionManager } from "@erp/core/api"; +import { UniqueID } from "@repo/rdx-ddd"; +import { Result } from "@repo/rdx-utils"; + +import type { IPaymentMethodFinder, IPaymentMethodStatusChanger } from "../services"; +import type { IPaymentMethodFullSnapshotBuilder } from "../snapshot-builders"; + +export type EnablePaymentMethodByIdUseCaseInput = { + companyId: UniqueID; + payment_method_id: string; +}; + +export class EnablePaymentMethodByIdUseCase { + constructor( + private readonly deps: { + finder: IPaymentMethodFinder; + changer: IPaymentMethodStatusChanger; + fullSnapshotBuilder: IPaymentMethodFullSnapshotBuilder; + transactionManager: ITransactionManager; + } + ) {} + + public execute(params: EnablePaymentMethodByIdUseCaseInput) { + const { payment_method_id, companyId } = params; + + const idOrError = UniqueID.create(payment_method_id); + if (idOrError.isFailure) return Result.fail(idOrError.error); + + const paymentMethodId = idOrError.data; + + return this.deps.transactionManager.complete(async (transaction: unknown) => { + try { + const findResult = await this.deps.finder.findPaymentMethodById( + companyId, + paymentMethodId, + transaction + ); + if (findResult.isFailure) { + return Result.fail(findResult.error); + } + + const enableResult = await this.deps.changer.changeStatus({ + paymentMethod: findResult.data, + action: "enable", + transaction, + }); + + if (enableResult.isFailure) { + return Result.fail(enableResult.error); + } + + return Result.ok(this.deps.fullSnapshotBuilder.toOutput(enableResult.data)); + } catch (error: unknown) { + return Result.fail(error as Error); + } + }); + } +} diff --git a/modules/catalogs/src/api/application/use-cases/index.ts b/modules/catalogs/src/api/application/use-cases/index.ts index 7f37505a..9c5c2ff7 100644 --- a/modules/catalogs/src/api/application/use-cases/index.ts +++ b/modules/catalogs/src/api/application/use-cases/index.ts @@ -1,5 +1,7 @@ export * from "./create-payment-method.use-case"; +export * from "./delete-payment-method-by-id.use-case"; export * from "./disable-payment-method-by-id.use-case"; +export * from "./enable-payment-method-by-id.use-case"; export * from "./get-payment-method-by-id.use-case"; export * from "./list-payment-methods.use-case"; export * from "./update-payment-method-by-id.use-case"; diff --git a/modules/catalogs/src/api/domain/payment-methods/errors.ts b/modules/catalogs/src/api/domain/payment-methods/errors.ts index 0cc1b93c..4a8ff677 100644 --- a/modules/catalogs/src/api/domain/payment-methods/errors.ts +++ b/modules/catalogs/src/api/domain/payment-methods/errors.ts @@ -7,6 +7,8 @@ export class InvalidPaymentMethodIdError extends DomainError { export const isInvalidPaymentMethodIdError = (e: unknown): e is InvalidPaymentMethodIdError => e instanceof InvalidPaymentMethodIdError; +// + export class InvalidPaymentMethodNameError extends DomainError { public readonly code = "PAYMENT_METHOD_NAME" as const; } @@ -14,10 +16,17 @@ export class InvalidPaymentMethodNameError extends DomainError { export const isInvalidPaymentMethodNameError = (e: unknown): e is InvalidPaymentMethodNameError => e instanceof InvalidPaymentMethodNameError; +// + export class PaymentMethodNotFoundError extends DomainError { public readonly code = "PAYMENT_METHOD_NOT_FOUND" as const; } +export const isPaymentMethodNotFoundError = (e: unknown): e is PaymentMethodNotFoundError => + e instanceof PaymentMethodNotFoundError; + +// + export class PaymentMethodCannotBeDeletedError extends DomainError { public readonly code = "PAYMENT_METHOD_CANNOT_BE_DELETED" as const; } @@ -25,3 +34,33 @@ export class PaymentMethodCannotBeDeletedError extends DomainError { export const isPaymentMethodCannotBeDeletedError = ( e: unknown ): e is PaymentMethodCannotBeDeletedError => e instanceof PaymentMethodCannotBeDeletedError; + +// + +export class PaymentMethodCannotBeDisabledError extends DomainError { + public readonly code = "PAYMENT_METHOD_CANNOT_BE_DISABLED" as const; +} + +export const isPaymentMethodCannotBeDisabledError = ( + e: unknown +): e is PaymentMethodCannotBeDisabledError => e instanceof PaymentMethodCannotBeDisabledError; + +// + +export class PaymentMethodCannotBeEnabledError extends DomainError { + public readonly code = "PAYMENT_METHOD_CANNOT_BE_ENABLED" as const; +} + +export const isPaymentMethodCannotBeEnabledError = ( + e: unknown +): e is PaymentMethodCannotBeEnabledError => e instanceof PaymentMethodCannotBeEnabledError; + +// + +export class PaymentMethodCannotBeUpdatedError extends DomainError { + public readonly code = "PAYMENT_METHOD_CANNOT_BE_UPDATED" as const; +} + +export const isPaymentMethodCannotBeUpdatedError = ( + e: unknown +): e is PaymentMethodCannotBeUpdatedError => e instanceof PaymentMethodCannotBeUpdatedError; diff --git a/modules/catalogs/src/api/domain/payment-methods/payment-method.aggregate.ts b/modules/catalogs/src/api/domain/payment-methods/payment-method.aggregate.ts index 8f2f5b2e..4f663698 100644 --- a/modules/catalogs/src/api/domain/payment-methods/payment-method.aggregate.ts +++ b/modules/catalogs/src/api/domain/payment-methods/payment-method.aggregate.ts @@ -1,6 +1,8 @@ import { AggregateRoot, type Name, type TextValue, type UniqueID } from "@repo/rdx-ddd"; import { type Maybe, Result } from "@repo/rdx-utils"; +import { PaymentMethodCannotBeDisabledError, PaymentMethodCannotBeUpdatedError } from "./errors"; + export interface IPaymentMethodCreateProps { companyId: UniqueID; name: Name; @@ -9,14 +11,14 @@ export interface IPaymentMethodCreateProps { isSystem: boolean; } -export type PaymentMethodPatchProps = Partial< - Omit ->; +export type PaymentMethodPatchProps = Partial<{ + name: Name; + description: Maybe; + isActive: boolean; +}>; export type PaymentMethodInternalProps = IPaymentMethodCreateProps; -export type PaymentMethodProps = PaymentMethodPatchProps; - export class PaymentMethod extends AggregateRoot { protected constructor(props: PaymentMethodInternalProps, id?: UniqueID) { super(props, id); // eslint-disable-line @typescript-eslint/no-unused-vars @@ -46,7 +48,23 @@ export class PaymentMethod extends AggregateRoot { return new PaymentMethod(props, id); } - private static validateCreateProps(_props: IPaymentMethodCreateProps): Result { + private static validateCreateProps(props: IPaymentMethodCreateProps): Result { + if (!props.companyId) { + return Result.fail(new Error("Payment method company ID is required")); + } + + if (!props.name) { + return Result.fail(new Error("Payment method name is required")); + } + + return Result.ok(); + } + + private static validatePatchProps(patchProps: PaymentMethodPatchProps): Result { + if (Object.keys(patchProps).length === 0) { + return Result.ok(); + } + return Result.ok(); } @@ -70,38 +88,96 @@ export class PaymentMethod extends AggregateRoot { return this.props.isSystem; } - public update(props: Partial): Result { - if (props.name !== undefined) { - this.props.name = props.name; + public update(patchProps: PaymentMethodPatchProps): Result { + if (this.isSystem) { + return Result.fail( + new PaymentMethodCannotBeUpdatedError("System payment methods cannot be updated.") + ); + } + const validationResult = PaymentMethod.validatePatchProps(patchProps); + + if (validationResult.isFailure) { + return Result.fail(validationResult.error); } - if (props.description !== undefined) { - this.props.description = props.description; + let hasChanges = false; + + if ( + patchProps.name !== undefined && + this.props.name.toPrimitive() !== patchProps.name.toPrimitive() + ) { + this.props.name = patchProps.name; + hasChanges = true; } - if (props.isActive !== undefined) { - this.props.isActive = props.isActive; + if ( + patchProps.description !== undefined && + !PaymentMethod.sameDescription(this.props.description, patchProps.description) + ) { + this.props.description = patchProps.description; + hasChanges = true; } - return Result.ok(); + if (patchProps.isActive !== undefined && this.props.isActive !== patchProps.isActive) { + this.props.isActive = patchProps.isActive; + hasChanges = true; + } + + return Result.ok(hasChanges); } - public disable(): Result { + public disable(): Result { + if (this.isSystem) { + return Result.fail( + new PaymentMethodCannotBeDisabledError("System payment methods cannot be disabled.") + ); + } + if (!this.isActive) { - return Result.ok(); + return Result.ok(false); } this.props.isActive = false; - return Result.ok(); + + return Result.ok(true); + } + + public enable(): Result { + if (this.isSystem) { + return Result.ok(false); + } + + if (this.isActive) { + return Result.ok(false); + } + + this.props.isActive = true; + + return Result.ok(true); } public toJSON() { return { id: this.id.toPrimitive(), - name: this.props.name.toPrimitive(), - description: this.props.description, + company_id: this.companyId.toPrimitive(), + name: this.name.toPrimitive(), + description: this.description.match( + (value) => value.toPrimitive(), + () => null + ), is_active: this.isActive, is_system: this.isSystem, }; } + + private static sameDescription(current: Maybe, next: Maybe): boolean { + return current.match( + (currentValue) => + next.match( + (nextValue) => currentValue.toPrimitive() === nextValue.toPrimitive(), + () => false + ), + () => next.isNone() + ); + } } diff --git a/modules/catalogs/src/api/infrastructure/di/payment-methods.di.ts b/modules/catalogs/src/api/infrastructure/di/payment-methods.di.ts index e3b54d52..456cd5cc 100644 --- a/modules/catalogs/src/api/infrastructure/di/payment-methods.di.ts +++ b/modules/catalogs/src/api/infrastructure/di/payment-methods.di.ts @@ -3,16 +3,21 @@ import type { Sequelize } from "sequelize"; import { buildPaymentMethodCreator, + buildPaymentMethodDeleter, buildPaymentMethodFinder, buildPaymentMethodInputMappers, buildPaymentMethodSnapshotBuilders, + buildPaymentMethodStatusChanger, + buildPaymentMethodUpdater, } from "../../application"; import type { IPaymentMethodRepository } from "../../application/repositories"; import type { IPaymentMethodFinder } from "../../application/services"; -import { PaymentMethodFinder, PaymentMethodUpdater } from "../../application/services"; +import { PaymentMethodFinder } from "../../application/services"; import { CreatePaymentMethodUseCase, - type DisablePaymentMethodByIdUseCase, + DeletePaymentMethodByIdUseCase, + DisablePaymentMethodByIdUseCase, + EnablePaymentMethodByIdUseCase, GetPaymentMethodByIdUseCase, ListPaymentMethodsUseCase, UpdatePaymentMethodByIdUseCase, @@ -28,7 +33,9 @@ export type PaymentMethodsInternalDeps = { getPaymentMethodById: () => GetPaymentMethodByIdUseCase; createPaymentMethod: () => CreatePaymentMethodUseCase; updatePaymentMethodById: () => UpdatePaymentMethodByIdUseCase; + deletePaymentMethodById: () => DeletePaymentMethodByIdUseCase; disablePaymentMethodById: () => DisablePaymentMethodByIdUseCase; + enablePaymentMethodById: () => EnablePaymentMethodByIdUseCase; }; }; @@ -45,10 +52,11 @@ export const buildPaymentMethodsDependencies = ( // Application helpers const inputMappers = buildPaymentMethodInputMappers(); - const finder = buildPaymentMethodFinder(repository); + const finder = buildPaymentMethodFinder({ repository }); const creator = buildPaymentMethodCreator({ repository }); - const updater = new PaymentMethodUpdater(repository); - //const disabler = new PaymentMethodDisabler(repository); + const updater = buildPaymentMethodUpdater({ repository }); + const deleter = buildPaymentMethodDeleter({ repository }); + const statusChanger = buildPaymentMethodStatusChanger({ repository }); const snapshotBuilders = buildPaymentMethodSnapshotBuilders(); @@ -57,8 +65,10 @@ export const buildPaymentMethodsDependencies = ( useCases: { listPaymentMethods: () => new ListPaymentMethodsUseCase(finder, snapshotBuilders.summary, transactionManager), + getPaymentMethodById: () => new GetPaymentMethodByIdUseCase(finder, snapshotBuilders.full, transactionManager), + createPaymentMethod: () => new CreatePaymentMethodUseCase({ dtoMapper: inputMappers.createInputMapper, @@ -75,13 +85,30 @@ export const buildPaymentMethodsDependencies = ( fullSnapshotBuilder: snapshotBuilders.full, transactionManager, }), - /*disablePaymentMethodById: () => + + deletePaymentMethodById: () => + new DeletePaymentMethodByIdUseCase({ + deleter, + finder, + fullSnapshotBuilder: snapshotBuilders.full, + transactionManager, + }), + + disablePaymentMethodById: () => new DisablePaymentMethodByIdUseCase({ finder, - disabler, - fullSnapshotBuilder, + changer: statusChanger, + fullSnapshotBuilder: snapshotBuilders.full, transactionManager, - }),*/ + }), + + enablePaymentMethodById: () => + new EnablePaymentMethodByIdUseCase({ + finder, + changer: statusChanger, + fullSnapshotBuilder: snapshotBuilders.full, + transactionManager, + }), }, }; }; diff --git a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/create-payment-method.controller.ts b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/create-payment-method.controller.ts index 23070994..88882eb2 100644 --- a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/create-payment-method.controller.ts +++ b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/create-payment-method.controller.ts @@ -7,13 +7,13 @@ import { import type { CreatePaymentMethodRequestDTO } from "../../../../../common"; import type { CreatePaymentMethodUseCase } from "../../../../application"; -import { paymentmethodsApiErrorMapper } from "../payment-methods-api-error-mapper"; +import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper"; export class CreatePaymentMethodController extends ExpressController { constructor(private readonly useCase: CreatePaymentMethodUseCase) { super(); - this.errorMapper = paymentmethodsApiErrorMapper; + this.errorMapper = paymentMethodsApiErrorMapper; // 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query this.registerGuards( diff --git a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/delete-payment-method-by-id.controller.ts b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/delete-payment-method-by-id.controller.ts new file mode 100644 index 00000000..44ff8e24 --- /dev/null +++ b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/delete-payment-method-by-id.controller.ts @@ -0,0 +1,39 @@ +import { + ExpressController, + forbidQueryFieldGuard, + requireAuthenticatedGuard, + requireCompanyContextGuard, +} from "@erp/core/api"; + +import type { DeletePaymentMethodByIdUseCase } from "../../../../application"; +import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper"; + +export class DeletePaymentMethodByIdController extends ExpressController { + constructor(private readonly useCase: DeletePaymentMethodByIdUseCase) { + super(); + + this.errorMapper = paymentMethodsApiErrorMapper; + + // 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query + this.registerGuards( + requireAuthenticatedGuard(), + requireCompanyContextGuard(), + forbidQueryFieldGuard("companyId") + ); + } + + protected async executeImpl() { + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + + const { payment_method_id } = this.req.params; + const result = await this.useCase.execute({ payment_method_id, companyId }); + + return result.match( + (data) => this.ok(data), + (err) => this.handleError(err) + ); + } +} diff --git a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/disable-payment-method-by-id.controller.ts b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/disable-payment-method-by-id.controller.ts index b7ecb04c..e321153a 100644 --- a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/disable-payment-method-by-id.controller.ts +++ b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/disable-payment-method-by-id.controller.ts @@ -1,15 +1,35 @@ -import { ExpressController } from "@erp/core/api"; +import { + ExpressController, + forbidQueryFieldGuard, + requireAuthenticatedGuard, + requireCompanyContextGuard, +} from "@erp/core/api"; import type { DisablePaymentMethodByIdUseCase } from "../../../../application"; +import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper"; export class DisablePaymentMethodByIdController extends ExpressController { constructor(private readonly useCase: DisablePaymentMethodByIdUseCase) { super(); + + this.errorMapper = paymentMethodsApiErrorMapper; + + // 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query + this.registerGuards( + requireAuthenticatedGuard(), + requireCompanyContextGuard(), + forbidQueryFieldGuard("companyId") + ); } protected async executeImpl() { - const id = this.req.params.payment_method_id; - const result = await this.useCase.execute({ id }); + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + + const { payment_method_id } = this.req.params; + const result = await this.useCase.execute({ payment_method_id, companyId }); return result.match( (data) => this.ok(data), diff --git a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/enable-payment-method-by-id.controller.ts b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/enable-payment-method-by-id.controller.ts new file mode 100644 index 00000000..ab3177a8 --- /dev/null +++ b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/enable-payment-method-by-id.controller.ts @@ -0,0 +1,39 @@ +import { + ExpressController, + forbidQueryFieldGuard, + requireAuthenticatedGuard, + requireCompanyContextGuard, +} from "@erp/core/api"; + +import type { EnablePaymentMethodByIdUseCase } from "../../../../application"; +import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper"; + +export class EnablePaymentMethodByIdController extends ExpressController { + constructor(private readonly useCase: EnablePaymentMethodByIdUseCase) { + super(); + + this.errorMapper = paymentMethodsApiErrorMapper; + + // 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query + this.registerGuards( + requireAuthenticatedGuard(), + requireCompanyContextGuard(), + forbidQueryFieldGuard("companyId") + ); + } + + protected async executeImpl() { + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + + const { payment_method_id } = this.req.params; + const result = await this.useCase.execute({ payment_method_id, companyId }); + + return result.match( + (data) => this.ok(data), + (err) => this.handleError(err) + ); + } +} diff --git a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/get-payment-method-by-id.controller.ts b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/get-payment-method-by-id.controller.ts index a921b5d2..6fcb8766 100644 --- a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/get-payment-method-by-id.controller.ts +++ b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/get-payment-method-by-id.controller.ts @@ -6,13 +6,13 @@ import { } from "@erp/core/api"; import type { GetPaymentMethodByIdUseCase } from "../../../../application"; -import { paymentmethodsApiErrorMapper } from "../payment-methods-api-error-mapper"; +import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper"; export class GetPaymentMethodByIdController extends ExpressController { constructor(private readonly useCase: GetPaymentMethodByIdUseCase) { super(); - this.errorMapper = paymentmethodsApiErrorMapper; + this.errorMapper = paymentMethodsApiErrorMapper; // 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query this.registerGuards( diff --git a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/index.ts b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/index.ts index 542c3595..118c1fc6 100644 --- a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/index.ts +++ b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/index.ts @@ -1,5 +1,7 @@ export * from "./create-payment-method.controller"; +export * from "./delete-payment-method-by-id.controller"; export * from "./disable-payment-method-by-id.controller"; +export * from "./enable-payment-method-by-id.controller"; export * from "./get-payment-method-by-id.controller"; export * from "./list-payment-methods.controller"; export * from "./update-payment-method-by-id.controller"; diff --git a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/list-payment-methods.controller.ts b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/list-payment-methods.controller.ts index a66f514a..6c1c8476 100644 --- a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/list-payment-methods.controller.ts +++ b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/list-payment-methods.controller.ts @@ -7,12 +7,12 @@ import { import { Criteria } from "@repo/rdx-criteria/server"; import type { ListPaymentMethodsUseCase } from "../../../../application"; -import { paymentmethodsApiErrorMapper } from "../payment-methods-api-error-mapper"; +import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper"; export class ListPaymentMethodsController extends ExpressController { constructor(private readonly useCase: ListPaymentMethodsUseCase) { super(); - this.errorMapper = paymentmethodsApiErrorMapper; + this.errorMapper = paymentMethodsApiErrorMapper; // 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query this.registerGuards( diff --git a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/update-payment-method-by-id.controller.ts b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/update-payment-method-by-id.controller.ts index 3f14c95b..c01285c1 100644 --- a/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/update-payment-method-by-id.controller.ts +++ b/modules/catalogs/src/api/infrastructure/express/payment-methods/controllers/update-payment-method-by-id.controller.ts @@ -7,13 +7,13 @@ import { } from "@erp/core/api"; import type { UpdatePaymentMethodByIdUseCase } from "../../../../application"; -import { paymentmethodsApiErrorMapper } from "../payment-methods-api-error-mapper"; +import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper"; export class UpdatePaymentMethodByIdController extends ExpressController { constructor(private readonly useCase: UpdatePaymentMethodByIdUseCase) { super(); - this.errorMapper = paymentmethodsApiErrorMapper; + this.errorMapper = paymentMethodsApiErrorMapper; // 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query this.registerGuards( diff --git a/modules/catalogs/src/api/infrastructure/express/payment-methods/payment-methods-api-error-mapper.ts b/modules/catalogs/src/api/infrastructure/express/payment-methods/payment-methods-api-error-mapper.ts index 287b9629..301e07f7 100644 --- a/modules/catalogs/src/api/infrastructure/express/payment-methods/payment-methods-api-error-mapper.ts +++ b/modules/catalogs/src/api/infrastructure/express/payment-methods/payment-methods-api-error-mapper.ts @@ -2,37 +2,96 @@ import { ApiErrorMapper, ConflictApiError, type ErrorToApiRule, + NotFoundApiError, ValidationApiError, } from "@erp/core/api"; import { type InvalidPaymentMethodIdError, + type InvalidPaymentMethodNameError, type PaymentMethodCannotBeDeletedError, + type PaymentMethodCannotBeDisabledError, + type PaymentMethodCannotBeEnabledError, + type PaymentMethodCannotBeUpdatedError, + type PaymentMethodNotFoundError, isInvalidPaymentMethodIdError, + isInvalidPaymentMethodNameError, isPaymentMethodCannotBeDeletedError, + isPaymentMethodCannotBeDisabledError, + isPaymentMethodCannotBeEnabledError, + isPaymentMethodCannotBeUpdatedError, + isPaymentMethodNotFoundError, } from "../../../domain/payment-methods"; -// Crea una regla específica (prioridad alta para sobreescribir mensajes) -const paymentmethodDuplicateRule: ErrorToApiRule = { +const invalidPaymentMethodIdRule: ErrorToApiRule = { priority: 120, - matches: (e) => isInvalidPaymentMethodIdError(e), - build: (e) => + matches: isInvalidPaymentMethodIdError, + build: (error) => new ConflictApiError( - (e as InvalidPaymentMethodIdError).message || + (error as InvalidPaymentMethodIdError).message || "Payment method with the provided id already exists." ), }; -const paymentmethodCannotBeDeletedRule: ErrorToApiRule = { +const invalidPaymentMethodNameRule: ErrorToApiRule = { priority: 120, - matches: (e) => isPaymentMethodCannotBeDeletedError(e), - build: (e) => + matches: isInvalidPaymentMethodNameError, + build: (error) => new ValidationApiError( - (e as PaymentMethodCannotBeDeletedError).message || "Payment method cannot be deleted." + (error as InvalidPaymentMethodNameError).message || "Payment method name is invalid." ), }; -// Cómo aplicarla: crea una nueva instancia del mapper con la regla extra -export const paymentmethodsApiErrorMapper: ApiErrorMapper = ApiErrorMapper.default() - .register(paymentmethodDuplicateRule) - .register(paymentmethodCannotBeDeletedRule); +const paymentMethodNotFoundRule: ErrorToApiRule = { + priority: 120, + matches: isPaymentMethodNotFoundError, + build: (error) => + new NotFoundApiError( + (error as PaymentMethodNotFoundError).message || "Payment method not found." + ), +}; + +const paymentMethodCannotBeDeletedRule: ErrorToApiRule = { + priority: 120, + matches: isPaymentMethodCannotBeDeletedError, + build: (error) => + new ValidationApiError( + (error as PaymentMethodCannotBeDeletedError).message || "Payment method cannot be deleted." + ), +}; + +const paymentMethodCannotBeDisabledRule: ErrorToApiRule = { + priority: 120, + matches: isPaymentMethodCannotBeDisabledError, + build: (error) => + new ValidationApiError( + (error as PaymentMethodCannotBeDisabledError).message || "Payment method cannot be disabled." + ), +}; + +const paymentMethodCannotBeEnabledRule: ErrorToApiRule = { + priority: 120, + matches: isPaymentMethodCannotBeEnabledError, + build: (error) => + new ValidationApiError( + (error as PaymentMethodCannotBeEnabledError).message || "Payment method cannot be enabled." + ), +}; + +const paymentMethodCannotBeUpdatedRule: ErrorToApiRule = { + priority: 120, + matches: isPaymentMethodCannotBeUpdatedError, + build: (error) => + new ValidationApiError( + (error as PaymentMethodCannotBeUpdatedError).message || "Payment method cannot be updated." + ), +}; + +export const paymentMethodsApiErrorMapper: ApiErrorMapper = ApiErrorMapper.default() + .register(invalidPaymentMethodIdRule) + .register(invalidPaymentMethodNameRule) + .register(paymentMethodNotFoundRule) + .register(paymentMethodCannotBeDeletedRule) + .register(paymentMethodCannotBeDisabledRule) + .register(paymentMethodCannotBeEnabledRule) + .register(paymentMethodCannotBeUpdatedRule); diff --git a/modules/catalogs/src/api/infrastructure/express/payment-methods/payment-methods.routes.ts b/modules/catalogs/src/api/infrastructure/express/payment-methods/payment-methods.routes.ts index 8d19d894..4f9c0194 100644 --- a/modules/catalogs/src/api/infrastructure/express/payment-methods/payment-methods.routes.ts +++ b/modules/catalogs/src/api/infrastructure/express/payment-methods/payment-methods.routes.ts @@ -4,16 +4,19 @@ import { type NextFunction, type Request, type Response, Router } from "express" import { CreatePaymentMethodRequestSchema, + DeletePaymentMethodByIdRequestSchema, GetPaymentMethodByIdRequestSchema, ListPaymentMethodsRequestSchema, UpdatePaymentMethodByIdParamsRequestSchema, UpdatePaymentMethodByIdRequestSchema, -} from "../../../../common/dto/payment-methods/request"; +} from "../../../../common"; import type { CatalogsInternalDeps } from "../../di/catalogs.di"; import { CreatePaymentMethodController, + DeletePaymentMethodByIdController, DisablePaymentMethodByIdController, + EnablePaymentMethodByIdController, GetPaymentMethodByIdController, ListPaymentMethodsController, UpdatePaymentMethodByIdController, @@ -64,7 +67,18 @@ export const paymentMethodsRouter = (params: StartParams) => { } ); - router.patch( + router.delete( + "/:payment_method_id", + validateRequest(DeletePaymentMethodByIdRequestSchema, "params"), + (req, res, next) => { + const controller = new DeletePaymentMethodByIdController( + deps.useCases.deletePaymentMethodById() + ); + return controller.execute(req, res, next); + } + ); + + router.put( "/:payment_method_id", validateRequest(UpdatePaymentMethodByIdParamsRequestSchema, "params"), validateRequest(UpdatePaymentMethodByIdRequestSchema, "body"), @@ -87,5 +101,16 @@ export const paymentMethodsRouter = (params: StartParams) => { } ); + router.patch( + "/:payment_method_id/enable", + validateRequest(GetPaymentMethodByIdRequestSchema, "params"), + (req, res, next) => { + const controller = new EnablePaymentMethodByIdController( + deps.useCases.enablePaymentMethodById() + ); + return controller.execute(req, res, next); + } + ); + app.use(`${config.server.apiBasePath}/catalogs/payment-methods`, router); }; diff --git a/modules/catalogs/src/api/infrastructure/persistence/sequelize/mappers/sequelize-payment-method-domain.mapper.ts b/modules/catalogs/src/api/infrastructure/persistence/sequelize/mappers/sequelize-payment-method-domain.mapper.ts index 9d044a93..b22ae5be 100644 --- a/modules/catalogs/src/api/infrastructure/persistence/sequelize/mappers/sequelize-payment-method-domain.mapper.ts +++ b/modules/catalogs/src/api/infrastructure/persistence/sequelize/mappers/sequelize-payment-method-domain.mapper.ts @@ -21,7 +21,7 @@ export class SequelizePaymentMethodDomainMapper extends SequelizeDomainMapper< > { public mapToDomain( source: PaymentMethodModel, - params?: MapperParamsType + _params?: MapperParamsType ): Result { try { const errors: ValidationErrorDetail[] = []; @@ -71,7 +71,7 @@ export class SequelizePaymentMethodDomainMapper extends SequelizeDomainMapper< public mapToPersistence( source: PaymentMethod, - params?: MapperParamsType + _params?: MapperParamsType ): Result { return Result.ok({ id: source.id.toPrimitive(), diff --git a/modules/catalogs/src/api/infrastructure/persistence/sequelize/mappers/sequelize-payment-method-summary.mapper.ts b/modules/catalogs/src/api/infrastructure/persistence/sequelize/mappers/sequelize-payment-method-summary.mapper.ts index 78e62b1d..73408a22 100644 --- a/modules/catalogs/src/api/infrastructure/persistence/sequelize/mappers/sequelize-payment-method-summary.mapper.ts +++ b/modules/catalogs/src/api/infrastructure/persistence/sequelize/mappers/sequelize-payment-method-summary.mapper.ts @@ -17,7 +17,7 @@ export class SequelizePaymentMethodSummaryMapper extends SequelizeQueryMapper< > { public mapToReadModel( raw: PaymentMethodModel, - params?: MapperParamsType + _params?: MapperParamsType ): Result { const errors: ValidationErrorDetail[] = []; diff --git a/modules/catalogs/src/api/infrastructure/persistence/sequelize/repositories/sequelize-payment-method.repository.ts b/modules/catalogs/src/api/infrastructure/persistence/sequelize/repositories/sequelize-payment-method.repository.ts index 74e27843..8fedb6fb 100644 --- a/modules/catalogs/src/api/infrastructure/persistence/sequelize/repositories/sequelize-payment-method.repository.ts +++ b/modules/catalogs/src/api/infrastructure/persistence/sequelize/repositories/sequelize-payment-method.repository.ts @@ -71,12 +71,14 @@ export class SequelizePaymentMethodRepository } const { id, ...payload } = dtoResult.data; - const [affected] = await PaymentMethodModel.update(payload, { + const [affected, updated] = await PaymentMethodModel.update(payload, { where: { id }, transaction, individualHooks: true, }); + console.log("Update result:", { affected, updated }); + if (affected === 0) { return Result.fail( new InfrastructureRepositoryError("Concurrency conflict or payment method not found") diff --git a/modules/catalogs/src/common/dto/payment-methods/request/delete-payment-method-by-id.request.dto.ts b/modules/catalogs/src/common/dto/payment-methods/request/delete-payment-method-by-id.request.dto.ts new file mode 100644 index 00000000..a17342cc --- /dev/null +++ b/modules/catalogs/src/common/dto/payment-methods/request/delete-payment-method-by-id.request.dto.ts @@ -0,0 +1,9 @@ +import { z } from "zod/v4"; + +export const DeletePaymentMethodByIdRequestSchema = z.object({ + payment_method_id: z.uuid(), +}); + +export type DeletePaymentMethodByIdRequestDTO = z.infer< + typeof DeletePaymentMethodByIdRequestSchema +>; diff --git a/modules/catalogs/src/common/dto/payment-methods/request/index.ts b/modules/catalogs/src/common/dto/payment-methods/request/index.ts index a73f5ad7..419ca9fb 100644 --- a/modules/catalogs/src/common/dto/payment-methods/request/index.ts +++ b/modules/catalogs/src/common/dto/payment-methods/request/index.ts @@ -1,4 +1,5 @@ export * from "./create-payment-method.request.dto"; +export * from "./delete-payment-method-by-id.request.dto"; export * from "./get-payment-method-by-id.request.dto"; export * from "./list-payment-methods.request.dto"; export * from "./update-payment-method-by-id.request.dto"; diff --git a/packages/rdx-ddd/src/value-objects/name.ts b/packages/rdx-ddd/src/value-objects/name.ts index eaf98832..4175884c 100644 --- a/packages/rdx-ddd/src/value-objects/name.ts +++ b/packages/rdx-ddd/src/value-objects/name.ts @@ -1,6 +1,8 @@ import { Result } from "@repo/rdx-utils"; import { z } from "zod/v4"; + import { translateZodValidationError } from "../helpers"; + import { ValueObject } from "./value-object"; interface NameProps { @@ -24,7 +26,7 @@ export class Name extends ValueObject { if (!valueIsValid.success) { return Result.fail(translateZodValidationError("Name creation failed", valueIsValid.error)); } - return Result.ok(new Name({ value })); + return Result.ok(new Name({ value: valueIsValid.data })); } static generateAcronym(name: string): string { diff --git a/packages/rdx-ddd/src/value-objects/text-value.ts b/packages/rdx-ddd/src/value-objects/text-value.ts index 5964697d..2238e622 100644 --- a/packages/rdx-ddd/src/value-objects/text-value.ts +++ b/packages/rdx-ddd/src/value-objects/text-value.ts @@ -1,6 +1,8 @@ import { Result } from "@repo/rdx-utils"; import { z } from "zod/v4"; + import { translateZodValidationError } from "../helpers"; + import { ValueObject } from "./value-object"; interface TextValueProps { diff --git a/packages/rdx-utils/src/helpers/maybe.ts b/packages/rdx-utils/src/helpers/maybe.ts index d8b90810..c6326ea0 100644 --- a/packages/rdx-utils/src/helpers/maybe.ts +++ b/packages/rdx-utils/src/helpers/maybe.ts @@ -16,6 +16,7 @@ export interface IMaybe { getOrUndefined(): T | undefined; map(fn: (value: T) => U): IMaybe; match(someFn: (value: T) => U, noneFn: () => U): U; + equals(other: IMaybe): boolean; } export function isMaybe(input: unknown): input is Maybe { @@ -40,6 +41,18 @@ export class Maybe implements IMaybe { return new Maybe(); } + equals(other: IMaybe): boolean { + if (this.isNone() && other.isNone()) { + return true; + } + + if (this.isSome() && other.isSome()) { + return this.unwrap() === other.unwrap(); + } + + return false; + } + isSome(): boolean { return this.value !== undefined; }