Facturas de cliente

This commit is contained in:
David Arranz 2025-09-14 12:04:57 +02:00
parent 2036ec2206
commit 4807e51d82
43 changed files with 174 additions and 296 deletions

View File

@ -64,12 +64,12 @@ export class InMemoryPresenterRegistry implements IPresenterRegistry {
if (!this.registry.has(exactKey)) {
throw new ApplicationError(
`Error. Presenter ${key.resource} ${key.projection} not registred!`
`[InMemoryPresenterRegistry] Presenter not registered: ${key.resource}::${key.projection}`
);
}
throw new ApplicationError(
`Error. Presenter ${key.resource} / ${key.projection} not registred!`
`[InMemoryPresenterRegistry] Presenter not registered: ${key.resource}::${key.projection}`
);
}

View File

@ -10,14 +10,9 @@ export class InMemoryMapperRegistry implements IMapperRegistry {
private _mappers: Map<string, any> = new Map();
private _normalizeKey(key: MapperDomainKey | MapperQueryKey): MapperKey {
const { resource, query } = key as {
resource: string;
query?: string;
};
return {
resource,
query: query ?? "DOMAIN", // 👈 valor por defecto
resource: key.resource,
query: "query" in key && key.query ? key.query : "DOMAIN",
};
}
@ -37,37 +32,37 @@ export class InMemoryMapperRegistry implements IMapperRegistry {
const exactKey = this._buildKey(normalizedKey);
if (!this._mappers.has(exactKey)) {
throw new InfrastructureError(`Error. Mapper ${normalizedKey.resource} not registred!`);
throw new InfrastructureError(
`[InMemoryMapperRegistry] Mapper not registered: ${normalizedKey.resource}::${normalizedKey.query}`
);
}
return this._mappers.get(exactKey);
}
private _registerMapper<T>(key: MapperDomainKey | MapperQueryKey, mapper: T) {
const exactKey = this._buildKey(this._normalizeKey(key));
this._mappers.set(exactKey, mapper);
}
getDomainMapper<T>(key: MapperDomainKey): T {
const normalizedKey = this._normalizeKey({
resource: key.resource,
return this._getMapper({
...key,
query: "DOMAIN",
});
return this._getMapper(normalizedKey);
}
getQueryMapper<T>(key: MapperQueryKey): T {
const normalizedKey = this._normalizeKey({
resource: key.resource,
query: "DOMAIN",
});
return this._getMapper(normalizedKey);
return this._getMapper(key);
}
registerDomainMapper<T>(key: MapperDomainKey, mapper: T) {
const exactKey = this._buildKey(this._normalizeKey(key));
this._mappers.set(exactKey, mapper);
this._registerMapper(key, mapper);
return this;
}
registerQueryMapper<T>(key: MapperQueryKey, mapper: T) {
const exactKey = this._buildKey(this._normalizeKey(key));
this._mappers.set(exactKey, mapper);
this._registerMapper(key, mapper);
return this;
}

View File

@ -4,7 +4,10 @@ import { GetCustomerInvoiceByIdResponseDTO } from "../../../../common/dto";
import { CustomerInvoice } from "../../../domain";
import { CustomerInvoiceItemsFullPresenter } from "./customer-invoice-items.full.presenter";
export class CustomerInvoiceFullPresenter extends Presenter {
export class CustomerInvoiceFullPresenter extends Presenter<
CustomerInvoice,
GetCustomerInvoiceByIdResponseDTO
> {
toOutput(invoice: CustomerInvoice): GetCustomerInvoiceByIdResponseDTO {
const itemsPresenter = this.presenterRegistry.getPresenter({
resource: "customer-invoice-items",

View File

@ -11,7 +11,10 @@ export class CreateCustomerInvoiceController extends ExpressController {
}
protected async executeImpl() {
const companyId = this.getTenantId()!; // garantizado por tenantGuard
const companyId = this.getTenantId();
if (!companyId) {
return this.forbiddenError("Tenant ID not found");
}
const dto = this.req.body as CreateCustomerInvoiceRequestDTO;
const result = await this.useCase.execute({ dto, companyId });

View File

@ -9,7 +9,10 @@ export class GetCustomerInvoiceController extends ExpressController {
}
protected async executeImpl() {
const companyId = this.getTenantId()!; // garantizado por tenantGuard
const companyId = this.getTenantId();
if (!companyId) {
return this.forbiddenError("Tenant ID not found");
}
const { invoice_id } = this.req.params;
const result = await this.useCase.execute({ invoice_id, companyId });

View File

@ -9,7 +9,7 @@ export class ListCustomerInvoicesController extends ExpressController {
this.useGuards(authGuard(), tenantGuard(), forbidQueryFieldGuard("companyId"));
}
private _xxxx() {
private getCriteriaWithDefaultOrder() {
if (this.criteria.hasOrder()) {
return this.criteria;
}
@ -19,9 +19,12 @@ export class ListCustomerInvoicesController extends ExpressController {
}
protected async executeImpl() {
const companyId = this.getTenantId()!; // garantizado por tenantGuard
const companyId = this.getTenantId();
if (!companyId) {
return this.forbiddenError("Tenant ID not found");
}
const criteria = this._xxxx();
const criteria = this.getCriteriaWithDefaultOrder();
const result = await this.useCase.execute({ criteria, companyId });
return result.match(

View File

@ -9,7 +9,10 @@ export class ReportCustomerInvoiceController extends ExpressController {
}
protected async executeImpl() {
const companyId = this.getTenantId()!; // garantizado por tenantGuard
const companyId = this.getTenantId();
if (!companyId) {
return this.forbiddenError("Tenant ID not found");
}
const { invoice_id } = this.req.params;
const result = await this.useCase.execute({ invoice_id, companyId });

View File

@ -1,47 +0,0 @@
import { toEmptyString } from "@repo/rdx-ddd";
import { CustomerCreationResponseDTO } from "../../../../common";
import { Customer } from "../../../domain";
export class CreateCustomersPresenter {
public toDTO(customer: Customer): CustomerCreationResponseDTO {
const address = customer.address.toPrimitive();
return {
id: customer.id.toPrimitive(),
company_id: customer.companyId.toPrimitive(),
reference: toEmptyString(customer.reference, (value) => value.toPrimitive()),
is_company: String(customer.isCompany),
name: customer.name.toPrimitive(),
trade_name: toEmptyString(customer.tradeName, (value) => value.toPrimitive()),
tin: toEmptyString(customer.tin, (value) => value.toPrimitive()),
street: toEmptyString(address.street, (value) => value.toPrimitive()),
street2: toEmptyString(address.street2, (value) => value.toPrimitive()),
city: toEmptyString(address.city, (value) => value.toPrimitive()),
province: toEmptyString(address.province, (value) => value.toPrimitive()),
postal_code: toEmptyString(address.postalCode, (value) => value.toPrimitive()),
country: toEmptyString(address.country, (value) => value.toPrimitive()),
email: toEmptyString(customer.email, (value) => value.toPrimitive()),
phone: toEmptyString(customer.phone, (value) => value.toPrimitive()),
fax: toEmptyString(customer.fax, (value) => value.toPrimitive()),
website: toEmptyString(customer.website, (value) => value.toPrimitive()),
legal_record: toEmptyString(customer.legalRecord, (value) => value.toPrimitive()),
default_taxes: customer.defaultTaxes.getAll().join(", "),
status: customer.isActive ? "active" : "inactive",
language_code: customer.languageCode.toPrimitive(),
currency_code: customer.currencyCode.toPrimitive(),
metadata: {
entity: "customer",
},
};
}
}

View File

@ -1 +0,0 @@
export * from "./create-customers.presenter";

View File

@ -1 +0,0 @@
export * from "./delete-customer.use-case";

View File

@ -1,2 +0,0 @@
export * from "./presenter";
export * from "./get-customer.use-case";

View File

@ -1,16 +0,0 @@
import { CustomerItem } from "#/server/domain";
import { IInvoicingContext } from "#/server/intrastructure";
import { Collection } from "@rdx/core";
export const customerItemPresenter = (items: Collection<CustomerItem>, context: IInvoicingContext) =>
items.totalCount > 0
? items.items.map((item: CustomerItem) => ({
description: item.description.toString(),
quantity: item.quantity.toString(),
unit_measure: "",
unit_price: item.unitPrice.toPrimitive() as IMoney_Response_DTO,
subtotal: item.calculateSubtotal().toPrimitive() as IMoney_Response_DTO,
tax_amount: item.calculateTaxAmount().toPrimitive() as IMoney_Response_DTO,
total: item.calculateTotal().toPrimitive() as IMoney_Response_DTO,
}))
: [];

View File

@ -1,26 +0,0 @@
import { ICustomerParticipant } from "@/contexts/invoicing/domain";
import { IInvoicingContext } from "@/contexts/invoicing/intrastructure/InvoicingContext";
import { ICreateCustomer_Participant_Response_DTO } from "@shared/contexts";
import { CustomerParticipantAddressPresenter } from "./CustomerParticipantAddress.presenter";
export const CustomerParticipantPresenter = async (
participant: ICustomerParticipant,
context: IInvoicingContext,
): Promise<ICreateCustomer_Participant_Response_DTO | undefined> => {
return {
id: participant.id.toString(),
tin: participant.tin.toString(),
first_name: participant.firstName.toString(),
last_name: participant.lastName.toString(),
company_name: participant.companyName.toString(),
billing_address: await CustomerParticipantAddressPresenter(
participant.billingAddress!,
context,
),
shipping_address: await CustomerParticipantAddressPresenter(
participant.shippingAddress!,
context,
),
};
};

View File

@ -1,19 +0,0 @@
import { CustomerParticipantAddress } from "@/contexts/invoicing/domain";
import { IInvoicingContext } from "@/contexts/invoicing/intrastructure/InvoicingContext";
import { ICreateCustomer_AddressParticipant_Response_DTO } from "@shared/contexts";
export const CustomerParticipantAddressPresenter = async (
address: CustomerParticipantAddress,
context: IInvoicingContext,
): Promise<ICreateCustomer_AddressParticipant_Response_DTO> => {
return {
id: address.id.toString(),
street: address.street.toString(),
city: address.city.toString(),
postal_code: address.postalCode.toString(),
province: address.province.toString(),
country: address.country.toString(),
email: address.email.toString(),
phone: address.phone.toString(),
};
};

View File

@ -1 +0,0 @@
export * from "./get-customer.presenter";

View File

@ -1,5 +1,2 @@
export * from "./create-customer";
export * from "./delete-customer";
export * from "./get-customer";
export * from "./list-customers";
export * from "./update-customer";
export * from "./presenters";
export * from "./use-cases";

View File

@ -1,2 +0,0 @@
export * from "./presenter";
export * from "./list-customers.use-case";

View File

@ -1,72 +0,0 @@
import { Criteria } from "@repo/rdx-criteria/server";
import { toEmptyString } from "@repo/rdx-ddd";
import { Collection } from "@repo/rdx-utils";
import { CustomerListResponsetDTO } from "../../../../common/dto";
import { Customer } from "../../../domain";
export class ListCustomersPresenter {
toDTO(customers: Collection<Customer>, criteria: Criteria): CustomerListResponsetDTO {
const items: CustomerListResponsetDTO["items"] = customers.map((customer) => {
const address = customer.address.toPrimitive();
return {
id: customer.id.toPrimitive(),
company_id: customer.companyId.toPrimitive(),
reference: toEmptyString(customer.reference, (value) => value.toPrimitive()),
is_company: String(customer.isCompany),
name: customer.name.toPrimitive(),
trade_name: toEmptyString(customer.tradeName, (value) => value.toPrimitive()),
tin: toEmptyString(customer.tin, (value) => value.toPrimitive()),
street: toEmptyString(address.street, (value) => value.toPrimitive()),
street2: toEmptyString(address.street2, (value) => value.toPrimitive()),
city: toEmptyString(address.city, (value) => value.toPrimitive()),
province: toEmptyString(address.province, (value) => value.toPrimitive()),
postal_code: toEmptyString(address.postalCode, (value) => value.toPrimitive()),
country: toEmptyString(address.country, (value) => value.toPrimitive()),
email: toEmptyString(customer.email, (value) => value.toPrimitive()),
phone: toEmptyString(customer.phone, (value) => value.toPrimitive()),
fax: toEmptyString(customer.fax, (value) => value.toPrimitive()),
website: toEmptyString(customer.website, (value) => value.toPrimitive()),
legal_record: toEmptyString(customer.legalRecord, (value) => value.toPrimitive()),
default_taxes: customer.defaultTaxes.getAll().join(", "),
status: customer.isActive ? "active" : "inactive",
language_code: customer.languageCode.toPrimitive(),
currency_code: customer.currencyCode.toPrimitive(),
metadata: {
entity: "customer",
//created_at: customer.createdAt.toPrimitive(),
//updated_at: customer.updatedAt.toPrimitive()
},
};
});
const totalItems = customers.total();
return {
page: criteria.pageNumber,
per_page: criteria.pageSize,
total_pages: Math.ceil(totalItems / criteria.pageSize),
total_items: totalItems,
items: items,
metadata: {
entity: "customers",
criteria: criteria.toJSON(),
//links: {
// self: `/api/customers?page=${criteria.pageNumber}&per_page=${criteria.pageSize}`,
// first: `/api/customers?page=1&per_page=${criteria.pageSize}`,
// last: `/api/customers?page=${Math.ceil(totalItems / criteria.pageSize)}&per_page=${criteria.pageSize}`,
//},
},
};
}
}

View File

@ -1,9 +1,10 @@
import { Presenter } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd";
import { GetCustomerByIdResponseDTO } from "../../../../common/dto";
import { Customer } from "../../../domain";
export class GetCustomerPresenter {
toDTO(customer: Customer): GetCustomerByIdResponseDTO {
export class CustomerFullPresenter extends Presenter<Customer, GetCustomerByIdResponseDTO> {
toOutput(customer: Customer): GetCustomerByIdResponseDTO {
const address = customer.address.toPrimitive();
return {

View File

@ -0,0 +1 @@
export * from "./customer.full.presenter";

View File

@ -0,0 +1,2 @@
export * from "./domain";
export * from "./queries";

View File

@ -1,9 +1,12 @@
import { Presenter } from "@erp/core/api";
import { CustomerListDTO } from "@erp/customer-invoices/api/infrastructure";
import { Criteria } from "@repo/rdx-criteria/server";
import { toEmptyString } from "@repo/rdx-ddd";
import { UpdateCustomerByIdResponseDTO } from "../../../../common/dto";
import { Customer } from "../../../domain";
import { Collection } from "@repo/rdx-utils";
import { CustomerListResponsetDTO } from "../../../../common/dto";
export class UpdateCustomerPresenter {
toDTO(customer: Customer): UpdateCustomerByIdResponseDTO {
export class ListCustomersPresenter extends Presenter {
protected _mapCustomer(customer: CustomerListDTO) {
const address = customer.address.toPrimitive();
return {
@ -31,20 +34,42 @@ export class UpdateCustomerPresenter {
fax: toEmptyString(customer.fax, (value) => value.toPrimitive()),
website: toEmptyString(customer.website, (value) => value.toPrimitive()),
legal_record: toEmptyString(customer.legalRecord, (value) => value.toPrimitive()),
default_taxes: customer.defaultTaxes.getAll().join(", "),
status: customer.isActive ? "active" : "inactive",
status: customer.status ? "active" : "inactive",
language_code: customer.languageCode.toPrimitive(),
currency_code: customer.currencyCode.toPrimitive(),
metadata: {
entity: "customer",
//id: customer.id.toPrimitive(),
//created_at: customer.createdAt.toPrimitive(),
//updated_at: customer.updatedAt.toPrimitive()
},
};
}
toOutput(params: {
customers: Collection<CustomerListDTO>;
criteria: Criteria;
}): CustomerListResponsetDTO {
const { customers, criteria } = params;
const items = customers.map((customer) => this._mapCustomer(customer));
const totalItems = customers.total();
return {
page: criteria.pageNumber,
per_page: criteria.pageSize,
total_pages: Math.ceil(totalItems / criteria.pageSize),
total_items: totalItems,
items: items,
metadata: {
entity: "customers",
criteria: criteria.toJSON(),
//links: {
// self: `/api/customer-invoices?page=${criteria.pageNumber}&per_page=${criteria.pageSize}`,
// first: `/api/customer-invoices?page=1&per_page=${criteria.pageSize}`,
// last: `/api/customer-invoices?page=${Math.ceil(totalItems / criteria.pageSize)}&per_page=${criteria.pageSize}`,
//},
},
};
}
}

View File

@ -1 +0,0 @@
export * from "./update-customer.presenter";

View File

@ -1,2 +1 @@
export * from "./presenter";
export * from "./create-customer.use-case";

View File

@ -24,8 +24,8 @@ import {
maybeFromNullableVO,
} from "@repo/rdx-ddd";
import { Collection, Result, isNullishOrEmpty } from "@repo/rdx-utils";
import { CreateCustomerRequestDTO } from "../../../common/dto";
import { CustomerProps, CustomerStatus } from "../../domain";
import { CreateCustomerRequestDTO } from "../../../../common";
import { CustomerProps, CustomerStatus } from "../../../domain";
/**
* Convierte el DTO a las props validadas (CustomerProps).

View File

@ -40,9 +40,7 @@ export class DeleteCustomerUseCase {
const customerExists = existsCheck.data;
if (!customerExists) {
return Result.fail(
new EntityNotFoundError("Customer", "id", customerId.toObjectString())
);
return Result.fail(new EntityNotFoundError("Customer", "id", customerId.toString()));
}
return await this.service.deleteCustomerByIdInCompany(customerId, companyId, transaction);

View File

@ -1,8 +1,8 @@
import { ITransactionManager } from "@erp/core/api";
import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { CustomerService } from "../../domain";
import { GetCustomerAssembler } from "./assembler";
import { CustomerFullPresenter } from "../presenters";
type GetCustomerUseCaseInput = {
companyId: UniqueID;
@ -13,19 +13,22 @@ export class GetCustomerUseCase {
constructor(
private readonly service: CustomerService,
private readonly transactionManager: ITransactionManager,
private readonly assembler: GetCustomerAssembler
private readonly presenterRegistry: IPresenterRegistry
) {}
public execute(params: GetCustomerUseCaseInput) {
const { customer_id, companyId } = params;
const idOrError = UniqueID.create(customer_id);
if (idOrError.isFailure) {
return Result.fail(idOrError.error);
}
const customerId = idOrError.data;
const presenter = this.presenterRegistry.getPresenter({
resource: "customer",
projection: "FULL",
}) as CustomerFullPresenter;
return this.transactionManager.complete(async (transaction) => {
try {
@ -39,8 +42,10 @@ export class GetCustomerUseCase {
return Result.fail(customerOrError.error);
}
const getDTO = this.assembler.toDTO(customerOrError.data);
return Result.ok(getDTO);
const customer = customerOrError.data;
const dto = presenter.toOutput(customer);
return Result.ok(dto);
} catch (error: unknown) {
return Result.fail(error as Error);
}

View File

@ -0,0 +1,5 @@
export * from "./create";
export * from "./delete-customer.use-case";
export * from "./get-customer.use-case";
export * from "./list-customers.use-case";
export * from "./update";

View File

@ -1,11 +1,11 @@
import { ITransactionManager } from "@erp/core/api";
import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
import { Criteria } from "@repo/rdx-criteria/server";
import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { Transaction } from "sequelize";
import { CustomerListResponsetDTO } from "../../../common/dto";
import { CustomerService } from "../../domain";
import { ListCustomersAssembler } from "./assembler";
import { ListCustomersPresenter } from "../presenters";
type ListCustomersUseCaseInput = {
companyId: UniqueID;
@ -14,19 +14,23 @@ type ListCustomersUseCaseInput = {
export class ListCustomersUseCase {
constructor(
private readonly customerService: CustomerService,
private readonly service: CustomerService,
private readonly transactionManager: ITransactionManager,
private readonly assembler: ListCustomersAssembler
private readonly presenterRegistry: IPresenterRegistry
) {}
public execute(
params: ListCustomersUseCaseInput
): Promise<Result<CustomerListResponsetDTO, Error>> {
const { criteria, companyId } = params;
const presenter = this.presenterRegistry.getPresenter({
resource: "customer",
projection: "LIST",
}) as ListCustomersPresenter;
return this.transactionManager.complete(async (transaction: Transaction) => {
try {
const result = await this.customerService.findCustomerByCriteriaInCompany(
const result = await this.service.findCustomerByCriteriaInCompany(
companyId,
criteria,
transaction
@ -36,7 +40,12 @@ export class ListCustomersUseCase {
return Result.fail(result.error);
}
const dto = this.assembler.toDTO(result.data, criteria);
const customers = result.data;
const dto = presenter.toOutput({
customers,
criteria,
});
return Result.ok(dto);
} catch (error: unknown) {
return Result.fail(error as Error);

View File

@ -1,2 +1 @@
export * from "./presenter";
export * from "./update-customer.use-case";

View File

@ -1,6 +1,7 @@
import { Criteria } from "@repo/rdx-criteria/server";
import { UniqueID } from "@repo/rdx-ddd";
import { Collection, Result } from "@repo/rdx-utils";
import { CustomerListDTO } from "../../infrastructure";
import { Customer, CustomerPatchProps, CustomerProps } from "../aggregates";
import { ICustomerRepository } from "../repositories";
@ -64,7 +65,7 @@ export class CustomerService {
companyId: UniqueID,
criteria: Criteria,
transaction?: any
): Promise<Result<Collection<Customer>, Error>> {
): Promise<Result<Collection<CustomerListDTO>, Error>> {
return this.repository.findByCriteriaInCompany(companyId, criteria, transaction);
}

View File

@ -4,16 +4,14 @@ import {
InMemoryPresenterRegistry,
SequelizeTransactionManager,
} from "@erp/core/api";
import {
CreateCustomerUseCase,
DeleteCustomerUseCase,
GetCustomerUseCase,
CustomerFullPresenter,
ListCustomersPresenter,
ListCustomersUseCase,
UpdateCustomerUseCase,
} from "../application";
import { GetCustomerUseCase } from "../application/use-cases/get-customer.use-case";
import { CustomerService } from "../domain";
import { CustomerDomainMapper } from "./mappers";
import { CustomerDomainMapper, CustomerListMapper } from "./mappers";
import { CustomerRepository } from "./sequelize";
export type CustomerDeps = {
@ -25,9 +23,9 @@ export type CustomerDeps = {
build: {
list: () => ListCustomersUseCase;
get: () => GetCustomerUseCase;
create: () => CreateCustomerUseCase;
/*create: () => CreateCustomerUseCase;
update: () => UpdateCustomerUseCase;
delete: () => DeleteCustomerUseCase;
delete: () => DeleteCustomerUseCase;*/
};
};
@ -37,10 +35,12 @@ export function buildCustomerDependencies(params: ModuleParams): CustomerDeps {
// Mapper Registry
const mapperRegistry = new InMemoryMapperRegistry();
mapperRegistry.registerDomainMapper({ resource: "customer" }, new CustomerDomainMapper());
mapperRegistry
.registerDomainMapper({ resource: "customer" }, new CustomerDomainMapper())
.registerQueryMapper({ resource: "customer", query: "LIST" }, new CustomerListMapper());
// Repository & Services
const repo = new CustomerRepository({ mapperRegistry: _mapperRegistry, database });
const repo = new CustomerRepository({ mapperRegistry, database });
const service = new CustomerService(repo);
// Presenter Registry
@ -48,11 +48,11 @@ export function buildCustomerDependencies(params: ModuleParams): CustomerDeps {
presenterRegistry.registerPresenters([
{
key: { resource: "customer", projection: "FULL" },
presenter: new ListCustomersAssembler(),
presenter: new CustomerFullPresenter(presenterRegistry),
},
{
key: { resource: "customer", projection: "LIST" },
presenter: new GetCustomerAssembler(),
presenter: new ListCustomersPresenter(presenterRegistry),
},
]);
@ -63,8 +63,8 @@ export function buildCustomerDependencies(params: ModuleParams): CustomerDeps {
presenterRegistry,
service,
build: {
/*list: () => new ListCustomersUseCase(_service!, transactionManager!, presenterRegistry!),
get: () => new GetCustomerUseCase(_service!, transactionManager!, presenterRegistry!),
list: () => new ListCustomersUseCase(service, transactionManager, presenterRegistry),
/*get: () => new GetCustomerUseCase(_service!, transactionManager!, presenterRegistry!),
create: () => new CreateCustomerUseCase(_service!, transactionManager!, presenterRegistry!),
update: () => new UpdateCustomerUseCase(_service!, transactionManager!, presenterRegistry!),
delete: () => new DeleteCustomerUseCase(_service!, transactionManager!),*/

View File

@ -10,7 +10,10 @@ export class CreateCustomerController extends ExpressController {
}
protected async executeImpl() {
const companyId = this.getTenantId()!; // garantizado por tenantGuard
const companyId = this.getTenantId();
if (!companyId) {
return this.forbiddenError("Tenant ID not found");
}
const dto = this.req.body as CreateCustomerRequestDTO;
const result = await this.useCase.execute({ dto, companyId });

View File

@ -9,7 +9,10 @@ export class DeleteCustomerController extends ExpressController {
}
async executeImpl(): Promise<any> {
const companyId = this.getTenantId()!; // garantizado por tenantGuard
const companyId = this.getTenantId();
if (!companyId) {
return this.forbiddenError("Tenant ID not found");
}
const { customer_id } = this.req.params;
const result = await this.useCase.execute({ customer_id, companyId });

View File

@ -9,7 +9,10 @@ export class GetCustomerController extends ExpressController {
}
protected async executeImpl() {
const companyId = this.getTenantId()!; // garantizado por tenantGuard
const companyId = this.getTenantId();
if (!companyId) {
return this.forbiddenError("Tenant ID not found");
}
const { customer_id } = this.req.params;
const result = await this.useCase.execute({ customer_id, companyId });

View File

@ -1,4 +1,5 @@
import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api";
import { Criteria } from "@repo/rdx-criteria/server";
import { ListCustomersUseCase } from "../../../application";
export class ListCustomersController extends ExpressController {
@ -8,9 +9,23 @@ export class ListCustomersController extends ExpressController {
this.useGuards(authGuard(), tenantGuard(), forbidQueryFieldGuard("companyId"));
}
private getCriteriaWithDefaultOrder() {
if (this.criteria.hasOrder()) {
return this.criteria;
}
const { filters, pageSize, pageNumber } = this.criteria.toPrimitives();
return Criteria.fromPrimitives(filters, "name", "ASC", pageSize, pageNumber);
}
protected async executeImpl() {
const companyId = this.getTenantId()!; // garantizado por tenantGuard
const result = await this.listCustomers.execute({ criteria: this.criteria, companyId });
const companyId = this.getTenantId();
if (!companyId) {
return this.forbiddenError("Tenant ID not found");
}
const criteria = this.getCriteriaWithDefaultOrder();
const result = await this.listCustomers.execute({ criteria, companyId });
return result.match(
(data) =>

View File

@ -10,7 +10,10 @@ export class UpdateCustomerController extends ExpressController {
}
protected async executeImpl() {
const companyId = this.getTenantId()!; // garantizado por tenantGuard
const companyId = this.getTenantId();
if (!companyId) {
return this.forbiddenError("Tenant ID not found");
}
const { customer_id } = this.req.params;
const dto = this.req.body as UpdateCustomerRequestDTO;

View File

@ -2,22 +2,9 @@ import { RequestWithAuth, enforceTenant, enforceUser, mockUser } from "@erp/auth
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,
UpdateCustomerByIdParamsRequestSchema,
UpdateCustomerByIdRequestSchema,
} from "../../../common/dto";
import { getCustomerDependencies } from "../dependencies";
import {
CreateCustomerController,
DeleteCustomerController,
GetCustomerController,
ListCustomersController,
} from "./controllers";
import { UpdateCustomerController } from "./controllers/update-customer.controller";
import { CustomerListRequestSchema, GetCustomerByIdRequestSchema } from "../../../common/dto";
import { buildCustomerDependencies } from "../dependencies";
import { GetCustomerController, ListCustomersController } from "./controllers";
export const customersRouter = (params: ModuleParams) => {
const { app, database, baseRoutePath, logger } = params as {
@ -27,7 +14,7 @@ export const customersRouter = (params: ModuleParams) => {
logger: ILogger;
};
const deps = getCustomerDependencies(params);
const deps = buildCustomerDependencies(params);
const router: Router = Router({ mergeParams: true });
@ -74,7 +61,7 @@ export const customersRouter = (params: ModuleParams) => {
}
);
router.post(
/*router.post(
"/",
//checkTabContext,
@ -108,7 +95,7 @@ export const customersRouter = (params: ModuleParams) => {
const controller = new DeleteCustomerController(useCase);
return controller.execute(req, res, next);
}
);
); */
app.use(`${baseRoutePath}/customers`, router);
};

View File

@ -5,6 +5,7 @@ export const CustomerListResponseSchema = createListViewResponseSchema(
z.object({
id: z.uuid(),
company_id: z.uuid(),
status: z.string(),
reference: z.string(),
is_company: z.string(),
@ -23,10 +24,9 @@ export const CustomerListResponseSchema = createListViewResponseSchema(
fax: z.string(),
website: z.string(),
legal_record: z.string(),
//legal_record: z.string(),
//default_taxes: z.string(),
default_taxes: z.string(),
status: z.string(),
language_code: z.string(),
currency_code: z.string(),