Facturas de cliente

This commit is contained in:
David Arranz 2025-09-14 10:18:02 +02:00
parent d7dc148439
commit 2036ec2206
7 changed files with 235 additions and 12 deletions

View File

@ -122,7 +122,10 @@ export class CustomerInvoiceRepository
transaction: Transaction
): Promise<Result<CustomerInvoice, Error>> {
try {
const mapper: ICustomerInvoiceDomainMapper = this._registry.getDomainMapper("FULL");
const mapper: ICustomerInvoiceDomainMapper = this._registry.getDomainMapper({
resource: "customer-invoice",
});
const { CustomerModel } = this._database.models;
const row = await CustomerInvoiceModel.findOne({
@ -182,7 +185,10 @@ export class CustomerInvoiceRepository
transaction: Transaction
): Promise<Result<Collection<CustomerInvoiceListDTO>, Error>> {
try {
const mapper: ICustomerInvoiceListMapper = this._registry.getQueryMapper("LIST");
const mapper: ICustomerInvoiceListMapper = this._registry.getQueryMapper({
resource: "customer-invoice",
query: "LIST",
});
const { CustomerModel } = this._database.models;
const converter = new CriteriaToSequelizeConverter();
const query = converter.convert(criteria);

View File

@ -42,7 +42,7 @@ export interface ICustomerRepository {
companyId: UniqueID,
criteria: Criteria,
transaction?: any
): Promise<Result<Collection<Customer>>>;
): Promise<Result<Collection<CustomerListDTO>>>;
/**
* Elimina un Customer por su ID, dentro de una empresa.

View File

@ -159,10 +159,7 @@ export class CustomerDomainMapper
});
}
if (errors.length > 0) {
return Result.fail(new ValidationErrorCollection("Customer props mapping failed", errors));
}
// Now, create the PostalAddress VO
const postalAddressProps = {
street: street!,
street2: street2!,
@ -178,6 +175,11 @@ export class CustomerDomainMapper
errors
);
// Si hubo errores de mapeo, devolvemos colección de validación
if (errors.length > 0) {
return Result.fail(new ValidationErrorCollection("Customer props mapping failed", errors));
}
const customerProps: CustomerProps = {
companyId: companyId!,
status: status!,

View File

@ -1 +1 @@
export * from "./customer.full.mapper";
export * from "./customer.mapper";

View File

@ -0,0 +1,212 @@
import {
City,
Country,
CurrencyCode,
EmailAddress,
LanguageCode,
Name,
PhoneNumber,
PostalAddress,
PostalCode,
Province,
Street,
TINNumber,
TextValue,
URLAddress,
UniqueID,
maybeFromNullableVO,
} from "@repo/rdx-ddd";
import {
ISequelizeQueryMapper,
MapperParamsType,
SequelizeQueryMapper,
ValidationErrorCollection,
ValidationErrorDetail,
extractOrPushError,
} from "@erp/core/api";
import { Maybe, Result } from "@repo/rdx-utils";
import { CustomerStatus } from "../../../domain";
import { CustomerModel } from "../../sequelize";
export type CustomerListDTO = {
id: UniqueID;
companyId: UniqueID;
status: CustomerStatus;
reference: Maybe<Name>;
isCompany: boolean;
name: Name;
tradeName: Maybe<Name>;
tin: Maybe<TINNumber>;
address: PostalAddress;
email: Maybe<EmailAddress>;
phone: Maybe<PhoneNumber>;
fax: Maybe<PhoneNumber>;
website: Maybe<URLAddress>;
languageCode: LanguageCode;
currencyCode: CurrencyCode;
};
export interface ICustomerListMapper
extends ISequelizeQueryMapper<CustomerModel, CustomerListDTO> {}
export class CustomerListMapper
extends SequelizeQueryMapper<CustomerModel, CustomerListDTO>
implements ICustomerListMapper
{
public mapToDTO(raw: CustomerModel, params?: MapperParamsType): Result<CustomerListDTO, Error> {
const errors: ValidationErrorDetail[] = [];
// 1) Valores escalares (atributos generales)
const customerId = extractOrPushError(UniqueID.create(raw.id), "id", errors);
const companyId = extractOrPushError(UniqueID.create(raw.company_id), "company_id", errors);
const isCompany = raw.is_company;
const status = extractOrPushError(CustomerStatus.create(raw.status), "status", errors);
const reference = extractOrPushError(
maybeFromNullableVO(raw.reference, (value) => Name.create(value)),
"reference",
errors
);
const name = extractOrPushError(Name.create(raw.name), "name", errors);
const tradeName = extractOrPushError(
maybeFromNullableVO(raw.trade_name, (value) => Name.create(value)),
"trade_name",
errors
);
const tinNumber = extractOrPushError(
maybeFromNullableVO(raw.tin, (value) => TINNumber.create(value)),
"tin",
errors
);
const street = extractOrPushError(
maybeFromNullableVO(raw.street, (value) => Street.create(value)),
"street",
errors
);
const street2 = extractOrPushError(
maybeFromNullableVO(raw.street2, (value) => Street.create(value)),
"street2",
errors
);
const city = extractOrPushError(
maybeFromNullableVO(raw.city, (value) => City.create(value)),
"city",
errors
);
const province = extractOrPushError(
maybeFromNullableVO(raw.province, (value) => Province.create(value)),
"province",
errors
);
const postalCode = extractOrPushError(
maybeFromNullableVO(raw.postal_code, (value) => PostalCode.create(value)),
"postal_code",
errors
);
const country = extractOrPushError(
maybeFromNullableVO(raw.country, (value) => Country.create(value)),
"country",
errors
);
const emailAddress = extractOrPushError(
maybeFromNullableVO(raw.email, (value) => EmailAddress.create(value)),
"email",
errors
);
const phoneNumber = extractOrPushError(
maybeFromNullableVO(raw.phone, (value) => PhoneNumber.create(value)),
"phone",
errors
);
const faxNumber = extractOrPushError(
maybeFromNullableVO(raw.fax, (value) => PhoneNumber.create(value)),
"fax",
errors
);
const website = extractOrPushError(
maybeFromNullableVO(raw.website, (value) => URLAddress.create(value)),
"website",
errors
);
const legalRecord = extractOrPushError(
maybeFromNullableVO(raw.legal_record, (value) => TextValue.create(value)),
"legal_record",
errors
);
const languageCode = extractOrPushError(
LanguageCode.create(raw.language_code),
"language_code",
errors
);
const currencyCode = extractOrPushError(
CurrencyCode.create(raw.currency_code),
"currency_code",
errors
);
// Valores compuestos (objetos de valor / agregados)
const postalAddressProps = {
street: street!,
street2: street2!,
city: city!,
postalCode: postalCode!,
province: province!,
country: country!,
};
const postalAddress = extractOrPushError(
PostalAddress.create(postalAddressProps),
"address",
errors
);
// Si hubo errores de mapeo, devolvemos colección de validación
if (errors.length > 0) {
return Result.fail(new ValidationErrorCollection("Customer invoice mapping failed", errors));
}
return Result.ok<CustomerListDTO>({
id: customerId!,
companyId: companyId!,
status: status!,
reference: reference!,
isCompany: isCompany,
name: name!,
tradeName: tradeName!,
tin: tinNumber!,
address: postalAddress!,
email: emailAddress!,
phone: phoneNumber!,
fax: faxNumber!,
website: website!,
languageCode: languageCode!,
currencyCode: currencyCode!,
});
}
}

View File

@ -0,0 +1 @@
export * from "./customer.list.mapper";

View File

@ -4,6 +4,7 @@ import { UniqueID } from "@repo/rdx-ddd";
import { Collection, Result } from "@repo/rdx-utils";
import { Transaction } from "sequelize";
import { Customer, ICustomerRepository } from "../../../domain";
import { CustomerListDTO, ICustomerDomainMapper, ICustomerListMapper } from "../../mappers";
import { CustomerModel } from "../models/customer.model";
export class CustomerRepository
@ -112,10 +113,11 @@ export class CustomerRepository
companyId: UniqueID,
criteria: Criteria,
transaction?: Transaction
): Promise<Result<Collection<Customer>>> {
): Promise<Result<Collection<CustomerListDTO>, Error>> {
try {
const mapper: ICustomerDomainMapper = this._registry.getDomainMapper({
const mapper: ICustomerListMapper = this._registry.getQueryMapper({
resource: "customer",
query: "LIST",
});
const converter = new CriteriaToSequelizeConverter();
@ -126,12 +128,12 @@ export class CustomerRepository
company_id: companyId.toString(),
};
const instances = await CustomerModel.findAll({
const { rows, count } = await CustomerModel.findAndCountAll({
...query,
transaction,
});
return mapper.mapArrayToDomain(instances);
return mapper.mapToDTOCollection(rows, count);
} catch (err: unknown) {
return Result.fail(translateSequelizeError(err));
}