.
This commit is contained in:
parent
93e0e6be65
commit
64b48d707b
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -52,9 +52,6 @@
|
||||
},
|
||||
|
||||
// other vscode settings
|
||||
"[handlebars]": {
|
||||
"editor.defaultFormatter": "mfeckies.handlebars-formatter"
|
||||
},
|
||||
"[sql]": {
|
||||
"editor.defaultFormatter": "cweijan.vscode-mysql-client2"
|
||||
}, // <- your root font size here
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { ITransactionManager } from "@erp/core/api";
|
||||
import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import type { Transaction } from "sequelize";
|
||||
|
||||
import type { CreateProformaRequestDTO } from "../../../../../common";
|
||||
import type { ICreateProformaInputMapper } from "../../mappers";
|
||||
@ -43,7 +44,7 @@ export class CreateProformaUseCase {
|
||||
|
||||
const { props, id } = mappedPropsResult.data;
|
||||
|
||||
return this.transactionManager.complete(async (transaction) => {
|
||||
return this.transactionManager.complete(async (transaction: Transaction) => {
|
||||
try {
|
||||
const createResult = await this.creator.create({ companyId, id, props, transaction });
|
||||
|
||||
|
||||
@ -1,10 +1,16 @@
|
||||
// application/customer-application-service.ts
|
||||
import { Criteria } from "@repo/rdx-criteria/server";
|
||||
import { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Collection, Result } from "@repo/rdx-utils";
|
||||
import { Transaction } from "sequelize";
|
||||
import { Customer, CustomerPatchProps, ICustomerProps, ICustomerRepository } from "../domain";
|
||||
import { CustomerListDTO } from "../infrastructure";
|
||||
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 { Transaction } from "sequelize";
|
||||
|
||||
import {
|
||||
Customer,
|
||||
type CustomerPatchProps,
|
||||
type ICustomerCreateProps,
|
||||
type ICustomerRepository,
|
||||
} from "../domain";
|
||||
import type { CustomerListDTO } from "../infrastructure";
|
||||
|
||||
export class CustomerApplicationService {
|
||||
constructor(private readonly repository: ICustomerRepository) {}
|
||||
@ -19,7 +25,7 @@ export class CustomerApplicationService {
|
||||
*/
|
||||
buildCustomerInCompany(
|
||||
companyId: UniqueID,
|
||||
props: Omit<ICustomerProps, "companyId">,
|
||||
props: Omit<ICustomerCreateProps, "companyId">,
|
||||
customerId?: UniqueID
|
||||
): Result<Customer, Error> {
|
||||
return Customer.create({ ...props, companyId }, customerId);
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
import type { ICustomerRepository } from "../repositories";
|
||||
import { CustomerCreator, type ICustomerCreator } from "../services";
|
||||
|
||||
export const buildCustomerCreator = (params: {
|
||||
repository: ICustomerRepository;
|
||||
}): ICustomerCreator => {
|
||||
const { repository } = params;
|
||||
|
||||
return new CustomerCreator({
|
||||
repository,
|
||||
});
|
||||
};
|
||||
@ -1,6 +1,8 @@
|
||||
import type { ICustomerRepository } from "../repositories";
|
||||
import { CustomerFinder, type ICustomerFinder } from "../services";
|
||||
|
||||
export function buildCustomerFinder(repository: ICustomerRepository): ICustomerFinder {
|
||||
export function buildCustomerFinder(params: { repository: ICustomerRepository }): ICustomerFinder {
|
||||
const { repository } = params;
|
||||
|
||||
return new CustomerFinder(repository);
|
||||
}
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
import type { ICatalogs } from "@erp/core/api";
|
||||
|
||||
import { CreateCustomerInputMapper, type ICreateCustomerInputMapper } from "../mappers";
|
||||
|
||||
export interface ICustomerInputMappers {
|
||||
createInputMapper: ICreateCustomerInputMapper;
|
||||
}
|
||||
|
||||
export const buildCustomerInputMappers = (catalogs: ICatalogs): ICustomerInputMappers => {
|
||||
const { taxCatalog } = catalogs;
|
||||
|
||||
// Mappers el DTO a las props validadas (CustomerProps) y luego construir agregado
|
||||
const createInputMapper = new CreateCustomerInputMapper({ taxCatalog });
|
||||
//const updateCustomerInputMapper = new UpdateCustomerInputMapper();
|
||||
|
||||
return {
|
||||
createInputMapper,
|
||||
};
|
||||
};
|
||||
@ -1,11 +1,12 @@
|
||||
import type { ITransactionManager } from "@erp/core/api";
|
||||
|
||||
import type { ICustomerFinder } from "../services";
|
||||
import type { ICreateCustomerInputMapper } from "../mappers";
|
||||
import type { ICustomerCreator, ICustomerFinder } from "../services";
|
||||
import type {
|
||||
ICustomerFullSnapshotBuilder,
|
||||
ICustomerSummarySnapshotBuilder,
|
||||
} from "../snapshot-builders";
|
||||
import { GetCustomerByIdUseCase, ListCustomersUseCase } from "../use-cases";
|
||||
import { CreateCustomerUseCase, GetCustomerByIdUseCase, ListCustomersUseCase } from "../use-cases";
|
||||
|
||||
export function buildGetCustomerByIdUseCase(deps: {
|
||||
finder: ICustomerFinder;
|
||||
@ -27,6 +28,20 @@ export function buildListCustomersUseCase(deps: {
|
||||
);
|
||||
}
|
||||
|
||||
export function buildCreateCustomerUseCase(deps: {
|
||||
creator: ICustomerCreator;
|
||||
dtoMapper: ICreateCustomerInputMapper;
|
||||
fullSnapshotBuilder: ICustomerFullSnapshotBuilder;
|
||||
transactionManager: ITransactionManager;
|
||||
}) {
|
||||
return new CreateCustomerUseCase({
|
||||
dtoMapper: deps.dtoMapper,
|
||||
creator: deps.creator,
|
||||
fullSnapshotBuilder: deps.fullSnapshotBuilder,
|
||||
transactionManager: deps.transactionManager,
|
||||
});
|
||||
}
|
||||
|
||||
/*export function buildReportCustomerUseCase(deps: {
|
||||
finder: ICustomerFinder;
|
||||
fullSnapshotBuilder: ICustomerFullSnapshotBuilder;
|
||||
@ -41,20 +56,6 @@ export function buildListCustomersUseCase(deps: {
|
||||
deps.documentService,
|
||||
deps.transactionManager
|
||||
);
|
||||
}
|
||||
|
||||
export function buildCreateCustomerUseCase(deps: {
|
||||
creator: ICustomerCreator;
|
||||
dtoMapper: ICreateCustomerInputMapper;
|
||||
fullSnapshotBuilder: ICustomerFullSnapshotBuilder;
|
||||
transactionManager: ITransactionManager;
|
||||
}) {
|
||||
return new CreateCustomerUseCase({
|
||||
dtoMapper: deps.dtoMapper,
|
||||
creator: deps.creator,
|
||||
fullSnapshotBuilder: deps.fullSnapshotBuilder,
|
||||
transactionManager: deps.transactionManager,
|
||||
});
|
||||
}*/
|
||||
|
||||
/*export function buildUpdateCustomerUseCase(deps: {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//export * from "./customer-creator.di";
|
||||
export * from "./customer-creator.di";
|
||||
export * from "./customer-finder.di";
|
||||
//export * from "./customer-input-mappers.di";
|
||||
export * from "./customer-input-mappers.di";
|
||||
export * from "./customer-snapshot-builders.di";
|
||||
export * from "./customer-use-cases.di";
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
export * from "./di";
|
||||
export * from "./mappers";
|
||||
export * from "./models";
|
||||
export * from "./repositories";
|
||||
export * from "./services";
|
||||
|
||||
@ -0,0 +1,238 @@
|
||||
import type { JsonTaxCatalogProvider } from "@erp/core";
|
||||
import {
|
||||
City,
|
||||
Country,
|
||||
CurrencyCode,
|
||||
DomainError,
|
||||
EmailAddress,
|
||||
LanguageCode,
|
||||
Name,
|
||||
PhoneNumber,
|
||||
type PostalAddressProps,
|
||||
PostalCode,
|
||||
Province,
|
||||
Street,
|
||||
TINNumber,
|
||||
type TaxCode,
|
||||
TextValue,
|
||||
URLAddress,
|
||||
UniqueID,
|
||||
ValidationErrorCollection,
|
||||
type ValidationErrorDetail,
|
||||
extractOrPushError,
|
||||
maybeFromNullableResult,
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Collection, Result } from "@repo/rdx-utils";
|
||||
|
||||
import type { CreateCustomerRequestDTO } from "../../../common";
|
||||
import { CustomerStatus, type ICustomerCreateProps } from "../../domain";
|
||||
|
||||
export interface ICreateCustomerInputMapper {
|
||||
map(
|
||||
dto: CreateCustomerRequestDTO,
|
||||
params: { companyId: UniqueID }
|
||||
): Result<{ id: UniqueID; props: ICustomerCreateProps }>;
|
||||
}
|
||||
|
||||
export class CreateCustomerInputMapper implements ICreateCustomerInputMapper {
|
||||
private readonly taxCatalog: JsonTaxCatalogProvider;
|
||||
|
||||
constructor(params: { taxCatalog: JsonTaxCatalogProvider }) {
|
||||
this.taxCatalog = params.taxCatalog;
|
||||
}
|
||||
|
||||
public map(
|
||||
dto: CreateCustomerRequestDTO,
|
||||
params: { companyId: UniqueID }
|
||||
): Result<{ id: UniqueID; props: ICustomerCreateProps }> {
|
||||
try {
|
||||
const errors: ValidationErrorDetail[] = [];
|
||||
const { companyId } = params;
|
||||
|
||||
const customerId = extractOrPushError(UniqueID.create(dto.id), "id", errors);
|
||||
|
||||
const status = CustomerStatus.createActive();
|
||||
const isCompany = dto.is_company === "true";
|
||||
|
||||
const reference = extractOrPushError(
|
||||
maybeFromNullableResult(dto.reference, (value) => Name.create(value)),
|
||||
"reference",
|
||||
errors
|
||||
);
|
||||
|
||||
const name = extractOrPushError(Name.create(dto.name), "name", errors);
|
||||
|
||||
const tradeName = extractOrPushError(
|
||||
maybeFromNullableResult(dto.trade_name, (value) => Name.create(value)),
|
||||
"trade_name",
|
||||
errors
|
||||
);
|
||||
|
||||
const tinNumber = extractOrPushError(
|
||||
maybeFromNullableResult(dto.tin, (value) => TINNumber.create(value)),
|
||||
"tin",
|
||||
errors
|
||||
);
|
||||
|
||||
const street = extractOrPushError(
|
||||
maybeFromNullableResult(dto.street, (value) => Street.create(value)),
|
||||
"street",
|
||||
errors
|
||||
);
|
||||
|
||||
const street2 = extractOrPushError(
|
||||
maybeFromNullableResult(dto.street2, (value) => Street.create(value)),
|
||||
"street2",
|
||||
errors
|
||||
);
|
||||
|
||||
const city = extractOrPushError(
|
||||
maybeFromNullableResult(dto.city, (value) => City.create(value)),
|
||||
"city",
|
||||
errors
|
||||
);
|
||||
|
||||
const province = extractOrPushError(
|
||||
maybeFromNullableResult(dto.province, (value) => Province.create(value)),
|
||||
"province",
|
||||
errors
|
||||
);
|
||||
|
||||
const postalCode = extractOrPushError(
|
||||
maybeFromNullableResult(dto.postal_code, (value) => PostalCode.create(value)),
|
||||
"postal_code",
|
||||
errors
|
||||
);
|
||||
|
||||
const country = extractOrPushError(
|
||||
maybeFromNullableResult(dto.country, (value) => Country.create(value)),
|
||||
"country",
|
||||
errors
|
||||
);
|
||||
|
||||
const primaryEmailAddress = extractOrPushError(
|
||||
maybeFromNullableResult(dto.email_primary, (value) => EmailAddress.create(value)),
|
||||
"email_primary",
|
||||
errors
|
||||
);
|
||||
|
||||
const secondaryEmailAddress = extractOrPushError(
|
||||
maybeFromNullableResult(dto.email_secondary, (value) => EmailAddress.create(value)),
|
||||
"email_secondary",
|
||||
errors
|
||||
);
|
||||
|
||||
const primaryPhoneNumber = extractOrPushError(
|
||||
maybeFromNullableResult(dto.phone_primary, (value) => PhoneNumber.create(value)),
|
||||
"phone_primary",
|
||||
errors
|
||||
);
|
||||
|
||||
const secondaryPhoneNumber = extractOrPushError(
|
||||
maybeFromNullableResult(dto.phone_secondary, (value) => PhoneNumber.create(value)),
|
||||
"phone_secondary",
|
||||
errors
|
||||
);
|
||||
|
||||
const primaryMobileNumber = extractOrPushError(
|
||||
maybeFromNullableResult(dto.mobile_primary, (value) => PhoneNumber.create(value)),
|
||||
"mobile_primary",
|
||||
errors
|
||||
);
|
||||
|
||||
const secondaryMobileNumber = extractOrPushError(
|
||||
maybeFromNullableResult(dto.mobile_secondary, (value) => PhoneNumber.create(value)),
|
||||
"mobile_secondary",
|
||||
errors
|
||||
);
|
||||
|
||||
const faxNumber = extractOrPushError(
|
||||
maybeFromNullableResult(dto.fax, (value) => PhoneNumber.create(value)),
|
||||
"fax",
|
||||
errors
|
||||
);
|
||||
|
||||
const website = extractOrPushError(
|
||||
maybeFromNullableResult(dto.website, (value) => URLAddress.create(value)),
|
||||
"website",
|
||||
errors
|
||||
);
|
||||
|
||||
const legalRecord = extractOrPushError(
|
||||
maybeFromNullableResult(dto.legal_record, (value) => TextValue.create(value)),
|
||||
"legal_record",
|
||||
errors
|
||||
);
|
||||
|
||||
const languageCode = extractOrPushError(
|
||||
LanguageCode.create(dto.language_code),
|
||||
"language_code",
|
||||
errors
|
||||
);
|
||||
|
||||
const currencyCode = extractOrPushError(
|
||||
CurrencyCode.create(dto.currency_code),
|
||||
"currency_code",
|
||||
errors
|
||||
);
|
||||
|
||||
const defaultTaxes = new Collection<TaxCode>();
|
||||
|
||||
/*if (!isNullishOrEmpty(dto.default_taxes)) {
|
||||
dto.default_taxes!.map((taxCode, index) => {
|
||||
const tax = extractOrPushError(TaxCode.create(taxCode), `default_taxes.${index}`, errors);
|
||||
if (tax) {
|
||||
defaultTaxes.add(tax!);
|
||||
}
|
||||
});
|
||||
}*/
|
||||
|
||||
if (errors.length > 0) {
|
||||
return Result.fail(new ValidationErrorCollection("Customer props mapping failed", errors));
|
||||
}
|
||||
|
||||
const postalAddressProps: PostalAddressProps = {
|
||||
street: street!,
|
||||
street2: street2!,
|
||||
city: city!,
|
||||
postalCode: postalCode!,
|
||||
province: province!,
|
||||
country: country!,
|
||||
};
|
||||
|
||||
const customerProps: ICustomerCreateProps = {
|
||||
companyId,
|
||||
status: status!,
|
||||
reference: reference!,
|
||||
|
||||
isCompany: isCompany,
|
||||
name: name!,
|
||||
tradeName: tradeName!,
|
||||
tin: tinNumber!,
|
||||
|
||||
address: postalAddressProps!,
|
||||
|
||||
emailPrimary: primaryEmailAddress!,
|
||||
emailSecondary: secondaryEmailAddress!,
|
||||
|
||||
phonePrimary: primaryPhoneNumber!,
|
||||
phoneSecondary: secondaryPhoneNumber!,
|
||||
|
||||
mobilePrimary: primaryMobileNumber!,
|
||||
mobileSecondary: secondaryMobileNumber!,
|
||||
|
||||
fax: faxNumber!,
|
||||
website: website!,
|
||||
|
||||
legalRecord: legalRecord!,
|
||||
defaultTaxes: defaultTaxes!,
|
||||
languageCode: languageCode!,
|
||||
currencyCode: currencyCode!,
|
||||
};
|
||||
|
||||
return Result.ok({ id: customerId!, props: customerProps });
|
||||
} catch (err: unknown) {
|
||||
return Result.fail(new DomainError("Customer props mapping failed", { cause: err }));
|
||||
}
|
||||
}
|
||||
}
|
||||
1
modules/customers/src/api/application/mappers/index.ts
Normal file
1
modules/customers/src/api/application/mappers/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./create-customer-input.mapper";
|
||||
@ -0,0 +1,75 @@
|
||||
import { DuplicateEntityError } from "@erp/core/api";
|
||||
import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import type { Transaction } from "sequelize";
|
||||
|
||||
import { Customer, type ICustomerCreateProps } from "../../domain";
|
||||
import type { ICustomerRepository } from "../repositories";
|
||||
import { CustomerNotExistsInCompanySpecification } from "../specs";
|
||||
|
||||
export interface ICustomerCreator {
|
||||
create(params: {
|
||||
companyId: UniqueID;
|
||||
id: UniqueID;
|
||||
props: ICustomerCreateProps;
|
||||
transaction: Transaction;
|
||||
}): Promise<Result<Customer, Error>>;
|
||||
}
|
||||
|
||||
type CustomerCreatorDeps = {
|
||||
repository: ICustomerRepository;
|
||||
};
|
||||
|
||||
export class CustomerCreator implements ICustomerCreator {
|
||||
private readonly repository: ICustomerRepository;
|
||||
|
||||
constructor(deps: CustomerCreatorDeps) {
|
||||
this.repository = deps.repository;
|
||||
}
|
||||
|
||||
async create(params: {
|
||||
companyId: UniqueID;
|
||||
id: UniqueID;
|
||||
props: ICustomerCreateProps;
|
||||
transaction: Transaction;
|
||||
}): Promise<Result<Customer, Error>> {
|
||||
const { companyId, id, props, transaction } = params;
|
||||
|
||||
// 1. Verificar unicidad
|
||||
const spec = new CustomerNotExistsInCompanySpecification(
|
||||
this.repository,
|
||||
companyId,
|
||||
transaction
|
||||
);
|
||||
|
||||
const isNew = await spec.isSatisfiedBy(id);
|
||||
|
||||
if (!isNew) {
|
||||
return Result.fail(new DuplicateEntityError("Customer", "id", String(id)));
|
||||
}
|
||||
|
||||
// 2. Crear agregado
|
||||
const createResult = Customer.create(
|
||||
{
|
||||
...props,
|
||||
companyId,
|
||||
},
|
||||
id
|
||||
);
|
||||
|
||||
if (createResult.isFailure) {
|
||||
return createResult;
|
||||
}
|
||||
|
||||
const newCustomer = createResult.data;
|
||||
|
||||
// 3. Persistir agregado
|
||||
const saveResult = await this.repository.create(newCustomer, transaction);
|
||||
|
||||
if (saveResult.isFailure) {
|
||||
return Result.fail(saveResult.error);
|
||||
}
|
||||
|
||||
return Result.ok(newCustomer);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import type { Transaction } from "sequelize";
|
||||
|
||||
import type { Customer, ICustomerCreateProps } from "../../domain";
|
||||
import type { ICustomerRepository } from "../repositories";
|
||||
|
||||
export interface ICustomerUpdater {
|
||||
update(params: {
|
||||
companyId: UniqueID;
|
||||
id: UniqueID;
|
||||
props: Partial<ICustomerCreateProps>;
|
||||
transaction: Transaction;
|
||||
}): Promise<Result<Customer, Error>>;
|
||||
}
|
||||
|
||||
type CustomerUpdaterDeps = {
|
||||
repository: ICustomerRepository;
|
||||
};
|
||||
|
||||
export class CustomerUpdater implements ICustomerUpdater {
|
||||
private readonly repository: ICustomerRepository;
|
||||
|
||||
constructor(deps: CustomerUpdaterDeps) {
|
||||
this.repository = deps.repository;
|
||||
}
|
||||
|
||||
async update(params: {
|
||||
companyId: UniqueID;
|
||||
id: UniqueID;
|
||||
props: Partial<ICustomerCreateProps>;
|
||||
transaction: Transaction;
|
||||
}): Promise<Result<Customer, Error>> {
|
||||
const { companyId, id, props, transaction } = params;
|
||||
|
||||
// Recuperar agregado existente
|
||||
const existingResult = await this.repository.getByIdInCompany(companyId, id, transaction);
|
||||
|
||||
if (existingResult.isFailure) {
|
||||
return Result.fail(existingResult.error);
|
||||
}
|
||||
|
||||
const customer = existingResult.data;
|
||||
|
||||
// Aplicar cambios en el agregado
|
||||
const updateResult = customer.update(props);
|
||||
|
||||
if (updateResult.isFailure) {
|
||||
return Result.fail(updateResult.error);
|
||||
}
|
||||
|
||||
// Persistir cambios
|
||||
const saveResult = await this.repository.update(customer, transaction);
|
||||
|
||||
if (saveResult.isFailure) {
|
||||
return Result.fail(saveResult.error);
|
||||
}
|
||||
|
||||
return Result.ok(customer);
|
||||
}
|
||||
}
|
||||
@ -1 +1,2 @@
|
||||
export * from "./customer-creator";
|
||||
export * from "./customer-finder";
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { CompositeSpecification, UniqueID } from "@repo/rdx-ddd";
|
||||
import { Transaction } from "sequelize";
|
||||
import { CustomerApplicationService } from "../../application";
|
||||
import { CompositeSpecification, type UniqueID } from "@repo/rdx-ddd";
|
||||
import type { Transaction } from "sequelize";
|
||||
|
||||
import type { ICustomerRepository } from "../../application";
|
||||
import { logger } from "../../helpers";
|
||||
|
||||
export class CustomerNotExistsInCompanySpecification extends CompositeSpecification<UniqueID> {
|
||||
constructor(
|
||||
private readonly service: CustomerApplicationService,
|
||||
private readonly repository: ICustomerRepository,
|
||||
private readonly companyId: UniqueID,
|
||||
private readonly transaction?: Transaction
|
||||
) {
|
||||
@ -13,7 +14,7 @@ export class CustomerNotExistsInCompanySpecification extends CompositeSpecificat
|
||||
}
|
||||
|
||||
public async isSatisfiedBy(customerId: UniqueID): Promise<boolean> {
|
||||
const existsCheck = await this.service.existsByIdInCompany(
|
||||
const existsCheck = await this.repository.existsByIdInCompany(
|
||||
this.companyId,
|
||||
customerId,
|
||||
this.transaction
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
import type { ITransactionManager } from "@erp/core/api";
|
||||
import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import type { Transaction } from "sequelize";
|
||||
|
||||
import type { CreateCustomerRequestDTO } from "../../../common";
|
||||
import type { ICreateCustomerInputMapper } from "../mappers";
|
||||
import type { ICustomerCreator } from "../services";
|
||||
import type { ICustomerFullSnapshotBuilder } from "../snapshot-builders";
|
||||
|
||||
type CreateCustomerUseCaseInput = {
|
||||
companyId: UniqueID;
|
||||
dto: CreateCustomerRequestDTO;
|
||||
};
|
||||
|
||||
type CreateCustomerUseCaseDeps = {
|
||||
dtoMapper: ICreateCustomerInputMapper;
|
||||
creator: ICustomerCreator;
|
||||
fullSnapshotBuilder: ICustomerFullSnapshotBuilder;
|
||||
transactionManager: ITransactionManager;
|
||||
};
|
||||
|
||||
export class CreateCustomerUseCase {
|
||||
private readonly dtoMapper: ICreateCustomerInputMapper;
|
||||
private readonly creator: ICustomerCreator;
|
||||
private readonly fullSnapshotBuilder: ICustomerFullSnapshotBuilder;
|
||||
private readonly transactionManager: ITransactionManager;
|
||||
|
||||
constructor(deps: CreateCustomerUseCaseDeps) {
|
||||
this.dtoMapper = deps.dtoMapper;
|
||||
this.creator = deps.creator;
|
||||
this.fullSnapshotBuilder = deps.fullSnapshotBuilder;
|
||||
this.transactionManager = deps.transactionManager;
|
||||
}
|
||||
|
||||
public execute(params: CreateCustomerUseCaseInput) {
|
||||
const { dto, companyId } = params;
|
||||
|
||||
const mappedPropsResult = this.dtoMapper.map(dto, { companyId });
|
||||
if (mappedPropsResult.isFailure) {
|
||||
return mappedPropsResult;
|
||||
}
|
||||
|
||||
const { props, id } = mappedPropsResult.data;
|
||||
|
||||
return this.transactionManager.complete(async (transaction: Transaction) => {
|
||||
try {
|
||||
const createResult = await this.creator.create({ companyId, id, props, transaction });
|
||||
|
||||
if (createResult.isFailure) {
|
||||
return createResult;
|
||||
}
|
||||
|
||||
const newCustomer = createResult.data;
|
||||
|
||||
const snapshot = this.fullSnapshotBuilder.toOutput(newCustomer);
|
||||
|
||||
return Result.ok(snapshot);
|
||||
} catch (error: unknown) {
|
||||
return Result.fail(error as Error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,90 +0,0 @@
|
||||
import {
|
||||
DuplicateEntityError,
|
||||
type IPresenterRegistry,
|
||||
type ITransactionManager,
|
||||
} from "@erp/core/api";
|
||||
import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import type { Transaction } from "sequelize";
|
||||
|
||||
import type { CreateCustomerRequestDTO } from "../../../../common";
|
||||
import { logger } from "../../..//helpers";
|
||||
import type { CustomerApplicationService } from "../../customer-application.service";
|
||||
import type { CustomerFullSnapshotBuilder } from "../../presenters";
|
||||
import { CustomerNotExistsInCompanySpecification } from "../../specs";
|
||||
|
||||
import { mapDTOToCreateCustomerProps } from "./map-dto-to-create-customer-props";
|
||||
|
||||
type CreateCustomerUseCaseInput = {
|
||||
companyId: UniqueID;
|
||||
dto: CreateCustomerRequestDTO;
|
||||
};
|
||||
|
||||
export class CreateCustomerUseCase {
|
||||
constructor(
|
||||
private readonly service: CustomerApplicationService,
|
||||
private readonly transactionManager: ITransactionManager,
|
||||
private readonly presenterRegistry: IPresenterRegistry
|
||||
) {}
|
||||
|
||||
public execute(params: CreateCustomerUseCaseInput) {
|
||||
const { dto, companyId } = params;
|
||||
const presenter = this.presenterRegistry.getPresenter({
|
||||
resource: "customer",
|
||||
projection: "FULL",
|
||||
}) as CustomerFullSnapshotBuilder;
|
||||
|
||||
// 1) Mapear DTO → props de dominio
|
||||
const dtoResult = mapDTOToCreateCustomerProps(dto);
|
||||
if (dtoResult.isFailure) {
|
||||
return Result.fail(dtoResult.error);
|
||||
}
|
||||
|
||||
const { props, id } = dtoResult.data;
|
||||
|
||||
// 2) Construir entidad de dominio
|
||||
const buildResult = this.service.buildCustomerInCompany(companyId, props, id);
|
||||
if (buildResult.isFailure) {
|
||||
return Result.fail(buildResult.error);
|
||||
}
|
||||
|
||||
const newCustomer = buildResult.data;
|
||||
|
||||
// 3) Ejecutar bajo transacción: verificar duplicado → persistir → ensamblar vista
|
||||
return this.transactionManager.complete(async (transaction: Transaction) => {
|
||||
try {
|
||||
// Verificar que no exista ya un cliente con el mismo id en la companyId
|
||||
const spec = new CustomerNotExistsInCompanySpecification(
|
||||
this.service,
|
||||
companyId,
|
||||
transaction
|
||||
);
|
||||
|
||||
const isNew = await spec.isSatisfiedBy(newCustomer.id);
|
||||
logger.debug(`isNew => ${isNew}`, { label: "CreateCustomerUseCase.execute" });
|
||||
|
||||
if (!isNew) {
|
||||
return Result.fail(new DuplicateEntityError("Customer", "id", String(newCustomer.id)));
|
||||
}
|
||||
|
||||
logger.debug(JSON.stringify(newCustomer, null, 6));
|
||||
|
||||
const saveResult = await this.service.createCustomerInCompany(
|
||||
companyId,
|
||||
newCustomer,
|
||||
transaction
|
||||
);
|
||||
if (saveResult.isFailure) {
|
||||
return Result.fail(saveResult.error);
|
||||
}
|
||||
|
||||
const customer = saveResult.data;
|
||||
const dto = presenter.toOutput(customer);
|
||||
|
||||
return Result.ok(dto);
|
||||
} catch (error: unknown) {
|
||||
return Result.fail(error as Error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export * from "./create-customer.use-case";
|
||||
@ -1,231 +0,0 @@
|
||||
import {
|
||||
City,
|
||||
Country,
|
||||
CurrencyCode,
|
||||
DomainError,
|
||||
EmailAddress,
|
||||
LanguageCode,
|
||||
Name,
|
||||
PhoneNumber,
|
||||
PostalAddress,
|
||||
PostalCode,
|
||||
Province,
|
||||
Street,
|
||||
TINNumber,
|
||||
TaxCode,
|
||||
TextValue,
|
||||
URLAddress,
|
||||
UniqueID,
|
||||
ValidationErrorCollection,
|
||||
type ValidationErrorDetail,
|
||||
extractOrPushError,
|
||||
maybeFromNullableResult,
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Collection, Result, isNullishOrEmpty } from "@repo/rdx-utils";
|
||||
|
||||
import type { CreateCustomerRequestDTO } from "../../../../common";
|
||||
import { type ICustomerProps, CustomerStatus } from "../../../domain";
|
||||
|
||||
/**
|
||||
* Convierte el DTO a las props validadas (CustomerProps).
|
||||
* No construye directamente el agregado.
|
||||
*
|
||||
* @param dto - DTO con los datos de la factura de cliente
|
||||
* @returns
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
export function mapDTOToCreateCustomerProps(dto: CreateCustomerRequestDTO) {
|
||||
try {
|
||||
const errors: ValidationErrorDetail[] = [];
|
||||
|
||||
const customerId = extractOrPushError(UniqueID.create(dto.id), "id", errors);
|
||||
|
||||
const status = CustomerStatus.createActive();
|
||||
const isCompany = dto.is_company === "true";
|
||||
|
||||
const reference = extractOrPushError(
|
||||
maybeFromNullableResult(dto.reference, (value) => Name.create(value)),
|
||||
"reference",
|
||||
errors
|
||||
);
|
||||
|
||||
const name = extractOrPushError(Name.create(dto.name), "name", errors);
|
||||
|
||||
const tradeName = extractOrPushError(
|
||||
maybeFromNullableResult(dto.trade_name, (value) => Name.create(value)),
|
||||
"trade_name",
|
||||
errors
|
||||
);
|
||||
|
||||
const tinNumber = extractOrPushError(
|
||||
maybeFromNullableResult(dto.tin, (value) => TINNumber.create(value)),
|
||||
"tin",
|
||||
errors
|
||||
);
|
||||
|
||||
const street = extractOrPushError(
|
||||
maybeFromNullableResult(dto.street, (value) => Street.create(value)),
|
||||
"street",
|
||||
errors
|
||||
);
|
||||
|
||||
const street2 = extractOrPushError(
|
||||
maybeFromNullableResult(dto.street2, (value) => Street.create(value)),
|
||||
"street2",
|
||||
errors
|
||||
);
|
||||
|
||||
const city = extractOrPushError(
|
||||
maybeFromNullableResult(dto.city, (value) => City.create(value)),
|
||||
"city",
|
||||
errors
|
||||
);
|
||||
|
||||
const province = extractOrPushError(
|
||||
maybeFromNullableResult(dto.province, (value) => Province.create(value)),
|
||||
"province",
|
||||
errors
|
||||
);
|
||||
|
||||
const postalCode = extractOrPushError(
|
||||
maybeFromNullableResult(dto.postal_code, (value) => PostalCode.create(value)),
|
||||
"postal_code",
|
||||
errors
|
||||
);
|
||||
|
||||
const country = extractOrPushError(
|
||||
maybeFromNullableResult(dto.country, (value) => Country.create(value)),
|
||||
"country",
|
||||
errors
|
||||
);
|
||||
|
||||
const primaryEmailAddress = extractOrPushError(
|
||||
maybeFromNullableResult(dto.email_primary, (value) => EmailAddress.create(value)),
|
||||
"email_primary",
|
||||
errors
|
||||
);
|
||||
|
||||
const secondaryEmailAddress = extractOrPushError(
|
||||
maybeFromNullableResult(dto.email_secondary, (value) => EmailAddress.create(value)),
|
||||
"email_secondary",
|
||||
errors
|
||||
);
|
||||
|
||||
const primaryPhoneNumber = extractOrPushError(
|
||||
maybeFromNullableResult(dto.phone_primary, (value) => PhoneNumber.create(value)),
|
||||
"phone_primary",
|
||||
errors
|
||||
);
|
||||
|
||||
const secondaryPhoneNumber = extractOrPushError(
|
||||
maybeFromNullableResult(dto.phone_secondary, (value) => PhoneNumber.create(value)),
|
||||
"phone_secondary",
|
||||
errors
|
||||
);
|
||||
|
||||
const primaryMobileNumber = extractOrPushError(
|
||||
maybeFromNullableResult(dto.mobile_primary, (value) => PhoneNumber.create(value)),
|
||||
"mobile_primary",
|
||||
errors
|
||||
);
|
||||
|
||||
const secondaryMobileNumber = extractOrPushError(
|
||||
maybeFromNullableResult(dto.mobile_secondary, (value) => PhoneNumber.create(value)),
|
||||
"mobile_secondary",
|
||||
errors
|
||||
);
|
||||
|
||||
const faxNumber = extractOrPushError(
|
||||
maybeFromNullableResult(dto.fax, (value) => PhoneNumber.create(value)),
|
||||
"fax",
|
||||
errors
|
||||
);
|
||||
|
||||
const website = extractOrPushError(
|
||||
maybeFromNullableResult(dto.website, (value) => URLAddress.create(value)),
|
||||
"website",
|
||||
errors
|
||||
);
|
||||
|
||||
const legalRecord = extractOrPushError(
|
||||
maybeFromNullableResult(dto.legal_record, (value) => TextValue.create(value)),
|
||||
"legal_record",
|
||||
errors
|
||||
);
|
||||
|
||||
const languageCode = extractOrPushError(
|
||||
LanguageCode.create(dto.language_code),
|
||||
"language_code",
|
||||
errors
|
||||
);
|
||||
|
||||
const currencyCode = extractOrPushError(
|
||||
CurrencyCode.create(dto.currency_code),
|
||||
"currency_code",
|
||||
errors
|
||||
);
|
||||
|
||||
const defaultTaxes = new Collection<TaxCode>();
|
||||
|
||||
if (!isNullishOrEmpty(dto.default_taxes)) {
|
||||
dto.default_taxes!.map((taxCode, index) => {
|
||||
const tax = extractOrPushError(TaxCode.create(taxCode), `default_taxes.${index}`, errors);
|
||||
if (tax) {
|
||||
defaultTaxes.add(tax!);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
return Result.fail(new ValidationErrorCollection("Customer props mapping failed", errors));
|
||||
}
|
||||
|
||||
const postalAddressProps = {
|
||||
street: street!,
|
||||
street2: street2!,
|
||||
city: city!,
|
||||
postalCode: postalCode!,
|
||||
province: province!,
|
||||
country: country!,
|
||||
};
|
||||
|
||||
const postalAddress = extractOrPushError(
|
||||
PostalAddress.create(postalAddressProps),
|
||||
"address",
|
||||
errors
|
||||
);
|
||||
|
||||
const customerProps: Omit<ICustomerProps, "companyId"> = {
|
||||
status: status!,
|
||||
reference: reference!,
|
||||
|
||||
isCompany: isCompany,
|
||||
name: name!,
|
||||
tradeName: tradeName!,
|
||||
tin: tinNumber!,
|
||||
|
||||
address: postalAddress!,
|
||||
|
||||
emailPrimary: primaryEmailAddress!,
|
||||
emailSecondary: secondaryEmailAddress!,
|
||||
phonePrimary: primaryPhoneNumber!,
|
||||
phoneSecondary: secondaryPhoneNumber!,
|
||||
mobilePrimary: primaryMobileNumber!,
|
||||
mobileSecondary: secondaryMobileNumber!,
|
||||
|
||||
fax: faxNumber!,
|
||||
website: website!,
|
||||
|
||||
legalRecord: legalRecord!,
|
||||
defaultTaxes: defaultTaxes!,
|
||||
languageCode: languageCode!,
|
||||
currencyCode: currencyCode!,
|
||||
};
|
||||
|
||||
return Result.ok({ id: customerId!, props: customerProps });
|
||||
} catch (err: unknown) {
|
||||
return Result.fail(new DomainError("Customer props mapping failed", { cause: err }));
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
export * from "./create";
|
||||
export * from "./create-customer.use-case";
|
||||
export * from "./delete-customer.use-case";
|
||||
export * from "./get-customer-by-id.use-case";
|
||||
export * from "./list-customers.use-case";
|
||||
export * from "./update";
|
||||
export * from "./update/update-customer.use-case";
|
||||
|
||||
@ -6,9 +6,6 @@ import type { Transaction } from "sequelize";
|
||||
import type { UpdateCustomerByIdRequestDTO } from "../../../../common/dto";
|
||||
import type { CustomerPatchProps } from "../../../domain";
|
||||
import type { CustomerApplicationService } from "../../customer-application.service";
|
||||
import type { CustomerFullSnapshotBuilder } from "../../presenters";
|
||||
|
||||
import { mapDTOToUpdateCustomerPatchProps } from "./map-dto-to-update-customer-props";
|
||||
|
||||
type UpdateCustomerUseCaseInput = {
|
||||
companyId: UniqueID;
|
||||
@ -30,17 +27,10 @@ export class UpdateCustomerUseCase {
|
||||
if (idOrError.isFailure) {
|
||||
return Result.fail(idOrError.error);
|
||||
}
|
||||
|
||||
const customerId = idOrError.data;
|
||||
const presenter = this.presenterRegistry.getPresenter({
|
||||
resource: "customer",
|
||||
projection: "FULL",
|
||||
}) as CustomerFullSnapshotBuilder;
|
||||
|
||||
// Mapear DTO → props de dominio
|
||||
const patchPropsResult = mapDTOToUpdateCustomerPatchProps(dto);
|
||||
const patchPropsResult = this.dtoMapper.map(dto, { companyId });
|
||||
if (patchPropsResult.isFailure) {
|
||||
return Result.fail(patchPropsResult.error);
|
||||
return patchPropsResult;
|
||||
}
|
||||
|
||||
const patchProps: CustomerPatchProps = patchPropsResult.data;
|
||||
|
||||
@ -5,8 +5,9 @@ import {
|
||||
type LanguageCode,
|
||||
type Name,
|
||||
type PhoneNumber,
|
||||
type PostalAddress,
|
||||
PostalAddress,
|
||||
type PostalAddressPatchProps,
|
||||
type PostalAddressProps,
|
||||
type TINNumber,
|
||||
type TaxCode,
|
||||
type TextValue,
|
||||
@ -17,7 +18,7 @@ import { type Collection, type Maybe, Result } from "@repo/rdx-utils";
|
||||
|
||||
import type { CustomerStatus } from "../value-objects";
|
||||
|
||||
export interface ICustomerProps {
|
||||
export interface ICustomerCreateProps {
|
||||
companyId: UniqueID;
|
||||
status: CustomerStatus;
|
||||
reference: Maybe<Name>;
|
||||
@ -27,7 +28,7 @@ export interface ICustomerProps {
|
||||
tradeName: Maybe<Name>;
|
||||
tin: Maybe<TINNumber>;
|
||||
|
||||
address: PostalAddress;
|
||||
address: PostalAddressProps;
|
||||
|
||||
emailPrimary: Maybe<EmailAddress>;
|
||||
emailSecondary: Maybe<EmailAddress>;
|
||||
@ -43,13 +44,15 @@ export interface ICustomerProps {
|
||||
|
||||
legalRecord: Maybe<TextValue>;
|
||||
|
||||
defaultTaxes: Collection<TaxCode>;
|
||||
defaultTaxes: TaxCode[];
|
||||
|
||||
languageCode: LanguageCode;
|
||||
currencyCode: CurrencyCode;
|
||||
}
|
||||
|
||||
export type CustomerPatchProps = Partial<Omit<ICustomerProps, "companyId" | "address">> & {
|
||||
export type CustomerPatchProps = Partial<
|
||||
Omit<ICustomerCreateProps, "companyId" | "address" | "isCompany" | "status">
|
||||
> & {
|
||||
address?: PostalAddressPatchProps;
|
||||
};
|
||||
|
||||
@ -90,13 +93,27 @@ export interface ICustomer {
|
||||
readonly currencyCode: CurrencyCode;
|
||||
}
|
||||
|
||||
type CreateCustomerProps = ICustomerProps;
|
||||
type InternalCustomerProps = ICustomerProps;
|
||||
type CustomerInternalProps = Omit<ICustomerCreateProps, "address"> & {
|
||||
readonly address: PostalAddress;
|
||||
};
|
||||
|
||||
export class Customer extends AggregateRoot<InternalCustomerProps> implements ICustomer {
|
||||
export class Customer extends AggregateRoot<CustomerInternalProps> implements ICustomer {
|
||||
static create(props: ICustomerCreateProps, id?: UniqueID): Result<Customer, Error> {
|
||||
const { address, ...internalProps } = props;
|
||||
|
||||
static create(props: CreateCustomerProps, id?: UniqueID): Result<Customer, Error> {
|
||||
const contact = new Customer(props, id);
|
||||
const postalAddressResult = PostalAddress.create(address);
|
||||
|
||||
if (postalAddressResult.isFailure) {
|
||||
return Result.fail(postalAddressResult.error);
|
||||
}
|
||||
|
||||
const contact = new Customer(
|
||||
{
|
||||
...internalProps,
|
||||
address: postalAddressResult.data,
|
||||
},
|
||||
id
|
||||
);
|
||||
|
||||
// Reglas de negocio / validaciones
|
||||
// ...
|
||||
@ -110,28 +127,26 @@ export class Customer extends AggregateRoot<InternalCustomerProps> implements IC
|
||||
}
|
||||
|
||||
// Rehidratación desde persistencia
|
||||
static rehydrate(props: InternalCustomerProps, id: UniqueID): Customer {
|
||||
static rehydrate(props: CustomerInternalProps, id: UniqueID): Customer {
|
||||
return new Customer(props, id);
|
||||
}
|
||||
|
||||
|
||||
public update(partialCustomer: CustomerPatchProps): Result<Customer, Error> {
|
||||
const { address: partialAddress, ...rest } = partialCustomer;
|
||||
const updatedProps = {
|
||||
...this.props,
|
||||
...rest,
|
||||
} as ICustomerProps;
|
||||
|
||||
Object.assign(this.props, rest);
|
||||
|
||||
if (partialAddress) {
|
||||
const updatedAddressOrError = this.address.update(partialAddress);
|
||||
if (updatedAddressOrError.isFailure) {
|
||||
return Result.fail(updatedAddressOrError.error);
|
||||
const addressResult = this.address.update(partialAddress);
|
||||
|
||||
if (addressResult.isFailure) {
|
||||
return Result.fail(addressResult.error);
|
||||
}
|
||||
|
||||
updatedProps.address = updatedAddressOrError.data;
|
||||
this.props.address = addressResult.data;
|
||||
}
|
||||
|
||||
return Customer.create(updatedProps, this.id);
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
@ -4,7 +4,7 @@ export type CustomersServicesDeps = {
|
||||
services: {
|
||||
listCustomers: (filters: unknown, context: unknown) => null;
|
||||
getCustomerById: (id: unknown, context: unknown) => null;
|
||||
generateCustomerReport: (id: unknown, options: unknown, context: unknown) => null;
|
||||
//generateCustomerReport: (id: unknown, options: unknown, context: unknown) => null;
|
||||
};
|
||||
};
|
||||
|
||||
@ -17,7 +17,7 @@ export function buildCustomerServices(deps: CustomersInternalDeps): CustomersSer
|
||||
getCustomerById: (id, context) => null,
|
||||
//internal.useCases.getCustomerById().execute(id, context),
|
||||
|
||||
generateCustomerReport: (id, options, context) => null,
|
||||
//generateCustomerReport: (id, options, context) => null,
|
||||
//internal.useCases.reportCustomer().execute(id, options, context),
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
import { type ModuleParams, buildCatalogs, buildTransactionManager } from "@erp/core/api";
|
||||
|
||||
import {
|
||||
type CreateCustomerUseCase,
|
||||
type GetCustomerByIdUseCase,
|
||||
type ListCustomersUseCase,
|
||||
type UpdateCustomerUseCase,
|
||||
buildCreateCustomerUseCase,
|
||||
buildCustomerCreator,
|
||||
buildCustomerFinder,
|
||||
buildCustomerInputMappers,
|
||||
buildCustomerSnapshotBuilders,
|
||||
buildGetCustomerByIdUseCase,
|
||||
buildListCustomersUseCase,
|
||||
@ -16,11 +21,13 @@ export type CustomersInternalDeps = {
|
||||
useCases: {
|
||||
listCustomers: () => ListCustomersUseCase;
|
||||
getCustomerById: () => GetCustomerByIdUseCase;
|
||||
createCustomer: () => CreateCustomerUseCase;
|
||||
updateCustomer: () => UpdateCustomerUseCase;
|
||||
|
||||
//reportCustomer: () => ReportCustomerUseCase;
|
||||
//createCustomer: () => CreateCustomerUseCase;
|
||||
|
||||
/*
|
||||
updateCustomer: () => UpdateCustomerUseCase;
|
||||
|
||||
deleteCustomer: () => DeleteCustomerUseCase;
|
||||
issueCustomer: () => IssueCustomerUseCase;
|
||||
changeStatusCustomer: () => ChangeStatusCustomerUseCase;*/
|
||||
@ -39,9 +46,9 @@ export function buildCustomersDependencies(params: ModuleParams): CustomersInter
|
||||
//const numberService = buildCustomerNumberGenerator();
|
||||
|
||||
// Application helpers
|
||||
//const inputMappers = buildCustomerInputMappers(catalogs);
|
||||
const finder = buildCustomerFinder(repository);
|
||||
//const creator = buildCustomerCreator({ numberService, repository });
|
||||
const inputMappers = buildCustomerInputMappers(catalogs);
|
||||
const finder = buildCustomerFinder({ repository });
|
||||
const creator = buildCustomerCreator({ repository });
|
||||
|
||||
const snapshotBuilders = buildCustomerSnapshotBuilders();
|
||||
//const documentGeneratorPipeline = buildCustomerDocumentService(params);
|
||||
@ -63,6 +70,22 @@ export function buildCustomersDependencies(params: ModuleParams): CustomersInter
|
||||
transactionManager,
|
||||
}),
|
||||
|
||||
createCustomer: () =>
|
||||
buildCreateCustomerUseCase({
|
||||
creator,
|
||||
dtoMapper: inputMappers.createInputMapper,
|
||||
fullSnapshotBuilder: snapshotBuilders.full,
|
||||
transactionManager,
|
||||
}),
|
||||
|
||||
updateCustomer: () =>
|
||||
buildUpdateCustomerUseCase({
|
||||
creator,
|
||||
dtoMapper: inputMappers.updateInputMapper,
|
||||
fullSnapshotBuilder: snapshotBuilders.full,
|
||||
transactionManager,
|
||||
}),
|
||||
|
||||
/*reportCustomer: () =>
|
||||
buildReportCustomerUseCase({
|
||||
finder,
|
||||
@ -72,13 +95,7 @@ export function buildCustomersDependencies(params: ModuleParams): CustomersInter
|
||||
transactionManager,
|
||||
}),
|
||||
|
||||
createCustomer: () =>
|
||||
buildCreateCustomerUseCase({
|
||||
creator,
|
||||
dtoMapper: inputMappers.createInputMapper,
|
||||
fullSnapshotBuilder: snapshotBuilders.full,
|
||||
transactionManager,
|
||||
}),*/
|
||||
*/
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,20 +1,18 @@
|
||||
import {
|
||||
mockUser,
|
||||
requireAuthenticated,
|
||||
requireCompanyContext,
|
||||
} from "@erp/auth/api";
|
||||
import { type ModuleParams, RequestWithAuth, validateRequest } from "@erp/core/api";
|
||||
import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api";
|
||||
import { type ModuleParams, type RequestWithAuth, validateRequest } from "@erp/core/api";
|
||||
import { type NextFunction, type Request, type Response, Router } from "express";
|
||||
|
||||
import {
|
||||
CreateCustomerRequestSchema,
|
||||
CustomerListRequestSchema,
|
||||
GetCustomerByIdRequestSchema
|
||||
GetCustomerByIdRequestSchema,
|
||||
} from "../../../common/dto";
|
||||
import type { CustomersInternalDeps } from "../di";
|
||||
|
||||
import {
|
||||
CreateCustomerController,
|
||||
GetCustomerController,
|
||||
ListCustomersController
|
||||
ListCustomersController,
|
||||
} from "./controllers";
|
||||
|
||||
export const customersRouter = (params: ModuleParams, deps: CustomersInternalDeps) => {
|
||||
@ -53,7 +51,7 @@ export const customersRouter = (params: ModuleParams, deps: CustomersInternalDep
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
router.get(
|
||||
"/:customer_id",
|
||||
//checkTabContext,
|
||||
|
||||
@ -65,17 +63,17 @@ export const customersRouter = (params: ModuleParams, deps: CustomersInternalDep
|
||||
}
|
||||
);
|
||||
|
||||
/* router.post(
|
||||
router.post(
|
||||
"/",
|
||||
//checkTabContext,
|
||||
|
||||
validateRequest(CreateCustomerRequestSchema, "body"),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const useCase = deps.useCases.create();
|
||||
const useCase = deps.useCases.createCustomer();
|
||||
const controller = new CreateCustomerController(useCase);
|
||||
return controller.execute(req, res, next);
|
||||
}
|
||||
); */
|
||||
);
|
||||
|
||||
/* router.put(
|
||||
"/:customer_id",
|
||||
|
||||
@ -24,7 +24,7 @@ import {
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Collection, Result } from "@repo/rdx-utils";
|
||||
|
||||
import { Customer, CustomerStatus, type ICustomerProps } from "../../../domain";
|
||||
import { Customer, CustomerStatus, type ICustomerCreateProps } from "../../../domain";
|
||||
import type { CustomerCreationAttributes, CustomerModel } from "../../sequelize";
|
||||
|
||||
export class SequelizeCustomerDomainMapper extends SequelizeDomainMapper<
|
||||
@ -199,7 +199,7 @@ export class SequelizeCustomerDomainMapper extends SequelizeDomainMapper<
|
||||
return Result.fail(new ValidationErrorCollection("Customer props mapping failed", errors));
|
||||
}
|
||||
|
||||
const customerProps: ICustomerProps = {
|
||||
const customerProps: ICustomerCreateProps = {
|
||||
companyId: companyId!,
|
||||
status: status!,
|
||||
reference: reference!,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { DomainEntity } from "./domain-entity";
|
||||
import { IDomainEvent } from "./events";
|
||||
import type { IDomainEvent } from "./events";
|
||||
|
||||
export abstract class AggregateRoot<T extends object> extends DomainEntity<T> {
|
||||
private _domainEvents: IDomainEvent[] = [];
|
||||
|
||||
@ -1,12 +1,20 @@
|
||||
import { UniqueID } from "./value-objects";
|
||||
|
||||
export abstract class DomainEntity<T extends object> {
|
||||
protected readonly props: T;
|
||||
private _props: T;
|
||||
public readonly id: UniqueID;
|
||||
|
||||
protected constructor(props: T, id?: UniqueID) {
|
||||
this.id = id ? id : UniqueID.generateNewID();
|
||||
this.props = props;
|
||||
this.id = id ?? UniqueID.generateNewID();
|
||||
this._props = props;
|
||||
}
|
||||
|
||||
protected get props(): T {
|
||||
return this._props;
|
||||
}
|
||||
|
||||
protected set props(value: T) {
|
||||
this._props = value;
|
||||
}
|
||||
|
||||
protected _flattenProps(props: T): { [s: string]: any } {
|
||||
@ -18,7 +26,8 @@ export abstract class DomainEntity<T extends object> {
|
||||
}, {});
|
||||
}
|
||||
|
||||
equals(other: DomainEntity<T>): boolean {
|
||||
equals(other?: DomainEntity<T>): boolean {
|
||||
if (!other) return false;
|
||||
return other instanceof DomainEntity && this.id.equals(other.id);
|
||||
}
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ export class PostalAddress extends ValueObject<PostalAddressProps> {
|
||||
return Result.ok(new PostalAddress(values));
|
||||
}
|
||||
|
||||
public update(partial: Partial<PostalAddressPatchProps>): Result<PostalAddress, Error> {
|
||||
public update(partial: PostalAddressPatchProps): Result<PostalAddress, Error> {
|
||||
const updatedProps = {
|
||||
...this.props,
|
||||
...partial,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user