Métodos de pago
This commit is contained in:
parent
996cf4ceb4
commit
cd4b8975c3
15
.ai/backend/agent.md
Normal file
15
.ai/backend/agent.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
Especialización backend.
|
||||||
|
|
||||||
|
Contiene:
|
||||||
|
|
||||||
|
- Clean Architecture
|
||||||
|
- DDD
|
||||||
|
- módulos
|
||||||
|
- Sequelize
|
||||||
|
- DTOs
|
||||||
|
- repositorios
|
||||||
|
- fiscalidad
|
||||||
|
- use cases
|
||||||
|
- Express
|
||||||
|
- snapshot builders
|
||||||
|
|
||||||
32
.ai/frontend/agent.md
Normal file
32
.ai/frontend/agent.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Frontend ERP Agent
|
||||||
|
|
||||||
|
## Objetivo
|
||||||
|
|
||||||
|
Construir frontend ERP modular, tipado, mantenible y desacoplado.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Stack
|
||||||
|
|
||||||
|
- React
|
||||||
|
- TypeScript
|
||||||
|
- Vite
|
||||||
|
- TanStack Query
|
||||||
|
- React Hook Form
|
||||||
|
- Zod
|
||||||
|
- Zustand (si aplica)
|
||||||
|
- Tailwind/Shadcn/etc
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Principios
|
||||||
|
|
||||||
|
- UI desacoplada de transporte
|
||||||
|
- SSR/CSR explícito
|
||||||
|
- Estado mínimo global
|
||||||
|
- Formularios tipados
|
||||||
|
- Server state != UI state
|
||||||
|
- Componentes pequeños
|
||||||
|
- Evitar smart components gigantes
|
||||||
|
- Accesibilidad obligatoria
|
||||||
|
- Diseño orientado a features
|
||||||
10
.ai/index.md
Normal file
10
.ai/index.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# ERP AI Context
|
||||||
|
|
||||||
|
## Shared => filosofía + convenciones
|
||||||
|
- ./shared/agent.md
|
||||||
|
|
||||||
|
## Backend => dominio + arquitectura + persistencia
|
||||||
|
- ./backend/agent.md
|
||||||
|
|
||||||
|
## Frontend => interacción + UX + estado + rendering
|
||||||
|
- ./frontend/agent.md
|
||||||
5
.ai/shared/convenciones.md
Normal file
5
.ai/shared/convenciones.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
- Mantener código en inglés.
|
||||||
|
- Comentarios solo si aportan, en español.
|
||||||
|
- No usar any.
|
||||||
|
- No crear abstracciones genéricas prematuras.
|
||||||
|
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import catalogsAPIModule from "@erp/catalogs/api";
|
||||||
import customerInvoicesAPIModule from "@erp/customer-invoices/api";
|
import customerInvoicesAPIModule from "@erp/customer-invoices/api";
|
||||||
import customersAPIModule from "@erp/customers/api";
|
import customersAPIModule from "@erp/customers/api";
|
||||||
import factuGESAPIModule from "@erp/factuges/api";
|
import factuGESAPIModule from "@erp/factuges/api";
|
||||||
@ -8,6 +9,7 @@ import { registerModule } from "./lib";
|
|||||||
|
|
||||||
export const registerModules = () => {
|
export const registerModules = () => {
|
||||||
//registerModule(authAPIModule);
|
//registerModule(authAPIModule);
|
||||||
|
registerModule(catalogsAPIModule);
|
||||||
registerModule(customersAPIModule);
|
registerModule(customersAPIModule);
|
||||||
registerModule(customerInvoicesAPIModule);
|
registerModule(customerInvoicesAPIModule);
|
||||||
registerModule(factuGESAPIModule);
|
registerModule(factuGESAPIModule);
|
||||||
|
|||||||
32
modules/catalogs/package.json
Normal file
32
modules/catalogs/package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "@erp/catalogs",
|
||||||
|
"description": "Catalogs module",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": false,
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||||
|
"check": "biome check .",
|
||||||
|
"lint": "biome lint .",
|
||||||
|
"clean": "rimraf .turbo node_modules dist"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": "./src/api/index.ts",
|
||||||
|
"./api": "./src/api/index.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@erp/core": "workspace:*",
|
||||||
|
"@erp/auth": "workspace:*",
|
||||||
|
"@repo/rdx-ddd": "workspace:*",
|
||||||
|
"@repo/rdx-utils": "workspace:*",
|
||||||
|
"@repo/rdx-criteria": "workspace:*",
|
||||||
|
"express": "^4.22.1",
|
||||||
|
"sequelize": "^6.37.8",
|
||||||
|
"zod": "^4.3.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/express": "^4.17.21",
|
||||||
|
"typescript": "^6.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
5
modules/catalogs/src/api/application/di/index.ts
Normal file
5
modules/catalogs/src/api/application/di/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export * from "./payment-method-creator.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-updater.di";
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import type { IPaymentMethodRepository } from "../repositories";
|
||||||
|
import { type IPaymentMethodCreator, PaymentMethodCreator } from "../services";
|
||||||
|
|
||||||
|
export const buildPaymentMethodCreator = (params: {
|
||||||
|
repository: IPaymentMethodRepository;
|
||||||
|
}): IPaymentMethodCreator => {
|
||||||
|
const { repository } = params;
|
||||||
|
|
||||||
|
return new PaymentMethodCreator(repository);
|
||||||
|
};
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import type { IPaymentMethodRepository } from "../repositories";
|
||||||
|
import { type IPaymentMethodFinder, PaymentMethodFinder } from "../services";
|
||||||
|
|
||||||
|
export function buildPaymentMethodFinder(
|
||||||
|
repository: IPaymentMethodRepository
|
||||||
|
): IPaymentMethodFinder {
|
||||||
|
return new PaymentMethodFinder(repository);
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import {
|
||||||
|
CreatePaymentMethodInputMapper,
|
||||||
|
type ICreatePaymentMethodInputMapper,
|
||||||
|
UpdatePaymentMethodInputMapper,
|
||||||
|
} from "../mappers";
|
||||||
|
|
||||||
|
export interface IPaymentMethodInputMappers {
|
||||||
|
createInputMapper: ICreatePaymentMethodInputMapper;
|
||||||
|
updateInputMapper: UpdatePaymentMethodInputMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const buildPaymentMethodInputMappers = (): IPaymentMethodInputMappers => {
|
||||||
|
// Mappers el DTO a las props validadas (PaymentMethodProps) y luego construir agregado
|
||||||
|
const createInputMapper = new CreatePaymentMethodInputMapper();
|
||||||
|
const updateInputMapper = new UpdatePaymentMethodInputMapper();
|
||||||
|
|
||||||
|
return {
|
||||||
|
createInputMapper,
|
||||||
|
updateInputMapper,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
// application/issued-invoices/di/snapshot-builders.di.ts
|
||||||
|
|
||||||
|
import {
|
||||||
|
PaymentMethodFullSnapshotBuilder,
|
||||||
|
PaymentMethodSummarySnapshotBuilder,
|
||||||
|
} from "../snapshot-builders";
|
||||||
|
|
||||||
|
export function buildPaymentMethodSnapshotBuilders() {
|
||||||
|
const fullSnapshotBuilder = new PaymentMethodFullSnapshotBuilder();
|
||||||
|
const summarySnapshotBuilder = new PaymentMethodSummarySnapshotBuilder();
|
||||||
|
|
||||||
|
return {
|
||||||
|
full: fullSnapshotBuilder,
|
||||||
|
summary: summarySnapshotBuilder,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import type { IPaymentMethodRepository } from "../repositories";
|
||||||
|
import { type IPaymentMethodUpdater, PaymentMethodUpdater } from "../services";
|
||||||
|
|
||||||
|
export const buildPaymentMethodUpdater = (params: {
|
||||||
|
repository: IPaymentMethodRepository;
|
||||||
|
}): IPaymentMethodUpdater => {
|
||||||
|
const { repository } = params;
|
||||||
|
|
||||||
|
return new PaymentMethodUpdater(repository);
|
||||||
|
};
|
||||||
7
modules/catalogs/src/api/application/index.ts
Normal file
7
modules/catalogs/src/api/application/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export * from "./di";
|
||||||
|
export * from "./mappers";
|
||||||
|
export * from "./models";
|
||||||
|
export * from "./repositories";
|
||||||
|
export * from "./services";
|
||||||
|
export * from "./snapshot-builders";
|
||||||
|
export * from "./use-cases";
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
import {
|
||||||
|
DomainError,
|
||||||
|
Name,
|
||||||
|
TextValue,
|
||||||
|
UniqueID,
|
||||||
|
ValidationErrorCollection,
|
||||||
|
type ValidationErrorDetail,
|
||||||
|
extractOrPushError,
|
||||||
|
maybeFromNullableResult,
|
||||||
|
} from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { CreatePaymentMethodRequestDTO } from "../../../common";
|
||||||
|
import type { IPaymentMethodCreateProps } from "../../domain";
|
||||||
|
|
||||||
|
export interface ICreatePaymentMethodInputMapper {
|
||||||
|
map(
|
||||||
|
dto: CreatePaymentMethodRequestDTO,
|
||||||
|
params: { companyId: UniqueID }
|
||||||
|
): Result<{ id: UniqueID; props: IPaymentMethodCreateProps }, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreatePaymentMethodInputMapper implements ICreatePaymentMethodInputMapper {
|
||||||
|
public map(
|
||||||
|
dto: CreatePaymentMethodRequestDTO,
|
||||||
|
params: { companyId: UniqueID }
|
||||||
|
): Result<{ id: UniqueID; props: IPaymentMethodCreateProps }, Error> {
|
||||||
|
const errors: ValidationErrorDetail[] = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const paymentMethodId = extractOrPushError(UniqueID.create(dto.id), "id", errors);
|
||||||
|
|
||||||
|
const name = extractOrPushError(Name.create(dto.name), "name", errors);
|
||||||
|
|
||||||
|
const description = extractOrPushError(
|
||||||
|
maybeFromNullableResult(dto.description, (value) => TextValue.create(value)),
|
||||||
|
"description",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const isActive = extractOrPushError(Result.ok(dto.is_active), "is_active", errors);
|
||||||
|
|
||||||
|
this.throwIfValidationErrors(errors);
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
companyId: params.companyId,
|
||||||
|
name: name!,
|
||||||
|
description: description!,
|
||||||
|
isActive: isActive!,
|
||||||
|
isSystem: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Result.ok({
|
||||||
|
id: paymentMethodId!,
|
||||||
|
props,
|
||||||
|
});
|
||||||
|
} catch (err: unknown) {
|
||||||
|
return Result.fail(new DomainError("Payment method props mapping failed", { cause: err }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private throwIfValidationErrors(errors: ValidationErrorDetail[]): void {
|
||||||
|
if (errors.length > 0) {
|
||||||
|
throw new ValidationErrorCollection("Payment method props mapping failed", errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
modules/catalogs/src/api/application/mappers/index.ts
Normal file
2
modules/catalogs/src/api/application/mappers/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./create-payment-method-input.mapper";
|
||||||
|
export * from "./update-payment-method-by-id-input.mapper";
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
import {
|
||||||
|
DomainError,
|
||||||
|
Name,
|
||||||
|
TextValue,
|
||||||
|
type UniqueID,
|
||||||
|
ValidationErrorCollection,
|
||||||
|
type ValidationErrorDetail,
|
||||||
|
extractOrPushError,
|
||||||
|
maybeFromNullableResult,
|
||||||
|
} from "@repo/rdx-ddd";
|
||||||
|
import { Result, isNullishOrEmpty, toPatchField } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { UpdatePaymentMethodByIdRequestDTO } from "../../../common";
|
||||||
|
import type { PaymentMethodPatchProps } from "../../domain";
|
||||||
|
|
||||||
|
export interface IUpdatePaymentMethodInputMapper {
|
||||||
|
map(
|
||||||
|
dto: UpdatePaymentMethodByIdRequestDTO,
|
||||||
|
params: { companyId: UniqueID }
|
||||||
|
): Result<PaymentMethodPatchProps>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Convierte el DTO de update de forma de pago en props de dominio.
|
||||||
|
* @remarks
|
||||||
|
* Respeta semántica PATCH en cabecera:
|
||||||
|
* - omitido: no modificar
|
||||||
|
* - null: limpiar valor cuando el campo lo permite
|
||||||
|
* - valor: asignar nuevo valor
|
||||||
|
*
|
||||||
|
* Para `items`, no aplica patch granular:
|
||||||
|
* - undefined: no tocar líneas
|
||||||
|
* - []: borrar todas las líneas
|
||||||
|
* - [...]: reemplazar colección completa
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class UpdatePaymentMethodInputMapper implements IUpdatePaymentMethodInputMapper {
|
||||||
|
public map(
|
||||||
|
dto: UpdatePaymentMethodByIdRequestDTO,
|
||||||
|
_params: { companyId: UniqueID }
|
||||||
|
): Result<PaymentMethodPatchProps> {
|
||||||
|
try {
|
||||||
|
const errors: ValidationErrorDetail[] = [];
|
||||||
|
const paymentmethodPatchProps: PaymentMethodPatchProps = {};
|
||||||
|
|
||||||
|
toPatchField(dto.name).ifSet((name) => {
|
||||||
|
if (isNullishOrEmpty(name)) {
|
||||||
|
errors.push({
|
||||||
|
path: "name",
|
||||||
|
message: "Name cannot be empty",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentmethodPatchProps.name = extractOrPushError(Name.create(name), "name", errors);
|
||||||
|
});
|
||||||
|
|
||||||
|
toPatchField(dto.description).ifSet((description) => {
|
||||||
|
paymentmethodPatchProps.description = extractOrPushError(
|
||||||
|
maybeFromNullableResult(description, (value) => TextValue.create(value)),
|
||||||
|
"description",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
toPatchField(dto.is_active).ifSet((isActive) => {
|
||||||
|
paymentmethodPatchProps.isActive = isActive;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.throwIfValidationErrors(errors);
|
||||||
|
|
||||||
|
return Result.ok(paymentmethodPatchProps);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
console.error(err);
|
||||||
|
return Result.fail(
|
||||||
|
new DomainError("PaymentMethod props mapping failed (update)", { cause: err })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private throwIfValidationErrors(errors: ValidationErrorDetail[]): void {
|
||||||
|
if (errors.length > 0) {
|
||||||
|
throw new ValidationErrorCollection("PaymentMethod props mapping failed", errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
modules/catalogs/src/api/application/models/index.ts
Normal file
2
modules/catalogs/src/api/application/models/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./payment-method-detail.model";
|
||||||
|
export * from "./payment-method-summary.model";
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
export type PaymentMethodDetail = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
is_active: boolean;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
};
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import type { Name, UniqueID } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
|
export type PaymentMethodSummary = {
|
||||||
|
id: UniqueID;
|
||||||
|
companyId: UniqueID;
|
||||||
|
name: Name;
|
||||||
|
isActive: boolean;
|
||||||
|
isSystem: boolean;
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./payment-method-repository.interface";
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
import type { Criteria } from "@repo/rdx-criteria/server";
|
||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import type { Collection, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { PaymentMethod } from "../../domain";
|
||||||
|
import type { PaymentMethodSummary } from "../models";
|
||||||
|
|
||||||
|
export interface IPaymentMethodRepository {
|
||||||
|
create(paymentMethod: PaymentMethod, transaction?: unknown): Promise<Result<void, Error>>;
|
||||||
|
update(paymentMethod: PaymentMethod, transaction?: unknown): Promise<Result<void, Error>>;
|
||||||
|
existsByIdInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
transaction?: unknown
|
||||||
|
): Promise<Result<boolean, Error>>;
|
||||||
|
getByIdInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
transaction?: unknown
|
||||||
|
): Promise<Result<PaymentMethod, Error>>;
|
||||||
|
findByCriteriaInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
criteria: Criteria,
|
||||||
|
transaction?: unknown
|
||||||
|
): Promise<Result<Collection<PaymentMethodSummary>, Error>>;
|
||||||
|
deleteByIdInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
transaction?: unknown
|
||||||
|
): Promise<Result<boolean, Error>>;
|
||||||
|
}
|
||||||
5
modules/catalogs/src/api/application/services/index.ts
Normal file
5
modules/catalogs/src/api/application/services/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export * from "./payment-method-creator";
|
||||||
|
export * from "./payment-method-disabler";
|
||||||
|
export * from "./payment-method-finder";
|
||||||
|
export * from "./payment-method-public-services";
|
||||||
|
export * from "./payment-method-updater";
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import { type IPaymentMethodCreateProps, PaymentMethod } from "../../domain";
|
||||||
|
import type { IPaymentMethodRepository } from "../repositories";
|
||||||
|
|
||||||
|
export interface IPaymentMethodCreatorParams {
|
||||||
|
companyId: UniqueID;
|
||||||
|
id: UniqueID;
|
||||||
|
props: IPaymentMethodCreateProps;
|
||||||
|
transaction: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPaymentMethodCreator {
|
||||||
|
create(params: IPaymentMethodCreatorParams): Promise<Result<PaymentMethod, Error>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PaymentMethodCreator implements IPaymentMethodCreator {
|
||||||
|
constructor(private readonly repository: IPaymentMethodRepository) {}
|
||||||
|
|
||||||
|
async create(params: IPaymentMethodCreatorParams): Promise<Result<PaymentMethod, Error>> {
|
||||||
|
const { companyId, id, props, transaction } = params;
|
||||||
|
|
||||||
|
// 1. Crear agregado
|
||||||
|
const paymentMethodResult = PaymentMethod.create({ ...props, companyId }, id);
|
||||||
|
|
||||||
|
if (paymentMethodResult.isFailure) {
|
||||||
|
return Result.fail(paymentMethodResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const paymentMethod = paymentMethodResult.data;
|
||||||
|
|
||||||
|
// 2. Persistir
|
||||||
|
const saveResult = await this.repository.create(paymentMethod, transaction);
|
||||||
|
if (saveResult.isFailure) {
|
||||||
|
return Result.fail(saveResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(paymentMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
import type { Transaction } from "sequelize";
|
||||||
|
|
||||||
|
import type { PaymentMethod } from "../../domain";
|
||||||
|
import type { IPaymentMethodRepository } from "../repositories";
|
||||||
|
|
||||||
|
export interface IPaymentMethodDisabler {
|
||||||
|
disable(params: {
|
||||||
|
paymentMethod: PaymentMethod;
|
||||||
|
transaction?: Transaction;
|
||||||
|
}): Promise<Result<PaymentMethod, Error>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PaymentMethodDisabler implements IPaymentMethodDisabler {
|
||||||
|
constructor(private readonly repository: IPaymentMethodRepository) {}
|
||||||
|
|
||||||
|
async disable(params: {
|
||||||
|
paymentMethod: PaymentMethod;
|
||||||
|
transaction?: Transaction;
|
||||||
|
}): Promise<Result<PaymentMethod, Error>> {
|
||||||
|
const { paymentMethod, transaction } = params;
|
||||||
|
|
||||||
|
const disableResult = paymentMethod.disable();
|
||||||
|
if (disableResult.isFailure) {
|
||||||
|
return Result.fail(disableResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const persistenceResult = await this.repository.update(paymentMethod, transaction);
|
||||||
|
if (persistenceResult.isFailure) {
|
||||||
|
return Result.fail(persistenceResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(paymentMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
import type { Criteria } from "@repo/rdx-criteria/server";
|
||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import type { Collection, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { PaymentMethod } from "../../domain";
|
||||||
|
import type { PaymentMethodSummary } from "../models";
|
||||||
|
import type { IPaymentMethodRepository } from "../repositories";
|
||||||
|
|
||||||
|
export interface IPaymentMethodFinder {
|
||||||
|
findPaymentMethodById(
|
||||||
|
companyId: UniqueID,
|
||||||
|
invoiceId: UniqueID,
|
||||||
|
transaction?: unknown
|
||||||
|
): Promise<Result<PaymentMethod, Error>>;
|
||||||
|
|
||||||
|
paymentmethodExists(
|
||||||
|
companyId: UniqueID,
|
||||||
|
invoiceId: UniqueID,
|
||||||
|
transaction?: unknown
|
||||||
|
): Promise<Result<boolean, Error>>;
|
||||||
|
|
||||||
|
findPaymentMethodsByCriteria(
|
||||||
|
companyId: UniqueID,
|
||||||
|
criteria: Criteria,
|
||||||
|
transaction?: unknown
|
||||||
|
): Promise<Result<Collection<PaymentMethodSummary>, Error>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PaymentMethodFinder implements IPaymentMethodFinder {
|
||||||
|
constructor(private readonly repository: IPaymentMethodRepository) {}
|
||||||
|
|
||||||
|
async findPaymentMethodById(
|
||||||
|
companyId: UniqueID,
|
||||||
|
paymentmethodId: UniqueID,
|
||||||
|
transaction?: unknown
|
||||||
|
): Promise<Result<PaymentMethod, Error>> {
|
||||||
|
return this.repository.getByIdInCompany(companyId, paymentmethodId, transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
async paymentmethodExists(
|
||||||
|
companyId: UniqueID,
|
||||||
|
paymentmethodId: UniqueID,
|
||||||
|
transaction?: unknown
|
||||||
|
): Promise<Result<boolean, Error>> {
|
||||||
|
return this.repository.existsByIdInCompany(companyId, paymentmethodId, transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findPaymentMethodsByCriteria(
|
||||||
|
companyId: UniqueID,
|
||||||
|
criteria: Criteria,
|
||||||
|
transaction?: unknown
|
||||||
|
): Promise<Result<Collection<PaymentMethodSummary>, Error>> {
|
||||||
|
return this.repository.findByCriteriaInCompany(companyId, criteria, transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import type { IPaymentMethodFinder } from "./payment-method-finder";
|
||||||
|
|
||||||
|
export type IPaymentMethodPublicServices = {
|
||||||
|
finder: IPaymentMethodFinder;
|
||||||
|
};
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { PaymentMethod, PaymentMethodPatchProps } from "../../domain";
|
||||||
|
import type { IPaymentMethodRepository } from "../repositories";
|
||||||
|
|
||||||
|
export interface IPaymentMethodUpdater {
|
||||||
|
update(params: {
|
||||||
|
companyId: UniqueID;
|
||||||
|
id: UniqueID;
|
||||||
|
patchProps: PaymentMethodPatchProps;
|
||||||
|
transaction?: unknown;
|
||||||
|
}): Promise<Result<PaymentMethod, Error>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PaymentMethodUpdater implements IPaymentMethodUpdater {
|
||||||
|
constructor(private readonly repository: IPaymentMethodRepository) {}
|
||||||
|
|
||||||
|
async update(params: {
|
||||||
|
companyId: UniqueID;
|
||||||
|
id: UniqueID;
|
||||||
|
patchProps: PaymentMethodPatchProps;
|
||||||
|
transaction?: unknown;
|
||||||
|
}): Promise<Result<PaymentMethod, Error>> {
|
||||||
|
const { companyId, id, patchProps, transaction } = params;
|
||||||
|
|
||||||
|
// Recuperar agregado existente
|
||||||
|
const existingResult = await this.repository.getByIdInCompany(companyId, id, transaction);
|
||||||
|
|
||||||
|
if (existingResult.isFailure) {
|
||||||
|
return Result.fail(existingResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const paymentMethod = existingResult.data;
|
||||||
|
|
||||||
|
// Aplicar cambios en el agregado
|
||||||
|
const updateResult = paymentMethod.update(patchProps);
|
||||||
|
|
||||||
|
if (updateResult.isFailure) {
|
||||||
|
return Result.fail(updateResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Persistir cambios
|
||||||
|
const saveResult = await this.repository.update(paymentMethod, transaction);
|
||||||
|
|
||||||
|
if (saveResult.isFailure) {
|
||||||
|
return Result.fail(saveResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(paymentMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from './payment-method-full-snapshot-builder';
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import type { GetPaymentMethodByIdResponseDTO } from "@erp/catalogs/common";
|
||||||
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
|
import { toNullable } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
|
import type { PaymentMethod } from "../../../domain";
|
||||||
|
|
||||||
|
export interface IPaymentMethodFullSnapshotBuilder
|
||||||
|
extends ISnapshotBuilder<PaymentMethod, GetPaymentMethodByIdResponseDTO> {}
|
||||||
|
|
||||||
|
export class PaymentMethodFullSnapshotBuilder implements IPaymentMethodFullSnapshotBuilder {
|
||||||
|
public toOutput(paymentMethod: PaymentMethod): GetPaymentMethodByIdResponseDTO {
|
||||||
|
return {
|
||||||
|
id: paymentMethod.id.toPrimitive(),
|
||||||
|
name: paymentMethod.name.toPrimitive(),
|
||||||
|
description: toNullable(paymentMethod.description, (value) => value.toPrimitive()),
|
||||||
|
is_active: paymentMethod.isActive,
|
||||||
|
is_system: paymentMethod.isSystem,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./full";
|
||||||
|
export * from "./summary";
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./payment-method-summary-snapshot-builder";
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { PaymentMethodSummaryDTO } from "../../../../common/";
|
||||||
|
import type { PaymentMethodSummary } from "../../models";
|
||||||
|
|
||||||
|
export interface IPaymentMethodSummarySnapshotBuilder
|
||||||
|
extends ISnapshotBuilder<PaymentMethodSummary, PaymentMethodSummaryDTO> {}
|
||||||
|
|
||||||
|
export class PaymentMethodSummarySnapshotBuilder implements IPaymentMethodSummarySnapshotBuilder {
|
||||||
|
public toOutput(paymentMethod: PaymentMethodSummary): PaymentMethodSummaryDTO {
|
||||||
|
return {
|
||||||
|
id: paymentMethod.id.toString(),
|
||||||
|
company_id: paymentMethod.companyId.toString(),
|
||||||
|
name: paymentMethod.name.toString(),
|
||||||
|
is_system: paymentMethod.isSystem,
|
||||||
|
is_active: paymentMethod.isActive,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
import type { CreatePaymentMethodRequestDTO } from "@erp/catalogs/common";
|
||||||
|
import type { ITransactionManager } from "@erp/core/api";
|
||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { ICreatePaymentMethodInputMapper } from "../mappers";
|
||||||
|
import type { IPaymentMethodCreator } from "../services";
|
||||||
|
import type { IPaymentMethodFullSnapshotBuilder } from "../snapshot-builders";
|
||||||
|
|
||||||
|
export type CreatePaymentMethodUseCaseInput = {
|
||||||
|
companyId: UniqueID;
|
||||||
|
dto: CreatePaymentMethodRequestDTO;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CreatePaymentMethodUseCaseDeps = {
|
||||||
|
dtoMapper: ICreatePaymentMethodInputMapper;
|
||||||
|
creator: IPaymentMethodCreator;
|
||||||
|
fullSnapshotBuilder: IPaymentMethodFullSnapshotBuilder;
|
||||||
|
transactionManager: ITransactionManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class CreatePaymentMethodUseCase {
|
||||||
|
constructor(private readonly deps: CreatePaymentMethodUseCaseDeps) {}
|
||||||
|
|
||||||
|
public execute(params: CreatePaymentMethodUseCaseInput) {
|
||||||
|
const { dto, companyId } = params;
|
||||||
|
console.log("Executing CreatePaymentMethodUseCase with params:", params);
|
||||||
|
|
||||||
|
const mappedPropsResult = this.deps.dtoMapper.map(dto, { companyId });
|
||||||
|
if (mappedPropsResult.isFailure) {
|
||||||
|
return mappedPropsResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { props, id } = mappedPropsResult.data;
|
||||||
|
|
||||||
|
return this.deps.transactionManager.complete(async (transaction: unknown) => {
|
||||||
|
try {
|
||||||
|
const createResult = await this.deps.creator.create({ companyId, id, props, transaction });
|
||||||
|
|
||||||
|
if (createResult.isFailure) {
|
||||||
|
return createResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
const snapshot = this.deps.fullSnapshotBuilder.toOutput(createResult.data);
|
||||||
|
return Result.ok(snapshot);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import type { ITransactionManager } from "@erp/core/api";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
import type { Transaction } from "sequelize";
|
||||||
|
|
||||||
|
import type { IPaymentMethodDisabler, IPaymentMethodFinder } from "../services";
|
||||||
|
import type { IPaymentMethodFullSnapshotBuilder } from "../snapshot-builders";
|
||||||
|
|
||||||
|
export type DisablePaymentMethodByIdUseCaseInput = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class DisablePaymentMethodByIdUseCase {
|
||||||
|
constructor(
|
||||||
|
private readonly deps: {
|
||||||
|
finder: IPaymentMethodFinder;
|
||||||
|
disabler: IPaymentMethodDisabler;
|
||||||
|
fullSnapshotBuilder: IPaymentMethodFullSnapshotBuilder;
|
||||||
|
transactionManager: ITransactionManager;
|
||||||
|
}
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public execute(params: DisablePaymentMethodByIdUseCaseInput) {
|
||||||
|
const { id } = params;
|
||||||
|
|
||||||
|
return this.deps.transactionManager.complete(async (transaction: unknown) => {
|
||||||
|
const tx = transaction as Transaction;
|
||||||
|
try {
|
||||||
|
const findResult = await this.deps.finder.getById(id, tx);
|
||||||
|
if (findResult.isFailure) {
|
||||||
|
return Result.fail(findResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const disableResult = await this.deps.disabler.disable({
|
||||||
|
paymentMethod: findResult.data,
|
||||||
|
transaction: tx,
|
||||||
|
});
|
||||||
|
if (disableResult.isFailure) {
|
||||||
|
return Result.fail(disableResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(this.deps.fullSnapshotBuilder.toOutput(disableResult.data));
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import type { ITransactionManager } from "@erp/core/api";
|
||||||
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { IPaymentMethodFinder } from "../services";
|
||||||
|
import type { IPaymentMethodFullSnapshotBuilder } from "../snapshot-builders";
|
||||||
|
|
||||||
|
export type GetPaymentMethodByIdUseCaseInput = {
|
||||||
|
companyId: UniqueID;
|
||||||
|
payment_method_id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class GetPaymentMethodByIdUseCase {
|
||||||
|
constructor(
|
||||||
|
private readonly finder: IPaymentMethodFinder,
|
||||||
|
private readonly fullSnapshotBuilder: IPaymentMethodFullSnapshotBuilder,
|
||||||
|
private readonly transactionManager: ITransactionManager
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public execute(params: GetPaymentMethodByIdUseCaseInput) {
|
||||||
|
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.transactionManager.complete(async (transaction: unknown) => {
|
||||||
|
try {
|
||||||
|
const result = await this.finder.findPaymentMethodById(
|
||||||
|
companyId,
|
||||||
|
paymentMethodId,
|
||||||
|
transaction
|
||||||
|
);
|
||||||
|
if (result.isFailure) {
|
||||||
|
return Result.fail(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(this.fullSnapshotBuilder.toOutput(result.data));
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
5
modules/catalogs/src/api/application/use-cases/index.ts
Normal file
5
modules/catalogs/src/api/application/use-cases/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export * from "./create-payment-method.use-case";
|
||||||
|
export * from "./disable-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";
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
import type { ITransactionManager } from "@erp/core/api";
|
||||||
|
import type { Criteria } from "@repo/rdx-criteria/server";
|
||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { IPaymentMethodFinder } from "../services";
|
||||||
|
import type { IPaymentMethodSummarySnapshotBuilder } from "../snapshot-builders";
|
||||||
|
|
||||||
|
type ListPaymentMethodsUseCaseInput = {
|
||||||
|
companyId: UniqueID;
|
||||||
|
criteria: Criteria;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ListPaymentMethodsUseCase {
|
||||||
|
constructor(
|
||||||
|
private readonly finder: IPaymentMethodFinder,
|
||||||
|
private readonly summarySnapshotBuilder: IPaymentMethodSummarySnapshotBuilder,
|
||||||
|
private readonly transactionManager: ITransactionManager
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public execute(params: ListPaymentMethodsUseCaseInput) {
|
||||||
|
const { criteria, companyId } = params;
|
||||||
|
|
||||||
|
return this.transactionManager.complete(async (transaction: unknown) => {
|
||||||
|
try {
|
||||||
|
const result = await this.finder.findPaymentMethodsByCriteria(
|
||||||
|
companyId,
|
||||||
|
criteria,
|
||||||
|
transaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.isFailure) {
|
||||||
|
return Result.fail(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const paymentMethods = result.data;
|
||||||
|
const totalItems = paymentMethods.total();
|
||||||
|
|
||||||
|
const items = paymentMethods.map((item) => this.summarySnapshotBuilder.toOutput(item));
|
||||||
|
|
||||||
|
const snapshot = {
|
||||||
|
page: criteria.pageNumber,
|
||||||
|
per_page: criteria.pageSize,
|
||||||
|
total_pages: Math.ceil(totalItems / criteria.pageSize),
|
||||||
|
total_items: totalItems,
|
||||||
|
items: items,
|
||||||
|
metadata: {
|
||||||
|
entity: "payment_methods",
|
||||||
|
criteria: criteria.toJSON(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return Result.ok(snapshot);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
import type { UpdatePaymentMethodByIdRequestDTO } from "@erp/catalogs/common";
|
||||||
|
import type { ITransactionManager } from "@erp/core/api";
|
||||||
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { IUpdatePaymentMethodInputMapper } from "../mappers";
|
||||||
|
import type { IPaymentMethodFinder, IPaymentMethodUpdater } from "../services";
|
||||||
|
import type { IPaymentMethodFullSnapshotBuilder } from "../snapshot-builders";
|
||||||
|
|
||||||
|
export type UpdatePaymentMethodByIdUseCaseInput = {
|
||||||
|
companyId: UniqueID;
|
||||||
|
payment_method_id: string;
|
||||||
|
dto: UpdatePaymentMethodByIdRequestDTO;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class UpdatePaymentMethodByIdUseCase {
|
||||||
|
constructor(
|
||||||
|
private readonly deps: {
|
||||||
|
updater: IPaymentMethodUpdater;
|
||||||
|
finder: IPaymentMethodFinder;
|
||||||
|
dtoMapper: IUpdatePaymentMethodInputMapper;
|
||||||
|
fullSnapshotBuilder: IPaymentMethodFullSnapshotBuilder;
|
||||||
|
transactionManager: ITransactionManager;
|
||||||
|
}
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public execute(params: UpdatePaymentMethodByIdUseCaseInput) {
|
||||||
|
const { companyId, payment_method_id, dto } = params;
|
||||||
|
|
||||||
|
const idOrError = UniqueID.create(payment_method_id);
|
||||||
|
if (idOrError.isFailure) {
|
||||||
|
return Result.fail(idOrError.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const paymentMethodId = idOrError.data;
|
||||||
|
|
||||||
|
// Mapear DTO → props de dominio
|
||||||
|
const patchPropsResult = this.deps.dtoMapper.map(dto, { companyId });
|
||||||
|
if (patchPropsResult.isFailure) {
|
||||||
|
return patchPropsResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
const patchProps = patchPropsResult.data;
|
||||||
|
|
||||||
|
return this.deps.transactionManager.complete(async (transaction: unknown) => {
|
||||||
|
try {
|
||||||
|
const updateResult = await this.deps.updater.update({
|
||||||
|
companyId,
|
||||||
|
id: paymentMethodId,
|
||||||
|
patchProps,
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (updateResult.isFailure) {
|
||||||
|
return Result.fail(updateResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(this.deps.fullSnapshotBuilder.toOutput(updateResult.data));
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
1
modules/catalogs/src/api/domain/index.ts
Normal file
1
modules/catalogs/src/api/domain/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./payment-methods";
|
||||||
27
modules/catalogs/src/api/domain/payment-methods/errors.ts
Normal file
27
modules/catalogs/src/api/domain/payment-methods/errors.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { DomainError } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
|
export class InvalidPaymentMethodIdError extends DomainError {
|
||||||
|
public readonly code = "PAYMENT_METHOD_INVALID_ID" as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isInvalidPaymentMethodIdError = (e: unknown): e is InvalidPaymentMethodIdError =>
|
||||||
|
e instanceof InvalidPaymentMethodIdError;
|
||||||
|
|
||||||
|
export class InvalidPaymentMethodNameError extends DomainError {
|
||||||
|
public readonly code = "PAYMENT_METHOD_NAME" as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 class PaymentMethodCannotBeDeletedError extends DomainError {
|
||||||
|
public readonly code = "PAYMENT_METHOD_CANNOT_BE_DELETED" as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isPaymentMethodCannotBeDeletedError = (
|
||||||
|
e: unknown
|
||||||
|
): e is PaymentMethodCannotBeDeletedError => e instanceof PaymentMethodCannotBeDeletedError;
|
||||||
4
modules/catalogs/src/api/domain/payment-methods/index.ts
Normal file
4
modules/catalogs/src/api/domain/payment-methods/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from "./errors";
|
||||||
|
export * from "./payment-method.aggregate";
|
||||||
|
export * from "./payment-method-name";
|
||||||
|
export * from "./payment-method-type";
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import { InvalidPaymentMethodNameError } from "./errors";
|
||||||
|
|
||||||
|
export class PaymentMethodName {
|
||||||
|
private constructor(private readonly value: string) {}
|
||||||
|
|
||||||
|
public static create(name: string): Result<PaymentMethodName, Error> {
|
||||||
|
const trimmed = name?.trim() ?? "";
|
||||||
|
if (trimmed.length === 0) {
|
||||||
|
return Result.fail(new InvalidPaymentMethodNameError("Payment method name cannot be empty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(new PaymentMethodName(trimmed));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromPersistence(name: string): PaymentMethodName {
|
||||||
|
return new PaymentMethodName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toString(): string {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toPrimitive(): string {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import { InvalidPaymentMethodTypeError } from "./errors";
|
||||||
|
|
||||||
|
export const PAYMENT_METHOD_TYPES = [
|
||||||
|
"cash",
|
||||||
|
"bank_transfer",
|
||||||
|
"card",
|
||||||
|
"direct_debit",
|
||||||
|
"other",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type PaymentMethodTypeValue = (typeof PAYMENT_METHOD_TYPES)[number];
|
||||||
|
|
||||||
|
export class PaymentMethodType {
|
||||||
|
private constructor(private readonly value: PaymentMethodTypeValue) {}
|
||||||
|
|
||||||
|
public static create(type: string): Result<PaymentMethodType, Error> {
|
||||||
|
const normalized = String(type).trim() as PaymentMethodTypeValue;
|
||||||
|
|
||||||
|
if (!PAYMENT_METHOD_TYPES.includes(normalized)) {
|
||||||
|
return Result.fail(
|
||||||
|
new InvalidPaymentMethodTypeError(
|
||||||
|
`Payment method type must be one of: ${PAYMENT_METHOD_TYPES.join(", ")}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(new PaymentMethodType(normalized));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromPersistence(type: PaymentMethodTypeValue): PaymentMethodType {
|
||||||
|
return new PaymentMethodType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toString(): PaymentMethodTypeValue {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toPrimitive(): PaymentMethodTypeValue {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
import { AggregateRoot, type Name, type TextValue, type UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { type Maybe, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
export interface IPaymentMethodCreateProps {
|
||||||
|
companyId: UniqueID;
|
||||||
|
name: Name;
|
||||||
|
description: Maybe<TextValue>;
|
||||||
|
isActive: boolean;
|
||||||
|
isSystem: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PaymentMethodPatchProps = Partial<
|
||||||
|
Omit<IPaymentMethodCreateProps, "companyId" | "isSystem">
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type PaymentMethodInternalProps = IPaymentMethodCreateProps;
|
||||||
|
|
||||||
|
export type PaymentMethodProps = PaymentMethodPatchProps;
|
||||||
|
|
||||||
|
export class PaymentMethod extends AggregateRoot<PaymentMethodInternalProps> {
|
||||||
|
protected constructor(props: PaymentMethodInternalProps, id?: UniqueID) {
|
||||||
|
super(props, id); // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
|
}
|
||||||
|
|
||||||
|
public static create(
|
||||||
|
props: IPaymentMethodCreateProps,
|
||||||
|
id?: UniqueID
|
||||||
|
): Result<PaymentMethod, Error> {
|
||||||
|
const validationResult = PaymentMethod.validateCreateProps(props);
|
||||||
|
|
||||||
|
if (validationResult.isFailure) {
|
||||||
|
return Result.fail(validationResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear instancia
|
||||||
|
const paymentMethod = new PaymentMethod(props, id);
|
||||||
|
|
||||||
|
// Disparar eventos de dominio
|
||||||
|
// ...
|
||||||
|
// ...
|
||||||
|
|
||||||
|
return Result.ok(paymentMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static rehydrate(props: PaymentMethodInternalProps, id: UniqueID): PaymentMethod {
|
||||||
|
return new PaymentMethod(props, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static validateCreateProps(_props: IPaymentMethodCreateProps): Result<void, Error> {
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get companyId(): UniqueID {
|
||||||
|
return this.props.companyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get name(): Name {
|
||||||
|
return this.props.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get description(): Maybe<TextValue> {
|
||||||
|
return this.props.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isActive(): boolean {
|
||||||
|
return this.props.isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isSystem(): boolean {
|
||||||
|
return this.props.isSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(props: Partial<PaymentMethodPatchProps>): Result<void, Error> {
|
||||||
|
if (props.name !== undefined) {
|
||||||
|
this.props.name = props.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.description !== undefined) {
|
||||||
|
this.props.description = props.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.isActive !== undefined) {
|
||||||
|
this.props.isActive = props.isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
public disable(): Result<void, Error> {
|
||||||
|
if (!this.isActive) {
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.isActive = false;
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON() {
|
||||||
|
return {
|
||||||
|
id: this.id.toPrimitive(),
|
||||||
|
name: this.props.name.toPrimitive(),
|
||||||
|
description: this.props.description,
|
||||||
|
is_active: this.isActive,
|
||||||
|
is_system: this.isSystem,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
44
modules/catalogs/src/api/index.ts
Normal file
44
modules/catalogs/src/api/index.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import type { IModuleServer } from "@erp/core/api";
|
||||||
|
|
||||||
|
import { models, paymentMethodsRouter } from "./infrastructure";
|
||||||
|
import {
|
||||||
|
buildCatalogsDependencies,
|
||||||
|
buildCatalogsPublicServices,
|
||||||
|
} from "./infrastructure/di/catalogs.di";
|
||||||
|
|
||||||
|
export * from "./infrastructure/persistence/sequelize";
|
||||||
|
|
||||||
|
export const catalogsAPIModule: IModuleServer = {
|
||||||
|
name: "catalogs",
|
||||||
|
version: "1.0.0",
|
||||||
|
dependencies: [],
|
||||||
|
|
||||||
|
async setup(params) {
|
||||||
|
const internal = buildCatalogsDependencies(params);
|
||||||
|
const publicServices = buildCatalogsPublicServices(params, internal);
|
||||||
|
|
||||||
|
params.logger.info("🚀 Catalogs module dependencies registered", {
|
||||||
|
label: this.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
models,
|
||||||
|
services: {
|
||||||
|
paymentMethod: publicServices,
|
||||||
|
},
|
||||||
|
internal,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async start(params) {
|
||||||
|
const { logger } = params;
|
||||||
|
|
||||||
|
paymentMethodsRouter(params);
|
||||||
|
|
||||||
|
logger.info("🚀 Catalogs module started", {
|
||||||
|
label: this.name,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default catalogsAPIModule;
|
||||||
23
modules/catalogs/src/api/infrastructure/di/catalogs.di.ts
Normal file
23
modules/catalogs/src/api/infrastructure/di/catalogs.di.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import type { ModuleParams, SetupParams } from "@erp/core/api";
|
||||||
|
|
||||||
|
import {
|
||||||
|
type PaymentMethodsInternalDeps,
|
||||||
|
buildPaymentMethodsDependencies,
|
||||||
|
buildPaymentMethodsPublicServices,
|
||||||
|
} from "./payment-methods.di";
|
||||||
|
|
||||||
|
export type CatalogsInternalDeps = {
|
||||||
|
paymentMethods: PaymentMethodsInternalDeps;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildCatalogsDependencies = (params: ModuleParams): CatalogsInternalDeps => {
|
||||||
|
return {
|
||||||
|
paymentMethods: buildPaymentMethodsDependencies(params),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildCatalogsPublicServices = (params: SetupParams, deps: CatalogsInternalDeps) => {
|
||||||
|
return {
|
||||||
|
paymentMethods: buildPaymentMethodsPublicServices(params, deps.paymentMethods),
|
||||||
|
};
|
||||||
|
};
|
||||||
1
modules/catalogs/src/api/infrastructure/di/index.ts
Normal file
1
modules/catalogs/src/api/infrastructure/di/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./catalogs.di";
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import {
|
||||||
|
SequelizePaymentMethodDomainMapper,
|
||||||
|
SequelizePaymentMethodSummaryMapper,
|
||||||
|
} from "../persistence/index";
|
||||||
|
|
||||||
|
export interface IPaymentMethodPersistenceMappers {
|
||||||
|
domainMapper: SequelizePaymentMethodDomainMapper;
|
||||||
|
listMapper: SequelizePaymentMethodSummaryMapper;
|
||||||
|
|
||||||
|
//createMapper: CreatePaymentMethodRequestMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const buildPaymentMethodPersistenceMappers = (): IPaymentMethodPersistenceMappers => {
|
||||||
|
// Mappers para el repositorio
|
||||||
|
const domainMapper = new SequelizePaymentMethodDomainMapper();
|
||||||
|
const listMapper = new SequelizePaymentMethodSummaryMapper();
|
||||||
|
|
||||||
|
// Mappers el DTO a las props validadas (CustomerProps) y luego construir agregado
|
||||||
|
//const createMapper = new CreatePaymentMethodRequestMapper(catalogs);
|
||||||
|
|
||||||
|
return {
|
||||||
|
domainMapper,
|
||||||
|
listMapper,
|
||||||
|
|
||||||
|
//createMapper,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import type { Sequelize } from "sequelize";
|
||||||
|
|
||||||
|
import { SequelizePaymentMethodRepository } from "../persistence";
|
||||||
|
|
||||||
|
import type { IPaymentMethodPersistenceMappers } from "./payment-method-persistence-mappers.di";
|
||||||
|
|
||||||
|
export const buildPaymentMethodRepository = (params: {
|
||||||
|
database: Sequelize;
|
||||||
|
mappers: IPaymentMethodPersistenceMappers;
|
||||||
|
}) => {
|
||||||
|
const { database, mappers } = params;
|
||||||
|
|
||||||
|
return new SequelizePaymentMethodRepository(mappers.domainMapper, mappers.listMapper, database);
|
||||||
|
};
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
import { type ModuleParams, type SetupParams, buildTransactionManager } from "@erp/core/api";
|
||||||
|
import type { Sequelize } from "sequelize";
|
||||||
|
|
||||||
|
import {
|
||||||
|
buildPaymentMethodCreator,
|
||||||
|
buildPaymentMethodFinder,
|
||||||
|
buildPaymentMethodInputMappers,
|
||||||
|
buildPaymentMethodSnapshotBuilders,
|
||||||
|
} from "../../application";
|
||||||
|
import type { IPaymentMethodRepository } from "../../application/repositories";
|
||||||
|
import type { IPaymentMethodFinder } from "../../application/services";
|
||||||
|
import { PaymentMethodFinder, PaymentMethodUpdater } from "../../application/services";
|
||||||
|
import {
|
||||||
|
CreatePaymentMethodUseCase,
|
||||||
|
type DisablePaymentMethodByIdUseCase,
|
||||||
|
GetPaymentMethodByIdUseCase,
|
||||||
|
ListPaymentMethodsUseCase,
|
||||||
|
UpdatePaymentMethodByIdUseCase,
|
||||||
|
} from "../../application/use-cases";
|
||||||
|
|
||||||
|
import { buildPaymentMethodPersistenceMappers } from "./payment-method-persistence-mappers.di";
|
||||||
|
import { buildPaymentMethodRepository } from "./payment-method-repositories.di";
|
||||||
|
|
||||||
|
export type PaymentMethodsInternalDeps = {
|
||||||
|
repository: IPaymentMethodRepository;
|
||||||
|
useCases: {
|
||||||
|
listPaymentMethods: () => ListPaymentMethodsUseCase;
|
||||||
|
getPaymentMethodById: () => GetPaymentMethodByIdUseCase;
|
||||||
|
createPaymentMethod: () => CreatePaymentMethodUseCase;
|
||||||
|
updatePaymentMethodById: () => UpdatePaymentMethodByIdUseCase;
|
||||||
|
disablePaymentMethodById: () => DisablePaymentMethodByIdUseCase;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildPaymentMethodsDependencies = (
|
||||||
|
params: ModuleParams
|
||||||
|
): PaymentMethodsInternalDeps => {
|
||||||
|
const { database } = params;
|
||||||
|
|
||||||
|
// Infrastructure
|
||||||
|
const transactionManager = buildTransactionManager(database as Sequelize);
|
||||||
|
const persistenceMappers = buildPaymentMethodPersistenceMappers();
|
||||||
|
|
||||||
|
const repository = buildPaymentMethodRepository({ database, mappers: persistenceMappers });
|
||||||
|
|
||||||
|
// Application helpers
|
||||||
|
const inputMappers = buildPaymentMethodInputMappers();
|
||||||
|
const finder = buildPaymentMethodFinder(repository);
|
||||||
|
const creator = buildPaymentMethodCreator({ repository });
|
||||||
|
const updater = new PaymentMethodUpdater(repository);
|
||||||
|
//const disabler = new PaymentMethodDisabler(repository);
|
||||||
|
|
||||||
|
const snapshotBuilders = buildPaymentMethodSnapshotBuilders();
|
||||||
|
|
||||||
|
return {
|
||||||
|
repository,
|
||||||
|
useCases: {
|
||||||
|
listPaymentMethods: () =>
|
||||||
|
new ListPaymentMethodsUseCase(finder, snapshotBuilders.summary, transactionManager),
|
||||||
|
getPaymentMethodById: () =>
|
||||||
|
new GetPaymentMethodByIdUseCase(finder, snapshotBuilders.full, transactionManager),
|
||||||
|
createPaymentMethod: () =>
|
||||||
|
new CreatePaymentMethodUseCase({
|
||||||
|
dtoMapper: inputMappers.createInputMapper,
|
||||||
|
creator,
|
||||||
|
fullSnapshotBuilder: snapshotBuilders.full,
|
||||||
|
transactionManager,
|
||||||
|
}),
|
||||||
|
|
||||||
|
updatePaymentMethodById: () =>
|
||||||
|
new UpdatePaymentMethodByIdUseCase({
|
||||||
|
updater,
|
||||||
|
finder,
|
||||||
|
dtoMapper: inputMappers.updateInputMapper,
|
||||||
|
fullSnapshotBuilder: snapshotBuilders.full,
|
||||||
|
transactionManager,
|
||||||
|
}),
|
||||||
|
/*disablePaymentMethodById: () =>
|
||||||
|
new DisablePaymentMethodByIdUseCase({
|
||||||
|
finder,
|
||||||
|
disabler,
|
||||||
|
fullSnapshotBuilder,
|
||||||
|
transactionManager,
|
||||||
|
}),*/
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildPaymentMethodsPublicServices = (
|
||||||
|
_params: SetupParams,
|
||||||
|
deps: PaymentMethodsInternalDeps
|
||||||
|
): { finder: IPaymentMethodFinder } => {
|
||||||
|
return {
|
||||||
|
finder: new PaymentMethodFinder(deps.repository),
|
||||||
|
};
|
||||||
|
};
|
||||||
1
modules/catalogs/src/api/infrastructure/express/index.ts
Normal file
1
modules/catalogs/src/api/infrastructure/express/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./payment-methods/payment-methods.routes";
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
import {
|
||||||
|
ExpressController,
|
||||||
|
forbidQueryFieldGuard,
|
||||||
|
requireAuthenticatedGuard,
|
||||||
|
requireCompanyContextGuard,
|
||||||
|
} from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { CreatePaymentMethodRequestDTO } from "../../../../../common";
|
||||||
|
import type { CreatePaymentMethodUseCase } from "../../../../application";
|
||||||
|
import { paymentmethodsApiErrorMapper } from "../payment-methods-api-error-mapper";
|
||||||
|
|
||||||
|
export class CreatePaymentMethodController extends ExpressController {
|
||||||
|
constructor(private readonly useCase: CreatePaymentMethodUseCase) {
|
||||||
|
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 dto = this.req.body satisfies CreatePaymentMethodRequestDTO;
|
||||||
|
const result = await this.useCase.execute({ dto, companyId });
|
||||||
|
|
||||||
|
return result.match(
|
||||||
|
(data) => this.created(data),
|
||||||
|
(err) => this.handleError(err)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import { ExpressController } from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { DisablePaymentMethodByIdUseCase } from "../../../../application";
|
||||||
|
|
||||||
|
export class DisablePaymentMethodByIdController extends ExpressController {
|
||||||
|
constructor(private readonly useCase: DisablePaymentMethodByIdUseCase) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async executeImpl() {
|
||||||
|
const id = this.req.params.payment_method_id;
|
||||||
|
const result = await this.useCase.execute({ id });
|
||||||
|
|
||||||
|
return result.match(
|
||||||
|
(data) => this.ok(data),
|
||||||
|
(err) => this.handleError(err)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
import {
|
||||||
|
ExpressController,
|
||||||
|
forbidQueryFieldGuard,
|
||||||
|
requireAuthenticatedGuard,
|
||||||
|
requireCompanyContextGuard,
|
||||||
|
} from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { GetPaymentMethodByIdUseCase } from "../../../../application";
|
||||||
|
import { paymentmethodsApiErrorMapper } from "../payment-methods-api-error-mapper";
|
||||||
|
|
||||||
|
export class GetPaymentMethodByIdController extends ExpressController {
|
||||||
|
constructor(private readonly useCase: GetPaymentMethodByIdUseCase) {
|
||||||
|
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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
export * from "./create-payment-method.controller";
|
||||||
|
export * from "./disable-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";
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import {
|
||||||
|
ExpressController,
|
||||||
|
forbidQueryFieldGuard,
|
||||||
|
requireAuthenticatedGuard,
|
||||||
|
requireCompanyContextGuard,
|
||||||
|
} from "@erp/core/api";
|
||||||
|
import { Criteria } from "@repo/rdx-criteria/server";
|
||||||
|
|
||||||
|
import type { ListPaymentMethodsUseCase } from "../../../../application";
|
||||||
|
import { paymentmethodsApiErrorMapper } from "../payment-methods-api-error-mapper";
|
||||||
|
|
||||||
|
export class ListPaymentMethodsController extends ExpressController {
|
||||||
|
constructor(private readonly useCase: ListPaymentMethodsUseCase) {
|
||||||
|
super();
|
||||||
|
this.errorMapper = paymentmethodsApiErrorMapper;
|
||||||
|
|
||||||
|
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||||
|
this.registerGuards(
|
||||||
|
requireAuthenticatedGuard(),
|
||||||
|
requireCompanyContextGuard(),
|
||||||
|
forbidQueryFieldGuard("companyId")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCriteriaWithDefaultOrder() {
|
||||||
|
if (this.criteria.hasOrder()) {
|
||||||
|
return this.criteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { q: quicksearch, filters, pageSize, pageNumber } = this.criteria.toPrimitives();
|
||||||
|
return Criteria.fromPrimitives(filters, "name", "ASC", pageSize, pageNumber, quicksearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async executeImpl() {
|
||||||
|
const companyId = this.getTenantId();
|
||||||
|
if (!companyId) {
|
||||||
|
return this.forbiddenError("Tenant ID not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const criteria = this.getCriteriaWithDefaultOrder();
|
||||||
|
const result = await this.useCase.execute({ criteria, companyId });
|
||||||
|
|
||||||
|
return result.match(
|
||||||
|
(data) =>
|
||||||
|
this.ok(data, {
|
||||||
|
"X-Total-Count": String(data.total_items),
|
||||||
|
"Pagination-Count": String(data.total_pages),
|
||||||
|
"Pagination-Page": String(data.page),
|
||||||
|
"Pagination-Limit": String(data.per_page),
|
||||||
|
}),
|
||||||
|
(err) => this.handleError(err)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
import type { UpdatePaymentMethodByIdRequestDTO } from "@erp/catalogs/common";
|
||||||
|
import {
|
||||||
|
ExpressController,
|
||||||
|
forbidQueryFieldGuard,
|
||||||
|
requireAuthenticatedGuard,
|
||||||
|
requireCompanyContextGuard,
|
||||||
|
} from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { UpdatePaymentMethodByIdUseCase } from "../../../../application";
|
||||||
|
import { paymentmethodsApiErrorMapper } from "../payment-methods-api-error-mapper";
|
||||||
|
|
||||||
|
export class UpdatePaymentMethodByIdController extends ExpressController {
|
||||||
|
constructor(private readonly useCase: UpdatePaymentMethodByIdUseCase) {
|
||||||
|
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;
|
||||||
|
if (!payment_method_id) {
|
||||||
|
return this.invalidInputError("Payment method ID missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
const dto = this.req.body as UpdatePaymentMethodByIdRequestDTO;
|
||||||
|
|
||||||
|
const result = await this.useCase.execute({ payment_method_id, companyId, dto });
|
||||||
|
|
||||||
|
return result.match(
|
||||||
|
(data) => this.ok(data),
|
||||||
|
(err) => this.handleError(err)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
import {
|
||||||
|
ApiErrorMapper,
|
||||||
|
ConflictApiError,
|
||||||
|
type ErrorToApiRule,
|
||||||
|
ValidationApiError,
|
||||||
|
} from "@erp/core/api";
|
||||||
|
|
||||||
|
import {
|
||||||
|
type InvalidPaymentMethodIdError,
|
||||||
|
type PaymentMethodCannotBeDeletedError,
|
||||||
|
isInvalidPaymentMethodIdError,
|
||||||
|
isPaymentMethodCannotBeDeletedError,
|
||||||
|
} from "../../../domain/payment-methods";
|
||||||
|
|
||||||
|
// Crea una regla específica (prioridad alta para sobreescribir mensajes)
|
||||||
|
const paymentmethodDuplicateRule: ErrorToApiRule = {
|
||||||
|
priority: 120,
|
||||||
|
matches: (e) => isInvalidPaymentMethodIdError(e),
|
||||||
|
build: (e) =>
|
||||||
|
new ConflictApiError(
|
||||||
|
(e as InvalidPaymentMethodIdError).message ||
|
||||||
|
"Payment method with the provided id already exists."
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
const paymentmethodCannotBeDeletedRule: ErrorToApiRule = {
|
||||||
|
priority: 120,
|
||||||
|
matches: (e) => isPaymentMethodCannotBeDeletedError(e),
|
||||||
|
build: (e) =>
|
||||||
|
new ValidationApiError(
|
||||||
|
(e as PaymentMethodCannotBeDeletedError).message || "Payment method cannot be deleted."
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cómo aplicarla: crea una nueva instancia del mapper con la regla extra
|
||||||
|
export const paymentmethodsApiErrorMapper: ApiErrorMapper = ApiErrorMapper.default()
|
||||||
|
.register(paymentmethodDuplicateRule)
|
||||||
|
.register(paymentmethodCannotBeDeletedRule);
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api";
|
||||||
|
import { type RequestWithAuth, type StartParams, validateRequest } from "@erp/core/api";
|
||||||
|
import { type NextFunction, type Request, type Response, Router } from "express";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CreatePaymentMethodRequestSchema,
|
||||||
|
GetPaymentMethodByIdRequestSchema,
|
||||||
|
ListPaymentMethodsRequestSchema,
|
||||||
|
UpdatePaymentMethodByIdParamsRequestSchema,
|
||||||
|
UpdatePaymentMethodByIdRequestSchema,
|
||||||
|
} from "../../../../common/dto/payment-methods/request";
|
||||||
|
import type { CatalogsInternalDeps } from "../../di/catalogs.di";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CreatePaymentMethodController,
|
||||||
|
DisablePaymentMethodByIdController,
|
||||||
|
GetPaymentMethodByIdController,
|
||||||
|
ListPaymentMethodsController,
|
||||||
|
UpdatePaymentMethodByIdController,
|
||||||
|
} from "./controllers";
|
||||||
|
|
||||||
|
export const paymentMethodsRouter = (params: StartParams) => {
|
||||||
|
const { app, config, getInternal } = params;
|
||||||
|
const deps = getInternal<CatalogsInternalDeps>("catalogs").paymentMethods;
|
||||||
|
|
||||||
|
const router = Router({ mergeParams: true });
|
||||||
|
|
||||||
|
// ----------------------------------------------
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "production") {
|
||||||
|
// 🔐 Autenticación + Tenancy para TODO el router
|
||||||
|
router.use(
|
||||||
|
(req: Request, res: Response, next: NextFunction) =>
|
||||||
|
mockUser(req as RequestWithAuth, res, next) // Debe ir antes de las rutas protegidas
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
router.use([
|
||||||
|
(req: Request, res: Response, next: NextFunction) =>
|
||||||
|
requireAuthenticated()(req as RequestWithAuth, res, next), // Debe ir antes de las rutas protegidas
|
||||||
|
|
||||||
|
(req: Request, res: Response, next: NextFunction) =>
|
||||||
|
requireCompanyContext()(req as RequestWithAuth, res, next), // Debe ir antes de las rutas protegidas
|
||||||
|
]);
|
||||||
|
|
||||||
|
// ----------------------------------------------
|
||||||
|
|
||||||
|
router.get("/", validateRequest(ListPaymentMethodsRequestSchema, "query"), (req, res, next) => {
|
||||||
|
const controller = new ListPaymentMethodsController(deps.useCases.listPaymentMethods());
|
||||||
|
return controller.execute(req, res, next);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/", validateRequest(CreatePaymentMethodRequestSchema, "body"), (req, res, next) => {
|
||||||
|
const controller = new CreatePaymentMethodController(deps.useCases.createPaymentMethod());
|
||||||
|
return controller.execute(req, res, next);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/:payment_method_id",
|
||||||
|
validateRequest(GetPaymentMethodByIdRequestSchema, "params"),
|
||||||
|
(req, res, next) => {
|
||||||
|
const controller = new GetPaymentMethodByIdController(deps.useCases.getPaymentMethodById());
|
||||||
|
return controller.execute(req, res, next);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/:payment_method_id",
|
||||||
|
validateRequest(UpdatePaymentMethodByIdParamsRequestSchema, "params"),
|
||||||
|
validateRequest(UpdatePaymentMethodByIdRequestSchema, "body"),
|
||||||
|
(req, res, next) => {
|
||||||
|
const controller = new UpdatePaymentMethodByIdController(
|
||||||
|
deps.useCases.updatePaymentMethodById()
|
||||||
|
);
|
||||||
|
return controller.execute(req, res, next);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/:payment_method_id/disable",
|
||||||
|
validateRequest(GetPaymentMethodByIdRequestSchema, "params"),
|
||||||
|
(req, res, next) => {
|
||||||
|
const controller = new DisablePaymentMethodByIdController(
|
||||||
|
deps.useCases.disablePaymentMethodById()
|
||||||
|
);
|
||||||
|
return controller.execute(req, res, next);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
app.use(`${config.server.apiBasePath}/catalogs/payment-methods`, router);
|
||||||
|
};
|
||||||
2
modules/catalogs/src/api/infrastructure/index.ts
Normal file
2
modules/catalogs/src/api/infrastructure/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./express";
|
||||||
|
export * from "./persistence";
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./sequelize";
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
export * from "./mappers";
|
||||||
|
export * from "./models";
|
||||||
|
export * from "./repositories";
|
||||||
|
|
||||||
|
import paymentMethodModelInit from "./models/sequelize-payment-method.model";
|
||||||
|
|
||||||
|
export const models = [paymentMethodModelInit];
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./sequelize-payment-method-domain.mapper";
|
||||||
|
export * from "./sequelize-payment-method-summary.mapper";
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
||||||
|
import {
|
||||||
|
Name,
|
||||||
|
TextValue,
|
||||||
|
UniqueID,
|
||||||
|
ValidationErrorCollection,
|
||||||
|
type ValidationErrorDetail,
|
||||||
|
extractOrPushError,
|
||||||
|
maybeFromNullableResult,
|
||||||
|
maybeToNullable,
|
||||||
|
} from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import { PaymentMethod } from "../../../../domain";
|
||||||
|
import type { PaymentMethodCreationAttributes, PaymentMethodModel } from "../models";
|
||||||
|
|
||||||
|
export class SequelizePaymentMethodDomainMapper extends SequelizeDomainMapper<
|
||||||
|
PaymentMethodModel,
|
||||||
|
PaymentMethodCreationAttributes,
|
||||||
|
PaymentMethod
|
||||||
|
> {
|
||||||
|
public mapToDomain(
|
||||||
|
source: PaymentMethodModel,
|
||||||
|
params?: MapperParamsType
|
||||||
|
): Result<PaymentMethod, Error> {
|
||||||
|
try {
|
||||||
|
const errors: ValidationErrorDetail[] = [];
|
||||||
|
|
||||||
|
const idResult = UniqueID.create(source.id, true);
|
||||||
|
if (idResult.isFailure) {
|
||||||
|
return Result.fail(idResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const companyId = extractOrPushError(
|
||||||
|
UniqueID.create(source.company_id),
|
||||||
|
"company_id",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const name = extractOrPushError(Name.create(source.name), "name", errors);
|
||||||
|
|
||||||
|
const description = extractOrPushError(
|
||||||
|
maybeFromNullableResult(source.description, (value) => TextValue.create(value)),
|
||||||
|
"description",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
// Si hubo errores de mapeo, devolvemos colección de validación
|
||||||
|
if (errors.length > 0) {
|
||||||
|
return Result.fail(
|
||||||
|
new ValidationErrorCollection("Payment method props mapping failed", errors)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const paymentMethod = PaymentMethod.rehydrate(
|
||||||
|
{
|
||||||
|
companyId: companyId!,
|
||||||
|
name: name!,
|
||||||
|
description: description!,
|
||||||
|
isActive: source.is_active,
|
||||||
|
isSystem: source.is_system,
|
||||||
|
},
|
||||||
|
idResult.data
|
||||||
|
);
|
||||||
|
|
||||||
|
return Result.ok(paymentMethod);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public mapToPersistence(
|
||||||
|
source: PaymentMethod,
|
||||||
|
params?: MapperParamsType
|
||||||
|
): Result<PaymentMethodCreationAttributes, Error> {
|
||||||
|
return Result.ok<PaymentMethodCreationAttributes>({
|
||||||
|
id: source.id.toPrimitive(),
|
||||||
|
company_id: source.companyId.toPrimitive(),
|
||||||
|
name: source.name.toPrimitive(),
|
||||||
|
description: maybeToNullable(source.description, (description) => description.toPrimitive()),
|
||||||
|
is_active: source.isActive,
|
||||||
|
is_system: source.isSystem,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import { type MapperParamsType, SequelizeQueryMapper } from "@erp/core/api";
|
||||||
|
import {
|
||||||
|
Name,
|
||||||
|
UniqueID,
|
||||||
|
ValidationErrorCollection,
|
||||||
|
type ValidationErrorDetail,
|
||||||
|
extractOrPushError,
|
||||||
|
} from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import type { PaymentMethodSummary } from "../../../../application/models";
|
||||||
|
import type { PaymentMethodModel } from "../models";
|
||||||
|
|
||||||
|
export class SequelizePaymentMethodSummaryMapper extends SequelizeQueryMapper<
|
||||||
|
PaymentMethodModel,
|
||||||
|
PaymentMethodSummary
|
||||||
|
> {
|
||||||
|
public mapToReadModel(
|
||||||
|
raw: PaymentMethodModel,
|
||||||
|
params?: MapperParamsType
|
||||||
|
): Result<PaymentMethodSummary, Error> {
|
||||||
|
const errors: ValidationErrorDetail[] = [];
|
||||||
|
|
||||||
|
// 1) Valores escalares (atributos generales)
|
||||||
|
const companyId = extractOrPushError(UniqueID.create(raw.company_id), "company_id", errors);
|
||||||
|
const id = extractOrPushError(UniqueID.create(raw.id), "id", errors);
|
||||||
|
const name = extractOrPushError(Name.create(raw.name), "name", errors);
|
||||||
|
const isActive = raw.is_active;
|
||||||
|
const isSystem = raw.is_system;
|
||||||
|
|
||||||
|
// Si hubo errores de mapeo, devolvemos colección de validación
|
||||||
|
if (errors.length > 0) {
|
||||||
|
return Result.fail(
|
||||||
|
new ValidationErrorCollection("PaymentMethod mapping failed [mapToDTO]", errors)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok<PaymentMethodSummary>({
|
||||||
|
id: id!,
|
||||||
|
companyId: companyId!,
|
||||||
|
name: name!,
|
||||||
|
isActive: isActive,
|
||||||
|
isSystem: isSystem,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./sequelize-payment-method.model";
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
import {
|
||||||
|
type CreationOptional,
|
||||||
|
DataTypes,
|
||||||
|
type InferAttributes,
|
||||||
|
type InferCreationAttributes,
|
||||||
|
Model,
|
||||||
|
type Sequelize,
|
||||||
|
} from "sequelize";
|
||||||
|
|
||||||
|
export type PaymentMethodCreationAttributes = InferCreationAttributes<PaymentMethodModel, {}> & {};
|
||||||
|
|
||||||
|
export class PaymentMethodModel extends Model<
|
||||||
|
InferAttributes<PaymentMethodModel>,
|
||||||
|
InferCreationAttributes<PaymentMethodModel>
|
||||||
|
> {
|
||||||
|
declare id: string;
|
||||||
|
declare company_id: string;
|
||||||
|
declare name: string;
|
||||||
|
declare description: CreationOptional<string | null>;
|
||||||
|
declare is_active: boolean;
|
||||||
|
declare is_system: boolean;
|
||||||
|
|
||||||
|
static associate(_database: Sequelize) {}
|
||||||
|
|
||||||
|
static hooks(_database: Sequelize) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (database: Sequelize) => {
|
||||||
|
PaymentMethodModel.init(
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
company_id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
is_active: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: true,
|
||||||
|
},
|
||||||
|
is_system: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sequelize: database,
|
||||||
|
modelName: "PaymentMethodModel",
|
||||||
|
tableName: "payment_methods",
|
||||||
|
|
||||||
|
underscored: true,
|
||||||
|
paranoid: true, // softs deletes
|
||||||
|
timestamps: true,
|
||||||
|
|
||||||
|
createdAt: "created_at",
|
||||||
|
updatedAt: "updated_at",
|
||||||
|
deletedAt: "deleted_at",
|
||||||
|
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: "idx_payment_methods_company",
|
||||||
|
fields: ["company_id", "deleted_at", "name"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
whereMergeStrategy: "and", // <- cómo tratar el merge de un scope
|
||||||
|
|
||||||
|
defaultScope: {},
|
||||||
|
|
||||||
|
scopes: {},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return PaymentMethodModel;
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./sequelize-payment-method.repository";
|
||||||
@ -0,0 +1,226 @@
|
|||||||
|
import {
|
||||||
|
EntityNotFoundError,
|
||||||
|
InfrastructureRepositoryError,
|
||||||
|
SequelizeRepository,
|
||||||
|
translateSequelizeError,
|
||||||
|
} from "@erp/core/api";
|
||||||
|
import { type Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server";
|
||||||
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
|
import { type Collection, Result } from "@repo/rdx-utils";
|
||||||
|
import type { Sequelize, Transaction } from "sequelize";
|
||||||
|
|
||||||
|
import type { IPaymentMethodRepository } from "../../../../application";
|
||||||
|
import type { PaymentMethodSummary } from "../../../../application/models";
|
||||||
|
import type { PaymentMethod } from "../../../../domain";
|
||||||
|
import type { SequelizePaymentMethodSummaryMapper } from "../mappers";
|
||||||
|
import type { SequelizePaymentMethodDomainMapper } from "../mappers/sequelize-payment-method-domain.mapper";
|
||||||
|
import { PaymentMethodModel } from "../models";
|
||||||
|
|
||||||
|
export class SequelizePaymentMethodRepository
|
||||||
|
extends SequelizeRepository<PaymentMethod>
|
||||||
|
implements IPaymentMethodRepository
|
||||||
|
{
|
||||||
|
constructor(
|
||||||
|
private readonly domainMapper: SequelizePaymentMethodDomainMapper,
|
||||||
|
private readonly summaryMapper: SequelizePaymentMethodSummaryMapper,
|
||||||
|
database: Sequelize
|
||||||
|
) {
|
||||||
|
super({ database });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Crea un nuevo método de pago
|
||||||
|
*
|
||||||
|
* @param paymentMethod - El método de pago nuevo a guardar.
|
||||||
|
* @param transaction - Transacción activa para la operación.
|
||||||
|
* @returns Result<void, Error>
|
||||||
|
*/
|
||||||
|
async create(
|
||||||
|
paymentMethod: PaymentMethod,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<void, Error>> {
|
||||||
|
try {
|
||||||
|
const dtoResult = this.domainMapper.mapToPersistence(paymentMethod);
|
||||||
|
if (dtoResult.isFailure) {
|
||||||
|
return Result.fail(dtoResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
await PaymentMethodModel.create(dtoResult.data, { transaction });
|
||||||
|
return Result.ok();
|
||||||
|
} catch (err: unknown) {
|
||||||
|
return Result.fail(translateSequelizeError(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualiza un método de pago existente.
|
||||||
|
*
|
||||||
|
* @param paymentMethod - El método de pago a actualizar.
|
||||||
|
* @param transaction - Transacción activa para la operación.
|
||||||
|
* @returns Result<void, Error>
|
||||||
|
*/
|
||||||
|
async update(
|
||||||
|
paymentMethod: PaymentMethod,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<void, Error>> {
|
||||||
|
try {
|
||||||
|
const dtoResult = this.domainMapper.mapToPersistence(paymentMethod);
|
||||||
|
if (dtoResult.isFailure) {
|
||||||
|
return Result.fail(dtoResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id, ...payload } = dtoResult.data;
|
||||||
|
const [affected] = await PaymentMethodModel.update(payload, {
|
||||||
|
where: { id },
|
||||||
|
transaction,
|
||||||
|
individualHooks: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (affected === 0) {
|
||||||
|
return Result.fail(
|
||||||
|
new InfrastructureRepositoryError("Concurrency conflict or payment method not found")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok();
|
||||||
|
} catch (err: unknown) {
|
||||||
|
return Result.fail(translateSequelizeError(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comprueba si existe un PaymentMethod con un `id` dentro de una `company`.
|
||||||
|
*
|
||||||
|
* @param companyId - Identificador UUID de la empresa a la que pertenece el método de pago.
|
||||||
|
* @param id - Identificador UUID del método de pago.
|
||||||
|
* @param transaction - Transacción activa para la operación.
|
||||||
|
* @returns Result<boolean, Error>
|
||||||
|
*/
|
||||||
|
async existsByIdInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<boolean, Error>> {
|
||||||
|
try {
|
||||||
|
const count = await PaymentMethodModel.count({
|
||||||
|
where: { id: id.toString(), company_id: companyId.toString() },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
return Result.ok(Boolean(count > 0));
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return Result.fail(translateSequelizeError(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recupera un método de pago por su ID y companyId.
|
||||||
|
*
|
||||||
|
* @param companyId - Identificador UUID de la empresa a la que pertenece el método de pago.
|
||||||
|
* @param id - Identificador UUID del método de pago.
|
||||||
|
* @param transaction - Transacción activa para la operación.
|
||||||
|
* @returns Result<PaymentMethod, Error>
|
||||||
|
*/
|
||||||
|
|
||||||
|
async getByIdInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<PaymentMethod, Error>> {
|
||||||
|
try {
|
||||||
|
const row = await PaymentMethodModel.findOne({
|
||||||
|
where: {
|
||||||
|
id: id.toString(),
|
||||||
|
company_id: companyId.toString(),
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
if (!row) {
|
||||||
|
return Result.fail(new EntityNotFoundError("PaymentMethod", "id", id.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.domainMapper.mapToDomain(row);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
return Result.fail(translateSequelizeError(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recupera múltiples customers dentro de una empresa según un criterio dinámico (búsqueda, paginación, etc.).
|
||||||
|
*
|
||||||
|
* @param companyId - Identificador UUID de la empresa a la que pertenece el cliente.
|
||||||
|
* @param criteria - Criterios de búsqueda.
|
||||||
|
* @param transaction - Transacción activa para la operación.
|
||||||
|
* @returns Result<Collection<CustomerListDTO>, Error>
|
||||||
|
*
|
||||||
|
* @see Criteria
|
||||||
|
*/
|
||||||
|
async findByCriteriaInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
criteria: Criteria,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<Collection<PaymentMethodSummary>, Error>> {
|
||||||
|
try {
|
||||||
|
const criteriaConverter = new CriteriaToSequelizeConverter();
|
||||||
|
const query = criteriaConverter.convert(criteria, {
|
||||||
|
searchableFields: [],
|
||||||
|
sortableFields: ["name"],
|
||||||
|
enableFullText: true,
|
||||||
|
database: this.database,
|
||||||
|
strictMode: true, // fuerza error si ORDER BY no permitido
|
||||||
|
});
|
||||||
|
|
||||||
|
query.where = {
|
||||||
|
...query.where,
|
||||||
|
company_id: companyId.toString(),
|
||||||
|
deleted_at: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const [rows, count] = await Promise.all([
|
||||||
|
PaymentMethodModel.findAll({
|
||||||
|
...query,
|
||||||
|
transaction,
|
||||||
|
}),
|
||||||
|
PaymentMethodModel.count({
|
||||||
|
where: query.where,
|
||||||
|
distinct: true, // evita duplicados por LEFT JOIN
|
||||||
|
transaction,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return this.summaryMapper.mapToReadModelCollection(rows, count);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
return Result.fail(translateSequelizeError(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Elimina o marca como eliminado una forma de pago.
|
||||||
|
*
|
||||||
|
* @param companyId - Identificador UUID de la empresa a la que pertenece la forma de pago.
|
||||||
|
* @param id - UUID de la forma de pago a eliminar.
|
||||||
|
* @param transaction - Transacción activa para la operación.
|
||||||
|
* @returns Result<boolean, Error>
|
||||||
|
*/
|
||||||
|
async deleteByIdInCompany(
|
||||||
|
companyId: UniqueID,
|
||||||
|
id: UniqueID,
|
||||||
|
transaction: Transaction
|
||||||
|
): Promise<Result<boolean, Error>> {
|
||||||
|
try {
|
||||||
|
const deleted = await PaymentMethodModel.destroy({
|
||||||
|
where: { id: id.toString(), company_id: companyId.toString() },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (deleted === 0) {
|
||||||
|
return Result.fail(new EntityNotFoundError("PaymentMethod", "id", id.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(true);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
return Result.fail(translateSequelizeError(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
modules/catalogs/src/common/dto/index.ts
Normal file
1
modules/catalogs/src/common/dto/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./payment-methods";
|
||||||
2
modules/catalogs/src/common/dto/payment-methods/index.ts
Normal file
2
modules/catalogs/src/common/dto/payment-methods/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./request";
|
||||||
|
export * from "./response";
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
|
export const CreatePaymentMethodRequestSchema = z.object({
|
||||||
|
id: z.uuid(),
|
||||||
|
|
||||||
|
name: z.string(),
|
||||||
|
description: z.string().nullable().optional(),
|
||||||
|
|
||||||
|
is_active: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CreatePaymentMethodRequestDTO = z.infer<typeof CreatePaymentMethodRequestSchema>;
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
|
export const GetPaymentMethodByIdRequestSchema = z.object({
|
||||||
|
payment_method_id: z.uuid(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GetPaymentMethodByIdRequestDTO = z.infer<typeof GetPaymentMethodByIdRequestSchema>;
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
export * from "./create-payment-method.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";
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { CriteriaSchema } from "@erp/core";
|
||||||
|
import type { z } from "zod/v4";
|
||||||
|
|
||||||
|
export const ListPaymentMethodsRequestSchema = CriteriaSchema;
|
||||||
|
export type ListPaymentMethodsRequestDTO = z.infer<typeof ListPaymentMethodsRequestSchema>;
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
|
export const UpdatePaymentMethodByIdParamsRequestSchema = z.object({
|
||||||
|
payment_method_id: z.uuid(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdatePaymentMethodByIdRequestSchema = z.object({
|
||||||
|
name: z.string().optional(),
|
||||||
|
description: z.string().nullable().optional(),
|
||||||
|
is_active: z.boolean().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type UpdatePaymentMethodByIdParamsRequestDTO = z.infer<
|
||||||
|
typeof UpdatePaymentMethodByIdParamsRequestSchema
|
||||||
|
>;
|
||||||
|
export type UpdatePaymentMethodByIdRequestDTO = z.infer<
|
||||||
|
typeof UpdatePaymentMethodByIdRequestSchema
|
||||||
|
>;
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import type { z } from "zod/v4";
|
||||||
|
|
||||||
|
import { PaymentMethodDetailSchema } from "../shared";
|
||||||
|
|
||||||
|
export const CreatePaymentMethodResponseSchema = PaymentMethodDetailSchema;
|
||||||
|
export type CreatePaymentMethodResponseDTO = z.infer<typeof CreatePaymentMethodResponseSchema>;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import type { z } from "zod/v4";
|
||||||
|
|
||||||
|
import { PaymentMethodDetailSchema } from "../shared";
|
||||||
|
|
||||||
|
export const DisablePaymentMethodByIdResponseSchema = PaymentMethodDetailSchema;
|
||||||
|
export type DisablePaymentMethodByIdResponseDTO = z.infer<
|
||||||
|
typeof DisablePaymentMethodByIdResponseSchema
|
||||||
|
>;
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import type { z } from "zod/v4";
|
||||||
|
|
||||||
|
import { PaymentMethodDetailSchema } from "../shared";
|
||||||
|
|
||||||
|
export const GetPaymentMethodByIdResponseSchema = PaymentMethodDetailSchema;
|
||||||
|
export type GetPaymentMethodByIdResponseDTO = z.infer<typeof GetPaymentMethodByIdResponseSchema>;
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
export * from "./create-payment-method.response.dto";
|
||||||
|
export * from "./disable-payment-method-by-id.response.dto";
|
||||||
|
export * from "./get-payment-method-by-id.response.dto";
|
||||||
|
export * from "./list-payment-methods.response.dto";
|
||||||
|
export * from "./update-payment-method-by-id.response.dto";
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
|
import { PaymentMethodSummarySchema } from "../shared";
|
||||||
|
|
||||||
|
export const ListPaymentMethodsResponseSchema = z.array(PaymentMethodSummarySchema);
|
||||||
|
export type ListPaymentMethodsResponseDTO = z.infer<typeof ListPaymentMethodsResponseSchema>;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import type { z } from "zod/v4";
|
||||||
|
|
||||||
|
import { PaymentMethodDetailSchema } from "../shared";
|
||||||
|
|
||||||
|
export const UpdatePaymentMethodByIdResponseSchema = PaymentMethodDetailSchema;
|
||||||
|
export type UpdatePaymentMethodByIdResponseDTO = z.infer<
|
||||||
|
typeof UpdatePaymentMethodByIdResponseSchema
|
||||||
|
>;
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./payment-method-detail.dto";
|
||||||
|
export * from "./payment-method-summary.dto";
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
|
export const PaymentMethodDetailSchema = z.object({
|
||||||
|
id: z.uuid(),
|
||||||
|
company_id: z.uuid(),
|
||||||
|
|
||||||
|
name: z.string(),
|
||||||
|
description: z.string().nullable(),
|
||||||
|
is_active: z.boolean(),
|
||||||
|
is_system: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type PaymentMethodDetailDTO = z.infer<typeof PaymentMethodDetailSchema>;
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
|
export const PaymentMethodSummarySchema = z.object({
|
||||||
|
id: z.uuid(),
|
||||||
|
company_id: z.uuid(),
|
||||||
|
|
||||||
|
name: z.string(),
|
||||||
|
is_active: z.boolean(),
|
||||||
|
is_system: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type PaymentMethodSummaryDTO = z.infer<typeof PaymentMethodSummarySchema>;
|
||||||
1
modules/catalogs/src/common/index.ts
Normal file
1
modules/catalogs/src/common/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./dto";
|
||||||
26
modules/catalogs/tsconfig.json
Normal file
26
modules/catalogs/tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"@erp/catalogs/*": ["./src/*"]
|
||||||
|
},
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"target": "ES2022",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2022"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@ -21,7 +21,7 @@ import {
|
|||||||
ListProformasUseCase,
|
ListProformasUseCase,
|
||||||
ReportProformaUseCase,
|
ReportProformaUseCase,
|
||||||
} from "../use-cases";
|
} from "../use-cases";
|
||||||
import { UpdateProformaUseCase } from "../use-cases/update-proforma.use-case";
|
import { UpdateProformaByIdUseCase } from "../use-cases/update-proforma-by-id.use-case";
|
||||||
|
|
||||||
export function buildGetProformaByIdUseCase(deps: {
|
export function buildGetProformaByIdUseCase(deps: {
|
||||||
finder: IProformaFinder;
|
finder: IProformaFinder;
|
||||||
@ -101,7 +101,7 @@ export function buildUpdateProformaUseCase(deps: {
|
|||||||
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||||
transactionManager: ITransactionManager;
|
transactionManager: ITransactionManager;
|
||||||
}) {
|
}) {
|
||||||
return new UpdateProformaUseCase({
|
return new UpdateProformaByIdUseCase({
|
||||||
dtoMapper: deps.dtoMapper,
|
dtoMapper: deps.dtoMapper,
|
||||||
updater: deps.updater,
|
updater: deps.updater,
|
||||||
fullSnapshotBuilder: deps.fullSnapshotBuilder,
|
fullSnapshotBuilder: deps.fullSnapshotBuilder,
|
||||||
|
|||||||
@ -5,4 +5,4 @@ export * from "./get-proforma-by-id.use-case";
|
|||||||
export * from "./issue-proforma.use-case";
|
export * from "./issue-proforma.use-case";
|
||||||
export * from "./list-proformas.use-case";
|
export * from "./list-proformas.use-case";
|
||||||
export * from "./report-proforma.use-case";
|
export * from "./report-proforma.use-case";
|
||||||
export * from "./update-proforma.use-case";
|
export * from "./update-proforma-by-id.use-case";
|
||||||
|
|||||||
@ -21,7 +21,7 @@ type UpdateProformaUseCaseDeps = {
|
|||||||
transactionManager: ITransactionManager;
|
transactionManager: ITransactionManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class UpdateProformaUseCase {
|
export class UpdateProformaByIdUseCase {
|
||||||
private readonly dtoMapper: IUpdateProformaInputMapper;
|
private readonly dtoMapper: IUpdateProformaInputMapper;
|
||||||
private readonly updater: IProformaUpdater;
|
private readonly updater: IProformaUpdater;
|
||||||
private readonly fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
private readonly fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||||
@ -7,7 +7,7 @@ import {
|
|||||||
type IssueProformaUseCase,
|
type IssueProformaUseCase,
|
||||||
type ListProformasUseCase,
|
type ListProformasUseCase,
|
||||||
type ReportProformaUseCase,
|
type ReportProformaUseCase,
|
||||||
type UpdateProformaUseCase,
|
type UpdateProformaByIdUseCase,
|
||||||
buildCreateProformaUseCase,
|
buildCreateProformaUseCase,
|
||||||
buildGetProformaByIdUseCase,
|
buildGetProformaByIdUseCase,
|
||||||
buildIssueProformaUseCase,
|
buildIssueProformaUseCase,
|
||||||
@ -37,7 +37,7 @@ export type ProformasInternalDeps = {
|
|||||||
issueProforma: (publicServices: {
|
issueProforma: (publicServices: {
|
||||||
issuedInvoiceServices: IIssuedInvoicePublicServices;
|
issuedInvoiceServices: IIssuedInvoicePublicServices;
|
||||||
}) => IssueProformaUseCase;
|
}) => IssueProformaUseCase;
|
||||||
updateProforma: () => UpdateProformaUseCase;
|
updateProforma: () => UpdateProformaByIdUseCase;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
deleteProforma: () => DeleteProformaUseCase;
|
deleteProforma: () => DeleteProformaUseCase;
|
||||||
|
|||||||
@ -27,7 +27,7 @@ export class CreateProformaController extends ExpressController {
|
|||||||
if (!companyId) {
|
if (!companyId) {
|
||||||
return this.forbiddenError("Tenant ID not found");
|
return this.forbiddenError("Tenant ID not found");
|
||||||
}
|
}
|
||||||
const dto = this.req.body as CreateProformaRequestDTO;
|
const dto = this.req.body satisfies CreateProformaRequestDTO;
|
||||||
|
|
||||||
const result = await this.useCase.execute({ dto, companyId });
|
const result = await this.useCase.execute({ dto, companyId });
|
||||||
|
|
||||||
|
|||||||
@ -6,11 +6,11 @@ import {
|
|||||||
} from "@erp/core/api";
|
} from "@erp/core/api";
|
||||||
|
|
||||||
import type { UpdateProformaByIdRequestDTO } from "../../../../../common/dto/index.ts";
|
import type { UpdateProformaByIdRequestDTO } from "../../../../../common/dto/index.ts";
|
||||||
import type { UpdateProformaUseCase } from "../../../../application/index.ts";
|
import type { UpdateProformaByIdUseCase } from "../../../../application/index.ts";
|
||||||
import { proformasApiErrorMapper } from "../proformas-api-error-mapper.ts";
|
import { proformasApiErrorMapper } from "../proformas-api-error-mapper.ts";
|
||||||
|
|
||||||
export class UpdateProformaController extends ExpressController {
|
export class UpdateProformaController extends ExpressController {
|
||||||
public constructor(private readonly useCase: UpdateProformaUseCase) {
|
public constructor(private readonly useCase: UpdateProformaByIdUseCase) {
|
||||||
super();
|
super();
|
||||||
this.errorMapper = proformasApiErrorMapper;
|
this.errorMapper = proformasApiErrorMapper;
|
||||||
|
|
||||||
|
|||||||
@ -56,6 +56,9 @@ importers:
|
|||||||
'@erp/auth':
|
'@erp/auth':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../modules/auth
|
version: link:../../modules/auth
|
||||||
|
'@erp/catalogs':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../modules/catalogs
|
||||||
'@erp/core':
|
'@erp/core':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../modules/core
|
version: link:../../modules/core
|
||||||
@ -375,6 +378,40 @@ importers:
|
|||||||
specifier: ^6.0.2
|
specifier: ^6.0.2
|
||||||
version: 6.0.2
|
version: 6.0.2
|
||||||
|
|
||||||
|
modules/catalogs:
|
||||||
|
dependencies:
|
||||||
|
'@erp/auth':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../auth
|
||||||
|
'@erp/core':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../core
|
||||||
|
'@repo/rdx-criteria':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/rdx-criteria
|
||||||
|
'@repo/rdx-ddd':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/rdx-ddd
|
||||||
|
'@repo/rdx-utils':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/rdx-utils
|
||||||
|
express:
|
||||||
|
specifier: ^4.22.1
|
||||||
|
version: 4.22.1
|
||||||
|
sequelize:
|
||||||
|
specifier: ^6.37.8
|
||||||
|
version: 6.37.8(mysql2@3.22.0(@types/node@25.6.0))(pg-hstore@2.3.4)
|
||||||
|
zod:
|
||||||
|
specifier: ^4.3.6
|
||||||
|
version: 4.3.6
|
||||||
|
devDependencies:
|
||||||
|
'@types/express':
|
||||||
|
specifier: ^4.17.21
|
||||||
|
version: 4.17.25
|
||||||
|
typescript:
|
||||||
|
specifier: ^6.0.2
|
||||||
|
version: 6.0.2
|
||||||
|
|
||||||
modules/core:
|
modules/core:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@hookform/resolvers':
|
'@hookform/resolvers':
|
||||||
|
|||||||
@ -7,7 +7,11 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"chatgpt.openOnStartup": true,
|
"chatgpt.openOnStartup": true,
|
||||||
"chat.tools.terminal.autoApprove": {
|
"chat.tools.terminal.autoApprove": {
|
||||||
"pnpm": true
|
"pnpm": true,
|
||||||
|
"/^cd /home/rodax/Documentos/uecko-erp && ls -l node_modules/@repo/typescript-config/root\\.json && node -p \"require\\.resolve\\('@repo/typescript-config/root\\.json'\\)\"$/": {
|
||||||
|
"approve": true,
|
||||||
|
"matchCommandLine": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user