.
This commit is contained in:
parent
bf9ed99a90
commit
05f048cc72
@ -1,6 +1,6 @@
|
||||
// application/issued-invoices/di/snapshot-builders.di.ts
|
||||
|
||||
import { IssuedInvoiceListItemSnapshotBuilder } from "../snapshot-builders";
|
||||
import { IssuedInvoiceSummarySnapshotBuilder } from "../snapshot-builders";
|
||||
import {
|
||||
IssuedInvoiceFullSnapshotBuilder,
|
||||
IssuedInvoiceItemsFullSnapshotBuilder,
|
||||
@ -30,7 +30,7 @@ export function buildIssuedInvoiceSnapshotBuilders() {
|
||||
taxesBuilder
|
||||
);
|
||||
|
||||
const listSnapshotBuilder = new IssuedInvoiceListItemSnapshotBuilder();
|
||||
const listSnapshotBuilder = new IssuedInvoiceSummarySnapshotBuilder();
|
||||
|
||||
const itemsReportBuilder = new IssuedInvoiceReportItemSnapshotBuilder();
|
||||
const taxesReportBuilder = new IssuedInvoiceReportTaxSnapshotBuilder();
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { ITransactionManager } from "@erp/core/api";
|
||||
|
||||
import type { IIssuedInvoiceFinder, IssuedInvoiceDocumentGeneratorService } from "../services";
|
||||
import type { IIssuedInvoiceListItemSnapshotBuilder } from "../snapshot-builders";
|
||||
import type { IIssuedInvoiceSummarySnapshotBuilder } from "../snapshot-builders";
|
||||
import type { IIssuedInvoiceFullSnapshotBuilder } from "../snapshot-builders/full";
|
||||
import type { IIssuedInvoiceReportSnapshotBuilder } from "../snapshot-builders/report";
|
||||
import {
|
||||
@ -24,12 +24,12 @@ export function buildGetIssuedInvoiceByIdUseCase(deps: {
|
||||
|
||||
export function buildListIssuedInvoicesUseCase(deps: {
|
||||
finder: IIssuedInvoiceFinder;
|
||||
itemSnapshotBuilder: IIssuedInvoiceListItemSnapshotBuilder;
|
||||
summarySnapshotBuilder: IIssuedInvoiceSummarySnapshotBuilder;
|
||||
transactionManager: ITransactionManager;
|
||||
}) {
|
||||
return new ListIssuedInvoicesUseCase(
|
||||
deps.finder,
|
||||
deps.itemSnapshotBuilder,
|
||||
deps.summarySnapshotBuilder,
|
||||
deps.transactionManager
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
export * from "./di";
|
||||
export * from "./mappers";
|
||||
export * from "./models";
|
||||
export * from "./repositories";
|
||||
export * from "./services";
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
export * from "./issued-invoice-domain-mapper.interface";
|
||||
export * from "./issued-invoice-list-mapper.interface";
|
||||
@ -1,9 +0,0 @@
|
||||
import type { MapperParamsType } from "@erp/core/api";
|
||||
import type { Result } from "@repo/rdx-utils";
|
||||
|
||||
import type { IssuedInvoice } from "../../../domain";
|
||||
|
||||
export interface IIssuedInvoiceDomainMapper {
|
||||
mapToPersistence(invoice: IssuedInvoice, params?: MapperParamsType): Result<unknown, Error>;
|
||||
mapToDomain(raw: unknown, params?: MapperParamsType): Result<IssuedInvoice, Error>;
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
import type { MapperParamsType } from "@erp/core/api";
|
||||
import type { Result } from "@repo/rdx-utils";
|
||||
|
||||
import type { IssuedInvoiceSummary } from "../models";
|
||||
|
||||
export interface IIssuedInvoiceSummaryMapper {
|
||||
mapToDTO(raw: unknown, params?: MapperParamsType): Result<IssuedInvoiceSummary, Error>;
|
||||
}
|
||||
@ -1,2 +1,2 @@
|
||||
export * from "./full";
|
||||
export * from "./list";
|
||||
export * from "./summary";
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
export * from "./issued-invoice-list-item-snapshot.interface";
|
||||
export * from "./issued-invoice-list-item-snapshot-builder";
|
||||
@ -0,0 +1,2 @@
|
||||
export * from "./issued-invoice-summary-snapshot.interface";
|
||||
export * from "./issued-invoice-summary-snapshot-builder";
|
||||
@ -3,13 +3,13 @@ import { maybeToEmptyString } from "@repo/rdx-ddd";
|
||||
|
||||
import type { IssuedInvoiceSummary } from "../../models";
|
||||
|
||||
import type { IIssuedInvoiceListItemSnapshot } from "./issued-invoice-list-item-snapshot.interface";
|
||||
import type { IIssuedInvoiceSummarySnapshot } from "./issued-invoice-summary-snapshot.interface";
|
||||
|
||||
export interface IIssuedInvoiceListItemSnapshotBuilder
|
||||
extends ISnapshotBuilder<IssuedInvoiceSummary, IIssuedInvoiceListItemSnapshot> {}
|
||||
export interface IIssuedInvoiceSummarySnapshotBuilder
|
||||
extends ISnapshotBuilder<IssuedInvoiceSummary, IIssuedInvoiceSummarySnapshot> {}
|
||||
|
||||
export class IssuedInvoiceListItemSnapshotBuilder implements IIssuedInvoiceListItemSnapshotBuilder {
|
||||
toOutput(invoice: IssuedInvoiceSummary): IIssuedInvoiceListItemSnapshot {
|
||||
export class IssuedInvoiceSummarySnapshotBuilder implements IIssuedInvoiceSummarySnapshotBuilder {
|
||||
toOutput(invoice: IssuedInvoiceSummary): IIssuedInvoiceSummarySnapshot {
|
||||
const recipient = invoice.recipient.toObjectString();
|
||||
|
||||
const verifactu = invoice.verifactu.match(
|
||||
@ -1,4 +1,4 @@
|
||||
export interface IIssuedInvoiceListItemSnapshot {
|
||||
export interface IIssuedInvoiceSummarySnapshot {
|
||||
id: string;
|
||||
company_id: string;
|
||||
|
||||
@ -5,7 +5,7 @@ import { Result } from "@repo/rdx-utils";
|
||||
import type { Transaction } from "sequelize";
|
||||
|
||||
import type { IIssuedInvoiceFinder } from "../services";
|
||||
import type { IIssuedInvoiceListItemSnapshotBuilder } from "../snapshot-builders";
|
||||
import type { IIssuedInvoiceSummarySnapshotBuilder } from "../snapshot-builders";
|
||||
|
||||
type ListIssuedInvoicesUseCaseInput = {
|
||||
companyId: UniqueID;
|
||||
@ -15,7 +15,7 @@ type ListIssuedInvoicesUseCaseInput = {
|
||||
export class ListIssuedInvoicesUseCase {
|
||||
constructor(
|
||||
private readonly finder: IIssuedInvoiceFinder,
|
||||
private readonly listItemSnapshotBuilder: IIssuedInvoiceListItemSnapshotBuilder,
|
||||
private readonly summarySnapshotBuilder: IIssuedInvoiceSummarySnapshotBuilder,
|
||||
private readonly transactionManager: ITransactionManager
|
||||
) {}
|
||||
|
||||
@ -37,7 +37,7 @@ export class ListIssuedInvoicesUseCase {
|
||||
const invoices = result.data;
|
||||
const totalInvoices = invoices.total();
|
||||
|
||||
const items = invoices.map((item) => this.listItemSnapshotBuilder.toOutput(item));
|
||||
const items = invoices.map((item) => this.summarySnapshotBuilder.toOutput(item));
|
||||
|
||||
const snapshot = {
|
||||
page: criteria.pageNumber,
|
||||
|
||||
@ -4,9 +4,9 @@ import {
|
||||
ProformaFullSnapshotBuilder,
|
||||
ProformaItemReportSnapshotBuilder,
|
||||
ProformaItemsFullSnapshotBuilder,
|
||||
ProformaListItemSnapshotBuilder,
|
||||
ProformaRecipientFullSnapshotBuilder,
|
||||
ProformaReportSnapshotBuilder,
|
||||
ProformaSummarySnapshotBuilder,
|
||||
ProformaTaxReportSnapshotBuilder,
|
||||
ProformaTaxesFullSnapshotBuilder,
|
||||
} from "../snapshot-builders";
|
||||
@ -24,7 +24,7 @@ export function buildProformaSnapshotBuilders() {
|
||||
taxesBuilder
|
||||
);
|
||||
|
||||
const listSnapshotBuilder = new ProformaListItemSnapshotBuilder();
|
||||
const summarySnapshotBuilder = new ProformaSummarySnapshotBuilder();
|
||||
|
||||
const itemsReportBuilder = new ProformaItemReportSnapshotBuilder();
|
||||
const taxesReportBuilder = new ProformaTaxReportSnapshotBuilder();
|
||||
@ -35,7 +35,7 @@ export function buildProformaSnapshotBuilders() {
|
||||
|
||||
return {
|
||||
full: fullSnapshotBuilder,
|
||||
list: listSnapshotBuilder,
|
||||
summary: summarySnapshotBuilder,
|
||||
report: reportSnapshotBuilder,
|
||||
};
|
||||
}
|
||||
|
||||
@ -7,8 +7,8 @@ import type {
|
||||
ProformaDocumentGeneratorService,
|
||||
} from "../services";
|
||||
import type {
|
||||
IProformaListItemSnapshotBuilder,
|
||||
IProformaReportSnapshotBuilder,
|
||||
IProformaSummarySnapshotBuilder,
|
||||
} from "../snapshot-builders";
|
||||
import type { IProformaFullSnapshotBuilder } from "../snapshot-builders/full";
|
||||
import { GetProformaByIdUseCase, ListProformasUseCase, ReportProformaUseCase } from "../use-cases";
|
||||
@ -24,10 +24,14 @@ export function buildGetProformaByIdUseCase(deps: {
|
||||
|
||||
export function buildListProformasUseCase(deps: {
|
||||
finder: IProformaFinder;
|
||||
itemSnapshotBuilder: IProformaListItemSnapshotBuilder;
|
||||
summarySnapshotBuilder: IProformaSummarySnapshotBuilder;
|
||||
transactionManager: ITransactionManager;
|
||||
}) {
|
||||
return new ListProformasUseCase(deps.finder, deps.itemSnapshotBuilder, deps.transactionManager);
|
||||
return new ListProformasUseCase(
|
||||
deps.finder,
|
||||
deps.summarySnapshotBuilder,
|
||||
deps.transactionManager
|
||||
);
|
||||
}
|
||||
|
||||
export function buildReportProformaUseCase(deps: {
|
||||
|
||||
@ -15,7 +15,7 @@ import {
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
|
||||
import type { CreateProformaItemRequestDTO, CreateProformaRequestDTO } from "../../../../../common";
|
||||
import type { CreateProformaItemRequestDTO, CreateProformaRequestDTO } from "../../../../common";
|
||||
import {
|
||||
type IProformaItemProps,
|
||||
type IProformaProps,
|
||||
@ -28,7 +28,7 @@ import {
|
||||
ItemDescription,
|
||||
ItemQuantity,
|
||||
type ProformaItemTaxesProps,
|
||||
} from "../../../../domain";
|
||||
} from "../../../domain";
|
||||
|
||||
/**
|
||||
* CreateProformaPropsMapper
|
||||
@ -1,3 +1,2 @@
|
||||
export * from "./inputs";
|
||||
export * from "./proforma-domain-mapper.interface";
|
||||
export * from "./proforma-list-mapper.interface";
|
||||
export * from "./create-proforma-input.mapper";
|
||||
export * from "./update-proforma-input.mapper";
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
export * from "./create-proforma-input.mapper";
|
||||
export * from "./update-proforma-input.mapper";
|
||||
@ -1,9 +0,0 @@
|
||||
import type { MapperParamsType } from "@erp/core/api";
|
||||
import type { Result } from "@repo/rdx-utils";
|
||||
|
||||
import type { Proforma } from "../../../domain";
|
||||
|
||||
export interface IProformaDomainMapper {
|
||||
mapToPersistence(proforma: Proforma, params?: MapperParamsType): Result<unknown, Error>;
|
||||
mapToDomain(raw: unknown, params?: MapperParamsType): Result<Proforma, Error>;
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
import type { MapperParamsType } from "@erp/core/api";
|
||||
import type { Result } from "@repo/rdx-utils";
|
||||
|
||||
import type { ProformaListDTO } from "../models";
|
||||
|
||||
export interface IProformaListMapper {
|
||||
mapToDTO(raw: unknown, params?: MapperParamsType): Result<ProformaListDTO, Error>;
|
||||
}
|
||||
@ -12,8 +12,8 @@ import {
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Result, isNullishOrEmpty, toPatchField } from "@repo/rdx-utils";
|
||||
|
||||
import type { UpdateProformaByIdRequestDTO } from "../../../../../common/dto";
|
||||
import type { ProformaPatchProps } from "../../../../domain";
|
||||
import type { UpdateProformaByIdRequestDTO } from "../../../../common/dto";
|
||||
import { InvoiceSerie, type ProformaPatchProps } from "../../../domain";
|
||||
|
||||
/**
|
||||
* UpdateProformaPropsMapper
|
||||
@ -1 +1 @@
|
||||
export * from "./proforma-resume";
|
||||
export * from "./proforma-summary";
|
||||
|
||||
@ -9,7 +9,7 @@ import type {
|
||||
InvoiceStatus,
|
||||
} from "../../../domain";
|
||||
|
||||
export type ProformaListDTO = {
|
||||
export type ProformaSummary = {
|
||||
id: UniqueID;
|
||||
companyId: UniqueID;
|
||||
|
||||
@ -3,7 +3,7 @@ import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import type { Collection, Result } from "@repo/rdx-utils";
|
||||
|
||||
import type { InvoiceStatus, Proforma } from "../../../domain";
|
||||
import type { ProformaListDTO } from "../models";
|
||||
import type { ProformaSummary } from "../models";
|
||||
|
||||
export interface IProformaRepository {
|
||||
create(proforma: Proforma, transaction?: unknown): Promise<Result<void, Error>>;
|
||||
@ -26,7 +26,7 @@ export interface IProformaRepository {
|
||||
companyId: UniqueID,
|
||||
criteria: Criteria,
|
||||
transaction: unknown
|
||||
): Promise<Result<Collection<ProformaListDTO>, Error>>;
|
||||
): Promise<Result<Collection<ProformaSummary>, Error>>;
|
||||
|
||||
deleteByIdInCompany(
|
||||
companyId: UniqueID,
|
||||
|
||||
@ -4,7 +4,7 @@ import type { Collection, Result } from "@repo/rdx-utils";
|
||||
import type { Transaction } from "sequelize";
|
||||
|
||||
import type { Proforma } from "../../../domain";
|
||||
import type { ProformaListDTO } from "../models";
|
||||
import type { ProformaSummary } from "../models";
|
||||
import type { IProformaRepository } from "../repositories";
|
||||
|
||||
export interface IProformaFinder {
|
||||
@ -24,7 +24,7 @@ export interface IProformaFinder {
|
||||
companyId: UniqueID,
|
||||
criteria: Criteria,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<Collection<ProformaListDTO>, Error>>;
|
||||
): Promise<Result<Collection<ProformaSummary>, Error>>;
|
||||
}
|
||||
|
||||
export class ProformaFinder implements IProformaFinder {
|
||||
@ -50,7 +50,7 @@ export class ProformaFinder implements IProformaFinder {
|
||||
companyId: UniqueID,
|
||||
criteria: Criteria,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<Collection<ProformaListDTO>, Error>> {
|
||||
): Promise<Result<Collection<ProformaSummary>, Error>> {
|
||||
return this.repository.findByCriteriaInCompany(companyId, criteria, transaction, {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export * from "./full";
|
||||
export * from "./list";
|
||||
export * from "./report";
|
||||
export * from "./summary";
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
export * from "./proforma-list-item-snapshot.interface";
|
||||
export * from "./proforma-list-item-snapshot-builder";
|
||||
@ -0,0 +1,2 @@
|
||||
export * from "./proforma-summary-snapshot.interface";
|
||||
export * from "./proforma-summary-snapshot-builder";
|
||||
@ -1,15 +1,15 @@
|
||||
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||
import { maybeToEmptyString } from "@repo/rdx-ddd";
|
||||
|
||||
import type { ProformaListDTO } from "../../models";
|
||||
import type { ProformaSummary } from "../../models";
|
||||
|
||||
import type { IProformaListItemSnapshot } from "./proforma-list-item-snapshot.interface";
|
||||
import type { IProformaSummarySnapshot } from "./proforma-summary-snapshot.interface";
|
||||
|
||||
export interface IProformaListItemSnapshotBuilder
|
||||
extends ISnapshotBuilder<ProformaListDTO, IProformaListItemSnapshot> {}
|
||||
export interface IProformaSummarySnapshotBuilder
|
||||
extends ISnapshotBuilder<ProformaSummary, IProformaSummarySnapshot> {}
|
||||
|
||||
export class ProformaListItemSnapshotBuilder implements IProformaListItemSnapshotBuilder {
|
||||
toOutput(proforma: ProformaListDTO): IProformaListItemSnapshot {
|
||||
export class ProformaSummarySnapshotBuilder implements IProformaSummarySnapshotBuilder {
|
||||
toOutput(proforma: ProformaSummary): IProformaSummarySnapshot {
|
||||
const recipient = proforma.recipient.toObjectString();
|
||||
|
||||
return {
|
||||
@ -1,4 +1,4 @@
|
||||
export interface IProformaListItemSnapshot {
|
||||
export interface IProformaSummarySnapshot {
|
||||
id: string;
|
||||
company_id: string;
|
||||
|
||||
@ -5,7 +5,7 @@ import { Result } from "@repo/rdx-utils";
|
||||
import type { Transaction } from "sequelize";
|
||||
|
||||
import type { IProformaFinder } from "../services";
|
||||
import type { IProformaListItemSnapshotBuilder } from "../snapshot-builders";
|
||||
import type { IProformaSummarySnapshotBuilder } from "../snapshot-builders";
|
||||
|
||||
type ListProformasUseCaseInput = {
|
||||
companyId: UniqueID;
|
||||
@ -15,7 +15,7 @@ type ListProformasUseCaseInput = {
|
||||
export class ListProformasUseCase {
|
||||
constructor(
|
||||
private readonly finder: IProformaFinder,
|
||||
private readonly listItemSnapshotBuilder: IProformaListItemSnapshotBuilder,
|
||||
private readonly summarySnapshotBuilder: IProformaSummarySnapshotBuilder,
|
||||
private readonly transactionManager: ITransactionManager
|
||||
) {}
|
||||
|
||||
@ -33,7 +33,7 @@ export class ListProformasUseCase {
|
||||
const proformas = result.data;
|
||||
const totalProformas = proformas.total();
|
||||
|
||||
const items = proformas.map((item) => this.listItemSnapshotBuilder.toOutput(item));
|
||||
const items = proformas.map((item) => this.summarySnapshotBuilder.toOutput(item));
|
||||
|
||||
const snapshot = {
|
||||
page: criteria.pageNumber,
|
||||
|
||||
@ -1,343 +0,0 @@
|
||||
import {
|
||||
AggregateRoot,
|
||||
type CurrencyCode,
|
||||
DomainValidationError,
|
||||
type LanguageCode,
|
||||
type Percentage,
|
||||
type TextValue,
|
||||
type UniqueID,
|
||||
type UtcDate,
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Collection, type Maybe, Result } from "@repo/rdx-utils";
|
||||
|
||||
import {
|
||||
CustomerInvoiceItems,
|
||||
type InvoicePaymentMethod,
|
||||
type VerifactuRecord,
|
||||
} from "../common/entities";
|
||||
import {
|
||||
InvoiceAmount,
|
||||
type InvoiceNumber,
|
||||
type InvoiceRecipient,
|
||||
type InvoiceSerie,
|
||||
type InvoiceStatus,
|
||||
InvoiceTaxGroup,
|
||||
type ItemAmount,
|
||||
} from "../common/value-objects";
|
||||
|
||||
export interface CustomerInvoiceProps {
|
||||
companyId: UniqueID;
|
||||
|
||||
isProforma: boolean;
|
||||
status: InvoiceStatus;
|
||||
|
||||
proformaId: Maybe<UniqueID>; // <- proforma padre en caso de issue
|
||||
|
||||
series: Maybe<InvoiceSerie>;
|
||||
invoiceNumber: InvoiceNumber;
|
||||
|
||||
invoiceDate: UtcDate;
|
||||
operationDate: Maybe<UtcDate>;
|
||||
|
||||
customerId: UniqueID;
|
||||
recipient: Maybe<InvoiceRecipient>;
|
||||
|
||||
reference: Maybe<string>;
|
||||
description: Maybe<string>;
|
||||
notes: Maybe<TextValue>;
|
||||
|
||||
languageCode: LanguageCode;
|
||||
currencyCode: CurrencyCode;
|
||||
|
||||
items: CustomerInvoiceItems;
|
||||
|
||||
paymentMethod: Maybe<InvoicePaymentMethod>;
|
||||
|
||||
discountPercentage: Percentage;
|
||||
|
||||
verifactu: Maybe<VerifactuRecord>;
|
||||
}
|
||||
|
||||
export type CustomerInvoicePatchProps = Partial<
|
||||
Omit<CustomerInvoiceProps, "companyId" | "items">
|
||||
> & {
|
||||
items?: CustomerInvoiceItems;
|
||||
};
|
||||
|
||||
export interface ICustomerInvoice {
|
||||
canTransitionTo(nextStatus: string): boolean;
|
||||
|
||||
hasRecipient: boolean;
|
||||
hasPaymentMethod: boolean;
|
||||
|
||||
//getTaxes(): Collection<InvoiceTaxGroup>;
|
||||
getProps(): CustomerInvoiceProps;
|
||||
}
|
||||
|
||||
export class CustomerInvoice
|
||||
extends AggregateRoot<CustomerInvoiceProps>
|
||||
implements ICustomerInvoice
|
||||
{
|
||||
private _items!: CustomerInvoiceItems;
|
||||
|
||||
protected constructor(props: CustomerInvoiceProps, id?: UniqueID) {
|
||||
super(props, id);
|
||||
this._items =
|
||||
props.items ||
|
||||
CustomerInvoiceItems.create({
|
||||
languageCode: props.languageCode,
|
||||
currencyCode: props.currencyCode,
|
||||
globalDiscountPercentage: props.discountPercentage,
|
||||
});
|
||||
}
|
||||
|
||||
static create(props: CustomerInvoiceProps, id?: UniqueID): Result<CustomerInvoice, Error> {
|
||||
const customerInvoice = new CustomerInvoice(props, id);
|
||||
|
||||
// Reglas de negocio / validaciones
|
||||
|
||||
if (!(customerInvoice.isProforma || customerInvoice.hasRecipient)) {
|
||||
return Result.fail(
|
||||
new DomainValidationError(
|
||||
"MISSING_CUSTOMER_DATA",
|
||||
"recipient",
|
||||
"Customer data must be provided for non-proforma invoices"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// 🔹 Disparar evento de dominio "CustomerInvoiceAuthenticatedEvent"
|
||||
//const { customerInvoice } = props;
|
||||
//user.addDomainEvent(new CustomerInvoiceAuthenticatedEvent(id, customerInvoice.toString()));
|
||||
|
||||
return Result.ok(customerInvoice);
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
public get companyId(): UniqueID {
|
||||
return this.props.companyId;
|
||||
}
|
||||
|
||||
public get customerId(): UniqueID {
|
||||
return this.props.customerId;
|
||||
}
|
||||
|
||||
public get isProforma(): boolean {
|
||||
return this.props.isProforma;
|
||||
}
|
||||
|
||||
public get proformaId(): Maybe<UniqueID> {
|
||||
return this.props.proformaId;
|
||||
}
|
||||
|
||||
public get status(): InvoiceStatus {
|
||||
return this.props.status;
|
||||
}
|
||||
|
||||
canTransitionTo(nextStatus: string): boolean {
|
||||
return this.props.status.canTransitionTo(nextStatus);
|
||||
}
|
||||
|
||||
public get series(): Maybe<InvoiceSerie> {
|
||||
return this.props.series;
|
||||
}
|
||||
|
||||
public get invoiceNumber() {
|
||||
return this.props.invoiceNumber;
|
||||
}
|
||||
|
||||
public get invoiceDate(): UtcDate {
|
||||
return this.props.invoiceDate;
|
||||
}
|
||||
|
||||
public get operationDate(): Maybe<UtcDate> {
|
||||
return this.props.operationDate;
|
||||
}
|
||||
|
||||
public get reference(): Maybe<string> {
|
||||
return this.props.reference;
|
||||
}
|
||||
|
||||
public get description(): Maybe<string> {
|
||||
return this.props.description;
|
||||
}
|
||||
|
||||
public get notes(): Maybe<TextValue> {
|
||||
return this.props.notes;
|
||||
}
|
||||
|
||||
public get recipient(): Maybe<InvoiceRecipient> {
|
||||
return this.props.recipient;
|
||||
}
|
||||
|
||||
public get paymentMethod(): Maybe<InvoicePaymentMethod> {
|
||||
return this.props.paymentMethod;
|
||||
}
|
||||
|
||||
public get languageCode(): LanguageCode {
|
||||
return this.props.languageCode;
|
||||
}
|
||||
|
||||
public get currencyCode(): CurrencyCode {
|
||||
return this.props.currencyCode;
|
||||
}
|
||||
|
||||
public get verifactu(): Maybe<VerifactuRecord> {
|
||||
return this.props.verifactu;
|
||||
}
|
||||
|
||||
public get discountPercentage(): Percentage {
|
||||
return this.props.discountPercentage;
|
||||
}
|
||||
|
||||
// Method to get the complete list of line items
|
||||
|
||||
public get items(): CustomerInvoiceItems {
|
||||
return this._items;
|
||||
}
|
||||
|
||||
public get hasRecipient() {
|
||||
return this.recipient.isSome();
|
||||
}
|
||||
|
||||
public get hasPaymentMethod() {
|
||||
return this.paymentMethod.isSome();
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
/**
|
||||
* @summary Convierte un ItemAmount a InvoiceAmount (mantiene moneda y escala homogénea).
|
||||
*/
|
||||
private _toInvoiceAmount(itemAmount: ItemAmount): InvoiceAmount {
|
||||
return InvoiceAmount.create({
|
||||
value: itemAmount.convertScale(InvoiceAmount.DEFAULT_SCALE).value,
|
||||
currency_code: this.currencyCode.code,
|
||||
}).data;
|
||||
}
|
||||
|
||||
// Cálculos
|
||||
|
||||
/**
|
||||
* @summary Calcula todos los totales de factura a partir de los totales de las líneas.
|
||||
* La cabecera NO recalcula lógica de porcentaje — toda la lógica está en Item/Items.
|
||||
*/
|
||||
public calculateAllAmounts() {
|
||||
const itemsTotals = this.items.calculateAllAmounts();
|
||||
|
||||
const subtotalAmount = this._toInvoiceAmount(itemsTotals.subtotalAmount);
|
||||
|
||||
const itemDiscountAmount = this._toInvoiceAmount(itemsTotals.itemDiscountAmount);
|
||||
const globalDiscountAmount = this._toInvoiceAmount(itemsTotals.globalDiscountAmount);
|
||||
const totalDiscountAmount = this._toInvoiceAmount(itemsTotals.totalDiscountAmount);
|
||||
|
||||
const taxableAmount = this._toInvoiceAmount(itemsTotals.taxableAmount);
|
||||
const taxesAmount = this._toInvoiceAmount(itemsTotals.taxesAmount);
|
||||
const totalAmount = this._toInvoiceAmount(itemsTotals.totalAmount);
|
||||
|
||||
const taxGroups = this._groupTaxes();
|
||||
|
||||
return {
|
||||
subtotalAmount,
|
||||
itemDiscountAmount,
|
||||
globalDiscountAmount,
|
||||
totalDiscountAmount,
|
||||
taxableAmount,
|
||||
taxesAmount,
|
||||
totalAmount,
|
||||
taxGroups,
|
||||
} as const;
|
||||
}
|
||||
|
||||
// Métodos públicos
|
||||
|
||||
public getProps(): CustomerInvoiceProps {
|
||||
return this.props;
|
||||
}
|
||||
|
||||
public update(partialInvoice: CustomerInvoicePatchProps): Result<CustomerInvoice, Error> {
|
||||
const { items, ...rest } = partialInvoice;
|
||||
|
||||
const updatedProps = {
|
||||
...this.props,
|
||||
...rest,
|
||||
} as CustomerInvoiceProps;
|
||||
|
||||
/*if (partialAddress) {
|
||||
const updatedAddressOrError = this.address.update(partialAddress);
|
||||
if (updatedAddressOrError.isFailure) {
|
||||
return Result.fail(updatedAddressOrError.error);
|
||||
}
|
||||
|
||||
updatedProps.address = updatedAddressOrError.data;
|
||||
}*/
|
||||
|
||||
return CustomerInvoice.create(updatedProps, this.id);
|
||||
}
|
||||
|
||||
public getSubtotalAmount(): InvoiceAmount {
|
||||
return this.calculateAllAmounts().subtotalAmount;
|
||||
}
|
||||
|
||||
public getItemDiscountAmount(): InvoiceAmount {
|
||||
return this.calculateAllAmounts().itemDiscountAmount;
|
||||
}
|
||||
|
||||
public getGlobalDiscountAmount(): InvoiceAmount {
|
||||
return this.calculateAllAmounts().globalDiscountAmount;
|
||||
}
|
||||
|
||||
public getTotalDiscountAmount(): InvoiceAmount {
|
||||
return this.calculateAllAmounts().totalDiscountAmount;
|
||||
}
|
||||
|
||||
public getTaxableAmount(): InvoiceAmount {
|
||||
return this.calculateAllAmounts().taxableAmount;
|
||||
}
|
||||
|
||||
public getTaxesAmount(): InvoiceAmount {
|
||||
return this.calculateAllAmounts().taxesAmount;
|
||||
}
|
||||
|
||||
public getTotalAmount(): InvoiceAmount {
|
||||
return this.calculateAllAmounts().totalAmount;
|
||||
}
|
||||
|
||||
public getTaxes(): Collection<InvoiceTaxGroup> {
|
||||
return this._groupTaxes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Agrupa impuestos a nivel factura usando el trío (iva|rec|ret),
|
||||
* construyendo InvoiceTaxGroup desde los datos de los ítems.
|
||||
*/
|
||||
private _groupTaxes(): Collection<InvoiceTaxGroup> {
|
||||
const map = this.items.groupTaxesByCode();
|
||||
const groups: InvoiceTaxGroup[] = [];
|
||||
|
||||
for (const [, entry] of map.entries()) {
|
||||
const { taxes, taxable } = entry;
|
||||
|
||||
const iva = taxes.iva.unwrap(); // IVA siempre obligatorio
|
||||
const rec = taxes.rec; // Maybe<Tax>
|
||||
const retention = taxes.retention; // Maybe<Tax>
|
||||
|
||||
const invoiceAmount = InvoiceAmount.create({
|
||||
value: taxable.convertScale(InvoiceAmount.DEFAULT_SCALE).value,
|
||||
currency_code: this.currencyCode.code,
|
||||
}).data;
|
||||
|
||||
const item = InvoiceTaxGroup.create({
|
||||
iva,
|
||||
rec,
|
||||
retention,
|
||||
taxableAmount: invoiceAmount,
|
||||
}).data;
|
||||
|
||||
groups.push(item);
|
||||
}
|
||||
|
||||
return new Collection(groups);
|
||||
}
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
import {
|
||||
AggregateRoot,
|
||||
type CurrencyCode,
|
||||
type LanguageCode,
|
||||
type Percentage,
|
||||
type TextValue,
|
||||
type UniqueID,
|
||||
type UtcDate,
|
||||
} from "@repo/rdx-ddd";
|
||||
import type { Maybe } from "@repo/rdx-utils";
|
||||
|
||||
import type { CustomerInvoiceItems, InvoicePaymentMethod } from "./entities";
|
||||
import type { InvoiceNumber, InvoiceRecipient, InvoiceSerie, InvoiceStatus } from "./value-objects";
|
||||
|
||||
export interface ICustomerInvoiceBaseProps {
|
||||
companyId: UniqueID;
|
||||
invoiceNumber: InvoiceNumber;
|
||||
invoiceDate: UtcDate;
|
||||
|
||||
status: InvoiceStatus;
|
||||
|
||||
series: Maybe<InvoiceSerie>;
|
||||
|
||||
operationDate: Maybe<UtcDate>;
|
||||
|
||||
customerId: UniqueID;
|
||||
recipient: Maybe<InvoiceRecipient>;
|
||||
|
||||
reference: Maybe<string>;
|
||||
description: Maybe<string>;
|
||||
notes: Maybe<TextValue>;
|
||||
|
||||
languageCode: LanguageCode;
|
||||
currencyCode: CurrencyCode;
|
||||
|
||||
items: CustomerInvoiceItems;
|
||||
|
||||
paymentMethod: Maybe<InvoicePaymentMethod>;
|
||||
|
||||
discountPercentage: Percentage;
|
||||
}
|
||||
|
||||
export abstract class CustomerInvoiceBase<
|
||||
TProps extends ICustomerInvoiceBaseProps,
|
||||
> extends AggregateRoot<TProps> {
|
||||
protected constructor(props: TProps, id?: UniqueID) {
|
||||
super(props, id);
|
||||
}
|
||||
|
||||
public get companyId(): UniqueID {
|
||||
return this.props.companyId;
|
||||
}
|
||||
|
||||
public get invoiceNumber(): InvoiceNumber {
|
||||
return this.props.invoiceNumber;
|
||||
}
|
||||
|
||||
public get invoiceDate(): UtcDate {
|
||||
return this.props.invoiceDate;
|
||||
}
|
||||
|
||||
public get currencyCode(): CurrencyCode {
|
||||
return this.props.currencyCode;
|
||||
}
|
||||
|
||||
public get languageCode(): LanguageCode {
|
||||
return this.props.languageCode;
|
||||
}
|
||||
|
||||
public get recipient(): Maybe<InvoiceRecipient> {
|
||||
return this.props.recipient;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
import type { ICatalogs } from "@erp/core/api";
|
||||
|
||||
import {
|
||||
SequelizeIssuedInvoiceDomainMapper,
|
||||
SequelizeIssuedInvoiceSummaryMapper,
|
||||
} from "../persistence";
|
||||
|
||||
export interface IIssuedInvoicePersistenceMappers {
|
||||
domainMapper: SequelizeIssuedInvoiceDomainMapper;
|
||||
summaryMapper: SequelizeIssuedInvoiceSummaryMapper;
|
||||
}
|
||||
|
||||
export const buildIssuedInvoicePersistenceMappers = (
|
||||
catalogs: ICatalogs
|
||||
): IIssuedInvoicePersistenceMappers => {
|
||||
const { taxCatalog } = catalogs;
|
||||
|
||||
// Mappers para el repositorio
|
||||
const domainMapper = new SequelizeIssuedInvoiceDomainMapper({
|
||||
taxCatalog,
|
||||
});
|
||||
const summaryMapper = new SequelizeIssuedInvoiceSummaryMapper();
|
||||
|
||||
return {
|
||||
domainMapper,
|
||||
summaryMapper,
|
||||
};
|
||||
};
|
||||
@ -1,19 +1,14 @@
|
||||
import { SpainTaxCatalogProvider } from "@erp/core";
|
||||
import type { Sequelize } from "sequelize";
|
||||
|
||||
import {
|
||||
IssuedInvoiceRepository,
|
||||
SequelizeIssuedInvoiceDomainMapper,
|
||||
SequelizeIssuedInvoiceListMapper,
|
||||
} from "../persistence";
|
||||
import { IssuedInvoiceRepository } from "../persistence";
|
||||
|
||||
export const buildIssuedInvoiceRepository = (database: Sequelize) => {
|
||||
const taxCatalog = SpainTaxCatalogProvider();
|
||||
import type { IIssuedInvoicePersistenceMappers } from "./issued-invoice-persistence-mappers.di";
|
||||
|
||||
const domainMapper = new SequelizeIssuedInvoiceDomainMapper({
|
||||
taxCatalog,
|
||||
});
|
||||
const listMapper = new SequelizeIssuedInvoiceListMapper();
|
||||
export const buildIssuedInvoiceRepository = (params: {
|
||||
database: Sequelize;
|
||||
mappers: IIssuedInvoicePersistenceMappers;
|
||||
}) => {
|
||||
const { database, mappers } = params;
|
||||
|
||||
return new IssuedInvoiceRepository(domainMapper, listMapper, database);
|
||||
return new IssuedInvoiceRepository(mappers.domainMapper, mappers.summaryMapper, database);
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type ModuleParams, buildTransactionManager } from "@erp/core/api";
|
||||
import { type ModuleParams, buildCatalogs, buildTransactionManager } from "@erp/core/api";
|
||||
|
||||
import {
|
||||
type GetIssuedInvoiceByIdUseCase,
|
||||
@ -12,6 +12,7 @@ import {
|
||||
} from "../../../application/issued-invoices";
|
||||
|
||||
import { buildIssuedInvoiceDocumentService } from "./issued-invoice-documents.di";
|
||||
import { buildIssuedInvoicePersistenceMappers } from "./issued-invoice-persistence-mappers.di";
|
||||
import { buildIssuedInvoiceRepository } from "./issued-invoice-repositories.di";
|
||||
|
||||
export type IssuedInvoicesInternalDeps = {
|
||||
@ -27,7 +28,10 @@ export function buildIssuedInvoicesDependencies(params: ModuleParams): IssuedInv
|
||||
|
||||
// Infrastructure
|
||||
const transactionManager = buildTransactionManager(database);
|
||||
const repository = buildIssuedInvoiceRepository(database);
|
||||
const catalogs = buildCatalogs();
|
||||
const persistenceMappers = buildIssuedInvoicePersistenceMappers(catalogs);
|
||||
|
||||
const repository = buildIssuedInvoiceRepository({ database, mappers: persistenceMappers });
|
||||
|
||||
// Application helpers
|
||||
const finder = buildIssuedInvoiceFinder(repository);
|
||||
@ -40,7 +44,7 @@ export function buildIssuedInvoicesDependencies(params: ModuleParams): IssuedInv
|
||||
listIssuedInvoices: () =>
|
||||
buildListIssuedInvoicesUseCase({
|
||||
finder,
|
||||
itemSnapshotBuilder: snapshotBuilders.list,
|
||||
summarySnapshotBuilder: snapshotBuilders.list,
|
||||
transactionManager,
|
||||
}),
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
||||
import { DiscountPercentage, type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
||||
import {
|
||||
CurrencyCode,
|
||||
LanguageCode,
|
||||
@ -13,9 +13,7 @@ import {
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
|
||||
|
||||
import type { IIssuedInvoiceDomainMapper } from "../../../../../../application";
|
||||
import {
|
||||
DiscountPercentage,
|
||||
type IIssuedInvoiceProps,
|
||||
InvoiceAmount,
|
||||
InvoiceNumber,
|
||||
@ -36,14 +34,11 @@ import { SequelizeIssuedInvoiceRecipientDomainMapper } from "./sequelize-issued-
|
||||
import { SequelizeIssuedInvoiceTaxesDomainMapper } from "./sequelize-issued-invoice-taxes-domain.mapper";
|
||||
import { SequelizeIssuedInvoiceVerifactuDomainMapper } from "./sequelize-verifactu-record-domain.mapper";
|
||||
|
||||
export class SequelizeIssuedInvoiceDomainMapper
|
||||
extends SequelizeDomainMapper<
|
||||
CustomerInvoiceModel,
|
||||
CustomerInvoiceCreationAttributes,
|
||||
IssuedInvoice
|
||||
>
|
||||
implements IIssuedInvoiceDomainMapper
|
||||
{
|
||||
export class SequelizeIssuedInvoiceDomainMapper extends SequelizeDomainMapper<
|
||||
CustomerInvoiceModel,
|
||||
CustomerInvoiceCreationAttributes,
|
||||
IssuedInvoice
|
||||
> {
|
||||
private _itemsMapper: SequelizeIssuedInvoiceItemDomainMapper;
|
||||
private _recipientMapper: SequelizeIssuedInvoiceRecipientDomainMapper;
|
||||
private _taxesMapper: SequelizeIssuedInvoiceTaxesDomainMapper;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { JsonTaxCatalogProvider } from "@erp/core";
|
||||
import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
||||
import { type MapperParamsType, SequelizeDomainMapper, TaxPercentage } from "@erp/core/api";
|
||||
import {
|
||||
Percentage,
|
||||
UniqueID,
|
||||
@ -19,8 +19,6 @@ import {
|
||||
type IssuedInvoice,
|
||||
IssuedInvoiceTax,
|
||||
ItemAmount,
|
||||
ItemDiscountPercentage,
|
||||
TaxPercentage,
|
||||
} from "../../../../../../domain";
|
||||
import type {
|
||||
CustomerInvoiceTaxCreationAttributes,
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
export * from "./domain";
|
||||
export * from "./list";
|
||||
export * from "./summary";
|
||||
|
||||
@ -1 +0,0 @@
|
||||
export * from "./sequelize-issued-invoice.list.mapper";
|
||||
@ -0,0 +1 @@
|
||||
export * from "./sequelize-issued-invoice-summary.mapper";
|
||||
@ -22,7 +22,7 @@ export class SequelizeIssuedInvoiceRecipientListMapper extends SequelizeQueryMap
|
||||
CustomerInvoiceModel,
|
||||
InvoiceRecipient
|
||||
> {
|
||||
public mapToDTO(
|
||||
public mapToReadModel(
|
||||
raw: CustomerInvoiceModel,
|
||||
params?: MapperParamsType
|
||||
): Result<InvoiceRecipient, Error> {
|
||||
@ -11,10 +11,7 @@ import {
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
|
||||
import type {
|
||||
IIssuedInvoiceSummaryMapper,
|
||||
IssuedInvoiceSummary,
|
||||
} from "../../../../../../application";
|
||||
import type { IssuedInvoiceSummary } from "../../../../../../application";
|
||||
import {
|
||||
InvoiceAmount,
|
||||
InvoiceNumber,
|
||||
@ -24,20 +21,20 @@ import {
|
||||
} from "../../../../../../domain";
|
||||
import type { CustomerInvoiceModel } from "../../../../../common";
|
||||
|
||||
import { SequelizeIssuedInvoiceRecipientListMapper } from "./sequelize-issued-invoice-recipient.list.mapper";
|
||||
import { SequelizeVerifactuRecordListMapper } from "./sequelize-verifactu-record.list.mapper";
|
||||
import { SequelizeIssuedInvoiceRecipientListMapper } from "./sequelize-issued-invoice-recipient-summary.mapper";
|
||||
import { SequelizeVerifactuRecordSummaryMapper } from "./sequelize-verifactu-record-summary.mapper";
|
||||
|
||||
export class SequelizeIssuedInvoiceListMapper
|
||||
extends SequelizeQueryMapper<CustomerInvoiceModel, IssuedInvoiceSummary>
|
||||
implements IIssuedInvoiceSummaryMapper
|
||||
{
|
||||
export class SequelizeIssuedInvoiceSummaryMapper extends SequelizeQueryMapper<
|
||||
CustomerInvoiceModel,
|
||||
IssuedInvoiceSummary
|
||||
> {
|
||||
private _recipientMapper: SequelizeIssuedInvoiceRecipientListMapper;
|
||||
private _verifactuMapper: SequelizeVerifactuRecordListMapper;
|
||||
private _verifactuMapper: SequelizeVerifactuRecordSummaryMapper;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._recipientMapper = new SequelizeIssuedInvoiceRecipientListMapper();
|
||||
this._verifactuMapper = new SequelizeVerifactuRecordListMapper();
|
||||
this._verifactuMapper = new SequelizeVerifactuRecordSummaryMapper();
|
||||
}
|
||||
|
||||
public mapToReadModel(
|
||||
@ -50,7 +47,7 @@ export class SequelizeIssuedInvoiceListMapper
|
||||
const attributes = this._mapAttributesToReadModel(raw, { errors, ...params });
|
||||
|
||||
// 2) Recipient (snapshot en la factura o include)
|
||||
const recipientResult = this._recipientMapper.mapToDTO(raw, {
|
||||
const recipientResult = this._recipientMapper.mapToReadModel(raw, {
|
||||
errors,
|
||||
attributes,
|
||||
...params,
|
||||
@ -66,7 +63,10 @@ export class SequelizeIssuedInvoiceListMapper
|
||||
// 4) Verifactu record
|
||||
let verifactu: Maybe<VerifactuRecord> = Maybe.none();
|
||||
if (raw.verifactu) {
|
||||
const verifactuResult = this._verifactuMapper.mapToDTO(raw.verifactu, { errors, ...params });
|
||||
const verifactuResult = this._verifactuMapper.mapToReadModel(raw.verifactu, {
|
||||
errors,
|
||||
...params,
|
||||
});
|
||||
|
||||
if (verifactuResult.isFailure) {
|
||||
errors.push({
|
||||
@ -12,11 +12,11 @@ import { Result } from "@repo/rdx-utils";
|
||||
import { VerifactuRecord, VerifactuRecordEstado } from "../../../../../../domain";
|
||||
import type { VerifactuRecordModel } from "../../../../../common";
|
||||
|
||||
export class SequelizeVerifactuRecordListMapper extends SequelizeQueryMapper<
|
||||
export class SequelizeVerifactuRecordSummaryMapper extends SequelizeQueryMapper<
|
||||
VerifactuRecordModel,
|
||||
VerifactuRecord
|
||||
> {
|
||||
public mapToDTO(
|
||||
public mapToReadModel(
|
||||
raw: VerifactuRecordModel,
|
||||
params?: MapperParamsType
|
||||
): Result<VerifactuRecord, Error> {
|
||||
@ -14,7 +14,7 @@ import {
|
||||
} from "../../../../common";
|
||||
import type {
|
||||
SequelizeIssuedInvoiceDomainMapper,
|
||||
SequelizeIssuedInvoiceListMapper,
|
||||
SequelizeIssuedInvoiceSummaryMapper,
|
||||
} from "../mappers";
|
||||
|
||||
export class IssuedInvoiceRepository
|
||||
@ -23,7 +23,7 @@ export class IssuedInvoiceRepository
|
||||
{
|
||||
constructor(
|
||||
private readonly domainMapper: SequelizeIssuedInvoiceDomainMapper,
|
||||
private readonly listMapper: SequelizeIssuedInvoiceListMapper,
|
||||
private readonly summaryMapper: SequelizeIssuedInvoiceSummaryMapper,
|
||||
database: Sequelize
|
||||
) {
|
||||
super({ database });
|
||||
@ -216,8 +216,8 @@ export class IssuedInvoiceRepository
|
||||
const { CustomerModel } = this.database.models;
|
||||
|
||||
try {
|
||||
const converter = new CriteriaToSequelizeConverter();
|
||||
const query = converter.convert(criteria, {
|
||||
const criteriaConverter = new CriteriaToSequelizeConverter();
|
||||
const query = criteriaConverter.convert(criteria, {
|
||||
searchableFields: ["invoice_number", "reference", "description"],
|
||||
mappings: {
|
||||
reference: "CustomerInvoiceModel.reference",
|
||||
@ -301,7 +301,7 @@ export class IssuedInvoiceRepository
|
||||
}),
|
||||
]);
|
||||
|
||||
return this.listMapper.mapToReadModelCollection(rows, count);
|
||||
return this.summaryMapper.mapToReadModelCollection(rows, count);
|
||||
} catch (err: unknown) {
|
||||
return Result.fail(translateSequelizeError(err));
|
||||
}
|
||||
|
||||
@ -1,14 +1,11 @@
|
||||
import {
|
||||
CreateProformaInputMapper,
|
||||
type ICatalogs,
|
||||
type IProformaDomainMapper,
|
||||
type IProformaListMapper,
|
||||
} from "../../../application";
|
||||
import { SequelizeProformaDomainMapper, SequelizeProformaListMapper } from "../persistence";
|
||||
import type { ICatalogs } from "@erp/core/api";
|
||||
|
||||
import { CreateProformaInputMapper } from "../../../application";
|
||||
import { SequelizeProformaDomainMapper, SequelizeProformaSummaryMapper } from "../persistence";
|
||||
|
||||
export interface IProformaPersistenceMappers {
|
||||
domainMapper: IProformaDomainMapper;
|
||||
listMapper: IProformaListMapper;
|
||||
domainMapper: SequelizeProformaDomainMapper;
|
||||
listMapper: SequelizeProformaSummaryMapper;
|
||||
|
||||
createMapper: CreateProformaInputMapper;
|
||||
}
|
||||
@ -22,7 +19,7 @@ export const buildProformaPersistenceMappers = (
|
||||
const domainMapper = new SequelizeProformaDomainMapper({
|
||||
taxCatalog,
|
||||
});
|
||||
const listMapper = new SequelizeProformaListMapper();
|
||||
const listMapper = new SequelizeProformaSummaryMapper();
|
||||
|
||||
// Mappers el DTO a las props validadas (CustomerProps) y luego construir agregado
|
||||
const createMapper = new CreateProformaInputMapper({ taxCatalog });
|
||||
|
||||
@ -60,7 +60,7 @@ export function buildProformasDependencies(params: ModuleParams): ProformasInter
|
||||
listProformas: () =>
|
||||
buildListProformasUseCase({
|
||||
finder,
|
||||
itemSnapshotBuilder: snapshotBuilders.list,
|
||||
summarySnapshotBuilder: snapshotBuilders.summary,
|
||||
transactionManager,
|
||||
}),
|
||||
|
||||
@ -90,76 +90,3 @@ export function buildProformasDependencies(params: ModuleParams): ProformasInter
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/*const mapperRegistry = new InMemoryMapperRegistry();
|
||||
mapperRegistry
|
||||
.registerDomainMapper(
|
||||
{ resource: "customer-invoice" },
|
||||
new CustomerInvoiceDomainMapper({ taxCatalog: catalogs.taxes })
|
||||
)
|
||||
.registerQueryMappers([
|
||||
{
|
||||
key: { resource: "customer-invoice", query: "LIST" },
|
||||
mapper: new CustomerInvoiceListMapper(),
|
||||
},
|
||||
]);
|
||||
|
||||
// Repository & Services
|
||||
const numberGenerator = new SequelizeInvoiceNumberGenerator();
|
||||
|
||||
|
||||
const appService = new CustomerInvoiceApplicationService(repository, numberGenerator);
|
||||
|
||||
// Presenter Registry
|
||||
const presenterRegistry = new InMemoryPresenterRegistry();
|
||||
presenterRegistry.registerPresenters([
|
||||
// FULL
|
||||
{
|
||||
key: { resource: "proforma-items", projection: "FULL" },
|
||||
presenter: new ProformaItemsFullPresenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: { resource: "proforma-recipient", projection: "FULL" },
|
||||
presenter: new ProformaRecipientFullPresenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: { resource: "proforma", projection: "FULL" },
|
||||
presenter: new ProformaFullPresenter(presenterRegistry),
|
||||
},
|
||||
|
||||
// LIST
|
||||
{
|
||||
key: { resource: "proforma", projection: "LIST" },
|
||||
presenter: new ProformaListPresenter(presenterRegistry),
|
||||
},
|
||||
|
||||
// REPORT
|
||||
{
|
||||
key: { resource: "proforma", projection: "REPORT" },
|
||||
presenter: new ProformaReportPresenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: { resource: "proforma-taxes", projection: "REPORT" },
|
||||
presenter: new ProformaTaxesReportPresenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: { resource: "proforma-items", projection: "REPORT" },
|
||||
presenter: new ProformaItemsReportPresenter(presenterRegistry),
|
||||
},
|
||||
]);
|
||||
|
||||
const useCases: ProformasDeps["useCases"] = {
|
||||
// Proformas
|
||||
list_proformas: () => new ListProformasUseCase(appService, transactionManager, presenterRegistry),
|
||||
get_proforma: () => new GetProformaUseCase(appService, transactionManager, presenterRegistry),
|
||||
create_proforma: () =>
|
||||
new CreateProformaUseCase(appService, transactionManager, presenterRegistry, catalogs.taxes),
|
||||
update_proforma: () =>
|
||||
new UpdateProformaUseCase(appService, transactionManager, presenterRegistry),
|
||||
delete_proforma: () => new DeleteProformaUseCase(appService, transactionManager),
|
||||
report_proforma: () =>
|
||||
new ReportProformaUseCase(appService, transactionManager, presenterRegistry),
|
||||
issue_proforma: () => new IssueProformaUseCase(appService, transactionManager, presenterRegistry),
|
||||
changeStatus_proforma: () => new ChangeStatusProformaUseCase(appService, transactionManager),
|
||||
|
||||
*/
|
||||
|
||||
@ -13,7 +13,6 @@ import {
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
|
||||
|
||||
import type { IProformaDomainMapper } from "../../../../../../application";
|
||||
import {
|
||||
type IProformaProps,
|
||||
InvoiceNumber,
|
||||
@ -32,10 +31,11 @@ import { SequelizeProformaItemDomainMapper } from "./sequelize-proforma-item-dom
|
||||
import { SequelizeProformaRecipientDomainMapper } from "./sequelize-proforma-recipient-domain.mapper";
|
||||
import { SequelizeProformaTaxesDomainMapper } from "./sequelize-proforma-taxes-domain.mapper";
|
||||
|
||||
export class SequelizeProformaDomainMapper
|
||||
extends SequelizeDomainMapper<CustomerInvoiceModel, CustomerInvoiceCreationAttributes, Proforma>
|
||||
implements IProformaDomainMapper
|
||||
{
|
||||
export class SequelizeProformaDomainMapper extends SequelizeDomainMapper<
|
||||
CustomerInvoiceModel,
|
||||
CustomerInvoiceCreationAttributes,
|
||||
Proforma
|
||||
> {
|
||||
private _itemsMapper: SequelizeProformaItemDomainMapper;
|
||||
private _recipientMapper: SequelizeProformaRecipientDomainMapper;
|
||||
private _taxesMapper: SequelizeProformaTaxesDomainMapper;
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
export * from "./domain";
|
||||
export * from "./list";
|
||||
export * from "./summary";
|
||||
|
||||
@ -1 +0,0 @@
|
||||
export * from "./sequelize-proforma.list.mapper";
|
||||
@ -0,0 +1 @@
|
||||
export * from "./sequelize-proforma-summary.mapper";
|
||||
@ -1,8 +1,4 @@
|
||||
import {
|
||||
type IQueryMapperWithBulk,
|
||||
type MapperParamsType,
|
||||
SequelizeQueryMapper,
|
||||
} from "@erp/core/api";
|
||||
import { type MapperParamsType, SequelizeQueryMapper } from "@erp/core/api";
|
||||
import {
|
||||
City,
|
||||
Country,
|
||||
@ -18,18 +14,15 @@ import {
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
|
||||
import type { ProformaListDTO } from "../../../../../../application";
|
||||
import type { ProformaSummary } from "../../../../../../application";
|
||||
import { InvoiceRecipient } from "../../../../../../domain";
|
||||
import type { CustomerInvoiceModel } from "../../../../../common";
|
||||
|
||||
interface IInvoiceRecipientListMapper
|
||||
extends IQueryMapperWithBulk<CustomerInvoiceModel, InvoiceRecipient> {}
|
||||
|
||||
export class SequelizeInvoiceRecipientListMapper
|
||||
extends SequelizeQueryMapper<CustomerInvoiceModel, InvoiceRecipient>
|
||||
implements IInvoiceRecipientListMapper
|
||||
{
|
||||
public mapToDTO(
|
||||
export class SequelizeInvoiceRecipientSummaryMapper extends SequelizeQueryMapper<
|
||||
CustomerInvoiceModel,
|
||||
InvoiceRecipient
|
||||
> {
|
||||
public mapToReadModel(
|
||||
raw: CustomerInvoiceModel,
|
||||
params?: MapperParamsType
|
||||
): Result<InvoiceRecipient, Error> {
|
||||
@ -39,7 +32,7 @@ export class SequelizeInvoiceRecipientListMapper
|
||||
|
||||
const { errors, attributes } = params as {
|
||||
errors: ValidationErrorDetail[];
|
||||
attributes: Partial<ProformaListDTO>;
|
||||
attributes: Partial<ProformaSummary>;
|
||||
};
|
||||
|
||||
const { isProforma } = attributes;
|
||||
@ -11,7 +11,7 @@ import {
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
|
||||
import type { IProformaListMapper, ProformaListDTO } from "../../../../../../application";
|
||||
import type { ProformaSummary } from "../../../../../../application";
|
||||
import {
|
||||
InvoiceAmount,
|
||||
InvoiceNumber,
|
||||
@ -20,30 +20,30 @@ import {
|
||||
} from "../../../../../../domain";
|
||||
import type { CustomerInvoiceModel } from "../../../../../common";
|
||||
|
||||
import { SequelizeInvoiceRecipientListMapper } from "./sequelize-proforma-recipient.list.mapper";
|
||||
import { SequelizeInvoiceRecipientSummaryMapper } from "./sequelize-proforma-recipient-summary.mapper";
|
||||
|
||||
export class SequelizeProformaListMapper
|
||||
extends SequelizeQueryMapper<CustomerInvoiceModel, ProformaListDTO>
|
||||
implements IProformaListMapper
|
||||
{
|
||||
private _recipientMapper: SequelizeInvoiceRecipientListMapper;
|
||||
export class SequelizeProformaSummaryMapper extends SequelizeQueryMapper<
|
||||
CustomerInvoiceModel,
|
||||
ProformaSummary
|
||||
> {
|
||||
private _recipientMapper: SequelizeInvoiceRecipientSummaryMapper;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._recipientMapper = new SequelizeInvoiceRecipientListMapper();
|
||||
this._recipientMapper = new SequelizeInvoiceRecipientSummaryMapper();
|
||||
}
|
||||
|
||||
public mapToDTO(
|
||||
public mapToReadModel(
|
||||
raw: CustomerInvoiceModel,
|
||||
params?: MapperParamsType
|
||||
): Result<ProformaListDTO, Error> {
|
||||
): Result<ProformaSummary, Error> {
|
||||
const errors: ValidationErrorDetail[] = [];
|
||||
|
||||
// 1) Valores escalares (atributos generales)
|
||||
const attributes = this.mapAttributesToDTO(raw, { errors, ...params });
|
||||
|
||||
// 2) Recipient (snapshot en la factura o include)
|
||||
const recipientResult = this._recipientMapper.mapToDTO(raw, {
|
||||
const recipientResult = this._recipientMapper.mapToReadModel(raw, {
|
||||
errors,
|
||||
attributes,
|
||||
...params,
|
||||
@ -9,26 +9,22 @@ import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import { type Collection, Result } from "@repo/rdx-utils";
|
||||
import type { FindOptions, InferAttributes, OrderItem, Sequelize, Transaction } from "sequelize";
|
||||
|
||||
import type {
|
||||
IProformaDomainMapper,
|
||||
IProformaListMapper,
|
||||
IProformaRepository,
|
||||
ProformaListDTO,
|
||||
} from "../../../../../application";
|
||||
import type { IProformaRepository, ProformaSummary } from "../../../../../application";
|
||||
import type { InvoiceStatus, Proforma } from "../../../../../domain";
|
||||
import {
|
||||
CustomerInvoiceItemModel,
|
||||
CustomerInvoiceModel,
|
||||
CustomerInvoiceTaxModel,
|
||||
} from "../../../../common";
|
||||
import type { SequelizeProformaDomainMapper, SequelizeProformaSummaryMapper } from "../mappers";
|
||||
|
||||
export class ProformaRepository
|
||||
extends SequelizeRepository<Proforma>
|
||||
implements IProformaRepository
|
||||
{
|
||||
constructor(
|
||||
private readonly domainMapper: IProformaDomainMapper,
|
||||
private readonly listMapper: IProformaListMapper,
|
||||
private readonly domainMapper: SequelizeProformaDomainMapper,
|
||||
private readonly summaryMapper: SequelizeProformaSummaryMapper,
|
||||
database: Sequelize
|
||||
) {
|
||||
super({ database });
|
||||
@ -339,12 +335,12 @@ export class ProformaRepository
|
||||
criteria: Criteria,
|
||||
transaction: Transaction,
|
||||
options: FindOptions<InferAttributes<CustomerInvoiceModel>> = {}
|
||||
): Promise<Result<Collection<ProformaListDTO>, Error>> {
|
||||
): Promise<Result<Collection<ProformaSummary>, Error>> {
|
||||
const { CustomerModel } = this.database.models;
|
||||
|
||||
try {
|
||||
const converter = new CriteriaToSequelizeConverter();
|
||||
const query = converter.convert(criteria, {
|
||||
const criteriaConverter = new CriteriaToSequelizeConverter();
|
||||
const query = criteriaConverter.convert(criteria, {
|
||||
searchableFields: ["invoice_number", "reference", "description"],
|
||||
mappings: {
|
||||
reference: "CustomerInvoiceModel.reference",
|
||||
@ -424,7 +420,7 @@ export class ProformaRepository
|
||||
}),
|
||||
]);
|
||||
|
||||
return this.listMapper.mapToReadModelCollection(rows, count);
|
||||
return this.summaryMapper.mapToReadModelCollection(rows, count);
|
||||
} catch (err: unknown) {
|
||||
return Result.fail(translateSequelizeError(err));
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { IProformaRepository } from "../repositories";
|
||||
import { type IProformaFinder, ProformaFinder } from "../services";
|
||||
import type { ICustomerRepository } from "../repositories";
|
||||
import { CustomerFinder, type ICustomerFinder } from "../services";
|
||||
|
||||
export function buildProformaFinder(repository: IProformaRepository): IProformaFinder {
|
||||
return new ProformaFinder(repository);
|
||||
export function buildCustomerFinder(repository: ICustomerRepository): ICustomerFinder {
|
||||
return new CustomerFinder(repository);
|
||||
}
|
||||
|
||||
@ -1,18 +1,9 @@
|
||||
// application/issued-invoices/di/snapshot-builders.di.ts
|
||||
|
||||
import {
|
||||
CustomerFullSnapshotBuilder,
|
||||
CustomerItemReportSnapshotBuilder,
|
||||
CustomerItemsFullSnapshotBuilder,
|
||||
CustomerListItemSnapshotBuilder,
|
||||
CustomerRecipientFullSnapshotBuilder,
|
||||
CustomerReportSnapshotBuilder,
|
||||
CustomerTaxReportSnapshotBuilder,
|
||||
CustomerTaxesFullSnapshotBuilder,
|
||||
} from "../snapshot-builders";
|
||||
import { CustomerSummarySnapshotBuilder } from "../snapshot-builders";
|
||||
|
||||
export function buildCustomerSnapshotBuilders() {
|
||||
const itemsBuilder = new CustomerItemsFullSnapshotBuilder();
|
||||
/*const itemsBuilder = new CustomerItemsFullSnapshotBuilder();
|
||||
|
||||
const taxesBuilder = new CustomerTaxesFullSnapshotBuilder();
|
||||
|
||||
@ -22,20 +13,20 @@ export function buildCustomerSnapshotBuilders() {
|
||||
itemsBuilder,
|
||||
recipientBuilder,
|
||||
taxesBuilder
|
||||
);
|
||||
);*/
|
||||
|
||||
const listSnapshotBuilder = new CustomerListItemSnapshotBuilder();
|
||||
const summarySnapshotBuilder = new CustomerSummarySnapshotBuilder();
|
||||
|
||||
const itemsReportBuilder = new CustomerItemReportSnapshotBuilder();
|
||||
/*const itemsReportBuilder = new CustomerItemReportSnapshotBuilder();
|
||||
const taxesReportBuilder = new CustomerTaxReportSnapshotBuilder();
|
||||
const reportSnapshotBuilder = new CustomerReportSnapshotBuilder(
|
||||
itemsReportBuilder,
|
||||
taxesReportBuilder
|
||||
);
|
||||
);*/
|
||||
|
||||
return {
|
||||
full: fullSnapshotBuilder,
|
||||
list: listSnapshotBuilder,
|
||||
report: reportSnapshotBuilder,
|
||||
//full: fullSnapshotBuilder,
|
||||
summary: summarySnapshotBuilder,
|
||||
//report: reportSnapshotBuilder,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,36 +1,30 @@
|
||||
import type { ITransactionManager } from "@erp/core/api";
|
||||
|
||||
import type { ICreateCustomerInputMapper } from "../mappers";
|
||||
import type {
|
||||
ICustomerCreator,
|
||||
ICustomerFinder,
|
||||
CustomerDocumentGeneratorService,
|
||||
} from "../services";
|
||||
import type {
|
||||
ICustomerListItemSnapshotBuilder,
|
||||
ICustomerReportSnapshotBuilder,
|
||||
} from "../snapshot-builders";
|
||||
import type { ICustomerFullSnapshotBuilder } from "../snapshot-builders/full";
|
||||
import { GetCustomerByIdUseCase, ListCustomersUseCase, ReportCustomerUseCase } from "../use-cases";
|
||||
import { CreateCustomerUseCase } from "../use-cases/create-customer";
|
||||
import type { ICustomerFinder } from "../services";
|
||||
import type { ICustomerSummarySnapshotBuilder } from "../snapshot-builders";
|
||||
import { ListCustomersUseCase } from "../use-cases";
|
||||
|
||||
export function buildGetCustomerByIdUseCase(deps: {
|
||||
/*export function buildGetCustomerByIdUseCase(deps: {
|
||||
finder: ICustomerFinder;
|
||||
fullSnapshotBuilder: ICustomerFullSnapshotBuilder;
|
||||
transactionManager: ITransactionManager;
|
||||
}) {
|
||||
return new GetCustomerByIdUseCase(deps.finder, deps.fullSnapshotBuilder, deps.transactionManager);
|
||||
}
|
||||
}*/
|
||||
|
||||
export function buildListCustomersUseCase(deps: {
|
||||
finder: ICustomerFinder;
|
||||
itemSnapshotBuilder: ICustomerListItemSnapshotBuilder;
|
||||
summarySnapshotBuilder: ICustomerSummarySnapshotBuilder;
|
||||
transactionManager: ITransactionManager;
|
||||
}) {
|
||||
return new ListCustomersUseCase(deps.finder, deps.itemSnapshotBuilder, deps.transactionManager);
|
||||
return new ListCustomersUseCase(
|
||||
deps.finder,
|
||||
deps.summarySnapshotBuilder,
|
||||
deps.transactionManager
|
||||
);
|
||||
}
|
||||
|
||||
export function buildReportCustomerUseCase(deps: {
|
||||
/*export function buildReportCustomerUseCase(deps: {
|
||||
finder: ICustomerFinder;
|
||||
fullSnapshotBuilder: ICustomerFullSnapshotBuilder;
|
||||
reportSnapshotBuilder: ICustomerReportSnapshotBuilder;
|
||||
@ -58,7 +52,7 @@ export function buildCreateCustomerUseCase(deps: {
|
||||
fullSnapshotBuilder: deps.fullSnapshotBuilder,
|
||||
transactionManager: deps.transactionManager,
|
||||
});
|
||||
}
|
||||
}*/
|
||||
|
||||
/*export function buildUpdateCustomerUseCase(deps: {
|
||||
finder: ICustomerFinder;
|
||||
|
||||
@ -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,5 +1,4 @@
|
||||
export * from "./di";
|
||||
export * from "./mappers";
|
||||
export * from "./models";
|
||||
export * from "./repositories";
|
||||
export * from "./services";
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
import type { DomainMapperWithBulk } from "@erp/core/api";
|
||||
|
||||
import type { Customer } from "../../domain";
|
||||
import type { CustomerCreationAttributes, CustomerModel } from "../../infrastructure";
|
||||
|
||||
export interface ICustomerDomainMapper
|
||||
extends DomainMapperWithBulk<CustomerModel | CustomerCreationAttributes, Customer> {}
|
||||
@ -1,7 +0,0 @@
|
||||
import type { IQueryMapperWithBulk } from "@erp/core/api";
|
||||
|
||||
import type { CustomerModel } from "../../infrastructure";
|
||||
import type { CustomerSummary } from "../models";
|
||||
|
||||
export interface ICustomerSummaryMapper
|
||||
extends IQueryMapperWithBulk<CustomerModel, CustomerSummary> {}
|
||||
@ -1,2 +0,0 @@
|
||||
export * from "./customer-domain-mapper.interface";
|
||||
export * from "./customer-summary-mapper.interface";
|
||||
@ -4,7 +4,7 @@ import type { Collection, Result } from "@repo/rdx-utils";
|
||||
import type { Transaction } from "sequelize";
|
||||
|
||||
import type { Customer } from "../../domain";
|
||||
import type { CustomerListDTO } from "../dtos";
|
||||
import type { CustomerSummary } from "../models";
|
||||
import type { ICustomerRepository } from "../repositories";
|
||||
|
||||
export interface ICustomerFinder {
|
||||
@ -24,7 +24,7 @@ export interface ICustomerFinder {
|
||||
companyId: UniqueID,
|
||||
criteria: Criteria,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<Collection<CustomerListDTO>, Error>>;
|
||||
): Promise<Result<Collection<CustomerSummary>, Error>>;
|
||||
}
|
||||
|
||||
export class CustomerFinder implements ICustomerFinder {
|
||||
@ -50,7 +50,7 @@ export class CustomerFinder implements ICustomerFinder {
|
||||
companyId: UniqueID,
|
||||
criteria: Criteria,
|
||||
transaction?: Transaction
|
||||
): Promise<Result<Collection<CustomerListDTO>, Error>> {
|
||||
): Promise<Result<Collection<CustomerSummary>, Error>> {
|
||||
return this.repository.findByCriteriaInCompany(companyId, criteria, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
//export * from "./full";
|
||||
export * from "./list";
|
||||
export * from "./summary";
|
||||
//export * from "./report";
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||
|
||||
import type { CustomerListDTO } from "../../dtos";
|
||||
|
||||
import type { ICustomerListItemSnapshot } from "./customer-list-item-snapshot.interface";
|
||||
|
||||
export interface ICustomerListItemSnapshotBuilder
|
||||
extends ISnapshotBuilder<CustomerListDTO, ICustomerListItemSnapshot> {}
|
||||
|
||||
export class CustomerListItemSnapshotBuilder implements ICustomerListItemSnapshotBuilder {
|
||||
toOutput(customer: CustomerListDTO): ICustomerListItemSnapshot {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export type ICustomerListItemSnapshot = {};
|
||||
@ -1,2 +0,0 @@
|
||||
export * from "./customer-list-item-snapshot.interface";
|
||||
export * from "./customer-list-item-snapshot-builder";
|
||||
@ -0,0 +1,49 @@
|
||||
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||
import { maybeToEmptyString } from "@repo/rdx-ddd";
|
||||
|
||||
import type { CustomerSummary } from "../../models";
|
||||
|
||||
import type { ICustomerSummarySnapshot } from "./customer-summary-snapshot.interface";
|
||||
|
||||
export interface ICustomerSummarySnapshotBuilder
|
||||
extends ISnapshotBuilder<CustomerSummary, ICustomerSummarySnapshot> {}
|
||||
|
||||
export class CustomerSummarySnapshotBuilder implements ICustomerSummarySnapshotBuilder {
|
||||
toOutput(customer: CustomerSummary): ICustomerSummarySnapshot {
|
||||
const { address } = customer;
|
||||
|
||||
return {
|
||||
id: customer.id.toString(),
|
||||
company_id: customer.companyId.toString(),
|
||||
status: customer.status.toString(),
|
||||
reference: maybeToEmptyString(customer.reference, (value) => value.toString()),
|
||||
|
||||
is_company: String(customer.isCompany),
|
||||
name: customer.name.toString(),
|
||||
trade_name: maybeToEmptyString(customer.tradeName, (value) => value.toString()),
|
||||
tin: maybeToEmptyString(customer.tin, (value) => value.toString()),
|
||||
|
||||
street: maybeToEmptyString(address.street, (value) => value.toString()),
|
||||
street2: maybeToEmptyString(address.street2, (value) => value.toString()),
|
||||
city: maybeToEmptyString(address.city, (value) => value.toString()),
|
||||
postal_code: maybeToEmptyString(address.postalCode, (value) => value.toString()),
|
||||
province: maybeToEmptyString(address.province, (value) => value.toString()),
|
||||
country: maybeToEmptyString(address.country, (value) => value.toString()),
|
||||
|
||||
email_primary: maybeToEmptyString(customer.emailPrimary, (value) => value.toString()),
|
||||
email_secondary: maybeToEmptyString(customer.emailSecondary, (value) => value.toString()),
|
||||
|
||||
phone_primary: maybeToEmptyString(customer.phonePrimary, (value) => value.toString()),
|
||||
phone_secondary: maybeToEmptyString(customer.phoneSecondary, (value) => value.toString()),
|
||||
|
||||
mobile_primary: maybeToEmptyString(customer.mobilePrimary, (value) => value.toString()),
|
||||
mobile_secondary: maybeToEmptyString(customer.mobileSecondary, (value) => value.toString()),
|
||||
|
||||
fax: maybeToEmptyString(customer.fax, (value) => value.toString()),
|
||||
website: maybeToEmptyString(customer.website, (value) => value.toString()),
|
||||
|
||||
language_code: customer.languageCode.code,
|
||||
currency_code: customer.currencyCode.code,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
export type ICustomerSummarySnapshot = {
|
||||
id: string;
|
||||
company_id: string;
|
||||
status: string;
|
||||
reference: string;
|
||||
|
||||
is_company: string;
|
||||
name: string;
|
||||
trade_name: string;
|
||||
tin: string;
|
||||
|
||||
street: string;
|
||||
street2: string;
|
||||
city: string;
|
||||
postal_code: string;
|
||||
province: string;
|
||||
country: string;
|
||||
|
||||
email_primary: string;
|
||||
email_secondary: string;
|
||||
|
||||
phone_primary: string;
|
||||
phone_secondary: string;
|
||||
|
||||
mobile_primary: string;
|
||||
mobile_secondary: string;
|
||||
|
||||
fax: string;
|
||||
website: string;
|
||||
|
||||
language_code: string;
|
||||
currency_code: string;
|
||||
|
||||
metadata?: Record<string, string>;
|
||||
};
|
||||
@ -0,0 +1,2 @@
|
||||
export * from "./customer-summary-snapshot.interface";
|
||||
export * from "./customer-summary-snapshot-builder";
|
||||
@ -4,9 +4,8 @@ import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import type { Transaction } from "sequelize";
|
||||
|
||||
import type { ListCustomersResponseDTO } from "../../../common/dto";
|
||||
import type { ICustomerFinder } from "../services";
|
||||
import type { ICustomerListItemSnapshotBuilder } from "../snapshot-builders/list";
|
||||
import type { ICustomerSummarySnapshotBuilder } from "../snapshot-builders/summary";
|
||||
|
||||
type ListCustomersUseCaseInput = {
|
||||
companyId: UniqueID;
|
||||
@ -16,13 +15,11 @@ type ListCustomersUseCaseInput = {
|
||||
export class ListCustomersUseCase {
|
||||
constructor(
|
||||
private readonly finder: ICustomerFinder,
|
||||
private readonly listItemSnapshotBuilder: ICustomerListItemSnapshotBuilder,
|
||||
private readonly summarySnapshotBuilder: ICustomerSummarySnapshotBuilder,
|
||||
private readonly transactionManager: ITransactionManager
|
||||
) {}
|
||||
|
||||
public execute(
|
||||
params: ListCustomersUseCaseInput
|
||||
): Promise<Result<ListCustomersResponseDTO, Error>> {
|
||||
public execute(params: ListCustomersUseCaseInput) {
|
||||
const { criteria, companyId } = params;
|
||||
|
||||
return this.transactionManager.complete(async (transaction: Transaction) => {
|
||||
@ -36,7 +33,7 @@ export class ListCustomersUseCase {
|
||||
const customers = result.data;
|
||||
const totalCustomers = customers.total();
|
||||
|
||||
const items = customers.map((item) => this.listItemSnapshotBuilder.toOutput(item));
|
||||
const items = customers.map((item) => this.summarySnapshotBuilder.toOutput(item));
|
||||
|
||||
const snapshot = {
|
||||
page: criteria.pageNumber,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { IModuleServer } from "@erp/core/api";
|
||||
|
||||
import { customersRouter, models } from "./infrastructure";
|
||||
import { buildCustomersDependencies, buildCustomerServices, CustomersInternalDeps } from "./infrastructure/di";
|
||||
|
||||
export * from "./infrastructure/sequelize";
|
||||
|
||||
@ -19,12 +20,11 @@ export const customersAPIModule: IModuleServer = {
|
||||
async setup(params) {
|
||||
const { env: ENV, app, database, baseRoutePath: API_BASE_PATH, logger } = params;
|
||||
|
||||
|
||||
// 1) Dominio interno
|
||||
//const customerInternalDeps = buildCustomerDependencies(params);
|
||||
const customerInternalDeps = buildCustomersDependencies(params);
|
||||
|
||||
// 2) Servicios públicos (Application Services)
|
||||
//const customerServices = buildCustomerServices(customerInternalDeps);
|
||||
const customerServices = buildCustomerServices(customerInternalDeps);
|
||||
|
||||
logger.info("🚀 Customers module dependencies registered", {
|
||||
label: this.name,
|
||||
@ -35,14 +35,10 @@ export const customersAPIModule: IModuleServer = {
|
||||
models,
|
||||
|
||||
// Servicios expuestos a otros módulos
|
||||
services: {
|
||||
//customers: customerServices,
|
||||
},
|
||||
|
||||
services: customerServices,
|
||||
|
||||
// Implementación privada del módulo
|
||||
internal: {
|
||||
//customers: customerInternalDeps,
|
||||
},
|
||||
internal:customerInternalDeps,
|
||||
};
|
||||
},
|
||||
|
||||
@ -57,7 +53,7 @@ export const customersAPIModule: IModuleServer = {
|
||||
const { app, baseRoutePath, logger, getInternal } = params;
|
||||
|
||||
// Recuperamos el dominio interno del módulo
|
||||
const customersInternalDeps = getInternal("customers", "customers");
|
||||
const customersInternalDeps = getInternal<CustomersInternalDeps>("customers");
|
||||
|
||||
// Registro de rutas HTTP
|
||||
customersRouter(params, customersInternalDeps);
|
||||
|
||||
@ -1,76 +0,0 @@
|
||||
import type { IMapperRegistry, IPresenterRegistry, ModuleParams } from "@erp/core/api";
|
||||
import {
|
||||
InMemoryMapperRegistry,
|
||||
InMemoryPresenterRegistry,
|
||||
SequelizeTransactionManager,
|
||||
} from "@erp/core/api";
|
||||
|
||||
import {
|
||||
CreateCustomerUseCase,
|
||||
GetCustomerUseCase,
|
||||
ListCustomersUseCase,
|
||||
UpdateCustomerUseCase,
|
||||
} from "../application";
|
||||
import { CustomerApplicationService } from "../application/customer-application.service";
|
||||
import { CustomerFullPresenter, ListCustomersPresenter } from "../application/presenters";
|
||||
|
||||
import { CustomerDomainMapper, CustomerSummaryMapper } from "./mappers";
|
||||
import { CustomerRepository } from "./sequelize";
|
||||
|
||||
export type CustomerDeps = {
|
||||
transactionManager: SequelizeTransactionManager;
|
||||
mapperRegistry: IMapperRegistry;
|
||||
presenterRegistry: IPresenterRegistry;
|
||||
repo: CustomerRepository;
|
||||
service: CustomerApplicationService;
|
||||
build: {
|
||||
list: () => ListCustomersUseCase;
|
||||
get: () => GetCustomerUseCase;
|
||||
create: () => CreateCustomerUseCase;
|
||||
update: () => UpdateCustomerUseCase;
|
||||
//delete: () => DeleteCustomerUseCase;
|
||||
};
|
||||
};
|
||||
|
||||
export function buildCustomerDependencies(params: ModuleParams): CustomerDeps {
|
||||
const { database } = params;
|
||||
const transactionManager = new SequelizeTransactionManager(database);
|
||||
|
||||
// Mapper Registry
|
||||
const mapperRegistry = new InMemoryMapperRegistry();
|
||||
mapperRegistry
|
||||
.registerDomainMapper({ resource: "customer" }, new CustomerDomainMapper())
|
||||
.registerQueryMapper({ resource: "customer", query: "LIST" }, new CustomerSummaryMapper());
|
||||
|
||||
// Repository & Services
|
||||
const repo = new CustomerRepository({ mapperRegistry, database });
|
||||
const service = new CustomerApplicationService(repo);
|
||||
|
||||
// Presenter Registry
|
||||
const presenterRegistry = new InMemoryPresenterRegistry();
|
||||
presenterRegistry.registerPresenters([
|
||||
{
|
||||
key: { resource: "customer", projection: "FULL" },
|
||||
presenter: new CustomerFullPresenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: { resource: "customer", projection: "LIST" },
|
||||
presenter: new ListCustomersPresenter(presenterRegistry),
|
||||
},
|
||||
]);
|
||||
|
||||
return {
|
||||
transactionManager,
|
||||
repo,
|
||||
mapperRegistry,
|
||||
presenterRegistry,
|
||||
service,
|
||||
build: {
|
||||
list: () => new ListCustomersUseCase(service, transactionManager, presenterRegistry),
|
||||
get: () => new GetCustomerUseCase(service, transactionManager, presenterRegistry),
|
||||
create: () => new CreateCustomerUseCase(service, transactionManager, presenterRegistry),
|
||||
update: () => new UpdateCustomerUseCase(service, transactionManager, presenterRegistry),
|
||||
//delete: () => new DeleteCustomerUseCase(_service!, transactionManager!),
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -1,36 +1,24 @@
|
||||
import {
|
||||
CreateCustomerInputMapper,
|
||||
type ICatalogs,
|
||||
type ICustomerDomainMapper,
|
||||
type ICustomerListMapper,
|
||||
} from "../../../application";
|
||||
import { SequelizeCustomerDomainMapper, SequelizeCustomerListMapper } from "../persistence";
|
||||
import { SequelizeCustomerDomainMapper, SequelizeCustomerSummaryMapper } from "../mappers";
|
||||
|
||||
export interface ICustomerPersistenceMappers {
|
||||
domainMapper: ICustomerDomainMapper;
|
||||
listMapper: ICustomerListMapper;
|
||||
domainMapper: SequelizeCustomerDomainMapper;
|
||||
summaryMapper: SequelizeCustomerSummaryMapper;
|
||||
|
||||
createMapper: CreateCustomerInputMapper;
|
||||
//createMapper: CreateCustomerInputMapper;
|
||||
}
|
||||
|
||||
export const buildCustomerPersistenceMappers = (
|
||||
catalogs: ICatalogs
|
||||
): ICustomerPersistenceMappers => {
|
||||
const { taxCatalog } = catalogs;
|
||||
|
||||
export const buildCustomerPersistenceMappers = (): ICustomerPersistenceMappers => {
|
||||
// Mappers para el repositorio
|
||||
const domainMapper = new SequelizeCustomerDomainMapper({
|
||||
taxCatalog,
|
||||
});
|
||||
const listMapper = new SequelizeCustomerListMapper();
|
||||
const domainMapper = new SequelizeCustomerDomainMapper();
|
||||
const summaryMapper = new SequelizeCustomerSummaryMapper();
|
||||
|
||||
// Mappers el DTO a las props validadas (CustomerProps) y luego construir agregado
|
||||
const createMapper = new CreateCustomerInputMapper({ taxCatalog });
|
||||
//const createMapper = new CreateCustomerInputMapper();
|
||||
|
||||
return {
|
||||
domainMapper,
|
||||
listMapper,
|
||||
summaryMapper,
|
||||
|
||||
createMapper,
|
||||
//createMapper,
|
||||
};
|
||||
};
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
import type { CustomersInternalDeps } from "./customers.di";
|
||||
|
||||
export type CustomersServicesDeps = {
|
||||
services: {
|
||||
listCustomers: (filters: unknown, context: unknown) => null;
|
||||
getCustomerById: (id: unknown, context: unknown) => null;
|
||||
generateCustomerReport: (id: unknown, options: unknown, context: unknown) => null;
|
||||
};
|
||||
};
|
||||
|
||||
export function buildCustomerServices(deps: CustomersInternalDeps): CustomersServicesDeps {
|
||||
return {
|
||||
services: {
|
||||
listCustomers: (filters, context) => null,
|
||||
//internal.useCases.listCustomers().execute(filters, context),
|
||||
|
||||
getCustomerById: (id, context) => null,
|
||||
//internal.useCases.getCustomerById().execute(id, context),
|
||||
|
||||
generateCustomerReport: (id, options, context) => null,
|
||||
//internal.useCases.reportCustomer().execute(id, options, context),
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import type { Sequelize } from "sequelize";
|
||||
|
||||
import { CustomerRepository } from "../persistence";
|
||||
import { CustomerRepository } from "../sequelize";
|
||||
|
||||
import type { ICustomerPersistenceMappers } from "./customer-persistence-mappers.di";
|
||||
|
||||
@ -10,5 +10,5 @@ export const buildCustomerRepository = (params: {
|
||||
}) => {
|
||||
const { database, mappers } = params;
|
||||
|
||||
return new CustomerRepository(mappers.domainMapper, mappers.listMapper, database);
|
||||
return new CustomerRepository(mappers.domainMapper, mappers.summaryMapper, database);
|
||||
};
|
||||
|
||||
@ -1,11 +1,21 @@
|
||||
import { type ModuleParams, buildCatalogs, buildTransactionManager } from "@erp/core/api";
|
||||
|
||||
import {
|
||||
type ListCustomersUseCase,
|
||||
buildCustomerFinder,
|
||||
buildCustomerSnapshotBuilders,
|
||||
buildListCustomersUseCase,
|
||||
} from "../../application";
|
||||
|
||||
import { buildCustomerPersistenceMappers } from "./customer-persistence-mappers.di";
|
||||
import { buildCustomerRepository } from "./customer-repositories.di";
|
||||
|
||||
export type CustomersInternalDeps = {
|
||||
useCases: {
|
||||
listCustomers: () => ListCustomersUseCase;
|
||||
getCustomerById: () => GetCustomerByIdUseCase;
|
||||
reportCustomer: () => ReportCustomerUseCase;
|
||||
createCustomer: () => CreateCustomerUseCase;
|
||||
//getCustomerById: () => GetCustomerByIdUseCase;
|
||||
//reportCustomer: () => ReportCustomerUseCase;
|
||||
//createCustomer: () => CreateCustomerUseCase;
|
||||
|
||||
/*
|
||||
updateCustomer: () => UpdateCustomerUseCase;
|
||||
@ -15,25 +25,24 @@ export type CustomersInternalDeps = {
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
export function buildCustomersDependencies(params: ModuleParams): CustomersInternalDeps {
|
||||
const { database } = params;
|
||||
|
||||
// Infrastructure
|
||||
const transactionManager = buildTransactionManager(database);
|
||||
const catalogs = buildCatalogs();
|
||||
const persistenceMappers = buildCustomerPersistenceMappers(catalogs);
|
||||
const persistenceMappers = buildCustomerPersistenceMappers();
|
||||
|
||||
const repository = buildCustomerRepository({ database, mappers: persistenceMappers });
|
||||
const numberService = buildCustomerNumberGenerator();
|
||||
//const numberService = buildCustomerNumberGenerator();
|
||||
|
||||
// Application helpers
|
||||
const inputMappers = buildCustomerInputMappers(catalogs);
|
||||
//const inputMappers = buildCustomerInputMappers(catalogs);
|
||||
const finder = buildCustomerFinder(repository);
|
||||
const creator = buildCustomerCreator({ numberService, repository });
|
||||
//const creator = buildCustomerCreator({ numberService, repository });
|
||||
|
||||
const snapshotBuilders = buildCustomersnapshotBuilders();
|
||||
const documentGeneratorPipeline = buildCustomerDocumentService(params);
|
||||
const snapshotBuilders = buildCustomerSnapshotBuilders();
|
||||
//const documentGeneratorPipeline = buildCustomerDocumentService(params);
|
||||
|
||||
// Internal use cases (factories)
|
||||
return {
|
||||
@ -41,11 +50,11 @@ export function buildCustomersDependencies(params: ModuleParams): CustomersInter
|
||||
listCustomers: () =>
|
||||
buildListCustomersUseCase({
|
||||
finder,
|
||||
itemSnapshotBuilder: snapshotBuilders.list,
|
||||
summarySnapshotBuilder: snapshotBuilders.summary,
|
||||
transactionManager,
|
||||
}),
|
||||
|
||||
getCustomerById: () =>
|
||||
/*getCustomerById: () =>
|
||||
buildGetCustomerByIdUseCase({
|
||||
finder,
|
||||
fullSnapshotBuilder: snapshotBuilders.full,
|
||||
@ -67,7 +76,7 @@ export function buildCustomersDependencies(params: ModuleParams): CustomersInter
|
||||
dtoMapper: inputMappers.createInputMapper,
|
||||
fullSnapshotBuilder: snapshotBuilders.full,
|
||||
transactionManager,
|
||||
}),
|
||||
}),*/
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -1 +1,2 @@
|
||||
export * from "./customers.di";
|
||||
export * from "./customer-public-services";
|
||||
export * from "./customers.di";
|
||||
|
||||
@ -1,39 +1,22 @@
|
||||
import {
|
||||
type RequestWithAuth,
|
||||
mockUser,
|
||||
requireAuthenticated,
|
||||
requireCompanyContext,
|
||||
} from "@erp/auth/api";
|
||||
import { type ModuleParams, validateRequest } from "@erp/core/api";
|
||||
import type { ILogger } from "@repo/rdx-logger";
|
||||
import { type Application, type NextFunction, type Request, type Response, Router } from "express";
|
||||
import type { Sequelize } from "sequelize";
|
||||
import { type ModuleParams, RequestWithAuth, validateRequest } from "@erp/core/api";
|
||||
import { type NextFunction, type Request, type Response, Router } from "express";
|
||||
|
||||
import {
|
||||
CreateCustomerRequestSchema,
|
||||
CustomerListRequestSchema,
|
||||
GetCustomerByIdRequestSchema,
|
||||
UpdateCustomerByIdParamsRequestSchema,
|
||||
UpdateCustomerByIdRequestSchema,
|
||||
CustomerListRequestSchema
|
||||
} from "../../../common/dto";
|
||||
import { buildCustomerDependencies } from "../dependencies";
|
||||
import type { CustomersInternalDeps } from "../di";
|
||||
|
||||
import {
|
||||
CreateCustomerController,
|
||||
GetCustomerController,
|
||||
ListCustomersController,
|
||||
UpdateCustomerController,
|
||||
ListCustomersController
|
||||
} from "./controllers";
|
||||
|
||||
export const customersRouter = (params: ModuleParams, deps: CustomerInternalDeps) => {
|
||||
const { app, baseRoutePath, logger } = params as {
|
||||
app: Application;
|
||||
database: Sequelize;
|
||||
baseRoutePath: string;
|
||||
logger: ILogger;
|
||||
};
|
||||
|
||||
const deps = buildCustomerDependencies(params);
|
||||
export const customersRouter = (params: ModuleParams, deps: CustomersInternalDeps) => {
|
||||
const { app, config } = params;
|
||||
|
||||
const router: Router = Router({ mergeParams: true });
|
||||
|
||||
@ -62,49 +45,49 @@ export const customersRouter = (params: ModuleParams, deps: CustomerInternalDeps
|
||||
|
||||
validateRequest(CustomerListRequestSchema, "params"),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const useCase = deps.build.list();
|
||||
const useCase = deps.useCases.listCustomers();
|
||||
const controller = new ListCustomersController(useCase /*, deps.presenters.list */);
|
||||
return controller.execute(req, res, next);
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
/* router.get(
|
||||
"/:customer_id",
|
||||
//checkTabContext,
|
||||
|
||||
validateRequest(GetCustomerByIdRequestSchema, "params"),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const useCase = deps.build.get();
|
||||
const useCase = deps.useCases.get();
|
||||
const controller = new GetCustomerController(useCase);
|
||||
return controller.execute(req, res, next);
|
||||
}
|
||||
);
|
||||
); */
|
||||
|
||||
router.post(
|
||||
/* router.post(
|
||||
"/",
|
||||
//checkTabContext,
|
||||
|
||||
validateRequest(CreateCustomerRequestSchema, "body"),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const useCase = deps.build.create();
|
||||
const useCase = deps.useCases.create();
|
||||
const controller = new CreateCustomerController(useCase);
|
||||
return controller.execute(req, res, next);
|
||||
}
|
||||
);
|
||||
); */
|
||||
|
||||
router.put(
|
||||
/* router.put(
|
||||
"/:customer_id",
|
||||
//checkTabContext,
|
||||
|
||||
validateRequest(UpdateCustomerByIdParamsRequestSchema, "params"),
|
||||
validateRequest(UpdateCustomerByIdRequestSchema, "body"),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const useCase = deps.build.update();
|
||||
const useCase = deps.useCases.update();
|
||||
const controller = new UpdateCustomerController(useCase);
|
||||
return controller.execute(req, res, next);
|
||||
}
|
||||
);
|
||||
|
||||
*/
|
||||
/*router.delete(
|
||||
"/:customer_id",
|
||||
//checkTabContext,
|
||||
@ -117,5 +100,5 @@ export const customersRouter = (params: ModuleParams, deps: CustomerInternalDeps
|
||||
}
|
||||
);*/
|
||||
|
||||
app.use(`${baseRoutePath}/customers`, router);
|
||||
app.use(`${config.server.apiBasePath}/customers`, router);
|
||||
};
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "./customer.mapper";
|
||||
export * from "./sequelize-customer.mapper";
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
||||
import type { ICustomerDomainMapper } from "@erp/customers/api/application";
|
||||
import {
|
||||
City,
|
||||
Country,
|
||||
@ -28,10 +27,11 @@ import { Collection, Result } from "@repo/rdx-utils";
|
||||
import { Customer, CustomerStatus, type ICustomerProps } from "../../../domain";
|
||||
import type { CustomerCreationAttributes, CustomerModel } from "../../sequelize";
|
||||
|
||||
export class CustomerDomainMapper
|
||||
extends SequelizeDomainMapper<CustomerModel, CustomerCreationAttributes, Customer>
|
||||
implements ICustomerDomainMapper
|
||||
{
|
||||
export class SequelizeCustomerDomainMapper extends SequelizeDomainMapper<
|
||||
CustomerModel,
|
||||
CustomerCreationAttributes,
|
||||
Customer
|
||||
> {
|
||||
public mapToDomain(source: CustomerModel, params?: MapperParamsType): Result<Customer, Error> {
|
||||
try {
|
||||
const errors: ValidationErrorDetail[] = [];
|
||||
@ -1,2 +1,2 @@
|
||||
export * from "./domain";
|
||||
export * from "./queries";
|
||||
export * from "./summary";
|
||||
|
||||
@ -1 +0,0 @@
|
||||
export * from "./customer-summary.mapper";
|
||||
@ -0,0 +1 @@
|
||||
export * from "./sequelize-customer-summary.mapper";
|
||||
@ -26,7 +26,7 @@ import type { CustomerSummary, ICustomerSummaryMapper } from "../../../applicati
|
||||
import { CustomerStatus } from "../../../domain";
|
||||
import type { CustomerModel } from "../../sequelize";
|
||||
|
||||
export class CustomerSummaryMapper
|
||||
export class SequelizeCustomerSummaryMapper
|
||||
extends SequelizeQueryMapper<CustomerModel, CustomerSummary>
|
||||
implements ICustomerSummaryMapper
|
||||
{
|
||||
@ -7,11 +7,11 @@ import {
|
||||
import { type Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server";
|
||||
import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import { type Collection, Result } from "@repo/rdx-utils";
|
||||
import type { Sequelize, Transaction } from "sequelize";
|
||||
import type { FindOptions, InferAttributes, OrderItem, Sequelize, Transaction } from "sequelize";
|
||||
|
||||
import type { CustomerSummary, ICustomerRepository } from "../../../application";
|
||||
import type { Customer } from "../../../domain";
|
||||
import type { ICustomerDomainMapper, ICustomerListMapper } from "../../mappers";
|
||||
import type { SequelizeCustomerDomainMapper, SequelizeCustomerSummaryMapper } from "../../mappers";
|
||||
import { CustomerModel } from "../models/customer.model";
|
||||
|
||||
export class CustomerRepository
|
||||
@ -19,8 +19,8 @@ export class CustomerRepository
|
||||
implements ICustomerRepository
|
||||
{
|
||||
constructor(
|
||||
private readonly domainMapper: ICustomerDomainMapper,
|
||||
private readonly listMapper: ICustomerListMapper,
|
||||
private readonly domainMapper: SequelizeCustomerDomainMapper,
|
||||
private readonly summaryMapper: SequelizeCustomerSummaryMapper,
|
||||
database: Sequelize
|
||||
) {
|
||||
super({ database });
|
||||
@ -120,23 +120,42 @@ export class CustomerRepository
|
||||
async getByIdInCompany(
|
||||
companyId: UniqueID,
|
||||
id: UniqueID,
|
||||
transaction?: Transaction
|
||||
transaction?: Transaction,
|
||||
options: FindOptions<InferAttributes<CustomerModel>> = {}
|
||||
): Promise<Result<Customer, Error>> {
|
||||
try {
|
||||
const mapper: ICustomerDomainMapper = this._registry.getDomainMapper({
|
||||
resource: "customer",
|
||||
});
|
||||
// Normalización defensiva de order/include
|
||||
const normalizedOrder = Array.isArray(options.order)
|
||||
? options.order
|
||||
: options.order
|
||||
? [options.order]
|
||||
: [];
|
||||
|
||||
const row = await CustomerModel.findOne({
|
||||
where: { id: id.toString(), company_id: companyId.toString() },
|
||||
const normalizedInclude = Array.isArray(options.include)
|
||||
? options.include
|
||||
: options.include
|
||||
? [options.include]
|
||||
: [];
|
||||
|
||||
const mergedOptions: FindOptions<InferAttributes<CustomerModel>> = {
|
||||
...options,
|
||||
where: {
|
||||
...(options.where ?? {}),
|
||||
id: id.toString(),
|
||||
company_id: companyId.toString(),
|
||||
},
|
||||
order: normalizedOrder,
|
||||
include: normalizedInclude,
|
||||
transaction,
|
||||
});
|
||||
};
|
||||
|
||||
const row = await CustomerModel.findOne(mergedOptions);
|
||||
|
||||
if (!row) {
|
||||
return Result.fail(new EntityNotFoundError("Customer", "id", id.toString()));
|
||||
}
|
||||
|
||||
const customer = mapper.mapToDomain(row);
|
||||
const customer = this.domainMapper.mapToDomain(row);
|
||||
return customer;
|
||||
} catch (error: unknown) {
|
||||
return Result.fail(translateSequelizeError(error));
|
||||
@ -156,7 +175,8 @@ export class CustomerRepository
|
||||
async findByCriteriaInCompany(
|
||||
companyId: UniqueID,
|
||||
criteria: Criteria,
|
||||
transaction?: Transaction
|
||||
transaction?: Transaction,
|
||||
options: FindOptions<InferAttributes<CustomerModel>> = {}
|
||||
): Promise<Result<Collection<CustomerSummary>, Error>> {
|
||||
try {
|
||||
const criteriaConverter = new CriteriaToSequelizeConverter();
|
||||
@ -182,18 +202,48 @@ export class CustomerRepository
|
||||
strictMode: true, // fuerza error si ORDER BY no permitido
|
||||
});
|
||||
|
||||
// Normalización defensiva de order/include
|
||||
const normalizedOrder = Array.isArray(options.order)
|
||||
? options.order
|
||||
: options.order
|
||||
? [options.order]
|
||||
: [];
|
||||
|
||||
const normalizedInclude = Array.isArray(options.include)
|
||||
? options.include
|
||||
: options.include
|
||||
? [options.include]
|
||||
: [];
|
||||
|
||||
query.where = {
|
||||
...query.where,
|
||||
company_id: companyId.toString(),
|
||||
deleted_at: null,
|
||||
};
|
||||
|
||||
query.order = [...(query.order as OrderItem[]), ...normalizedOrder];
|
||||
|
||||
query.include = normalizedInclude;
|
||||
|
||||
// Reemplazar findAndCountAll por findAll + count (más control y mejor rendimiento)
|
||||
/*
|
||||
const { rows, count } = await CustomerModel.findAndCountAll({
|
||||
...query,
|
||||
transaction,
|
||||
});
|
||||
});*/
|
||||
const [rows, count] = await Promise.all([
|
||||
CustomerModel.findAll({
|
||||
...query,
|
||||
transaction,
|
||||
}),
|
||||
CustomerModel.count({
|
||||
where: query.where,
|
||||
distinct: true, // evita duplicados por LEFT JOIN
|
||||
transaction,
|
||||
}),
|
||||
]);
|
||||
|
||||
return this.listMapper.mapToDTOCollection(rows, count);
|
||||
return this.summaryMapper.mapToReadModelCollection(rows, count);
|
||||
} catch (err: unknown) {
|
||||
return Result.fail(translateSequelizeError(err));
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user