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 customersAPIModule from "@erp/customers/api";
|
||||
import factuGESAPIModule from "@erp/factuges/api";
|
||||
@ -8,6 +9,7 @@ import { registerModule } from "./lib";
|
||||
|
||||
export const registerModules = () => {
|
||||
//registerModule(authAPIModule);
|
||||
registerModule(catalogsAPIModule);
|
||||
registerModule(customersAPIModule);
|
||||
registerModule(customerInvoicesAPIModule);
|
||||
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,
|
||||
ReportProformaUseCase,
|
||||
} 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: {
|
||||
finder: IProformaFinder;
|
||||
@ -101,7 +101,7 @@ export function buildUpdateProformaUseCase(deps: {
|
||||
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||
transactionManager: ITransactionManager;
|
||||
}) {
|
||||
return new UpdateProformaUseCase({
|
||||
return new UpdateProformaByIdUseCase({
|
||||
dtoMapper: deps.dtoMapper,
|
||||
updater: deps.updater,
|
||||
fullSnapshotBuilder: deps.fullSnapshotBuilder,
|
||||
|
||||
@ -5,4 +5,4 @@ export * from "./get-proforma-by-id.use-case";
|
||||
export * from "./issue-proforma.use-case";
|
||||
export * from "./list-proformas.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;
|
||||
};
|
||||
|
||||
export class UpdateProformaUseCase {
|
||||
export class UpdateProformaByIdUseCase {
|
||||
private readonly dtoMapper: IUpdateProformaInputMapper;
|
||||
private readonly updater: IProformaUpdater;
|
||||
private readonly fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||
@ -7,7 +7,7 @@ import {
|
||||
type IssueProformaUseCase,
|
||||
type ListProformasUseCase,
|
||||
type ReportProformaUseCase,
|
||||
type UpdateProformaUseCase,
|
||||
type UpdateProformaByIdUseCase,
|
||||
buildCreateProformaUseCase,
|
||||
buildGetProformaByIdUseCase,
|
||||
buildIssueProformaUseCase,
|
||||
@ -37,7 +37,7 @@ export type ProformasInternalDeps = {
|
||||
issueProforma: (publicServices: {
|
||||
issuedInvoiceServices: IIssuedInvoicePublicServices;
|
||||
}) => IssueProformaUseCase;
|
||||
updateProforma: () => UpdateProformaUseCase;
|
||||
updateProforma: () => UpdateProformaByIdUseCase;
|
||||
|
||||
/*
|
||||
deleteProforma: () => DeleteProformaUseCase;
|
||||
|
||||
@ -27,7 +27,7 @@ export class CreateProformaController extends ExpressController {
|
||||
if (!companyId) {
|
||||
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 });
|
||||
|
||||
|
||||
@ -6,11 +6,11 @@ import {
|
||||
} from "@erp/core/api";
|
||||
|
||||
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";
|
||||
|
||||
export class UpdateProformaController extends ExpressController {
|
||||
public constructor(private readonly useCase: UpdateProformaUseCase) {
|
||||
public constructor(private readonly useCase: UpdateProformaByIdUseCase) {
|
||||
super();
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
|
||||
|
||||
@ -56,6 +56,9 @@ importers:
|
||||
'@erp/auth':
|
||||
specifier: workspace:*
|
||||
version: link:../../modules/auth
|
||||
'@erp/catalogs':
|
||||
specifier: workspace:*
|
||||
version: link:../../modules/catalogs
|
||||
'@erp/core':
|
||||
specifier: workspace:*
|
||||
version: link:../../modules/core
|
||||
@ -375,6 +378,40 @@ importers:
|
||||
specifier: ^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:
|
||||
dependencies:
|
||||
'@hookform/resolvers':
|
||||
|
||||
@ -7,7 +7,11 @@
|
||||
"settings": {
|
||||
"chatgpt.openOnStartup": true,
|
||||
"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