Facturas de cliente

This commit is contained in:
David Arranz 2025-09-13 20:45:55 +02:00
parent c7b42fa8ba
commit d7dc148439
82 changed files with 416 additions and 844 deletions

View File

@ -45,17 +45,19 @@ export interface IPresenterRegistry {
/** /**
* Obtiene un mapper de dominio por clave de proyección. * Obtiene un mapper de dominio por clave de proyección.
*/ */
getPresenter<TSource, TOutput>(key: PresenterKey): IPresenter<TSource, TOutput>; getPresenter<TSource = unknown, TOutput = unknown>(
key: PresenterKey
): IPresenter<TSource, TOutput>;
/** /**
* Registra un mapper de dominio bajo una clave de proyección. * Registra un mapper de dominio bajo una clave de proyección.
*/ */
registerPresenter<TSource, TOutput>( registerPresenter<TSource = unknown, TOutput = unknown>(
key: PresenterKey, key: PresenterKey,
presenter: IPresenter<TSource, TOutput> presenter: IPresenter<TSource, TOutput>
): this; ): this;
registerPresenters( registerPresenters(
presenters: Array<{ key: PresenterKey; presenter: IPresenter<any, any> }> presenters: Array<{ key: PresenterKey; presenter: IPresenter<unknown, unknown> }>
): this; ): this;
} }

View File

@ -12,6 +12,20 @@ export class InMemoryPresenterRegistry implements IPresenterRegistry {
}; };
} }
/**
* 🔹 Construye la clave única para el registro.
*/
private _buildKey(key: PresenterKey): string {
const { resource, projection, format, version, locale } = key;
return [
resource.toLowerCase(),
projection.toLowerCase(),
format!.toLowerCase(),
version ?? "latest",
locale ?? "default",
].join("::");
}
private _registerPresenter<TSource, TOutput>( private _registerPresenter<TSource, TOutput>(
key: PresenterKey, key: PresenterKey,
presenter: IPresenter<TSource, TOutput> presenter: IPresenter<TSource, TOutput>
@ -54,7 +68,9 @@ export class InMemoryPresenterRegistry implements IPresenterRegistry {
); );
} }
throw new ApplicationError(`Error. Presenter ${key.resource} ${key.projection} not registred!`); throw new ApplicationError(
`Error. Presenter ${key.resource} / ${key.projection} not registred!`
);
} }
registerPresenter<TSource, TOutput>( registerPresenter<TSource, TOutput>(
@ -73,18 +89,4 @@ export class InMemoryPresenterRegistry implements IPresenterRegistry {
presenters.forEach(({ key, presenter }) => this._registerPresenter(key, presenter)); presenters.forEach(({ key, presenter }) => this._registerPresenter(key, presenter));
return this; return this;
} }
/**
* 🔹 Construye la clave única para el registro.
*/
private _buildKey(key: PresenterKey): string {
const { resource, projection, format, version, locale } = key;
return [
resource.toLowerCase(),
projection.toLowerCase(),
format!.toLowerCase(),
version ?? "latest",
locale ?? "default",
].join("::");
}
} }

View File

@ -5,7 +5,9 @@ export type IPresenterParams = {
presenterRegistry: IPresenterRegistry; presenterRegistry: IPresenterRegistry;
} & Record<string, unknown>; } & Record<string, unknown>;
export abstract class Presenter<T, S> implements IPresenter<T, S> { export abstract class Presenter<TSource = unknown, TOutput = unknown>
implements IPresenter<TSource, TOutput>
{
constructor(protected presenterRegistry: IPresenterRegistry) {} constructor(protected presenterRegistry: IPresenterRegistry) {}
abstract toOutput(source: T): S; abstract toOutput(source: TSource): TOutput;
} }

View File

