.
This commit is contained in:
parent
36e215ad9c
commit
7e63288e12
@ -59,7 +59,9 @@ function buildSequelize(): Sequelize {
|
||||
idle: 10_000,
|
||||
acquire: 30_000,
|
||||
},
|
||||
// dialectOptions: { /* según dialecto (ssl, etc.) */ },
|
||||
dialectOptions: {
|
||||
timezone: ENV.APP_TIMEZONE,
|
||||
},
|
||||
} as const;
|
||||
|
||||
if (ENV.DATABASE_URL && ENV.DATABASE_URL.trim() !== "") {
|
||||
|
||||
@ -5,9 +5,9 @@ import { RequestWithAuth } from "./auth-types";
|
||||
export function mockUser(req: RequestWithAuth, res: Response, next: NextFunction) {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
req.user = {
|
||||
id: UniqueID.create("9e4dc5b3-96b9-4968-9490-14bd032fec5f").data,
|
||||
userId: UniqueID.create("9e4dc5b3-96b9-4968-9490-14bd032fec5f").data,
|
||||
email: EmailAddress.create("dev@example.com").data,
|
||||
companyId: UniqueID.create("1e4dc5b3-96b9-4968-9490-14bd032fec5f").data,
|
||||
companyId: UniqueID.create("5e4dc5b3-96b9-4968-9490-14bd032fec5f").data,
|
||||
roles: ["admin"],
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { DomainError } from "./domain-error";
|
||||
|
||||
export class EntityNotFoundError extends DomainError {
|
||||
constructor(entity: string, field: string, value: string, options?: ErrorOptions) {
|
||||
constructor(entity: string, field: string, value: any, options?: ErrorOptions) {
|
||||
super(`Entity '${entity}' with ${field} '${value}' was not found.`, options);
|
||||
this.name = "EntityNotFoundError";
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import { mapDTOToCreateCustomerProps } from "./map-dto-to-create-customer-props"
|
||||
|
||||
type CreateCustomerUseCaseInput = {
|
||||
dto: CreateCustomerRequestDTO;
|
||||
companyId: UniqueID;
|
||||
};
|
||||
|
||||
export class CreateCustomerUseCase {
|
||||
@ -19,7 +20,7 @@ export class CreateCustomerUseCase {
|
||||
) {}
|
||||
|
||||
public execute(params: CreateCustomerUseCaseInput) {
|
||||
const { dto } = params;
|
||||
const { dto, companyId } = params;
|
||||
|
||||
// 1) Mapear DTO → props de dominio
|
||||
const dtoResult = mapDTOToCreateCustomerProps(dto);
|
||||
@ -27,14 +28,12 @@ export class CreateCustomerUseCase {
|
||||
return Result.fail(dtoResult.error);
|
||||
}
|
||||
|
||||
const mapped = dtoResult.data;
|
||||
const id = mapped.id;
|
||||
const { companyId, ...customerProps } = mapped.props;
|
||||
const { props, id } = dtoResult.data;
|
||||
|
||||
console.debug("Creating customer with props:", customerProps);
|
||||
console.debug("Creating customer with props:", props);
|
||||
|
||||
// 3) Construir entidad de dominio
|
||||
const buildResult = this.service.buildCustomerInCompany(companyId, customerProps, id);
|
||||
const buildResult = this.service.buildCustomerInCompany(companyId, props, id);
|
||||
if (buildResult.isFailure) {
|
||||
return Result.fail(buildResult.error);
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ export function mapDTOToCreateCustomerProps(dto: CreateCustomerRequestDTO) {
|
||||
const customerId = extractOrPushError(UniqueID.create(dto.id), "id", errors);
|
||||
const companyId = extractOrPushError(UniqueID.create(dto.company_id), "company_id", errors);
|
||||
|
||||
const isCompany = dto.is_company;
|
||||
const isCompany = dto.is_company === "true";
|
||||
const status = extractOrPushError(CustomerStatus.create(dto.status), "status", errors);
|
||||
const reference = extractOrPushError(
|
||||
maybeFromNullableVO(dto.reference, (value) => Name.create(value)),
|
||||
@ -146,7 +146,7 @@ export function mapDTOToCreateCustomerProps(dto: CreateCustomerRequestDTO) {
|
||||
|
||||
const defaultTaxes = new Collection<TaxCode>();
|
||||
|
||||
dto.default_taxes.map((taxCode, index) => {
|
||||
dto.default_taxes.split(",").map((taxCode, index) => {
|
||||
const tax = extractOrPushError(TaxCode.create(taxCode), `default_taxes.${index}`, errors);
|
||||
if (tax) {
|
||||
defaultTaxes.add(tax!);
|
||||
|
||||
@ -33,11 +33,13 @@ export class DeleteCustomerUseCase {
|
||||
return Result.fail(existsCheck.error);
|
||||
}
|
||||
|
||||
if (!existsCheck.data) {
|
||||
const customerExists = existsCheck.data;
|
||||
|
||||
if (!customerExists) {
|
||||
return Result.fail(new EntityNotFoundError("Customer", "id", validId.toString()));
|
||||
}
|
||||
|
||||
return await this.service.deleteCustomerByIdInCompany(validId, transaction);
|
||||
return await this.service.deleteCustomerByIdInCompany(validId, companyId, transaction);
|
||||
} catch (error: unknown) {
|
||||
return Result.fail(error as Error);
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ export interface ICustomerRepository {
|
||||
* Guarda (crea o actualiza) un Customer en la base de datos.
|
||||
* Retorna el objeto actualizado tras la operación.
|
||||
*/
|
||||
save(customer: Customer, transaction?: any): Promise<Result<Customer, Error>>;
|
||||
save(customer: Customer, transaction: any): Promise<Result<Customer, Error>>;
|
||||
|
||||
/**
|
||||
* Comprueba si existe un Customer con un `id` dentro de una `company`.
|
||||
@ -48,5 +48,5 @@ export interface ICustomerRepository {
|
||||
* Elimina un Customer por su ID, dentro de una empresa.
|
||||
* Retorna `void` si se elimina correctamente, o `NotFoundError` si no existía.
|
||||
*/
|
||||
deleteByIdInCompany(companyId: UniqueID, id: UniqueID, transaction?: any): Promise<Result<void>>;
|
||||
deleteByIdInCompany(companyId: UniqueID, id: UniqueID, transaction: any): Promise<Result<void>>;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api";
|
||||
import { UpdateCustomerRequestDTO } from "../../../../common/dto";
|
||||
import { CreateCustomerRequestDTO } from "../../../../common/dto";
|
||||
import { CreateCustomerUseCase } from "../../../application";
|
||||
|
||||
export class CreateCustomerController extends ExpressController {
|
||||
@ -11,12 +11,9 @@ export class CreateCustomerController extends ExpressController {
|
||||
|
||||
protected async executeImpl() {
|
||||
const companyId = this.getTenantId()!; // garantizado por tenantGuard
|
||||
const dto = this.req.body as UpdateCustomerRequestDTO;
|
||||
const dto = this.req.body as CreateCustomerRequestDTO;
|
||||
|
||||
// Inyectar empresa del usuario autenticado (ownership)
|
||||
dto.company_id = companyId.toString();
|
||||
|
||||
const result = await this.useCase.execute({ dto });
|
||||
const result = await this.useCase.execute({ dto, companyId });
|
||||
|
||||
return result.match(
|
||||
(data) => this.created(data),
|
||||
|
||||
@ -3,6 +3,7 @@ import { ILogger, ModuleParams, validateRequest } from "@erp/core/api";
|
||||
import { Application, NextFunction, Request, Response, Router } from "express";
|
||||
import { Sequelize } from "sequelize";
|
||||
import {
|
||||
CreateCustomerRequestSchema,
|
||||
CustomerListRequestSchema,
|
||||
DeleteCustomerByIdRequestSchema,
|
||||
GetCustomerByIdRequestSchema,
|
||||
@ -15,6 +16,7 @@ import {
|
||||
GetCustomerController,
|
||||
ListCustomersController,
|
||||
} from "./controllers";
|
||||
import { UpdateCustomerController } from "./controllers/update-customer.controller";
|
||||
|
||||
export const customersRouter = (params: ModuleParams) => {
|
||||
const { app, database, baseRoutePath, logger } = params as {
|
||||
@ -64,7 +66,7 @@ export const customersRouter = (params: ModuleParams) => {
|
||||
"/",
|
||||
//checkTabContext,
|
||||
|
||||
validateRequest(UpdateCustomerRequestSchema),
|
||||
validateRequest(CreateCustomerRequestSchema),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const useCase = deps.build.create();
|
||||
const controller = new CreateCustomerController(useCase);
|
||||
@ -72,15 +74,17 @@ export const customersRouter = (params: ModuleParams) => {
|
||||
}
|
||||
);
|
||||
|
||||
/*routes.put(
|
||||
router.put(
|
||||
"/:customerId",
|
||||
validateAndParseBody(IUpdateCustomerRequestSchema),
|
||||
checkTabContext,
|
||||
//checkTabContext,
|
||||
|
||||
validateRequest(UpdateCustomerRequestSchema),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
buildUpdateCustomerController().execute(req, res, next);
|
||||
const useCase = deps.build.update();
|
||||
const controller = new UpdateCustomerController(useCase);
|
||||
return controller.execute(req, res, next);
|
||||
}
|
||||
);*/
|
||||
);
|
||||
|
||||
router.delete(
|
||||
"/:id",
|
||||
|
||||
@ -56,7 +56,7 @@ export class CustomerRepository
|
||||
async existsByIdInCompany(
|
||||
companyId: UniqueID,
|
||||
id: UniqueID,
|
||||
transaction?: any
|
||||
transaction?: Transaction
|
||||
): Promise<Result<boolean, Error>> {
|
||||
try {
|
||||
const count = await CustomerModel.count({
|
||||
@ -80,7 +80,7 @@ export class CustomerRepository
|
||||
async getByIdInCompany(
|
||||
companyId: UniqueID,
|
||||
id: UniqueID,
|
||||
transaction?: any
|
||||
transaction?: Transaction
|
||||
): Promise<Result<Customer, Error>> {
|
||||
try {
|
||||
const row = await CustomerModel.findOne({
|
||||
@ -113,7 +113,7 @@ export class CustomerRepository
|
||||
async findByCriteriaInCompany(
|
||||
companyId: UniqueID,
|
||||
criteria: Criteria,
|
||||
transaction?: any
|
||||
transaction?: Transaction
|
||||
): Promise<Result<Collection<Customer>>> {
|
||||
try {
|
||||
const converter = new CriteriaToSequelizeConverter();
|
||||
@ -124,8 +124,6 @@ export class CustomerRepository
|
||||
company_id: companyId.toString(),
|
||||
};
|
||||
|
||||
console.debug({ model: "CustomerModel", criteria, query });
|
||||
|
||||
const instances = await CustomerModel.findAll({
|
||||
...query,
|
||||
transaction,
|
||||
@ -150,17 +148,17 @@ export class CustomerRepository
|
||||
async deleteByIdInCompany(
|
||||
companyId: UniqueID,
|
||||
id: UniqueID,
|
||||
transaction?: any
|
||||
transaction: Transaction
|
||||
): Promise<Result<void>> {
|
||||
try {
|
||||
console.log(id, companyId);
|
||||
const deleted = await CustomerModel.destroy({
|
||||
where: { id: id.toString(), company_id: companyId.toString() },
|
||||
transaction,
|
||||
});
|
||||
|
||||
if (deleted === 0) {
|
||||
return Result.fail(new Error(`Customer with id ${id} not found in company ${companyId}.`));
|
||||
}
|
||||
console.log(deleted);
|
||||
|
||||
return Result.ok<void>();
|
||||
} catch (err: unknown) {
|
||||
// , `Error deleting customer ${id} in company ${companyId}`
|
||||
|
||||
@ -5,7 +5,7 @@ export const CreateCustomerRequestSchema = z.object({
|
||||
company_id: z.uuid(),
|
||||
reference: z.string().default(""),
|
||||
|
||||
is_company: z.boolean().default(false),
|
||||
is_company: z.string().toLowerCase().default("false"),
|
||||
name: z.string().default(""),
|
||||
trade_name: z.string().default(""),
|
||||
tin: z.string().default(""),
|
||||
@ -24,10 +24,10 @@ export const CreateCustomerRequestSchema = z.object({
|
||||
|
||||
legal_record: z.string().default(""),
|
||||
|
||||
default_taxes: z.array(z.string()).default([]),
|
||||
status: z.string().default("active"),
|
||||
language_code: z.string().default("es"),
|
||||
currency_code: z.string().default("EUR"),
|
||||
default_taxes: z.string().default(""),
|
||||
status: z.string().toLowerCase().default("active"),
|
||||
language_code: z.string().toLowerCase().default("es"),
|
||||
currency_code: z.string().toUpperCase().default("EUR"),
|
||||
});
|
||||
|
||||
export type CreateCustomerRequestDTO = z.infer<typeof CreateCustomerRequestSchema>;
|
||||
|
||||
@ -3,7 +3,7 @@ import * as z from "zod/v4";
|
||||
export const UpdateCustomerRequestSchema = z.object({
|
||||
reference: z.string().optional(),
|
||||
|
||||
is_company: z.boolean().optional(),
|
||||
is_company: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
trade_name: z.string().optional(),
|
||||
tin: z.string().optional(),
|
||||
@ -22,7 +22,7 @@ export const UpdateCustomerRequestSchema = z.object({
|
||||
|
||||
legal_record: z.string().optional(),
|
||||
|
||||
default_taxes: z.array(z.string()).optional(), // completo (sustituye), o null => vaciar
|
||||
default_taxes: z.string().optional(), // completo (sustituye), o null => vaciar
|
||||
language_code: z.string().optional(),
|
||||
currency_code: z.string().optional(),
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user