@ -61,7 +61,7 @@ export type DomainMapperWithBulk<TPersistence, TDomain> = IDomainMapper<TPersist
* - Responsabilidad: transformar registros de persistencia en DTOs para lectura (listados, resúmenes, informes). * - Responsabilidad: transformar registros de persistencia en DTOs para lectura (listados, resúmenes, informes).
* - No intenta reconstruir agregados ni validar value objects de dominio. * - No intenta reconstruir agregados ni validar value objects de dominio.
**/ **/
export interface IReadModelMapperWithBulk<TPersistence, TDTO> { export interface IQueryMapperWithBulk<TPersistence, TDTO> {
/** /**
* Convierte un registro crudo en un DTO de lectura. * Convierte un registro crudo en un DTO de lectura.
*/ */

View File

@ -1,38 +1,42 @@
/** /**
* 🔑 Claves de proyección comunes para seleccionar mappers en lectura. * 🔑 Claves de proyección comunes para seleccionar mappers de dominio y de consulta.
* Puedes extender con otras cadenas según tus necesidades ("SUMMARY", "EXPORT", etc.).
*/ */
export type MapperProjectionKey = "FULL" | "LIST" | "REPORT" | (string & {});
export type MapperKey = {
resource: string;
query: "LIST" | "REPORT" | (string & {}); // "SUMMARY", "EXPORT", etc.
};
export type MapperDomainKey = Pick<MapperKey, "resource">;
export type MapperQueryKey = MapperKey;
/** /**
* 🏗 Registro/Fábrica de mappers (Strategy/Factory) * 🏗 Registro/Fábrica de mappers (Strategy/Factory)
* - Permite resolver diferentes mappers según la proyección (FULL, SUMMARY, etc.) * - Permite resolver diferentes mappers según la consulta (FULL, SUMMARY, etc.)
* - Facilita inyección y test (DIP), evitando dependencias duras en implementaciones concretas. * - Facilita inyección y test (DIP), evitando dependencias duras en implementaciones concretas.
* *
* Ejemplo de uso:
* - registry.registerDomainMapper("FULL", customerInvoiceFullMapper);
* - registry.registerReadModelMapper("SUMMARY", customerInvoiceSummaryMapper);
* - registry.registerReadModelMapper("REPORT", customerInvoiceReportMapper);
*/ */
export interface IMapperRegistry { export interface IMapperRegistry {
/** /**
* Obtiene un mapper de dominio por clave de proyección. * Obtiene un mapper de dominio por clave de proyección.
*/ */
getDomainMapper<T>(key: MapperProjectionKey): T; getDomainMapper<T>(key: MapperDomainKey): T;
/** /**
* Obtiene un mapper de read model por clave de proyección. * Obtiene un mapper de read model por clave de proyección.
*/ */
getReadModelMapper<T>(key: MapperProjectionKey): T; getQueryMapper<T>(key: MapperQueryKey): T;
/** /**
* Registra un mapper de dominio bajo una clave de proyección. * Registra mapper de dominio bajo una clave de proyección.
*/ */
registerDomainMapper<T>(key: MapperProjectionKey, mapper: T): void; registerDomainMapper<T>(key: MapperDomainKey, mapper: T): this;
/** /**
* Registra un mapper de read model bajo una clave de proyección. * Registra un mapper de query / read model bajo una clave de proyección.
*/ */
registerReadModelMapper<T>(key: MapperProjectionKey, mapper: T): void; registerQueryMapper<T>(key: MapperQueryKey, mapper: T): this;
registerQueryMappers(mappers: Array<{ key: MapperQueryKey; mapper: any }>): this;
} }

View File

@ -1,30 +1,78 @@
import { InfrastructureError } from "../errors"; import { InfrastructureError } from "../errors";
import { IMapperRegistry, MapperProjectionKey } from "./mapper-registry.interface"; import {
IMapperRegistry,
MapperDomainKey,
MapperKey,
MapperQueryKey,
} from "./mapper-registry.interface";
export class InMemoryMapperRegistry implements IMapperRegistry { export class InMemoryMapperRegistry implements IMapperRegistry {
private domainMappers: Map<MapperProjectionKey, any> = new Map(); private _mappers: Map<string, any> = new Map();
private readModelMappers: Map<MapperProjectionKey, any> = new Map();
getDomainMapper<T>(key: MapperProjectionKey): T { private _normalizeKey(key: MapperDomainKey | MapperQueryKey): MapperKey {
if (!this.domainMappers.has(key)) { const { resource, query } = key as {
throw new InfrastructureError(`Error. Domain model mapper ${key} not registred!`); resource: string;
query?: string;
};
return {
resource,
query: query ?? "DOMAIN", // 👈 valor por defecto
};
}
/**
* 🔹 Construye la clave única para el registro.
*/
private _buildKey(key: MapperKey): string {
const { resource, query } = key as {
resource: string;
query?: string;
};
return [resource.toLowerCase(), query ?? "DOMAIN"].join("::");
}
private _getMapper<T>(key: MapperKey): T {
const normalizedKey = this._normalizeKey(key);
const exactKey = this._buildKey(normalizedKey);
if (!this._mappers.has(exactKey)) {
throw new InfrastructureError(`Error. Mapper ${normalizedKey.resource} not registred!`);
} }
return this.domainMappers.get(key); return this._mappers.get(exactKey);
} }
getReadModelMapper<T>(key: MapperProjectionKey): T { getDomainMapper<T>(key: MapperDomainKey): T {
if (!this.readModelMappers.has(key)) { const normalizedKey = this._normalizeKey({
throw new InfrastructureError(`Error. Read model mapper ${key} not registred!`); resource: key.resource,
} query: "DOMAIN",
return this.readModelMappers.get(key); });
return this._getMapper(normalizedKey);
} }
registerDomainMapper<T>(key: MapperProjectionKey, mapper: T): void { getQueryMapper<T>(key: MapperQueryKey): T {
this.domainMappers.set(key, mapper); const normalizedKey = this._normalizeKey({
resource: key.resource,
query: "DOMAIN",
});
return this._getMapper(normalizedKey);
} }
registerReadModelMapper<T>(key: MapperProjectionKey, mapper: T): void { registerDomainMapper<T>(key: MapperDomainKey, mapper: T) {
this.readModelMappers.set(key, mapper); const exactKey = this._buildKey(this._normalizeKey(key));
this._mappers.set(exactKey, mapper);
return this;
}
registerQueryMapper<T>(key: MapperQueryKey, mapper: T) {
const exactKey = this._buildKey(this._normalizeKey(key));
this._mappers.set(exactKey, mapper);
return this;
}
registerQueryMappers(mappers: Array<{ key: MapperQueryKey; mapper: any }>): this {
mappers.forEach(({ key, mapper }) => this.registerQueryMapper(key, mapper));
return this;
} }
} }

View File

@ -1,7 +1,6 @@
import { DomainMapperWithBulk, IReadModelMapperWithBulk } from "../../../domain"; import { DomainMapperWithBulk, IQueryMapperWithBulk } from "../../../domain";
export interface ISequelizeDomainMapper<TModel, TModelAttributes, TEntity> export interface ISequelizeDomainMapper<TModel, TModelAttributes, TEntity>
extends DomainMapperWithBulk<TModel | TModelAttributes, TEntity> {} extends DomainMapperWithBulk<TModel | TModelAttributes, TEntity> {}
export interface ISequelizeReadModelMapper<TModel, TDTO> export interface ISequelizeQueryMapper<TModel, TDTO> extends IQueryMapperWithBulk<TModel, TDTO> {}
extends IReadModelMapperWithBulk<TModel, TDTO> {}

View File

@ -1,10 +1,10 @@
import { Collection, Result } from "@repo/rdx-utils"; import { Collection, Result } from "@repo/rdx-utils";
import { Model } from "sequelize"; import { Model } from "sequelize";
import { MapperParamsType } from "../../../domain"; import { MapperParamsType } from "../../../domain";
import { ISequelizeReadModelMapper } from "./sequelize-mapper.interface"; import { ISequelizeQueryMapper } from "./sequelize-mapper.interface";
export abstract class SequelizeReadModelMapper<TModel extends Model, TEntity> export abstract class SequelizeQueryMapper<TModel extends Model, TEntity>
implements ISequelizeReadModelMapper<TModel, TEntity> implements ISequelizeQueryMapper<TModel, TEntity>
{ {
public abstract mapToDTO(raw: TModel, params?: MapperParamsType): Result<TEntity, Error>; public abstract mapToDTO(raw: TModel, params?: MapperParamsType): Result<TEntity, Error>;

View File

@ -1,9 +1,18 @@
import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server"; import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server";
import { IAggregateRootRepository, UniqueID } from "@repo/rdx-ddd"; import { IAggregateRootRepository, UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import { FindOptions, ModelDefined, Transaction } from "sequelize"; import { FindOptions, ModelDefined, Sequelize, Transaction } from "sequelize";
import { IMapperRegistry } from "../mappers";
export abstract class SequelizeRepository<T> implements IAggregateRootRepository<T> { export abstract class SequelizeRepository<T> implements IAggregateRootRepository<T> {
protected readonly _database!: Sequelize;
protected readonly _registry!: IMapperRegistry;
constructor(params: { mapperRegistry: IMapperRegistry; database: Sequelize }) {
this._registry = params.mapperRegistry;
this._database = params.database;
}
protected convertCriteria(criteria: Criteria): FindOptions { protected convertCriteria(criteria: Criteria): FindOptions {
return new CriteriaToSequelizeConverter().convert(criteria); return new CriteriaToSequelizeConverter().convert(criteria);
} }

View File

@ -1,45 +0,0 @@
import { toEmptyString } from "@repo/rdx-ddd";
import { CreateCustomerInvoiceResponseDTO } from "../../../../common/dto";
import { CustomerInvoice } from "../../../domain";
type CreateCustomerInvoiceItemsByInvoiceIdResponseDTO = CreateCustomerInvoiceResponseDTO["items"];
export class CreateCustomerInvoiceItemsPresenter {
toDTO(invoice: CustomerInvoice): CreateCustomerInvoiceItemsByInvoiceIdResponseDTO {
const { items } = invoice;
return items.map((item, index) => ({
id: item.id.toString(),
position: String(index),
description: toEmptyString(item.description, (value) => value.toPrimitive()),
quantity: item.quantity.match(
(quantity) => {
const { value, scale } = quantity.toPrimitive();
return { value: value.toString(), scale: scale.toString() };
},
() => ({ value: "", scale: "" })
),
unit_amount: item.unitAmount.match(
(unitAmount) => {
const { value, scale } = unitAmount.toPrimitive();
return { value: value.toString(), scale: scale.toString() };
},
() => ({ value: "", scale: "" })
),
discount_percentage: item.discountPercentage.match(
(discountPercentage) => {
const { value, scale } = discountPercentage.toPrimitive();
return { value: value.toString(), scale: scale.toString() };
},
() => ({ value: "", scale: "" })
),
total_amount: {
value: item.totalAmount.toPrimitive().value.toString(),
scale: item.totalAmount.toPrimitive().scale.toString(),
},
}));
}
}

View File

@ -1,69 +0,0 @@
import { UpdateCustomerInvoiceByIdResponseDTO } from "@erp/customer-invoices/common/dto";
import { toEmptyString } from "@repo/rdx-ddd";
import { CustomerInvoice } from "../../../domain";
import { CreateCustomerInvoiceItemsPresenter } from "./create-customer-invoice-items.presenter";
export class CreateCustomerInvoicePresenter {
private _itemsPresenter!: CreateCustomerInvoiceItemsPresenter;
constructor() {
this._itemsPresenter = new CreateCustomerInvoiceItemsPresenter();
}
public toDTO(invoice: CustomerInvoice): UpdateCustomerInvoiceByIdResponseDTO {
const items = this._itemsPresenter.toDTO(invoice);
return {
id: invoice.id.toPrimitive(),
company_id: invoice.companyId.toPrimitive(),
invoice_number: invoice.invoiceNumber.toString(),
status: invoice.status.toPrimitive(),
series: invoice.series.toString(),
invoice_date: invoice.invoiceDate.toDateString(),
operation_date: toEmptyString(invoice.operationDate, (value) => value.toDateString()),
notes: toEmptyString(invoice.notes, (value) => value.toPrimitive()),
language_code: invoice.languageCode.toPrimitive(),
currency_code: invoice.currencyCode.toPrimitive(),
subtotal_amount: {
value: invoice.subtotalAmount.value.toString(),
scale: invoice.subtotalAmount.scale.toString(),
},
discount_percentage: {
value: invoice.discountPercentage.value.toString(),
scale: invoice.discountPercentage.scale.toString(),
},
discount_amount: {
value: invoice.discountAmount.value.toString(),
scale: invoice.discountAmount.scale.toString(),
},
taxable_amount: {
value: invoice.taxableAmount.value.toString(),
scale: invoice.taxableAmount.scale.toString(),
},
tax_amount: {
value: invoice.taxAmount.value.toString(),
scale: invoice.taxAmount.scale.toString(),
},
total_amount: {
value: invoice.totalAmount.value.toString(),
scale: invoice.totalAmount.scale.toString(),
},
items,
metadata: {
entity: "customer-invoices",
},
};
}
}

View File

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

View File

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

View File

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

View File

@ -1,16 +0,0 @@
import { CustomerInvoiceItem } from "#/server/domain";
import { IInvoicingContext } from "#/server/intrastructure";
import { Collection } from "@rdx/core";
export const customerInvoiceItemPresenter = (items: Collection<CustomerInvoiceItem>, context: IInvoicingContext) =>
items.totalCount > 0
? items.items.map((item: CustomerInvoiceItem) => ({
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 { ICustomerInvoiceParticipant } from "@/contexts/invoicing/domain";
import { IInvoicingContext } from "@/contexts/invoicing/intrastructure/InvoicingContext";
import { ICreateCustomerInvoice_Participant_Response_DTO } from "@shared/contexts";
import { CustomerInvoiceParticipantAddressPresenter } from "./CustomerInvoiceParticipantAddress.presenter";
export const CustomerInvoiceParticipantPresenter = async (
participant: ICustomerInvoiceParticipant,
context: IInvoicingContext,
): Promise<ICreateCustomerInvoice_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 CustomerInvoiceParticipantAddressPresenter(
participant.billingAddress!,
context,
),
shipping_address: await CustomerInvoiceParticipantAddressPresenter(
participant.shippingAddress!,
context,
),
};
};

View File

@ -1,19 +0,0 @@
import { CustomerInvoiceParticipantAddress } from "@/contexts/invoicing/domain";
import { IInvoicingContext } from "@/contexts/invoicing/intrastructure/InvoicingContext";
import { ICreateCustomerInvoice_AddressParticipant_Response_DTO } from "@shared/contexts";
export const CustomerInvoiceParticipantAddressPresenter = async (
address: CustomerInvoiceParticipantAddress,
context: IInvoicingContext,
): Promise<ICreateCustomerInvoice_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,47 +0,0 @@
import { toEmptyString } from "@repo/rdx-ddd";
import { GetCustomerInvoiceByIdResponseDTO } from "../../../../common/dto";
import { CustomerInvoice } from "../../../domain";
type GetCustomerInvoiceItemsByInvoiceIdResponseDTO = GetCustomerInvoiceByIdResponseDTO["items"];
export class GetCustomerInvoiceItemsPresenter {
toDTO(invoice: CustomerInvoice): GetCustomerInvoiceItemsByInvoiceIdResponseDTO {
const { items } = invoice;
return items.map((item, index) => {
const amounts = item.getAllAmounts();
return {
id: item.id.toString(),
position: String(index),
description: toEmptyString(item.description, (value) => value.toPrimitive()),
quantity: item.quantity.match(
(quantity) => {
const { value, scale } = quantity.toPrimitive();
return { value: value.toString(), scale: scale.toString() };
},
() => ({ value: "", scale: "" })
),
unit_amount: item.unitAmount.match(
(unitAmount) => {
const { value, scale } = unitAmount.toPrimitive();
return { value: value.toString(), scale: scale.toString() };
},
() => ({ value: "", scale: "" })
),
discount_percentage: item.discountPercentage.match(
(discountPercentage) => {
const { value, scale } = discountPercentage.toPrimitive();
return { value: value.toString(), scale: scale.toString() };
},
() => ({ value: "", scale: "" })
),
total_amount: amounts.totalAmount.toPrimitive(),
};
});
}
}

View File

@ -1,76 +0,0 @@
import { IPresenter, IPresenterRegistry } from "@erp/core/api";
import { toEmptyString } from "@repo/rdx-ddd";
import { GetCustomerInvoiceByIdResponseDTO } from "../../../../common/dto";
import { CustomerInvoice } from "../../../domain";
import { GetCustomerInvoiceItemsPresenter } from "./get-invoice-items.presenter";
export class GetCustomerInvoicePresentwer implements IPresenter {
private _itemsPresenter!: GetCustomerInvoiceItemsPresenter;
constructor(private presenterRegistry: IPresenterRegistry) {
this._itemsPresenter = this.presenterRegistry.getPresenter({
resource: "customer-invoice-item",
projection: "FULL",
});
}
toOutput(params: {
invoice: CustomerInvoice;
}): GetCustomerInvoiceByIdResponseDTO {
const { invoice } = params;
const items = this._itemsPresenter.toDTO(invoice);
return {
id: invoice.id.toString(),
company_id: invoice.companyId.toString(),
invoice_number: invoice.invoiceNumber.toString(),
status: invoice.status.toPrimitive(),
series: toEmptyString(invoice.series, (value) => value.toString()),
invoice_date: invoice.invoiceDate.toDateString(),
operation_date: toEmptyString(invoice.operationDate, (value) => value.toDateString()),
notes: toEmptyString(invoice.notes, (value) => value.toString()),
language_code: invoice.languageCode.toString(),
currency_code: invoice.currencyCode.toString(),
/*subtotal_amount: {
value: invoice.subtotalAmount.value.toString(),
scale: invoice.subtotalAmount.scale.toString(),
},
discount_percentage: {
value: invoice.discountPercentage.value.toString(),
scale: invoice.discountPercentage.scale.toString(),
},
discount_amount: {
value: invoice.discountAmount.value.toString(),
scale: invoice.discountAmount.scale.toString(),
},
taxable_amount: {
value: invoice.taxableAmount.value.toString(),
scale: invoice.taxableAmount.scale.toString(),
},
tax_amount: {
value: invoice.taxAmount.value.toString(),
scale: invoice.taxAmount.scale.toString(),
},
total_amount: {
value: invoice.totalAmount.value.toString(),
scale: invoice.totalAmount.scale.toString(),
},*/
items,
metadata: {
entity: "customer-invoices",
},
};
}
}

View File

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

View File

@ -1,7 +1,2 @@
export * from "./create-customer-invoice";
export * from "./delete-customer-invoice";
export * from "./get-customer-invoice";
export * from "./list-customer-invoices";
export * from "./report-customer-invoice";
//export * from "./update-customer-invoice";
export * from "./presenters"; export * from "./presenters";
export * from "./use-cases";

View File

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

View File

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

View File

@ -1,74 +0,0 @@
import { Presenter } from "@erp/core/api";
import { CustomerInvoiceListDTO } from "@erp/customer-invoices/api/infrastructure";
import { Criteria } from "@repo/rdx-criteria/server";
import { toEmptyString } from "@repo/rdx-ddd";
import { ArrayElement, Collection } from "@repo/rdx-utils";
import { CustomerInvoiceListResponseDTO } from "../../../../common/dto";
export class CustomerInvoicesListPresenter extends Presenter {
protected _map(invoice: CustomerInvoiceListDTO) {
const recipientDTO = invoice.recipient.toObjectString();
const invoiceDTO: ArrayElement<CustomerInvoiceListResponseDTO["items"]> = {
id: invoice.id.toString(),
company_id: invoice.companyId.toString(),
customer_id: invoice.customerId.toString(),
invoice_number: invoice.invoiceNumber.toString(),
status: invoice.status.toPrimitive(),
series: toEmptyString(invoice.series, (value) => value.toString()),
invoice_date: invoice.invoiceDate.toDateString(),
operation_date: toEmptyString(invoice.operationDate, (value) => value.toDateString()),
recipient: {
customer_id: invoice.customerId.toString(),
...recipientDTO,
},
language_code: invoice.languageCode.code,
currency_code: invoice.currencyCode.code,
taxes: invoice.taxes,
subtotal_amount: invoice.subtotalAmount.toObjectString(),
discount_amount: invoice.discountAmount.toObjectString(),
taxable_amount: invoice.taxableAmount.toObjectString(),
taxes_amount: invoice.taxesAmount.toObjectString(),
total_amount: invoice.totalAmount.toObjectString(),
metadata: {
entity: "customer-invoice",
},
};
return invoiceDTO;
}
toOutput(params: {
customerInvoices: Collection<CustomerInvoiceListDTO>;
criteria: Criteria;
}): CustomerInvoiceListResponseDTO {
const { customerInvoices, criteria } = params;
const invoices = customerInvoices.map((invoice) => this._map(invoice));
const totalItems = customerInvoices.total();
return {
page: criteria.pageNumber,
per_page: criteria.pageSize,
total_pages: Math.ceil(totalItems / criteria.pageSize),
total_items: totalItems,
items: invoices,
metadata: {
entity: "customer-invoices",
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 "./customer-invoices.list.presenter";

View File

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

View File

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

View File

@ -1,46 +0,0 @@
import { toEmptyString } from "@repo/rdx-ddd";
import { UpdateCustomerInvoiceByIdResponseDTO } from "../../../../common/dto";
import { CustomerInvoice } from "../../../domain";
type UpdateCustomerInvoiceItemsByInvoiceIdResponseDTO =
UpdateCustomerInvoiceByIdResponseDTO["items"];
export class UpdateCustomerInvoiceItemsPresenter {
toDTO(invoice: CustomerInvoice): UpdateCustomerInvoiceItemsByInvoiceIdResponseDTO {
const { items } = invoice;
return items.map((item, index) => ({
id: item.id.toString(),
position: String(index),
description: toEmptyString(item.description, (value) => value.toPrimitive()),
quantity: item.quantity.match(
(quantity) => {
const { value, scale } = quantity.toPrimitive();
return { value: value.toString(), scale: scale.toString() };
},
() => ({ value: "", scale: "" })
),
unit_amount: item.unitAmount.match(
(unitAmount) => {
const { value, scale } = unitAmount.toPrimitive();
return { value: value.toString(), scale: scale.toString() };
},
() => ({ value: "", scale: "" })
),
discount_percentage: item.discountPercentage.match(
(discountPercentage) => {
const { value, scale } = discountPercentage.toPrimitive();
return { value: value.toString(), scale: scale.toString() };
},
() => ({ value: "", scale: "" })
),
total_amount: {
value: item.totalAmount.toPrimitive().value.toString(),
scale: item.totalAmount.toPrimitive().scale.toString(),
},
}));
}
}

View File

@ -1,110 +0,0 @@
import { toEmptyString } from "@repo/rdx-ddd";
import { UpdateCustomerInvoiceByIdResponseDTO } from "../../../../common/dto";
import { CustomerInvoice } from "../../../domain";
import { UpdateCustomerInvoiceItemsPresenter } from "./update-invoice-items.presenter";
export class UpdateCustomerInvoicePresenter {
private _itemsPresenter!: UpdateCustomerInvoiceItemsPresenter;
constructor() {
this._itemsPresenter = new UpdateCustomerInvoiceItemsPresenter();
}
public toDTO(invoice: CustomerInvoice): UpdateCustomerInvoiceByIdResponseDTO {
const items = this._itemsPresenter.toDTO(invoice);
return {
id: invoice.id.toPrimitive(),
company_id: invoice.companyId.toPrimitive(),
invoice_number: invoice.invoiceNumber.toString(),
status: invoice.status.toPrimitive(),
series: invoice.series.toString(),
invoice_date: invoice.invoiceDate.toDateString(),
operation_date: toEmptyString(invoice.operationDate, (value) => value.toDateString()),
notes: toEmptyString(invoice.notes, (value) => value.toPrimitive()),
language_code: invoice.languageCode.toPrimitive(),
currency_code: invoice.currencyCode.toPrimitive(),
subtotal_amount: {
value: invoice.subtotalAmount.value.toString(),
scale: invoice.subtotalAmount.scale.toString(),
},
discount_percentage: {
value: invoice.discountPercentage.value.toString(),
scale: invoice.discountPercentage.scale.toString(),
},
discount_amount: {
value: invoice.discountAmount.value.toString(),
scale: invoice.discountAmount.scale.toString(),
},
taxable_amount: {
value: invoice.taxableAmount.value.toString(),
scale: invoice.taxableAmount.scale.toString(),
},
tax_amount: {
value: invoice.taxAmount.value.toString(),
scale: invoice.taxAmount.scale.toString(),
},
total_amount: {
value: invoice.totalAmount.value.toString(),
scale: invoice.totalAmount.scale.toString(),
},
items,
metadata: {
entity: "customer-invoices",
},
//subtotal: customerInvoice.calculateSubtotal().toPrimitive(),
//total: customerInvoice.calculateTotal().toPrimitive(),
/*items:
customerInvoice.items.size() > 0
? customerInvoice.items.map((item: CustomerInvoiceItem) => ({
description: item.description.toString(),
quantity: item.quantity.toPrimitive(),
unit_measure: "",
unit_price: item.unitPrice.toPrimitive(),
subtotal: item.calculateSubtotal().toPrimitive(),
//tax_amount: item.calculateTaxAmount().toPrimitive(),
total: item.calculateTotal().toPrimitive(),
}))
: [],*/
//sender: {}, //await CustomerInvoiceParticipantPresenter(customerInvoice.senderId, context),
/*recipient: await CustomerInvoiceParticipantPresenter(customerInvoice.recipient, context),
items: customerInvoiceItemPresenter(customerInvoice.items, context),
payment_term: {
payment_type: "",
due_date: "",
},
due_amount: {
currency: customerInvoice.currency.toString(),
precision: 2,
amount: 0,
},
custom_fields: [],
metadata: {
create_time: "",
last_updated_time: "",
delete_time: "",
},*/
};
}
}

View File

@ -3,8 +3,8 @@ import { DuplicateEntityError, ITransactionManager } from "@erp/core/api";
import { UniqueID } from "@repo/rdx-ddd"; import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import { Transaction } from "sequelize"; import { Transaction } from "sequelize";
import { CreateCustomerInvoiceRequestDTO } from "../../../common/dto"; import { CreateCustomerInvoiceRequestDTO } from "../../../../common/dto";
import { CustomerInvoiceService } from "../../domain"; import { CustomerInvoiceService } from "../../../domain";
import { CreateCustomerInvoiceAssembler } from "./assembler"; import { CreateCustomerInvoiceAssembler } from "./assembler";
import { CreateCustomerInvoicePropsMapper } from "./map-dto-to-create-customer-invoice-props"; import { CreateCustomerInvoicePropsMapper } from "./map-dto-to-create-customer-invoice-props";
@ -18,8 +18,7 @@ export class CreateCustomerInvoiceUseCase {
private readonly service: CustomerInvoiceService, private readonly service: CustomerInvoiceService,
private readonly transactionManager: ITransactionManager, private readonly transactionManager: ITransactionManager,
private readonly assembler: CreateCustomerInvoiceAssembler, private readonly assembler: CreateCustomerInvoiceAssembler,
private readonly taxCatalog: JsonTaxCatalogProvider, private readonly taxCatalog: JsonTaxCatalogProvider
) {} ) {}
public execute(params: CreateCustomerInvoiceUseCaseInput) { public execute(params: CreateCustomerInvoiceUseCaseInput) {

View File

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

View File

@ -20,7 +20,7 @@ import { Result } from "@repo/rdx-utils";
import { import {
CreateCustomerInvoiceItemRequestDTO, CreateCustomerInvoiceItemRequestDTO,
CreateCustomerInvoiceRequestDTO, CreateCustomerInvoiceRequestDTO,
} from "../../../common/dto"; } from "../../../../common/dto";
import { import {
CustomerInvoiceItem, CustomerInvoiceItem,
CustomerInvoiceItemDescription, CustomerInvoiceItemDescription,
@ -33,7 +33,7 @@ import {
ItemAmount, ItemAmount,
ItemDiscount, ItemDiscount,
ItemQuantity, ItemQuantity,
} from "../../domain"; } from "../../../domain";
/** /**
* Convierte el DTO a las props validadas (CustomerProps). * Convierte el DTO a las props validadas (CustomerProps).
@ -51,7 +51,7 @@ export class CreateCustomerInvoicePropsMapper {
private languageCode?: LanguageCode; private languageCode?: LanguageCode;
private currencyCode?: CurrencyCode; private currencyCode?: CurrencyCode;
constructor(params: {taxCatalog: JsonTaxCatalogProvider}) { constructor(params: { taxCatalog: JsonTaxCatalogProvider }) {
this.taxCatalog = params.taxCatalog; this.taxCatalog = params.taxCatalog;
this.errors = []; this.errors = [];
} }
@ -63,8 +63,16 @@ export class CreateCustomerInvoicePropsMapper {
const defaultStatus = CustomerInvoiceStatus.createDraft(); const defaultStatus = CustomerInvoiceStatus.createDraft();
const invoiceId = extractOrPushError(UniqueID.create(dto.id), "id", this.errors); const invoiceId = extractOrPushError(UniqueID.create(dto.id), "id", this.errors);
const companyId = extractOrPushError(UniqueID.create(dto.company_id), "company_id", this.errors); const companyId = extractOrPushError(
const customerId = extractOrPushError(UniqueID.create(dto.customer_id), "customer_id", this.errors); UniqueID.create(dto.company_id),
"company_id",
this.errors
);
const customerId = extractOrPushError(
UniqueID.create(dto.customer_id),
"customer_id",
this.errors
);
const invoiceNumber = extractOrPushError( const invoiceNumber = extractOrPushError(
CustomerInvoiceNumber.create(dto.invoice_number), CustomerInvoiceNumber.create(dto.invoice_number),
@ -230,4 +238,3 @@ export class CreateCustomerInvoicePropsMapper {
return taxes; return taxes;
} }
} }

View File

@ -2,7 +2,7 @@ import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
import { UniqueID } from "@repo/rdx-ddd"; import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import { CustomerInvoiceService } from "../../domain"; import { CustomerInvoiceService } from "../../domain";
import { CustomerInvoiceFullPresenter } from "../presenters/full-domain"; import { CustomerInvoiceFullPresenter } from "../presenters/domain";
type GetCustomerInvoiceUseCaseInput = { type GetCustomerInvoiceUseCaseInput = {
companyId: UniqueID; companyId: UniqueID;

View File

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

View File

@ -5,7 +5,7 @@ import { Result } from "@repo/rdx-utils";
import { Transaction } from "sequelize"; import { Transaction } from "sequelize";
import { CustomerInvoiceListResponseDTO } from "../../../common/dto"; import { CustomerInvoiceListResponseDTO } from "../../../common/dto";
import { CustomerInvoiceService } from "../../domain"; import { CustomerInvoiceService } from "../../domain";
import { CustomerInvoicesListPresenter } from "../presenters/list"; import { ListCustomerInvoicesPresenter } from "../presenters";
type ListCustomerInvoicesUseCaseInput = { type ListCustomerInvoicesUseCaseInput = {
companyId: UniqueID; companyId: UniqueID;
@ -26,7 +26,7 @@ export class ListCustomerInvoicesUseCase {
const presenter = this.presenterRegistry.getPresenter({ const presenter = this.presenterRegistry.getPresenter({
resource: "customer-invoice", resource: "customer-invoice",
projection: "LIST", projection: "LIST",
}) as CustomerInvoicesListPresenter; }) as ListCustomerInvoicesPresenter;
return this.transactionManager.complete(async (transaction: Transaction) => { return this.transactionManager.complete(async (transaction: Transaction) => {
try { try {

View File

@ -0,0 +1,54 @@
import { EntityNotFoundError, ITransactionManager } from "@erp/core/api";
import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { CustomerInvoiceService } from "../../../domain";
type DeleteCustomerInvoiceUseCaseInput = {
companyId: UniqueID;
invoice_id: string;
};
export class DeleteCustomerInvoiceUseCase {
constructor(
private readonly service: CustomerInvoiceService,
private readonly transactionManager: ITransactionManager
) {}
public execute(params: DeleteCustomerInvoiceUseCaseInput) {
const { invoice_id, companyId } = params;
const idOrError = UniqueID.create(invoice_id);
if (idOrError.isFailure) {
return Result.fail(idOrError.error);
}
const invoiceId = idOrError.data;
return this.transactionManager.complete(async (transaction) => {
try {
const existsCheck = await this.service.existsByIdInCompany(
companyId,
invoiceId,
transaction
);
if (existsCheck.isFailure) {
return Result.fail(existsCheck.error);
}
const invoiceExists = existsCheck.data;
if (!invoiceExists) {
return Result.fail(
new EntityNotFoundError("Customer invoice", "id", invoiceId.toString())
);
}
return await this.service.deleteInvoiceByIdInCompany(companyId, invoiceId, transaction);
} catch (error: unknown) {
return Result.fail(error as Error);
}
});
}
}

View File

@ -1,7 +1,7 @@
import { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
import { UniqueID } from "@repo/rdx-ddd"; import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import { CustomerInvoiceService } from "../../domain"; import { CustomerInvoiceService } from "../../../domain";
import { CustomerInvoiceReportPDFPresenter } from "./reporter"; import { CustomerInvoiceReportPDFPresenter } from "./reporter";
type ReportCustomerInvoiceUseCaseInput = { type ReportCustomerInvoiceUseCaseInput = {

View File

@ -2,7 +2,7 @@ import { Presenter } from "@erp/core/api";
import * as handlebars from "handlebars"; import * as handlebars from "handlebars";
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import path from "node:path"; import path from "node:path";
import { CustomerInvoice } from "../../../domain"; import { CustomerInvoice } from "../../../../domain";
export class CustomerInvoiceReportHTMLPresenter extends Presenter { export class CustomerInvoiceReportHTMLPresenter extends Presenter {
toOutput(customerInvoice: CustomerInvoice): string { toOutput(customerInvoice: CustomerInvoice): string {

View File

@ -1,7 +1,7 @@
import { Presenter } from "@erp/core/api"; import { Presenter } from "@erp/core/api";
import puppeteer from "puppeteer"; import puppeteer from "puppeteer";
import report from "puppeteer-report"; import report from "puppeteer-report";
import { CustomerInvoice } from "../../../domain"; import { CustomerInvoice } from "../../../../domain";
import { CustomerInvoiceReportHTMLPresenter } from "./customer-invoice.report.html"; import { CustomerInvoiceReportHTMLPresenter } from "./customer-invoice.report.html";
// https://plnkr.co/edit/lWk6Yd?preview // https://plnkr.co/edit/lWk6Yd?preview

View File

@ -1,28 +1,31 @@
import { JsonTaxCatalogProvider, spainTaxCatalogProvider } from "@erp/core"; // modules/invoice/infrastructure/invoice-dependencies.factory.ts
import type { IMapperRegistry, IPresenterRegistry, ModuleParams } from "@erp/core/api"; import type { IMapperRegistry, IPresenterRegistry, ModuleParams } from "@erp/core/api";
import { import {
InMemoryMapperRegistry, InMemoryMapperRegistry,
InMemoryPresenterRegistry, InMemoryPresenterRegistry,
SequelizeTransactionManager, SequelizeTransactionManager,
} from "@erp/core/api"; } from "@erp/core/api";
import { import {
CreateCustomerInvoiceUseCase, CreateCustomerInvoiceUseCase,
CustomerInvoiceFullPresenter, CustomerInvoiceFullPresenter,
CustomerInvoiceItemsFullPresenter, CustomerInvoiceItemsFullPresenter,
CustomerInvoiceReportHTMLPresenter, CustomerInvoiceReportHTMLPresenter,
CustomerInvoiceReportPDFPresenter, CustomerInvoiceReportPDFPresenter,
DeleteCustomerInvoiceUseCase,
GetCustomerInvoiceUseCase, GetCustomerInvoiceUseCase,
ListCustomerInvoicesPresenter, ListCustomerInvoicesPresenter,
ListCustomerInvoicesUseCase, ListCustomerInvoicesUseCase,
ReportCustomerInvoiceUseCase, ReportCustomerInvoiceUseCase,
} from "../application"; } from "../application";
import { JsonTaxCatalogProvider, spainTaxCatalogProvider } from "@erp/core";
import { CustomerInvoiceService } from "../domain"; import { CustomerInvoiceService } from "../domain";
import { CustomerInvoiceFullMapper, CustomerInvoiceListMapper } from "./mappers"; import { CustomerInvoiceDomainMapper, CustomerInvoiceListMapper } from "./mappers";
import { CustomerInvoiceRepository } from "./sequelize"; import { CustomerInvoiceRepository } from "./sequelize";
type InvoiceDeps = { export type CustomerInvoiceDeps = {
transactionManager: SequelizeTransactionManager; transactionManager: SequelizeTransactionManager;
mapperRegistry: IMapperRegistry; mapperRegistry: IMapperRegistry;
presenterRegistry: IPresenterRegistry; presenterRegistry: IPresenterRegistry;
@ -36,111 +39,97 @@ type InvoiceDeps = {
get: () => GetCustomerInvoiceUseCase; get: () => GetCustomerInvoiceUseCase;
create: () => CreateCustomerInvoiceUseCase; create: () => CreateCustomerInvoiceUseCase;
//update: () => UpdateCustomerInvoiceUseCase; //update: () => UpdateCustomerInvoiceUseCase;
delete: () => DeleteCustomerInvoiceUseCase; //delete: () => DeleteCustomerInvoiceUseCase;
report: () => ReportCustomerInvoiceUseCase; report: () => ReportCustomerInvoiceUseCase;
}; };
}; };
let _presenterRegistry: IPresenterRegistry | null = null; export function buildCustomerInvoiceDependencies(params: ModuleParams): CustomerInvoiceDeps {
let _mapperRegistry: IMapperRegistry | null = null;
let _repo: CustomerInvoiceRepository | null = null;
let _service: CustomerInvoiceService | null = null;
let _catalogs: InvoiceDeps["catalogs"] | null = null;
export function getInvoiceDependencies(params: ModuleParams): InvoiceDeps {
const { database } = params; const { database } = params;
const transactionManager = new SequelizeTransactionManager(database); const transactionManager = new SequelizeTransactionManager(database);
if (!_catalogs) _catalogs = { taxes: spainTaxCatalogProvider }; const catalogs = { taxes: spainTaxCatalogProvider };
if (!_mapperRegistry) { // Mapper Registry
_mapperRegistry = new InMemoryMapperRegistry(); const mapperRegistry = new InMemoryMapperRegistry();
_mapperRegistry.registerDomainMapper( mapperRegistry
"FULL", .registerDomainMapper(
new CustomerInvoiceFullMapper({ { resource: "customer-invoice" },
taxCatalog: _catalogs!.taxes, new CustomerInvoiceDomainMapper({ taxCatalog: catalogs.taxes })
}) )
); .registerQueryMappers([
_mapperRegistry.registerReadModelMapper("LIST", new CustomerInvoiceListMapper());
}
if (!_repo) _repo = new CustomerInvoiceRepository({ mapperRegistry: _mapperRegistry, database });
if (!_service) _service = new CustomerInvoiceService(_repo);
if (!_presenterRegistry) {
_presenterRegistry = new InMemoryPresenterRegistry();
_presenterRegistry.registerPresenters([
{ {
key: { key: { resource: "customer-invoice", query: "LIST" },
resource: "customer-invoice-items", mapper: new CustomerInvoiceListMapper(),
projection: "FULL",
},
presenter: new CustomerInvoiceItemsFullPresenter(_presenterRegistry),
},
{
key: {
resource: "customer-invoice",
projection: "FULL",
},
presenter: new CustomerInvoiceFullPresenter(_presenterRegistry),
},
{
key: {
resource: "customer-invoice",
projection: "LIST",
},
presenter: new ListCustomerInvoicesPresenter(_presenterRegistry),
},
{
key: {
resource: "customer-invoice",
projection: "REPORT",
format: "HTML",
},
presenter: new CustomerInvoiceReportHTMLPresenter(_presenterRegistry),
},
{
key: {
resource: "customer-invoice",
projection: "REPORT",
format: "PDF",
},
presenter: new CustomerInvoiceReportPDFPresenter(_presenterRegistry),
}, },
]); ]);
}
// Repository & Services
const repo = new CustomerInvoiceRepository({ mapperRegistry, database });
const service = new CustomerInvoiceService(repo);
// Presenter Registry
const presenterRegistry = new InMemoryPresenterRegistry();
presenterRegistry.registerPresenters([
{
key: {
resource: "customer-invoice-items",
projection: "FULL",
},
presenter: new CustomerInvoiceItemsFullPresenter(presenterRegistry),
},
{
key: {
resource: "customer-invoice",
projection: "FULL",
},
presenter: new CustomerInvoiceFullPresenter(presenterRegistry),
},
{
key: {
resource: "customer-invoice",
projection: "LIST",
},
presenter: new ListCustomerInvoicesPresenter(presenterRegistry),
},
{
key: {
resource: "customer-invoice",
projection: "REPORT",
format: "HTML",
},
presenter: new CustomerInvoiceReportHTMLPresenter(presenterRegistry),
},
{
key: {
resource: "customer-invoice",
projection: "REPORT",
format: "PDF",
},
presenter: new CustomerInvoiceReportPDFPresenter(presenterRegistry),
},
]);
return { return {
transactionManager, transactionManager,
repo: _repo, repo,
mapperRegistry: _mapperRegistry, mapperRegistry,
presenterRegistry: _presenterRegistry, presenterRegistry,
service: _service, service,
catalogs: _catalogs, catalogs,
build: { build: {
list: () => list: () => new ListCustomerInvoicesUseCase(service, transactionManager, presenterRegistry),
new ListCustomerInvoicesUseCase(_service!, transactionManager!, _presenterRegistry!), get: () => new GetCustomerInvoiceUseCase(service, transactionManager, presenterRegistry),
get: () => new GetCustomerInvoiceUseCase(_service!, transactionManager!, _presenterRegistry!),
create: () => create: () =>
new CreateCustomerInvoiceUseCase( new CreateCustomerInvoiceUseCase(
_service!, service,
transactionManager!, transactionManager,
_presenterRegistry!, presenterRegistry,
_catalogs!.taxes catalogs.taxes
), ),
/*update: () => // update: () => new UpdateCustomerInvoiceUseCase(service, transactionManager),
new UpdateCustomerInvoiceUseCase( // delete: () => new DeleteCustomerInvoiceUseCase(service, transactionManager),
_service!,
transactionManager!,
_presenterRegistry!,
_catalogs!.taxes
),*/
delete: () => new DeleteCustomerInvoiceUseCase(_service!, transactionManager!),
report: () => report: () =>
new ReportCustomerInvoiceUseCase(_service!, transactionManager!, _presenterRegistry!), new ReportCustomerInvoiceUseCase(service, transactionManager, presenterRegistry),
}, },
}; };
} }

View File

@ -5,28 +5,26 @@ import { Sequelize } from "sequelize";
import { import {
CreateCustomerInvoiceRequestSchema, CreateCustomerInvoiceRequestSchema,
CustomerInvoiceListRequestSchema, CustomerInvoiceListRequestSchema,
DeleteCustomerInvoiceByIdRequestSchema,
GetCustomerInvoiceByIdRequestSchema, GetCustomerInvoiceByIdRequestSchema,
ReportCustomerInvoiceByIdRequestSchema, ReportCustomerInvoiceByIdRequestSchema,
} from "../../../common/dto"; } from "../../../common/dto";
import { getInvoiceDependencies } from "../dependencies"; import { buildCustomerInvoiceDependencies } from "../dependencies";
import { import {
CreateCustomerInvoiceController, CreateCustomerInvoiceController,
DeleteCustomerInvoiceController,
GetCustomerInvoiceController, GetCustomerInvoiceController,
ListCustomerInvoicesController, ListCustomerInvoicesController,
ReportCustomerInvoiceController, ReportCustomerInvoiceController,
} from "./controllers"; } from "./controllers";
export const customerInvoicesRouter = (params: ModuleParams) => { export const customerInvoicesRouter = (params: ModuleParams) => {
const { app, database, baseRoutePath, logger } = params as { const { app, baseRoutePath, logger } = params as {
app: Application; app: Application;
database: Sequelize; database: Sequelize;
baseRoutePath: string; baseRoutePath: string;
logger: ILogger; logger: ILogger;
}; };
const deps = getInvoiceDependencies(params); const deps = buildCustomerInvoiceDependencies(params);
const router: Router = Router({ mergeParams: true }); const router: Router = Router({ mergeParams: true });
@ -94,7 +92,7 @@ export const customerInvoicesRouter = (params: ModuleParams) => {
} }
);*/ );*/
router.delete( /*router.delete(
"/:invoice_id", "/:invoice_id",
//checkTabContext, //checkTabContext,
@ -104,7 +102,7 @@ export const customerInvoicesRouter = (params: ModuleParams) => {
const controller = new DeleteCustomerInvoiceController(useCase); const controller = new DeleteCustomerInvoiceController(useCase);
return controller.execute(req, res, next); return controller.execute(req, res, next);
} }
); );*/
router.get( router.get(
"/:invoice_id/report", "/:invoice_id/report",

View File

@ -19,28 +19,28 @@ import {
ItemTaxes, ItemTaxes,
} from "../../../domain"; } from "../../../domain";
import { CustomerInvoiceItemCreationAttributes, CustomerInvoiceItemModel } from "../../sequelize"; import { CustomerInvoiceItemCreationAttributes, CustomerInvoiceItemModel } from "../../sequelize";
import { ItemTaxesMapper } from "./item-taxes.full.mapper"; import { ItemTaxesDomainMapper } from "./item-taxes.mapper";
export interface ICustomerInvoiceItemMapper export interface ICustomerInvoiceItemDomainMapper
extends ISequelizeDomainMapper< extends ISequelizeDomainMapper<
CustomerInvoiceItemModel, CustomerInvoiceItemModel,
CustomerInvoiceItemCreationAttributes, CustomerInvoiceItemCreationAttributes,
CustomerInvoiceItem CustomerInvoiceItem
> {} > {}
export class CustomerInvoiceItemMapper export class CustomerInvoiceItemDomainMapper
extends SequelizeDomainMapper< extends SequelizeDomainMapper<
CustomerInvoiceItemModel, CustomerInvoiceItemModel,
CustomerInvoiceItemCreationAttributes, CustomerInvoiceItemCreationAttributes,
CustomerInvoiceItem CustomerInvoiceItem
> >
implements ICustomerInvoiceItemMapper implements ICustomerInvoiceItemDomainMapper
{ {
private _taxesMapper: ItemTaxesMapper; private _taxesMapper: ItemTaxesDomainMapper;
constructor(params: MapperParamsType) { constructor(params: MapperParamsType) {
super(); super();
this._taxesMapper = new ItemTaxesMapper(params); this._taxesMapper = new ItemTaxesDomainMapper(params);
} }
private mapAttributesToDomain(source: CustomerInvoiceItemModel, params?: MapperParamsType) { private mapAttributesToDomain(source: CustomerInvoiceItemModel, params?: MapperParamsType) {

View File

@ -26,24 +26,24 @@ import {
} from "../../../domain"; } from "../../../domain";
import { InvoiceTaxes } from "../../../domain/entities/invoice-taxes"; import { InvoiceTaxes } from "../../../domain/entities/invoice-taxes";
import { CustomerInvoiceCreationAttributes, CustomerInvoiceModel } from "../../sequelize"; import { CustomerInvoiceCreationAttributes, CustomerInvoiceModel } from "../../sequelize";
import { CustomerInvoiceItemMapper as CustomerInvoiceItemFullMapper } from "./customer-invoice-item.full.mapper"; import { CustomerInvoiceItemDomainMapper as CustomerInvoiceItemFullMapper } from "./customer-invoice-item.mapper";
import { InvoiceRecipientMapper as InvoiceRecipientFullMapper } from "./invoice-recipient.full.mapper"; import { InvoiceRecipientDomainMapper as InvoiceRecipientFullMapper } from "./invoice-recipient.mapper";
import { TaxesMapper as TaxesFullMapper } from "./taxes.full.mapper"; import { TaxesDomainMapper as TaxesFullMapper } from "./taxes.mapper";
export interface ICustomerInvoiceFullMapper export interface ICustomerInvoiceDomainMapper
extends ISequelizeDomainMapper< extends ISequelizeDomainMapper<
CustomerInvoiceModel, CustomerInvoiceModel,
CustomerInvoiceCreationAttributes, CustomerInvoiceCreationAttributes,
CustomerInvoice CustomerInvoice
> {} > {}
export class CustomerInvoiceFullMapper export class CustomerInvoiceDomainMapper
extends SequelizeDomainMapper< extends SequelizeDomainMapper<
CustomerInvoiceModel, CustomerInvoiceModel,
CustomerInvoiceCreationAttributes, CustomerInvoiceCreationAttributes,
CustomerInvoice CustomerInvoice
> >
implements ICustomerInvoiceFullMapper implements ICustomerInvoiceDomainMapper
{ {
private _itemsMapper: CustomerInvoiceItemFullMapper; private _itemsMapper: CustomerInvoiceItemFullMapper;
private _recipientMapper: InvoiceRecipientFullMapper; private _recipientMapper: InvoiceRecipientFullMapper;

View File

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

View File

@ -19,7 +19,7 @@ import { Maybe, Result } from "@repo/rdx-utils";
import { CustomerInvoiceProps, InvoiceRecipient } from "../../../domain"; import { CustomerInvoiceProps, InvoiceRecipient } from "../../../domain";
import { CustomerInvoiceModel } from "../../sequelize"; import { CustomerInvoiceModel } from "../../sequelize";
export class InvoiceRecipientMapper { export class InvoiceRecipientDomainMapper {
public mapToDomain( public mapToDomain(
source: CustomerInvoiceModel, source: CustomerInvoiceModel,
params?: MapperParamsType params?: MapperParamsType

View File

@ -14,7 +14,7 @@ import {
CustomerInvoiceItemTaxModel, CustomerInvoiceItemTaxModel,
} from "../../sequelize"; } from "../../sequelize";
export class ItemTaxesMapper extends SequelizeDomainMapper< export class ItemTaxesDomainMapper extends SequelizeDomainMapper<
CustomerInvoiceItemTaxModel, CustomerInvoiceItemTaxModel,
CustomerInvoiceItemCreationAttributes, CustomerInvoiceItemCreationAttributes,
ItemTax ItemTax
@ -62,10 +62,10 @@ export class ItemTaxesMapper extends SequelizeDomainMapper<
return createResult; return createResult;
} }
public mapToPersistence( /*public mapToPersistence(
source: ItemTax, source: ItemTax,
params?: MapperParamsType params?: MapperParamsType
): CustomerInvoiceItemCreationAttributes { ): CustomerInvoiceItemCreationAttributes {
throw new Error("not implemented"); throw new Error("not implemented");
} }*/
} }

View File

@ -12,7 +12,7 @@ import { CustomerInvoiceProps } from "../../../domain";
import { InvoiceTax } from "../../../domain/entities/invoice-taxes"; import { InvoiceTax } from "../../../domain/entities/invoice-taxes";
import { CustomerInvoiceTaxCreationAttributes, CustomerInvoiceTaxModel } from "../../sequelize"; import { CustomerInvoiceTaxCreationAttributes, CustomerInvoiceTaxModel } from "../../sequelize";
export class TaxesMapper extends SequelizeDomainMapper< export class TaxesDomainMapper extends SequelizeDomainMapper<
CustomerInvoiceTaxModel, CustomerInvoiceTaxModel,
CustomerInvoiceTaxCreationAttributes, CustomerInvoiceTaxCreationAttributes,
InvoiceTax InvoiceTax
@ -64,10 +64,10 @@ export class TaxesMapper extends SequelizeDomainMapper<
return createResult; return createResult;
} }
public mapToPersistence( /*public mapToPersistence(
source: InvoiceTax, source: InvoiceTax,
params?: MapperParamsType params?: MapperParamsType
): CustomerInvoiceTaxCreationAttributes { ): CustomerInvoiceTaxCreationAttributes {
throw new Error("not implemented"); throw new Error("not implemented");
} }*/
} }

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import { import {
ISequelizeReadModelMapper, ISequelizeQueryMapper,
MapperParamsType, MapperParamsType,
SequelizeReadModelMapper, SequelizeQueryMapper,
ValidationErrorCollection, ValidationErrorCollection,
ValidationErrorDetail, ValidationErrorDetail,
extractOrPushError, extractOrPushError,
@ -56,10 +56,10 @@ export type CustomerInvoiceListDTO = {
}; };
export interface ICustomerInvoiceListMapper export interface ICustomerInvoiceListMapper
extends ISequelizeReadModelMapper<CustomerInvoiceModel, CustomerInvoiceListDTO> {} extends ISequelizeQueryMapper<CustomerInvoiceModel, CustomerInvoiceListDTO> {}
export class CustomerInvoiceListMapper export class CustomerInvoiceListMapper
extends SequelizeReadModelMapper<CustomerInvoiceModel, CustomerInvoiceListDTO> extends SequelizeQueryMapper<CustomerInvoiceModel, CustomerInvoiceListDTO>
implements ICustomerInvoiceListMapper implements ICustomerInvoiceListMapper
{ {
private _recipientMapper: InvoiceRecipientListMapper; private _recipientMapper: InvoiceRecipientListMapper;

View File

@ -10,9 +10,9 @@ import {
} from "@repo/rdx-ddd"; } from "@repo/rdx-ddd";
import { import {
IReadModelMapperWithBulk, IQueryMapperWithBulk,
MapperParamsType, MapperParamsType,
SequelizeReadModelMapper, SequelizeQueryMapper,
ValidationErrorDetail, ValidationErrorDetail,
extractOrPushError, extractOrPushError,
} from "@erp/core/api"; } from "@erp/core/api";
@ -23,10 +23,10 @@ import { CustomerInvoiceModel } from "../../sequelize";
import { CustomerInvoiceListDTO } from "./customer-invoice.list.mapper"; import { CustomerInvoiceListDTO } from "./customer-invoice.list.mapper";
interface IInvoiceRecipientListMapper interface IInvoiceRecipientListMapper
extends IReadModelMapperWithBulk<CustomerInvoiceModel, InvoiceRecipient> {} extends IQueryMapperWithBulk<CustomerInvoiceModel, InvoiceRecipient> {}
export class InvoiceRecipientListMapper export class InvoiceRecipientListMapper
extends SequelizeReadModelMapper<CustomerInvoiceModel, InvoiceRecipient> extends SequelizeQueryMapper<CustomerInvoiceModel, InvoiceRecipient>
implements IInvoiceRecipientListMapper implements IInvoiceRecipientListMapper
{ {
public mapToDTO( public mapToDTO(

View File

@ -1,37 +1,23 @@
import { import { EntityNotFoundError, SequelizeRepository, translateSequelizeError } from "@erp/core/api";
EntityNotFoundError,
IMapperRegistry,
SequelizeRepository,
translateSequelizeError,
} from "@erp/core/api";
import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server"; import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server";
import { UniqueID } from "@repo/rdx-ddd"; import { UniqueID } from "@repo/rdx-ddd";
import { Collection, Result } from "@repo/rdx-utils"; import { Collection, Result } from "@repo/rdx-utils";
import { Sequelize, Transaction } from "sequelize"; import { Transaction } from "sequelize";
import { CustomerInvoice, ICustomerInvoiceRepository } from "../../domain"; import { CustomerInvoice, ICustomerInvoiceRepository } from "../../domain";
import { import {
CustomerInvoiceListDTO, CustomerInvoiceListDTO,
ICustomerInvoiceFullMapper, ICustomerInvoiceDomainMapper,
ICustomerInvoiceListMapper, ICustomerInvoiceListMapper,
} from "../mappers"; } from "../mappers";
import { CustomerInvoiceItemTaxModel } from "./customer-invoice-item-tax.model"; import { CustomerInvoiceItemTaxModel } from "./models/customer-invoice-item-tax.model";
import { CustomerInvoiceItemModel } from "./customer-invoice-item.model"; import { CustomerInvoiceItemModel } from "./models/customer-invoice-item.model";
import { CustomerInvoiceTaxModel } from "./customer-invoice-tax.model"; import { CustomerInvoiceTaxModel } from "./models/customer-invoice-tax.model";
import { CustomerInvoiceModel } from "./customer-invoice.model"; import { CustomerInvoiceModel } from "./models/customer-invoice.model";
export class CustomerInvoiceRepository export class CustomerInvoiceRepository
extends SequelizeRepository<CustomerInvoice> extends SequelizeRepository<CustomerInvoice>
implements ICustomerInvoiceRepository implements ICustomerInvoiceRepository
{ {
private readonly _database!: Sequelize;
private readonly _registry!: IMapperRegistry;
constructor(params: { mapperRegistry: IMapperRegistry; database: Sequelize }) {
super();
this._registry = params.mapperRegistry;
this._database = params.database;
}
// Listado por tenant con criteria saneada // Listado por tenant con criteria saneada
/* async searchInCompany(criteria: any, companyId: string): Promise<{ /* async searchInCompany(criteria: any, companyId: string): Promise<{
rows: InvoiceListRow[]; rows: InvoiceListRow[];
@ -78,7 +64,9 @@ export class CustomerInvoiceRepository
transaction: Transaction transaction: Transaction
): Promise<Result<CustomerInvoice, Error>> { ): Promise<Result<CustomerInvoice, Error>> {
try { try {
const mapper: ICustomerInvoiceFullMapper = this._registry.getDomainMapper("FULL"); const mapper: ICustomerInvoiceDomainMapper = this._registry.getDomainMapper({
resource: "customer-invoice",
});
const mapperData = mapper.mapToPersistence(invoice); const mapperData = mapper.mapToPersistence(invoice);
if (mapperData.isFailure) { if (mapperData.isFailure) {
@ -134,7 +122,7 @@ export class CustomerInvoiceRepository
transaction: Transaction transaction: Transaction
): Promise<Result<CustomerInvoice, Error>> { ): Promise<Result<CustomerInvoice, Error>> {
try { try {
const mapper: ICustomerInvoiceFullMapper = this._registry.getDomainMapper("FULL"); const mapper: ICustomerInvoiceDomainMapper = this._registry.getDomainMapper("FULL");
const { CustomerModel } = this._database.models; const { CustomerModel } = this._database.models;
const row = await CustomerInvoiceModel.findOne({ const row = await CustomerInvoiceModel.findOne({
@ -194,7 +182,7 @@ export class CustomerInvoiceRepository
transaction: Transaction transaction: Transaction
): Promise<Result<Collection<CustomerInvoiceListDTO>, Error>> { ): Promise<Result<Collection<CustomerInvoiceListDTO>, Error>> {
try { try {
const mapper: ICustomerInvoiceListMapper = this._registry.getReadModelMapper("LIST"); const mapper: ICustomerInvoiceListMapper = this._registry.getQueryMapper("LIST");
const { CustomerModel } = this._database.models; const { CustomerModel } = this._database.models;
const converter = new CriteriaToSequelizeConverter(); const converter = new CriteriaToSequelizeConverter();
const query = converter.convert(criteria); const query = converter.convert(criteria);

View File

@ -1,13 +1,10 @@
import customerInvoiceItemTaxesModelInit from "./customer-invoice-item-tax.model"; import customerInvoiceItemTaxesModelInit from "./models/customer-invoice-item-tax.model";
import customerInvoiceItemModelInit from "./customer-invoice-item.model"; import customerInvoiceItemModelInit from "./models/customer-invoice-item.model";
import customerInvoiceTaxesModelInit from "./customer-invoice-tax.model"; import customerInvoiceTaxesModelInit from "./models/customer-invoice-tax.model";
import customerInvoiceModelInit from "./customer-invoice.model"; import customerInvoiceModelInit from "./models/customer-invoice.model";
export * from "./customer-invoice-item-tax.model";
export * from "./customer-invoice-item.model";
export * from "./customer-invoice-tax.model";
export * from "./customer-invoice.model";
export * from "./customer-invoice.repository"; export * from "./customer-invoice.repository";
export * from "./models";
// Array de inicializadores para que registerModels() lo use // Array de inicializadores para que registerModels() lo use
export const models = [ export const models = [

View File

@ -6,7 +6,7 @@ import {
NonAttribute, NonAttribute,
Sequelize, Sequelize,
} from "sequelize"; } from "sequelize";
import { CustomerInvoiceItem } from "../../domain"; import { CustomerInvoiceItem } from "../../../domain";
export type CustomerInvoiceItemTaxCreationAttributes = InferCreationAttributes< export type CustomerInvoiceItemTaxCreationAttributes = InferCreationAttributes<
CustomerInvoiceItemTaxModel, CustomerInvoiceItemTaxModel,

View File

@ -6,7 +6,7 @@ import {
NonAttribute, NonAttribute,
Sequelize, Sequelize,
} from "sequelize"; } from "sequelize";
import { CustomerInvoice } from "../../domain"; import { CustomerInvoice } from "../../../domain";
export type CustomerInvoiceTaxCreationAttributes = InferCreationAttributes< export type CustomerInvoiceTaxCreationAttributes = InferCreationAttributes<
CustomerInvoiceTaxModel, CustomerInvoiceTaxModel,

View File

@ -0,0 +1,4 @@
export * from "./customer-invoice-item-tax.model";
export * from "./customer-invoice-item.model";
export * from "./customer-invoice-tax.model";
export * from "./customer-invoice.model";

View File

@ -1,5 +1,9 @@
import type { IMapperRegistry, IPresenterRegistry, ModuleParams } from "@erp/core/api"; import type { IMapperRegistry, IPresenterRegistry, ModuleParams } from "@erp/core/api";
import { InMemoryMapperRegistry, SequelizeTransactionManager } from "@erp/core/api"; import {
InMemoryMapperRegistry,
InMemoryPresenterRegistry,
SequelizeTransactionManager,
} from "@erp/core/api";
import { import {
CreateCustomerUseCase, CreateCustomerUseCase,
@ -9,10 +13,10 @@ import {
UpdateCustomerUseCase, UpdateCustomerUseCase,
} from "../application"; } from "../application";
import { CustomerService } from "../domain"; import { CustomerService } from "../domain";
import { CustomerMapper } from "./mappers"; import { CustomerDomainMapper } from "./mappers";
import { CustomerRepository } from "./sequelize"; import { CustomerRepository } from "./sequelize";
type CustomerDeps = { export type CustomerDeps = {
transactionManager: SequelizeTransactionManager; transactionManager: SequelizeTransactionManager;
mapperRegistry: IMapperRegistry; mapperRegistry: IMapperRegistry;
presenterRegistry: IPresenterRegistry; presenterRegistry: IPresenterRegistry;
@ -27,46 +31,37 @@ type CustomerDeps = {
}; };
}; };
const _presenterRegistry: IPresenterRegistry | null = null; export function buildCustomerDependencies(params: ModuleParams): CustomerDeps {
let _mapperRegistry: IMapperRegistry | null = null;
let _repo: CustomerRepository | null = null;
let _service: CustomerService | null = null;
export function getCustomerDependencies(params: ModuleParams): CustomerDeps {
const { database } = params; const { database } = params;
const transactionManager = new SequelizeTransactionManager(database); const transactionManager = new SequelizeTransactionManager(database);
if (!_mapperRegistry) { // Mapper Registry
_mapperRegistry = new InMemoryMapperRegistry(); const mapperRegistry = new InMemoryMapperRegistry();
_mapperRegistry.registerDomainMapper("FULL", new CustomerMapper()); mapperRegistry.registerDomainMapper({ resource: "customer" }, new CustomerDomainMapper());
}
if (!_repo) _repo = new CustomerRepository({ mapperRegistry: _mapperRegistry }); // Repository & Services
if (!_service) _service = new CustomerService(_repo); const repo = new CustomerRepository({ mapperRegistry: _mapperRegistry, database });
const service = new CustomerService(repo);
/*if (!_presenterRegistry) { // Presenter Registry
_presenterRegistry = new InMemoryPresenterRegistry(); const presenterRegistry = new InMemoryPresenterRegistry();
presenterRegistry.registerPresenters([
_presenterRegistry.registerPresenters([ {
{ key: { resource: "customer", projection: "FULL" },
key: { resource: "customer", projection: "FULL" }, presenter: new ListCustomersAssembler(),
presenter: new ListCustomersAssembler(), },
}, {
{ key: { resource: "customer", projection: "LIST" },
key: { resource: "customer", projection: "LIST" }, presenter: new GetCustomerAssembler(),
presenter: new GetCustomerAssembler(), },
}, ]);
]);
}*/
return { return {
transactionManager, transactionManager,
repo: _repo, repo,
mapperRegistry: _mapperRegistry, mapperRegistry,
//presenterRegistry: _presenterRegistry, presenterRegistry,
service: _service, service,
build: { build: {
/*list: () => new ListCustomersUseCase(_service!, transactionManager!, presenterRegistry!), /*list: () => new ListCustomersUseCase(_service!, transactionManager!, presenterRegistry!),
get: () => new GetCustomerUseCase(_service!, transactionManager!, presenterRegistry!), get: () => new GetCustomerUseCase(_service!, transactionManager!, presenterRegistry!),

View File

@ -1,5 +1,5 @@
import { import {
ISequelizeMapper, ISequelizeDomainMapper,
MapperParamsType, MapperParamsType,
SequelizeDomainMapper, SequelizeDomainMapper,
ValidationErrorCollection, ValidationErrorCollection,
@ -27,15 +27,15 @@ import {
toNullable, toNullable,
} from "@repo/rdx-ddd"; } from "@repo/rdx-ddd";
import { Collection, Result, isNullishOrEmpty } from "@repo/rdx-utils"; import { Collection, Result, isNullishOrEmpty } from "@repo/rdx-utils";
import { Customer, CustomerProps, CustomerStatus } from "../../domain"; import { Customer, CustomerProps, CustomerStatus } from "../../../domain";
import { CustomerCreationAttributes, CustomerModel } from "../sequelize"; import { CustomerCreationAttributes, CustomerModel } from "../../sequelize";
export interface ICustomerMapper export interface ICustomerDomainMapper
extends ISequelizeMapper<CustomerModel, CustomerCreationAttributes, Customer> {} extends ISequelizeDomainMapper<CustomerModel, CustomerCreationAttributes, Customer> {}
export class CustomerMapper export class CustomerDomainMapper
extends SequelizeDomainMapper<CustomerModel, CustomerCreationAttributes, Customer> extends SequelizeDomainMapper<CustomerModel, CustomerCreationAttributes, Customer>
implements ICustomerMapper implements ICustomerDomainMapper
{ {
public mapToDomain(source: CustomerModel, params?: MapperParamsType): Result<Customer, Error> { public mapToDomain(source: CustomerModel, params?: MapperParamsType): Result<Customer, Error> {
try { try {

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import customerModelInit from "./customer.model"; import customerModelInit from "./models/customer.model";
export * from "./customer.model"; export * from "./models";
export * from "./customer.repository"; export * from "./repositories";
// Array de inicializadores para que registerModels() lo use // Array de inicializadores para que registerModels() lo use
export const models = [customerModelInit]; export const models = [customerModelInit];

View File

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

View File

@ -3,21 +3,13 @@ import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/serve
import { UniqueID } from "@repo/rdx-ddd"; import { UniqueID } from "@repo/rdx-ddd";
import { Collection, Result } from "@repo/rdx-utils"; import { Collection, Result } from "@repo/rdx-utils";
import { Transaction } from "sequelize"; import { Transaction } from "sequelize";
import { Customer, ICustomerRepository } from "../../domain"; import { Customer, ICustomerRepository } from "../../../domain";
import { ICustomerMapper } from "../mappers/customer.mapper"; import { CustomerModel } from "../models/customer.model";
import { CustomerModel } from "./customer.model";
export class CustomerRepository export class CustomerRepository
extends SequelizeRepository<Customer> extends SequelizeRepository<Customer>
implements ICustomerRepository implements ICustomerRepository
{ {
private readonly mapper!: ICustomerMapper;
constructor(mapper: ICustomerMapper) {
super();
this.mapper = mapper;
}
/** /**
* *
* Guarda un nuevo cliente o actualiza uno existente. * Guarda un nuevo cliente o actualiza uno existente.
@ -28,9 +20,20 @@ export class CustomerRepository
*/ */
async save(customer: Customer, transaction: Transaction): Promise<Result<Customer, Error>> { async save(customer: Customer, transaction: Transaction): Promise<Result<Customer, Error>> {
try { try {
const data = this.mapper.mapToPersistence(customer); const mapper: ICustomerDomainMapper = this._registry.getDomainMapper({
resource: "customer",
});
const mapperData = mapper.mapToPersistence(customer);
if (mapperData.isFailure) {
return Result.fail(mapperData.error);
}
const { data } = mapperData;
const [instance] = await CustomerModel.upsert(data, { transaction, returning: true }); const [instance] = await CustomerModel.upsert(data, { transaction, returning: true });
const savedCustomer = this.mapper.mapToDomain(instance); const savedCustomer = mapper.mapToDomain(instance);
return savedCustomer; return savedCustomer;
} catch (err: unknown) { } catch (err: unknown) {
return Result.fail(translateSequelizeError(err)); return Result.fail(translateSequelizeError(err));
@ -75,6 +78,10 @@ export class CustomerRepository
transaction?: Transaction transaction?: Transaction
): Promise<Result<Customer, Error>> { ): Promise<Result<Customer, Error>> {
try { try {
const mapper: ICustomerDomainMapper = this._registry.getDomainMapper({
resource: "customer",
});
const row = await CustomerModel.findOne({ const row = await CustomerModel.findOne({
where: { id: id.toString(), company_id: companyId.toString() }, where: { id: id.toString(), company_id: companyId.toString() },
transaction, transaction,
@ -84,7 +91,7 @@ export class CustomerRepository
return Result.fail(new EntityNotFoundError("Customer", "id", id.toString())); return Result.fail(new EntityNotFoundError("Customer", "id", id.toString()));
} }
const customer = this.mapper.mapToDomain(row); const customer = mapper.mapToDomain(row);
return customer; return customer;
} catch (error: any) { } catch (error: any) {
return Result.fail(translateSequelizeError(error)); return Result.fail(translateSequelizeError(error));
@ -107,6 +114,10 @@ export class CustomerRepository
transaction?: Transaction transaction?: Transaction
): Promise<Result<Collection<Customer>>> { ): Promise<Result<Collection<Customer>>> {
try { try {
const mapper: ICustomerDomainMapper = this._registry.getDomainMapper({
resource: "customer",
});
const converter = new CriteriaToSequelizeConverter(); const converter = new CriteriaToSequelizeConverter();
const query = converter.convert(criteria); const query = converter.convert(criteria);
@ -120,7 +131,7 @@ export class CustomerRepository
transaction, transaction,
}); });
return this.mapper.mapArrayToDomain(instances); return mapper.mapArrayToDomain(instances);
} catch (err: unknown) { } catch (err: unknown) {
return Result.fail(translateSequelizeError(err)); return Result.fail(translateSequelizeError(err));
} }

View File

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