.
This commit is contained in:
parent
bf9ed99a90
commit
05f048cc72
@ -1,6 +1,6 @@
|
|||||||
// application/issued-invoices/di/snapshot-builders.di.ts
|
// application/issued-invoices/di/snapshot-builders.di.ts
|
||||||
|
|
||||||
import { IssuedInvoiceListItemSnapshotBuilder } from "../snapshot-builders";
|
import { IssuedInvoiceSummarySnapshotBuilder } from "../snapshot-builders";
|
||||||
import {
|
import {
|
||||||
IssuedInvoiceFullSnapshotBuilder,
|
IssuedInvoiceFullSnapshotBuilder,
|
||||||
IssuedInvoiceItemsFullSnapshotBuilder,
|
IssuedInvoiceItemsFullSnapshotBuilder,
|
||||||
@ -30,7 +30,7 @@ export function buildIssuedInvoiceSnapshotBuilders() {
|
|||||||
taxesBuilder
|
taxesBuilder
|
||||||
);
|
);
|
||||||
|
|
||||||
const listSnapshotBuilder = new IssuedInvoiceListItemSnapshotBuilder();
|
const listSnapshotBuilder = new IssuedInvoiceSummarySnapshotBuilder();
|
||||||
|
|
||||||
const itemsReportBuilder = new IssuedInvoiceReportItemSnapshotBuilder();
|
const itemsReportBuilder = new IssuedInvoiceReportItemSnapshotBuilder();
|
||||||
const taxesReportBuilder = new IssuedInvoiceReportTaxSnapshotBuilder();
|
const taxesReportBuilder = new IssuedInvoiceReportTaxSnapshotBuilder();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { ITransactionManager } from "@erp/core/api";
|
import type { ITransactionManager } from "@erp/core/api";
|
||||||
|
|
||||||
import type { IIssuedInvoiceFinder, IssuedInvoiceDocumentGeneratorService } from "../services";
|
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 { IIssuedInvoiceFullSnapshotBuilder } from "../snapshot-builders/full";
|
||||||
import type { IIssuedInvoiceReportSnapshotBuilder } from "../snapshot-builders/report";
|
import type { IIssuedInvoiceReportSnapshotBuilder } from "../snapshot-builders/report";
|
||||||
import {
|
import {
|
||||||
@ -24,12 +24,12 @@ export function buildGetIssuedInvoiceByIdUseCase(deps: {
|
|||||||
|
|
||||||
export function buildListIssuedInvoicesUseCase(deps: {
|
export function buildListIssuedInvoicesUseCase(deps: {
|
||||||
finder: IIssuedInvoiceFinder;
|
finder: IIssuedInvoiceFinder;
|
||||||
itemSnapshotBuilder: IIssuedInvoiceListItemSnapshotBuilder;
|
summarySnapshotBuilder: IIssuedInvoiceSummarySnapshotBuilder;
|
||||||
transactionManager: ITransactionManager;
|
transactionManager: ITransactionManager;
|
||||||
}) {
|
}) {
|
||||||
return new ListIssuedInvoicesUseCase(
|
return new ListIssuedInvoicesUseCase(
|
||||||
deps.finder,
|
deps.finder,
|
||||||
deps.itemSnapshotBuilder,
|
deps.summarySnapshotBuilder,
|
||||||
deps.transactionManager
|
deps.transactionManager
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
export * from "./di";
|
export * from "./di";
|
||||||
export * from "./mappers";
|
|
||||||
export * from "./models";
|
export * from "./models";
|
||||||
export * from "./repositories";
|
export * from "./repositories";
|
||||||
export * from "./services";
|
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 "./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 { 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
|
export interface IIssuedInvoiceSummarySnapshotBuilder
|
||||||
extends ISnapshotBuilder<IssuedInvoiceSummary, IIssuedInvoiceListItemSnapshot> {}
|
extends ISnapshotBuilder<IssuedInvoiceSummary, IIssuedInvoiceSummarySnapshot> {}
|
||||||
|
|
||||||
export class IssuedInvoiceListItemSnapshotBuilder implements IIssuedInvoiceListItemSnapshotBuilder {
|
export class IssuedInvoiceSummarySnapshotBuilder implements IIssuedInvoiceSummarySnapshotBuilder {
|
||||||
toOutput(invoice: IssuedInvoiceSummary): IIssuedInvoiceListItemSnapshot {
|
toOutput(invoice: IssuedInvoiceSummary): IIssuedInvoiceSummarySnapshot {
|
||||||
const recipient = invoice.recipient.toObjectString();
|
const recipient = invoice.recipient.toObjectString();
|
||||||
|
|
||||||
const verifactu = invoice.verifactu.match(
|
const verifactu = invoice.verifactu.match(
|
||||||
@ -1,4 +1,4 @@
|
|||||||
export interface IIssuedInvoiceListItemSnapshot {
|
export interface IIssuedInvoiceSummarySnapshot {
|
||||||
id: string;
|
id: string;
|
||||||
company_id: string;
|
company_id: string;
|
||||||
|
|
||||||
@ -5,7 +5,7 @@ import { Result } from "@repo/rdx-utils";
|
|||||||
import type { Transaction } from "sequelize";
|
import type { Transaction } from "sequelize";
|
||||||
|
|
||||||
import type { IIssuedInvoiceFinder } from "../services";
|
import type { IIssuedInvoiceFinder } from "../services";
|
||||||
import type { IIssuedInvoiceListItemSnapshotBuilder } from "../snapshot-builders";
|
import type { IIssuedInvoiceSummarySnapshotBuilder } from "../snapshot-builders";
|
||||||
|
|
||||||
type ListIssuedInvoicesUseCaseInput = {
|
type ListIssuedInvoicesUseCaseInput = {
|
||||||
companyId: UniqueID;
|
companyId: UniqueID;
|
||||||
@ -15,7 +15,7 @@ type ListIssuedInvoicesUseCaseInput = {
|
|||||||
export class ListIssuedInvoicesUseCase {
|
export class ListIssuedInvoicesUseCase {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly finder: IIssuedInvoiceFinder,
|
private readonly finder: IIssuedInvoiceFinder,
|
||||||
private readonly listItemSnapshotBuilder: IIssuedInvoiceListItemSnapshotBuilder,
|
private readonly summarySnapshotBuilder: IIssuedInvoiceSummarySnapshotBuilder,
|
||||||
private readonly transactionManager: ITransactionManager
|
private readonly transactionManager: ITransactionManager
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ export class ListIssuedInvoicesUseCase {
|
|||||||
const invoices = result.data;
|
const invoices = result.data;
|
||||||
const totalInvoices = invoices.total();
|
const totalInvoices = invoices.total();
|
||||||
|
|
||||||
const items = invoices.map((item) => this.listItemSnapshotBuilder.toOutput(item));
|
const items = invoices.map((item) => this.summarySnapshotBuilder.toOutput(item));
|
||||||
|
|
||||||
const snapshot = {
|
const snapshot = {
|
||||||
page: criteria.pageNumber,
|
page: criteria.pageNumber,
|
||||||
|
|||||||
@ -4,9 +4,9 @@ import {
|
|||||||
ProformaFullSnapshotBuilder,
|
ProformaFullSnapshotBuilder,
|
||||||
ProformaItemReportSnapshotBuilder,
|
ProformaItemReportSnapshotBuilder,
|
||||||
ProformaItemsFullSnapshotBuilder,
|
ProformaItemsFullSnapshotBuilder,
|
||||||
ProformaListItemSnapshotBuilder,
|
|
||||||
ProformaRecipientFullSnapshotBuilder,
|
ProformaRecipientFullSnapshotBuilder,
|
||||||
ProformaReportSnapshotBuilder,
|
ProformaReportSnapshotBuilder,
|
||||||
|
ProformaSummarySnapshotBuilder,
|
||||||
ProformaTaxReportSnapshotBuilder,
|
ProformaTaxReportSnapshotBuilder,
|
||||||
ProformaTaxesFullSnapshotBuilder,
|
ProformaTaxesFullSnapshotBuilder,
|
||||||
} from "../snapshot-builders";
|
} from "../snapshot-builders";
|
||||||
@ -24,7 +24,7 @@ export function buildProformaSnapshotBuilders() {
|
|||||||
taxesBuilder
|
taxesBuilder
|
||||||
);
|
);
|
||||||
|
|
||||||
const listSnapshotBuilder = new ProformaListItemSnapshotBuilder();
|
const summarySnapshotBuilder = new ProformaSummarySnapshotBuilder();
|
||||||
|
|
||||||
const itemsReportBuilder = new ProformaItemReportSnapshotBuilder();
|
const itemsReportBuilder = new ProformaItemReportSnapshotBuilder();
|
||||||
const taxesReportBuilder = new ProformaTaxReportSnapshotBuilder();
|
const taxesReportBuilder = new ProformaTaxReportSnapshotBuilder();
|
||||||
@ -35,7 +35,7 @@ export function buildProformaSnapshotBuilders() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
full: fullSnapshotBuilder,
|
full: fullSnapshotBuilder,
|
||||||
list: listSnapshotBuilder,
|
summary: summarySnapshotBuilder,
|
||||||
report: reportSnapshotBuilder,
|
report: reportSnapshotBuilder,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import type {
|
|||||||
ProformaDocumentGeneratorService,
|
ProformaDocumentGeneratorService,
|
||||||
} from "../services";
|
} from "../services";
|
||||||
import type {
|
import type {
|
||||||
IProformaListItemSnapshotBuilder,
|
|
||||||
IProformaReportSnapshotBuilder,
|
IProformaReportSnapshotBuilder,
|
||||||
|
IProformaSummarySnapshotBuilder,
|
||||||
} from "../snapshot-builders";
|
} from "../snapshot-builders";
|
||||||
import type { IProformaFullSnapshotBuilder } from "../snapshot-builders/full";
|
import type { IProformaFullSnapshotBuilder } from "../snapshot-builders/full";
|
||||||
import { GetProformaByIdUseCase, ListProformasUseCase, ReportProformaUseCase } from "../use-cases";
|
import { GetProformaByIdUseCase, ListProformasUseCase, ReportProformaUseCase } from "../use-cases";
|
||||||
@ -24,10 +24,14 @@ export function buildGetProformaByIdUseCase(deps: {
|
|||||||
|
|
||||||
export function buildListProformasUseCase(deps: {
|
export function buildListProformasUseCase(deps: {
|
||||||
finder: IProformaFinder;
|
finder: IProformaFinder;
|
||||||
itemSnapshotBuilder: IProformaListItemSnapshotBuilder;
|
summarySnapshotBuilder: IProformaSummarySnapshotBuilder;
|
||||||
transactionManager: ITransactionManager;
|
transactionManager: ITransactionManager;
|
||||||
}) {
|
}) {
|
||||||
return new ListProformasUseCase(deps.finder, deps.itemSnapshotBuilder, deps.transactionManager);
|
return new ListProformasUseCase(
|
||||||
|
deps.finder,
|
||||||
|
deps.summarySnapshotBuilder,
|
||||||
|
deps.transactionManager
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildReportProformaUseCase(deps: {
|
export function buildReportProformaUseCase(deps: {
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import {
|
|||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Maybe, Result } from "@repo/rdx-utils";
|
import { Maybe, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { CreateProformaItemRequestDTO, CreateProformaRequestDTO } from "../../../../../common";
|
import type { CreateProformaItemRequestDTO, CreateProformaRequestDTO } from "../../../../common";
|
||||||
import {
|
import {
|
||||||
type IProformaItemProps,
|
type IProformaItemProps,
|
||||||
type IProformaProps,
|
type IProformaProps,
|
||||||
@ -28,7 +28,7 @@ import {
|
|||||||
ItemDescription,
|
ItemDescription,
|
||||||
ItemQuantity,
|
ItemQuantity,
|
||||||
type ProformaItemTaxesProps,
|
type ProformaItemTaxesProps,
|
||||||
} from "../../../../domain";
|
} from "../../../domain";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CreateProformaPropsMapper
|
* CreateProformaPropsMapper
|
||||||
@ -1,3 +1,2 @@
|
|||||||
export * from "./inputs";
|
export * from "./create-proforma-input.mapper";
|
||||||
export * from "./proforma-domain-mapper.interface";
|
export * from "./update-proforma-input.mapper";
|
||||||
export * from "./proforma-list-mapper.interface";
|
|
||||||
|
|||||||
@ -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";
|
} from "@repo/rdx-ddd";
|
||||||
import { Result, isNullishOrEmpty, toPatchField } from "@repo/rdx-utils";
|
import { Result, isNullishOrEmpty, toPatchField } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { UpdateProformaByIdRequestDTO } from "../../../../../common/dto";
|
import type { UpdateProformaByIdRequestDTO } from "../../../../common/dto";
|
||||||
import type { ProformaPatchProps } from "../../../../domain";
|
import { InvoiceSerie, type ProformaPatchProps } from "../../../domain";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdateProformaPropsMapper
|
* UpdateProformaPropsMapper
|
||||||
@ -1 +1 @@
|
|||||||
export * from "./proforma-resume";
|
export * from "./proforma-summary";
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import type {
|
|||||||
InvoiceStatus,
|
InvoiceStatus,
|
||||||
} from "../../../domain";
|
} from "../../../domain";
|
||||||
|
|
||||||
export type ProformaListDTO = {
|
export type ProformaSummary = {
|
||||||
id: UniqueID;
|
id: UniqueID;
|
||||||
companyId: UniqueID;
|
companyId: UniqueID;
|
||||||
|
|
||||||
@ -3,7 +3,7 @@ import type { UniqueID } from "@repo/rdx-ddd";
|
|||||||
import type { Collection, Result } from "@repo/rdx-utils";
|
import type { Collection, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { InvoiceStatus, Proforma } from "../../../domain";
|
import type { InvoiceStatus, Proforma } from "../../../domain";
|
||||||
import type { ProformaListDTO } from "../models";
|
import type { ProformaSummary } from "../models";
|
||||||
|
|
||||||
export interface IProformaRepository {
|
export interface IProformaRepository {
|
||||||
create(proforma: Proforma, transaction?: unknown): Promise<Result<void, Error>>;
|
create(proforma: Proforma, transaction?: unknown): Promise<Result<void, Error>>;
|
||||||
@ -26,7 +26,7 @@ export interface IProformaRepository {
|
|||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
criteria: Criteria,
|
criteria: Criteria,
|
||||||
transaction: unknown
|
transaction: unknown
|
||||||
): Promise<Result<Collection<ProformaListDTO>, Error>>;
|
): Promise<Result<Collection<ProformaSummary>, Error>>;
|
||||||
|
|
||||||
deleteByIdInCompany(
|
deleteByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import type { Collection, Result } from "@repo/rdx-utils";
|
|||||||
import type { Transaction } from "sequelize";
|
import type { Transaction } from "sequelize";
|
||||||
|
|
||||||
import type { Proforma } from "../../../domain";
|
import type { Proforma } from "../../../domain";
|
||||||
import type { ProformaListDTO } from "../models";
|
import type { ProformaSummary } from "../models";
|
||||||
import type { IProformaRepository } from "../repositories";
|
import type { IProformaRepository } from "../repositories";
|
||||||
|
|
||||||
export interface IProformaFinder {
|
export interface IProformaFinder {
|
||||||
@ -24,7 +24,7 @@ export interface IProformaFinder {
|
|||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
criteria: Criteria,
|
criteria: Criteria,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
): Promise<Result<Collection<ProformaListDTO>, Error>>;
|
): Promise<Result<Collection<ProformaSummary>, Error>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProformaFinder implements IProformaFinder {
|
export class ProformaFinder implements IProformaFinder {
|
||||||
@ -50,7 +50,7 @@ export class ProformaFinder implements IProformaFinder {
|
|||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
criteria: Criteria,
|
criteria: Criteria,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
): Promise<Result<Collection<ProformaListDTO>, Error>> {
|
): Promise<Result<Collection<ProformaSummary>, Error>> {
|
||||||
return this.repository.findByCriteriaInCompany(companyId, criteria, transaction, {});
|
return this.repository.findByCriteriaInCompany(companyId, criteria, transaction, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
export * from "./full";
|
export * from "./full";
|
||||||
export * from "./list";
|
|
||||||
export * from "./report";
|
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 type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
import { maybeToEmptyString } from "@repo/rdx-ddd";
|
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
|
export interface IProformaSummarySnapshotBuilder
|
||||||
extends ISnapshotBuilder<ProformaListDTO, IProformaListItemSnapshot> {}
|
extends ISnapshotBuilder<ProformaSummary, IProformaSummarySnapshot> {}
|
||||||
|
|
||||||
export class ProformaListItemSnapshotBuilder implements IProformaListItemSnapshotBuilder {
|
export class ProformaSummarySnapshotBuilder implements IProformaSummarySnapshotBuilder {
|
||||||
toOutput(proforma: ProformaListDTO): IProformaListItemSnapshot {
|
toOutput(proforma: ProformaSummary): IProformaSummarySnapshot {
|
||||||
const recipient = proforma.recipient.toObjectString();
|
const recipient = proforma.recipient.toObjectString();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -1,4 +1,4 @@
|
|||||||
export interface IProformaListItemSnapshot {
|
export interface IProformaSummarySnapshot {
|
||||||
id: string;
|
id: string;
|
||||||
company_id: string;
|
company_id: string;
|
||||||
|
|
||||||
@ -5,7 +5,7 @@ import { Result } from "@repo/rdx-utils";
|
|||||||
import type { Transaction } from "sequelize";
|
import type { Transaction } from "sequelize";
|
||||||
|
|
||||||
import type { IProformaFinder } from "../services";
|
import type { IProformaFinder } from "../services";
|
||||||
import type { IProformaListItemSnapshotBuilder } from "../snapshot-builders";
|
import type { IProformaSummarySnapshotBuilder } from "../snapshot-builders";
|
||||||
|
|
||||||
type ListProformasUseCaseInput = {
|
type ListProformasUseCaseInput = {
|
||||||
companyId: UniqueID;
|
companyId: UniqueID;
|
||||||
@ -15,7 +15,7 @@ type ListProformasUseCaseInput = {
|
|||||||
export class ListProformasUseCase {
|
export class ListProformasUseCase {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly finder: IProformaFinder,
|
private readonly finder: IProformaFinder,
|
||||||
private readonly listItemSnapshotBuilder: IProformaListItemSnapshotBuilder,
|
private readonly summarySnapshotBuilder: IProformaSummarySnapshotBuilder,
|
||||||
private readonly transactionManager: ITransactionManager
|
private readonly transactionManager: ITransactionManager
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ export class ListProformasUseCase {
|
|||||||
const proformas = result.data;
|
const proformas = result.data;
|
||||||
const totalProformas = proformas.total();
|
const totalProformas = proformas.total();
|
||||||
|
|
||||||
const items = proformas.map((item) => this.listItemSnapshotBuilder.toOutput(item));
|
const items = proformas.map((item) => this.summarySnapshotBuilder.toOutput(item));
|
||||||
|
|
||||||
const snapshot = {
|
const snapshot = {
|
||||||
page: criteria.pageNumber,
|
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 type { Sequelize } from "sequelize";
|
||||||
|
|
||||||
import {
|
import { IssuedInvoiceRepository } from "../persistence";
|
||||||
IssuedInvoiceRepository,
|
|
||||||
SequelizeIssuedInvoiceDomainMapper,
|
|
||||||
SequelizeIssuedInvoiceListMapper,
|
|
||||||
} from "../persistence";
|
|
||||||
|
|
||||||
export const buildIssuedInvoiceRepository = (database: Sequelize) => {
|
import type { IIssuedInvoicePersistenceMappers } from "./issued-invoice-persistence-mappers.di";
|
||||||
const taxCatalog = SpainTaxCatalogProvider();
|
|
||||||
|
|
||||||
const domainMapper = new SequelizeIssuedInvoiceDomainMapper({
|
export const buildIssuedInvoiceRepository = (params: {
|
||||||
taxCatalog,
|
database: Sequelize;
|
||||||
});
|
mappers: IIssuedInvoicePersistenceMappers;
|
||||||
const listMapper = new SequelizeIssuedInvoiceListMapper();
|
}) => {
|
||||||
|
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 {
|
import {
|
||||||
type GetIssuedInvoiceByIdUseCase,
|
type GetIssuedInvoiceByIdUseCase,
|
||||||
@ -12,6 +12,7 @@ import {
|
|||||||
} from "../../../application/issued-invoices";
|
} from "../../../application/issued-invoices";
|
||||||
|
|
||||||
import { buildIssuedInvoiceDocumentService } from "./issued-invoice-documents.di";
|
import { buildIssuedInvoiceDocumentService } from "./issued-invoice-documents.di";
|
||||||
|
import { buildIssuedInvoicePersistenceMappers } from "./issued-invoice-persistence-mappers.di";
|
||||||
import { buildIssuedInvoiceRepository } from "./issued-invoice-repositories.di";
|
import { buildIssuedInvoiceRepository } from "./issued-invoice-repositories.di";
|
||||||
|
|
||||||
export type IssuedInvoicesInternalDeps = {
|
export type IssuedInvoicesInternalDeps = {
|
||||||
@ -27,7 +28,10 @@ export function buildIssuedInvoicesDependencies(params: ModuleParams): IssuedInv
|
|||||||
|
|
||||||
// Infrastructure
|
// Infrastructure
|
||||||
const transactionManager = buildTransactionManager(database);
|
const transactionManager = buildTransactionManager(database);
|
||||||
const repository = buildIssuedInvoiceRepository(database);
|
const catalogs = buildCatalogs();
|
||||||
|
const persistenceMappers = buildIssuedInvoicePersistenceMappers(catalogs);
|
||||||
|
|
||||||
|
const repository = buildIssuedInvoiceRepository({ database, mappers: persistenceMappers });
|
||||||
|
|
||||||
// Application helpers
|
// Application helpers
|
||||||
const finder = buildIssuedInvoiceFinder(repository);
|
const finder = buildIssuedInvoiceFinder(repository);
|
||||||
@ -40,7 +44,7 @@ export function buildIssuedInvoicesDependencies(params: ModuleParams): IssuedInv
|
|||||||
listIssuedInvoices: () =>
|
listIssuedInvoices: () =>
|
||||||
buildListIssuedInvoicesUseCase({
|
buildListIssuedInvoicesUseCase({
|
||||||
finder,
|
finder,
|
||||||
itemSnapshotBuilder: snapshotBuilders.list,
|
summarySnapshotBuilder: snapshotBuilders.list,
|
||||||
transactionManager,
|
transactionManager,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
import { DiscountPercentage, type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
||||||
import {
|
import {
|
||||||
CurrencyCode,
|
CurrencyCode,
|
||||||
LanguageCode,
|
LanguageCode,
|
||||||
@ -13,9 +13,7 @@ import {
|
|||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
|
import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { IIssuedInvoiceDomainMapper } from "../../../../../../application";
|
|
||||||
import {
|
import {
|
||||||
DiscountPercentage,
|
|
||||||
type IIssuedInvoiceProps,
|
type IIssuedInvoiceProps,
|
||||||
InvoiceAmount,
|
InvoiceAmount,
|
||||||
InvoiceNumber,
|
InvoiceNumber,
|
||||||
@ -36,14 +34,11 @@ import { SequelizeIssuedInvoiceRecipientDomainMapper } from "./sequelize-issued-
|
|||||||
import { SequelizeIssuedInvoiceTaxesDomainMapper } from "./sequelize-issued-invoice-taxes-domain.mapper";
|
import { SequelizeIssuedInvoiceTaxesDomainMapper } from "./sequelize-issued-invoice-taxes-domain.mapper";
|
||||||
import { SequelizeIssuedInvoiceVerifactuDomainMapper } from "./sequelize-verifactu-record-domain.mapper";
|
import { SequelizeIssuedInvoiceVerifactuDomainMapper } from "./sequelize-verifactu-record-domain.mapper";
|
||||||
|
|
||||||
export class SequelizeIssuedInvoiceDomainMapper
|
export class SequelizeIssuedInvoiceDomainMapper extends SequelizeDomainMapper<
|
||||||
extends SequelizeDomainMapper<
|
CustomerInvoiceModel,
|
||||||
CustomerInvoiceModel,
|
CustomerInvoiceCreationAttributes,
|
||||||
CustomerInvoiceCreationAttributes,
|
IssuedInvoice
|
||||||
IssuedInvoice
|
> {
|
||||||
>
|
|
||||||
implements IIssuedInvoiceDomainMapper
|
|
||||||
{
|
|
||||||
private _itemsMapper: SequelizeIssuedInvoiceItemDomainMapper;
|
private _itemsMapper: SequelizeIssuedInvoiceItemDomainMapper;
|
||||||
private _recipientMapper: SequelizeIssuedInvoiceRecipientDomainMapper;
|
private _recipientMapper: SequelizeIssuedInvoiceRecipientDomainMapper;
|
||||||
private _taxesMapper: SequelizeIssuedInvoiceTaxesDomainMapper;
|
private _taxesMapper: SequelizeIssuedInvoiceTaxesDomainMapper;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type { JsonTaxCatalogProvider } from "@erp/core";
|
import type { JsonTaxCatalogProvider } from "@erp/core";
|
||||||
import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
import { type MapperParamsType, SequelizeDomainMapper, TaxPercentage } from "@erp/core/api";
|
||||||
import {
|
import {
|
||||||
Percentage,
|
Percentage,
|
||||||
UniqueID,
|
UniqueID,
|
||||||
@ -19,8 +19,6 @@ import {
|
|||||||
type IssuedInvoice,
|
type IssuedInvoice,
|
||||||
IssuedInvoiceTax,
|
IssuedInvoiceTax,
|
||||||
ItemAmount,
|
ItemAmount,
|
||||||
ItemDiscountPercentage,
|
|
||||||
TaxPercentage,
|
|
||||||
} from "../../../../../../domain";
|
} from "../../../../../../domain";
|
||||||
import type {
|
import type {
|
||||||
CustomerInvoiceTaxCreationAttributes,
|
CustomerInvoiceTaxCreationAttributes,
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export * from "./domain";
|
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,
|
CustomerInvoiceModel,
|
||||||
InvoiceRecipient
|
InvoiceRecipient
|
||||||
> {
|
> {
|
||||||
public mapToDTO(
|
public mapToReadModel(
|
||||||
raw: CustomerInvoiceModel,
|
raw: CustomerInvoiceModel,
|
||||||
params?: MapperParamsType
|
params?: MapperParamsType
|
||||||
): Result<InvoiceRecipient, Error> {
|
): Result<InvoiceRecipient, Error> {
|
||||||
@ -11,10 +11,7 @@ import {
|
|||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Maybe, Result } from "@repo/rdx-utils";
|
import { Maybe, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type {
|
import type { IssuedInvoiceSummary } from "../../../../../../application";
|
||||||
IIssuedInvoiceSummaryMapper,
|
|
||||||
IssuedInvoiceSummary,
|
|
||||||
} from "../../../../../../application";
|
|
||||||
import {
|
import {
|
||||||
InvoiceAmount,
|
InvoiceAmount,
|
||||||
InvoiceNumber,
|
InvoiceNumber,
|
||||||
@ -24,20 +21,20 @@ import {
|
|||||||
} from "../../../../../../domain";
|
} from "../../../../../../domain";
|
||||||
import type { CustomerInvoiceModel } from "../../../../../common";
|
import type { CustomerInvoiceModel } from "../../../../../common";
|
||||||
|
|
||||||
import { SequelizeIssuedInvoiceRecipientListMapper } from "./sequelize-issued-invoice-recipient.list.mapper";
|
import { SequelizeIssuedInvoiceRecipientListMapper } from "./sequelize-issued-invoice-recipient-summary.mapper";
|
||||||
import { SequelizeVerifactuRecordListMapper } from "./sequelize-verifactu-record.list.mapper";
|
import { SequelizeVerifactuRecordSummaryMapper } from "./sequelize-verifactu-record-summary.mapper";
|
||||||
|
|
||||||
export class SequelizeIssuedInvoiceListMapper
|
export class SequelizeIssuedInvoiceSummaryMapper extends SequelizeQueryMapper<
|
||||||
extends SequelizeQueryMapper<CustomerInvoiceModel, IssuedInvoiceSummary>
|
CustomerInvoiceModel,
|
||||||
implements IIssuedInvoiceSummaryMapper
|
IssuedInvoiceSummary
|
||||||
{
|
> {
|
||||||
private _recipientMapper: SequelizeIssuedInvoiceRecipientListMapper;
|
private _recipientMapper: SequelizeIssuedInvoiceRecipientListMapper;
|
||||||
private _verifactuMapper: SequelizeVerifactuRecordListMapper;
|
private _verifactuMapper: SequelizeVerifactuRecordSummaryMapper;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._recipientMapper = new SequelizeIssuedInvoiceRecipientListMapper();
|
this._recipientMapper = new SequelizeIssuedInvoiceRecipientListMapper();
|
||||||
this._verifactuMapper = new SequelizeVerifactuRecordListMapper();
|
this._verifactuMapper = new SequelizeVerifactuRecordSummaryMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
public mapToReadModel(
|
public mapToReadModel(
|
||||||
@ -50,7 +47,7 @@ export class SequelizeIssuedInvoiceListMapper
|
|||||||
const attributes = this._mapAttributesToReadModel(raw, { errors, ...params });
|
const attributes = this._mapAttributesToReadModel(raw, { errors, ...params });
|
||||||
|
|
||||||
// 2) Recipient (snapshot en la factura o include)
|
// 2) Recipient (snapshot en la factura o include)
|
||||||
const recipientResult = this._recipientMapper.mapToDTO(raw, {
|
const recipientResult = this._recipientMapper.mapToReadModel(raw, {
|
||||||
errors,
|
errors,
|
||||||
attributes,
|
attributes,
|
||||||
...params,
|
...params,
|
||||||
@ -66,7 +63,10 @@ export class SequelizeIssuedInvoiceListMapper
|
|||||||
// 4) Verifactu record
|
// 4) Verifactu record
|
||||||
let verifactu: Maybe<VerifactuRecord> = Maybe.none();
|
let verifactu: Maybe<VerifactuRecord> = Maybe.none();
|
||||||
if (raw.verifactu) {
|
if (raw.verifactu) {
|
||||||
const verifactuResult = this._verifactuMapper.mapToDTO(raw.verifactu, { errors, ...params });
|
const verifactuResult = this._verifactuMapper.mapToReadModel(raw.verifactu, {
|
||||||
|
errors,
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
|
||||||
if (verifactuResult.isFailure) {
|
if (verifactuResult.isFailure) {
|
||||||
errors.push({
|
errors.push({
|
||||||
@ -12,11 +12,11 @@ import { Result } from "@repo/rdx-utils";
|
|||||||
import { VerifactuRecord, VerifactuRecordEstado } from "../../../../../../domain";
|
import { VerifactuRecord, VerifactuRecordEstado } from "../../../../../../domain";
|
||||||
import type { VerifactuRecordModel } from "../../../../../common";
|
import type { VerifactuRecordModel } from "../../../../../common";
|
||||||
|
|
||||||
export class SequelizeVerifactuRecordListMapper extends SequelizeQueryMapper<
|
export class SequelizeVerifactuRecordSummaryMapper extends SequelizeQueryMapper<
|
||||||
VerifactuRecordModel,
|
VerifactuRecordModel,
|
||||||
VerifactuRecord
|
VerifactuRecord
|
||||||
> {
|
> {
|
||||||
public mapToDTO(
|
public mapToReadModel(
|
||||||
raw: VerifactuRecordModel,
|
raw: VerifactuRecordModel,
|
||||||
params?: MapperParamsType
|
params?: MapperParamsType
|
||||||
): Result<VerifactuRecord, Error> {
|
): Result<VerifactuRecord, Error> {
|
||||||
@ -14,7 +14,7 @@ import {
|
|||||||
} from "../../../../common";
|
} from "../../../../common";
|
||||||
import type {
|
import type {
|
||||||
SequelizeIssuedInvoiceDomainMapper,
|
SequelizeIssuedInvoiceDomainMapper,
|
||||||
SequelizeIssuedInvoiceListMapper,
|
SequelizeIssuedInvoiceSummaryMapper,
|
||||||
} from "../mappers";
|
} from "../mappers";
|
||||||
|
|
||||||
export class IssuedInvoiceRepository
|
export class IssuedInvoiceRepository
|
||||||
@ -23,7 +23,7 @@ export class IssuedInvoiceRepository
|
|||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private readonly domainMapper: SequelizeIssuedInvoiceDomainMapper,
|
private readonly domainMapper: SequelizeIssuedInvoiceDomainMapper,
|
||||||
private readonly listMapper: SequelizeIssuedInvoiceListMapper,
|
private readonly summaryMapper: SequelizeIssuedInvoiceSummaryMapper,
|
||||||
database: Sequelize
|
database: Sequelize
|
||||||
) {
|
) {
|
||||||
super({ database });
|
super({ database });
|
||||||
@ -216,8 +216,8 @@ export class IssuedInvoiceRepository
|
|||||||
const { CustomerModel } = this.database.models;
|
const { CustomerModel } = this.database.models;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const converter = new CriteriaToSequelizeConverter();
|
const criteriaConverter = new CriteriaToSequelizeConverter();
|
||||||
const query = converter.convert(criteria, {
|
const query = criteriaConverter.convert(criteria, {
|
||||||
searchableFields: ["invoice_number", "reference", "description"],
|
searchableFields: ["invoice_number", "reference", "description"],
|
||||||
mappings: {
|
mappings: {
|
||||||
reference: "CustomerInvoiceModel.reference",
|
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) {
|
} catch (err: unknown) {
|
||||||
return Result.fail(translateSequelizeError(err));
|
return Result.fail(translateSequelizeError(err));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,11 @@
|
|||||||
import {
|
import type { ICatalogs } from "@erp/core/api";
|
||||||
CreateProformaInputMapper,
|
|
||||||
type ICatalogs,
|
import { CreateProformaInputMapper } from "../../../application";
|
||||||
type IProformaDomainMapper,
|
import { SequelizeProformaDomainMapper, SequelizeProformaSummaryMapper } from "../persistence";
|
||||||
type IProformaListMapper,
|
|
||||||
} from "../../../application";
|
|
||||||
import { SequelizeProformaDomainMapper, SequelizeProformaListMapper } from "../persistence";
|
|
||||||
|
|
||||||
export interface IProformaPersistenceMappers {
|
export interface IProformaPersistenceMappers {
|
||||||
domainMapper: IProformaDomainMapper;
|
domainMapper: SequelizeProformaDomainMapper;
|
||||||
listMapper: IProformaListMapper;
|
listMapper: SequelizeProformaSummaryMapper;
|
||||||
|
|
||||||
createMapper: CreateProformaInputMapper;
|
createMapper: CreateProformaInputMapper;
|
||||||
}
|
}
|
||||||
@ -22,7 +19,7 @@ export const buildProformaPersistenceMappers = (
|
|||||||
const domainMapper = new SequelizeProformaDomainMapper({
|
const domainMapper = new SequelizeProformaDomainMapper({
|
||||||
taxCatalog,
|
taxCatalog,
|
||||||
});
|
});
|
||||||
const listMapper = new SequelizeProformaListMapper();
|
const listMapper = new SequelizeProformaSummaryMapper();
|
||||||
|
|
||||||
// Mappers el DTO a las props validadas (CustomerProps) y luego construir agregado
|
// Mappers el DTO a las props validadas (CustomerProps) y luego construir agregado
|
||||||
const createMapper = new CreateProformaInputMapper({ taxCatalog });
|
const createMapper = new CreateProformaInputMapper({ taxCatalog });
|
||||||
|
|||||||
@ -60,7 +60,7 @@ export function buildProformasDependencies(params: ModuleParams): ProformasInter
|
|||||||
listProformas: () =>
|
listProformas: () =>
|
||||||
buildListProformasUseCase({
|
buildListProformasUseCase({
|
||||||
finder,
|
finder,
|
||||||
itemSnapshotBuilder: snapshotBuilders.list,
|
summarySnapshotBuilder: snapshotBuilders.summary,
|
||||||
transactionManager,
|
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";
|
} from "@repo/rdx-ddd";
|
||||||
import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
|
import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { IProformaDomainMapper } from "../../../../../../application";
|
|
||||||
import {
|
import {
|
||||||
type IProformaProps,
|
type IProformaProps,
|
||||||
InvoiceNumber,
|
InvoiceNumber,
|
||||||
@ -32,10 +31,11 @@ import { SequelizeProformaItemDomainMapper } from "./sequelize-proforma-item-dom
|
|||||||
import { SequelizeProformaRecipientDomainMapper } from "./sequelize-proforma-recipient-domain.mapper";
|
import { SequelizeProformaRecipientDomainMapper } from "./sequelize-proforma-recipient-domain.mapper";
|
||||||
import { SequelizeProformaTaxesDomainMapper } from "./sequelize-proforma-taxes-domain.mapper";
|
import { SequelizeProformaTaxesDomainMapper } from "./sequelize-proforma-taxes-domain.mapper";
|
||||||
|
|
||||||
export class SequelizeProformaDomainMapper
|
export class SequelizeProformaDomainMapper extends SequelizeDomainMapper<
|
||||||
extends SequelizeDomainMapper<CustomerInvoiceModel, CustomerInvoiceCreationAttributes, Proforma>
|
CustomerInvoiceModel,
|
||||||
implements IProformaDomainMapper
|
CustomerInvoiceCreationAttributes,
|
||||||
{
|
Proforma
|
||||||
|
> {
|
||||||
private _itemsMapper: SequelizeProformaItemDomainMapper;
|
private _itemsMapper: SequelizeProformaItemDomainMapper;
|
||||||
private _recipientMapper: SequelizeProformaRecipientDomainMapper;
|
private _recipientMapper: SequelizeProformaRecipientDomainMapper;
|
||||||
private _taxesMapper: SequelizeProformaTaxesDomainMapper;
|
private _taxesMapper: SequelizeProformaTaxesDomainMapper;
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export * from "./domain";
|
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 {
|
import { type MapperParamsType, SequelizeQueryMapper } from "@erp/core/api";
|
||||||
type IQueryMapperWithBulk,
|
|
||||||
type MapperParamsType,
|
|
||||||
SequelizeQueryMapper,
|
|
||||||
} from "@erp/core/api";
|
|
||||||
import {
|
import {
|
||||||
City,
|
City,
|
||||||
Country,
|
Country,
|
||||||
@ -18,18 +14,15 @@ import {
|
|||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { ProformaListDTO } from "../../../../../../application";
|
import type { ProformaSummary } from "../../../../../../application";
|
||||||
import { InvoiceRecipient } from "../../../../../../domain";
|
import { InvoiceRecipient } from "../../../../../../domain";
|
||||||
import type { CustomerInvoiceModel } from "../../../../../common";
|
import type { CustomerInvoiceModel } from "../../../../../common";
|
||||||
|
|
||||||
interface IInvoiceRecipientListMapper
|
export class SequelizeInvoiceRecipientSummaryMapper extends SequelizeQueryMapper<
|
||||||
extends IQueryMapperWithBulk<CustomerInvoiceModel, InvoiceRecipient> {}
|
CustomerInvoiceModel,
|
||||||
|
InvoiceRecipient
|
||||||
export class SequelizeInvoiceRecipientListMapper
|
> {
|
||||||
extends SequelizeQueryMapper<CustomerInvoiceModel, InvoiceRecipient>
|
public mapToReadModel(
|
||||||
implements IInvoiceRecipientListMapper
|
|
||||||
{
|
|
||||||
public mapToDTO(
|
|
||||||
raw: CustomerInvoiceModel,
|
raw: CustomerInvoiceModel,
|
||||||
params?: MapperParamsType
|
params?: MapperParamsType
|
||||||
): Result<InvoiceRecipient, Error> {
|
): Result<InvoiceRecipient, Error> {
|
||||||
@ -39,7 +32,7 @@ export class SequelizeInvoiceRecipientListMapper
|
|||||||
|
|
||||||
const { errors, attributes } = params as {
|
const { errors, attributes } = params as {
|
||||||
errors: ValidationErrorDetail[];
|
errors: ValidationErrorDetail[];
|
||||||
attributes: Partial<ProformaListDTO>;
|
attributes: Partial<ProformaSummary>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const { isProforma } = attributes;
|
const { isProforma } = attributes;
|
||||||
@ -11,7 +11,7 @@ import {
|
|||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { IProformaListMapper, ProformaListDTO } from "../../../../../../application";
|
import type { ProformaSummary } from "../../../../../../application";
|
||||||
import {
|
import {
|
||||||
InvoiceAmount,
|
InvoiceAmount,
|
||||||
InvoiceNumber,
|
InvoiceNumber,
|
||||||
@ -20,30 +20,30 @@ import {
|
|||||||
} from "../../../../../../domain";
|
} from "../../../../../../domain";
|
||||||
import type { CustomerInvoiceModel } from "../../../../../common";
|
import type { CustomerInvoiceModel } from "../../../../../common";
|
||||||
|
|
||||||
import { SequelizeInvoiceRecipientListMapper } from "./sequelize-proforma-recipient.list.mapper";
|
import { SequelizeInvoiceRecipientSummaryMapper } from "./sequelize-proforma-recipient-summary.mapper";
|
||||||
|
|
||||||
export class SequelizeProformaListMapper
|
export class SequelizeProformaSummaryMapper extends SequelizeQueryMapper<
|
||||||
extends SequelizeQueryMapper<CustomerInvoiceModel, ProformaListDTO>
|
CustomerInvoiceModel,
|
||||||
implements IProformaListMapper
|
ProformaSummary
|
||||||
{
|
> {
|
||||||
private _recipientMapper: SequelizeInvoiceRecipientListMapper;
|
private _recipientMapper: SequelizeInvoiceRecipientSummaryMapper;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._recipientMapper = new SequelizeInvoiceRecipientListMapper();
|
this._recipientMapper = new SequelizeInvoiceRecipientSummaryMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
public mapToDTO(
|
public mapToReadModel(
|
||||||
raw: CustomerInvoiceModel,
|
raw: CustomerInvoiceModel,
|
||||||
params?: MapperParamsType
|
params?: MapperParamsType
|
||||||
): Result<ProformaListDTO, Error> {
|
): Result<ProformaSummary, Error> {
|
||||||
const errors: ValidationErrorDetail[] = [];
|
const errors: ValidationErrorDetail[] = [];
|
||||||
|
|
||||||
// 1) Valores escalares (atributos generales)
|
// 1) Valores escalares (atributos generales)
|
||||||
const attributes = this.mapAttributesToDTO(raw, { errors, ...params });
|
const attributes = this.mapAttributesToDTO(raw, { errors, ...params });
|
||||||
|
|
||||||
// 2) Recipient (snapshot en la factura o include)
|
// 2) Recipient (snapshot en la factura o include)
|
||||||
const recipientResult = this._recipientMapper.mapToDTO(raw, {
|
const recipientResult = this._recipientMapper.mapToReadModel(raw, {
|
||||||
errors,
|
errors,
|
||||||
attributes,
|
attributes,
|
||||||
...params,
|
...params,
|
||||||
@ -9,26 +9,22 @@ import type { UniqueID } from "@repo/rdx-ddd";
|
|||||||
import { type Collection, Result } from "@repo/rdx-utils";
|
import { type Collection, Result } from "@repo/rdx-utils";
|
||||||
import type { FindOptions, InferAttributes, OrderItem, Sequelize, Transaction } from "sequelize";
|
import type { FindOptions, InferAttributes, OrderItem, Sequelize, Transaction } from "sequelize";
|
||||||
|
|
||||||
import type {
|
import type { IProformaRepository, ProformaSummary } from "../../../../../application";
|
||||||
IProformaDomainMapper,
|
|
||||||
IProformaListMapper,
|
|
||||||
IProformaRepository,
|
|
||||||
ProformaListDTO,
|
|
||||||
} from "../../../../../application";
|
|
||||||
import type { InvoiceStatus, Proforma } from "../../../../../domain";
|
import type { InvoiceStatus, Proforma } from "../../../../../domain";
|
||||||
import {
|
import {
|
||||||
CustomerInvoiceItemModel,
|
CustomerInvoiceItemModel,
|
||||||
CustomerInvoiceModel,
|
CustomerInvoiceModel,
|
||||||
CustomerInvoiceTaxModel,
|
CustomerInvoiceTaxModel,
|
||||||
} from "../../../../common";
|
} from "../../../../common";
|
||||||
|
import type { SequelizeProformaDomainMapper, SequelizeProformaSummaryMapper } from "../mappers";
|
||||||
|
|
||||||
export class ProformaRepository
|
export class ProformaRepository
|
||||||
extends SequelizeRepository<Proforma>
|
extends SequelizeRepository<Proforma>
|
||||||
implements IProformaRepository
|
implements IProformaRepository
|
||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private readonly domainMapper: IProformaDomainMapper,
|
private readonly domainMapper: SequelizeProformaDomainMapper,
|
||||||
private readonly listMapper: IProformaListMapper,
|
private readonly summaryMapper: SequelizeProformaSummaryMapper,
|
||||||
database: Sequelize
|
database: Sequelize
|
||||||
) {
|
) {
|
||||||
super({ database });
|
super({ database });
|
||||||
@ -339,12 +335,12 @@ export class ProformaRepository
|
|||||||
criteria: Criteria,
|
criteria: Criteria,
|
||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
options: FindOptions<InferAttributes<CustomerInvoiceModel>> = {}
|
options: FindOptions<InferAttributes<CustomerInvoiceModel>> = {}
|
||||||
): Promise<Result<Collection<ProformaListDTO>, Error>> {
|
): Promise<Result<Collection<ProformaSummary>, Error>> {
|
||||||
const { CustomerModel } = this.database.models;
|
const { CustomerModel } = this.database.models;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const converter = new CriteriaToSequelizeConverter();
|
const criteriaConverter = new CriteriaToSequelizeConverter();
|
||||||
const query = converter.convert(criteria, {
|
const query = criteriaConverter.convert(criteria, {
|
||||||
searchableFields: ["invoice_number", "reference", "description"],
|
searchableFields: ["invoice_number", "reference", "description"],
|
||||||
mappings: {
|
mappings: {
|
||||||
reference: "CustomerInvoiceModel.reference",
|
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) {
|
} catch (err: unknown) {
|
||||||
return Result.fail(translateSequelizeError(err));
|
return Result.fail(translateSequelizeError(err));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { IProformaRepository } from "../repositories";
|
import type { ICustomerRepository } from "../repositories";
|
||||||
import { type IProformaFinder, ProformaFinder } from "../services";
|
import { CustomerFinder, type ICustomerFinder } from "../services";
|
||||||
|
|
||||||
export function buildProformaFinder(repository: IProformaRepository): IProformaFinder {
|
export function buildCustomerFinder(repository: ICustomerRepository): ICustomerFinder {
|
||||||
return new ProformaFinder(repository);
|
return new CustomerFinder(repository);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,9 @@
|
|||||||
// application/issued-invoices/di/snapshot-builders.di.ts
|
// application/issued-invoices/di/snapshot-builders.di.ts
|
||||||
|
|
||||||
import {
|
import { CustomerSummarySnapshotBuilder } from "../snapshot-builders";
|
||||||
CustomerFullSnapshotBuilder,
|
|
||||||
CustomerItemReportSnapshotBuilder,
|
|
||||||
CustomerItemsFullSnapshotBuilder,
|
|
||||||
CustomerListItemSnapshotBuilder,
|
|
||||||
CustomerRecipientFullSnapshotBuilder,
|
|
||||||
CustomerReportSnapshotBuilder,
|
|
||||||
CustomerTaxReportSnapshotBuilder,
|
|
||||||
CustomerTaxesFullSnapshotBuilder,
|
|
||||||
} from "../snapshot-builders";
|
|
||||||
|
|
||||||
export function buildCustomerSnapshotBuilders() {
|
export function buildCustomerSnapshotBuilders() {
|
||||||
const itemsBuilder = new CustomerItemsFullSnapshotBuilder();
|
/*const itemsBuilder = new CustomerItemsFullSnapshotBuilder();
|
||||||
|
|
||||||
const taxesBuilder = new CustomerTaxesFullSnapshotBuilder();
|
const taxesBuilder = new CustomerTaxesFullSnapshotBuilder();
|
||||||
|
|
||||||
@ -22,20 +13,20 @@ export function buildCustomerSnapshotBuilders() {
|
|||||||
itemsBuilder,
|
itemsBuilder,
|
||||||
recipientBuilder,
|
recipientBuilder,
|
||||||
taxesBuilder
|
taxesBuilder
|
||||||
);
|
);*/
|
||||||
|
|
||||||
const listSnapshotBuilder = new CustomerListItemSnapshotBuilder();
|
const summarySnapshotBuilder = new CustomerSummarySnapshotBuilder();
|
||||||
|
|
||||||
const itemsReportBuilder = new CustomerItemReportSnapshotBuilder();
|
/*const itemsReportBuilder = new CustomerItemReportSnapshotBuilder();
|
||||||
const taxesReportBuilder = new CustomerTaxReportSnapshotBuilder();
|
const taxesReportBuilder = new CustomerTaxReportSnapshotBuilder();
|
||||||
const reportSnapshotBuilder = new CustomerReportSnapshotBuilder(
|
const reportSnapshotBuilder = new CustomerReportSnapshotBuilder(
|
||||||
itemsReportBuilder,
|
itemsReportBuilder,
|
||||||
taxesReportBuilder
|
taxesReportBuilder
|
||||||
);
|
);*/
|
||||||
|
|
||||||
return {
|
return {
|
||||||
full: fullSnapshotBuilder,
|
//full: fullSnapshotBuilder,
|
||||||
list: listSnapshotBuilder,
|
summary: summarySnapshotBuilder,
|
||||||
report: reportSnapshotBuilder,
|
//report: reportSnapshotBuilder,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,36 +1,30 @@
|
|||||||
import type { ITransactionManager } from "@erp/core/api";
|
import type { ITransactionManager } from "@erp/core/api";
|
||||||
|
|
||||||
import type { ICreateCustomerInputMapper } from "../mappers";
|
import type { ICustomerFinder } from "../services";
|
||||||
import type {
|
import type { ICustomerSummarySnapshotBuilder } from "../snapshot-builders";
|
||||||
ICustomerCreator,
|
import { ListCustomersUseCase } from "../use-cases";
|
||||||
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";
|
|
||||||
|
|
||||||
export function buildGetCustomerByIdUseCase(deps: {
|
/*export function buildGetCustomerByIdUseCase(deps: {
|
||||||
finder: ICustomerFinder;
|
finder: ICustomerFinder;
|
||||||
fullSnapshotBuilder: ICustomerFullSnapshotBuilder;
|
fullSnapshotBuilder: ICustomerFullSnapshotBuilder;
|
||||||
transactionManager: ITransactionManager;
|
transactionManager: ITransactionManager;
|
||||||
}) {
|
}) {
|
||||||
return new GetCustomerByIdUseCase(deps.finder, deps.fullSnapshotBuilder, deps.transactionManager);
|
return new GetCustomerByIdUseCase(deps.finder, deps.fullSnapshotBuilder, deps.transactionManager);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
export function buildListCustomersUseCase(deps: {
|
export function buildListCustomersUseCase(deps: {
|
||||||
finder: ICustomerFinder;
|
finder: ICustomerFinder;
|
||||||
itemSnapshotBuilder: ICustomerListItemSnapshotBuilder;
|
summarySnapshotBuilder: ICustomerSummarySnapshotBuilder;
|
||||||
transactionManager: ITransactionManager;
|
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;
|
finder: ICustomerFinder;
|
||||||
fullSnapshotBuilder: ICustomerFullSnapshotBuilder;
|
fullSnapshotBuilder: ICustomerFullSnapshotBuilder;
|
||||||
reportSnapshotBuilder: ICustomerReportSnapshotBuilder;
|
reportSnapshotBuilder: ICustomerReportSnapshotBuilder;
|
||||||
@ -58,7 +52,7 @@ export function buildCreateCustomerUseCase(deps: {
|
|||||||
fullSnapshotBuilder: deps.fullSnapshotBuilder,
|
fullSnapshotBuilder: deps.fullSnapshotBuilder,
|
||||||
transactionManager: deps.transactionManager,
|
transactionManager: deps.transactionManager,
|
||||||
});
|
});
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/*export function buildUpdateCustomerUseCase(deps: {
|
/*export function buildUpdateCustomerUseCase(deps: {
|
||||||
finder: ICustomerFinder;
|
finder: ICustomerFinder;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export * from "./customer-creator.di";
|
//export * from "./customer-creator.di";
|
||||||
export * from "./customer-finder.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-snapshot-builders.di";
|
||||||
export * from "./customer-use-cases.di";
|
export * from "./customer-use-cases.di";
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
export * from "./di";
|
export * from "./di";
|
||||||
export * from "./mappers";
|
|
||||||
export * from "./models";
|
export * from "./models";
|
||||||
export * from "./repositories";
|
export * from "./repositories";
|
||||||
export * from "./services";
|
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 { Transaction } from "sequelize";
|
||||||
|
|
||||||
import type { Customer } from "../../domain";
|
import type { Customer } from "../../domain";
|
||||||
import type { CustomerListDTO } from "../dtos";
|
import type { CustomerSummary } from "../models";
|
||||||
import type { ICustomerRepository } from "../repositories";
|
import type { ICustomerRepository } from "../repositories";
|
||||||
|
|
||||||
export interface ICustomerFinder {
|
export interface ICustomerFinder {
|
||||||
@ -24,7 +24,7 @@ export interface ICustomerFinder {
|
|||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
criteria: Criteria,
|
criteria: Criteria,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
): Promise<Result<Collection<CustomerListDTO>, Error>>;
|
): Promise<Result<Collection<CustomerSummary>, Error>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CustomerFinder implements ICustomerFinder {
|
export class CustomerFinder implements ICustomerFinder {
|
||||||
@ -50,7 +50,7 @@ export class CustomerFinder implements ICustomerFinder {
|
|||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
criteria: Criteria,
|
criteria: Criteria,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
): Promise<Result<Collection<CustomerListDTO>, Error>> {
|
): Promise<Result<Collection<CustomerSummary>, Error>> {
|
||||||
return this.repository.findByCriteriaInCompany(companyId, criteria, transaction);
|
return this.repository.findByCriteriaInCompany(companyId, criteria, transaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
//export * from "./full";
|
//export * from "./full";
|
||||||
export * from "./list";
|
export * from "./summary";
|
||||||
//export * from "./report";
|
//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 { Result } from "@repo/rdx-utils";
|
||||||
import type { Transaction } from "sequelize";
|
import type { Transaction } from "sequelize";
|
||||||
|
|
||||||
import type { ListCustomersResponseDTO } from "../../../common/dto";
|
|
||||||
import type { ICustomerFinder } from "../services";
|
import type { ICustomerFinder } from "../services";
|
||||||
import type { ICustomerListItemSnapshotBuilder } from "../snapshot-builders/list";
|
import type { ICustomerSummarySnapshotBuilder } from "../snapshot-builders/summary";
|
||||||
|
|
||||||
type ListCustomersUseCaseInput = {
|
type ListCustomersUseCaseInput = {
|
||||||
companyId: UniqueID;
|
companyId: UniqueID;
|
||||||
@ -16,13 +15,11 @@ type ListCustomersUseCaseInput = {
|
|||||||
export class ListCustomersUseCase {
|
export class ListCustomersUseCase {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly finder: ICustomerFinder,
|
private readonly finder: ICustomerFinder,
|
||||||
private readonly listItemSnapshotBuilder: ICustomerListItemSnapshotBuilder,
|
private readonly summarySnapshotBuilder: ICustomerSummarySnapshotBuilder,
|
||||||
private readonly transactionManager: ITransactionManager
|
private readonly transactionManager: ITransactionManager
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public execute(
|
public execute(params: ListCustomersUseCaseInput) {
|
||||||
params: ListCustomersUseCaseInput
|
|
||||||
): Promise<Result<ListCustomersResponseDTO, Error>> {
|
|
||||||
const { criteria, companyId } = params;
|
const { criteria, companyId } = params;
|
||||||
|
|
||||||
return this.transactionManager.complete(async (transaction: Transaction) => {
|
return this.transactionManager.complete(async (transaction: Transaction) => {
|
||||||
@ -36,7 +33,7 @@ export class ListCustomersUseCase {
|
|||||||
const customers = result.data;
|
const customers = result.data;
|
||||||
const totalCustomers = customers.total();
|
const totalCustomers = customers.total();
|
||||||
|
|
||||||
const items = customers.map((item) => this.listItemSnapshotBuilder.toOutput(item));
|
const items = customers.map((item) => this.summarySnapshotBuilder.toOutput(item));
|
||||||
|
|
||||||
const snapshot = {
|
const snapshot = {
|
||||||
page: criteria.pageNumber,
|
page: criteria.pageNumber,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { IModuleServer } from "@erp/core/api";
|
import type { IModuleServer } from "@erp/core/api";
|
||||||
|
|
||||||
import { customersRouter, models } from "./infrastructure";
|
import { customersRouter, models } from "./infrastructure";
|
||||||
|
import { buildCustomersDependencies, buildCustomerServices, CustomersInternalDeps } from "./infrastructure/di";
|
||||||
|
|
||||||
export * from "./infrastructure/sequelize";
|
export * from "./infrastructure/sequelize";
|
||||||
|
|
||||||
@ -19,12 +20,11 @@ export const customersAPIModule: IModuleServer = {
|
|||||||
async setup(params) {
|
async setup(params) {
|
||||||
const { env: ENV, app, database, baseRoutePath: API_BASE_PATH, logger } = params;
|
const { env: ENV, app, database, baseRoutePath: API_BASE_PATH, logger } = params;
|
||||||
|
|
||||||
|
|
||||||
// 1) Dominio interno
|
// 1) Dominio interno
|
||||||
//const customerInternalDeps = buildCustomerDependencies(params);
|
const customerInternalDeps = buildCustomersDependencies(params);
|
||||||
|
|
||||||
// 2) Servicios públicos (Application Services)
|
// 2) Servicios públicos (Application Services)
|
||||||
//const customerServices = buildCustomerServices(customerInternalDeps);
|
const customerServices = buildCustomerServices(customerInternalDeps);
|
||||||
|
|
||||||
logger.info("🚀 Customers module dependencies registered", {
|
logger.info("🚀 Customers module dependencies registered", {
|
||||||
label: this.name,
|
label: this.name,
|
||||||
@ -35,14 +35,10 @@ export const customersAPIModule: IModuleServer = {
|
|||||||
models,
|
models,
|
||||||
|
|
||||||
// Servicios expuestos a otros módulos
|
// Servicios expuestos a otros módulos
|
||||||
services: {
|
services: customerServices,
|
||||||
//customers: customerServices,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Implementación privada del módulo
|
// Implementación privada del módulo
|
||||||
internal: {
|
internal:customerInternalDeps,
|
||||||
//customers: customerInternalDeps,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -57,7 +53,7 @@ export const customersAPIModule: IModuleServer = {
|
|||||||
const { app, baseRoutePath, logger, getInternal } = params;
|
const { app, baseRoutePath, logger, getInternal } = params;
|
||||||
|
|
||||||
// Recuperamos el dominio interno del módulo
|
// Recuperamos el dominio interno del módulo
|
||||||
const customersInternalDeps = getInternal("customers", "customers");
|
const customersInternalDeps = getInternal<CustomersInternalDeps>("customers");
|
||||||
|
|
||||||
// Registro de rutas HTTP
|
// Registro de rutas HTTP
|
||||||
customersRouter(params, customersInternalDeps);
|
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 {
|
import { SequelizeCustomerDomainMapper, SequelizeCustomerSummaryMapper } from "../mappers";
|
||||||
CreateCustomerInputMapper,
|
|
||||||
type ICatalogs,
|
|
||||||
type ICustomerDomainMapper,
|
|
||||||
type ICustomerListMapper,
|
|
||||||
} from "../../../application";
|
|
||||||
import { SequelizeCustomerDomainMapper, SequelizeCustomerListMapper } from "../persistence";
|
|
||||||
|
|
||||||
export interface ICustomerPersistenceMappers {
|
export interface ICustomerPersistenceMappers {
|
||||||
domainMapper: ICustomerDomainMapper;
|
domainMapper: SequelizeCustomerDomainMapper;
|
||||||
listMapper: ICustomerListMapper;
|
summaryMapper: SequelizeCustomerSummaryMapper;
|
||||||
|
|
||||||
createMapper: CreateCustomerInputMapper;
|
//createMapper: CreateCustomerInputMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const buildCustomerPersistenceMappers = (
|
export const buildCustomerPersistenceMappers = (): ICustomerPersistenceMappers => {
|
||||||
catalogs: ICatalogs
|
|
||||||
): ICustomerPersistenceMappers => {
|
|
||||||
const { taxCatalog } = catalogs;
|
|
||||||
|
|
||||||
// Mappers para el repositorio
|
// Mappers para el repositorio
|
||||||
const domainMapper = new SequelizeCustomerDomainMapper({
|
const domainMapper = new SequelizeCustomerDomainMapper();
|
||||||
taxCatalog,
|
const summaryMapper = new SequelizeCustomerSummaryMapper();
|
||||||
});
|
|
||||||
const listMapper = new SequelizeCustomerListMapper();
|
|
||||||
|
|
||||||
// Mappers el DTO a las props validadas (CustomerProps) y luego construir agregado
|
// Mappers el DTO a las props validadas (CustomerProps) y luego construir agregado
|
||||||
const createMapper = new CreateCustomerInputMapper({ taxCatalog });
|
//const createMapper = new CreateCustomerInputMapper();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
domainMapper,
|
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 type { Sequelize } from "sequelize";
|
||||||
|
|
||||||
import { CustomerRepository } from "../persistence";
|
import { CustomerRepository } from "../sequelize";
|
||||||
|
|
||||||
import type { ICustomerPersistenceMappers } from "./customer-persistence-mappers.di";
|
import type { ICustomerPersistenceMappers } from "./customer-persistence-mappers.di";
|
||||||
|
|
||||||
@ -10,5 +10,5 @@ export const buildCustomerRepository = (params: {
|
|||||||
}) => {
|
}) => {
|
||||||
const { database, mappers } = 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 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 = {
|
export type CustomersInternalDeps = {
|
||||||
useCases: {
|
useCases: {
|
||||||
listCustomers: () => ListCustomersUseCase;
|
listCustomers: () => ListCustomersUseCase;
|
||||||
getCustomerById: () => GetCustomerByIdUseCase;
|
//getCustomerById: () => GetCustomerByIdUseCase;
|
||||||
reportCustomer: () => ReportCustomerUseCase;
|
//reportCustomer: () => ReportCustomerUseCase;
|
||||||
createCustomer: () => CreateCustomerUseCase;
|
//createCustomer: () => CreateCustomerUseCase;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
updateCustomer: () => UpdateCustomerUseCase;
|
updateCustomer: () => UpdateCustomerUseCase;
|
||||||
@ -15,25 +25,24 @@ export type CustomersInternalDeps = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export function buildCustomersDependencies(params: ModuleParams): CustomersInternalDeps {
|
export function buildCustomersDependencies(params: ModuleParams): CustomersInternalDeps {
|
||||||
const { database } = params;
|
const { database } = params;
|
||||||
|
|
||||||
// Infrastructure
|
// Infrastructure
|
||||||
const transactionManager = buildTransactionManager(database);
|
const transactionManager = buildTransactionManager(database);
|
||||||
const catalogs = buildCatalogs();
|
const catalogs = buildCatalogs();
|
||||||
const persistenceMappers = buildCustomerPersistenceMappers(catalogs);
|
const persistenceMappers = buildCustomerPersistenceMappers();
|
||||||
|
|
||||||
const repository = buildCustomerRepository({ database, mappers: persistenceMappers });
|
const repository = buildCustomerRepository({ database, mappers: persistenceMappers });
|
||||||
const numberService = buildCustomerNumberGenerator();
|
//const numberService = buildCustomerNumberGenerator();
|
||||||
|
|
||||||
// Application helpers
|
// Application helpers
|
||||||
const inputMappers = buildCustomerInputMappers(catalogs);
|
//const inputMappers = buildCustomerInputMappers(catalogs);
|
||||||
const finder = buildCustomerFinder(repository);
|
const finder = buildCustomerFinder(repository);
|
||||||
const creator = buildCustomerCreator({ numberService, repository });
|
//const creator = buildCustomerCreator({ numberService, repository });
|
||||||
|
|
||||||
const snapshotBuilders = buildCustomersnapshotBuilders();
|
const snapshotBuilders = buildCustomerSnapshotBuilders();
|
||||||
const documentGeneratorPipeline = buildCustomerDocumentService(params);
|
//const documentGeneratorPipeline = buildCustomerDocumentService(params);
|
||||||
|
|
||||||
// Internal use cases (factories)
|
// Internal use cases (factories)
|
||||||
return {
|
return {
|
||||||
@ -41,11 +50,11 @@ export function buildCustomersDependencies(params: ModuleParams): CustomersInter
|
|||||||
listCustomers: () =>
|
listCustomers: () =>
|
||||||
buildListCustomersUseCase({
|
buildListCustomersUseCase({
|
||||||
finder,
|
finder,
|
||||||
itemSnapshotBuilder: snapshotBuilders.list,
|
summarySnapshotBuilder: snapshotBuilders.summary,
|
||||||
transactionManager,
|
transactionManager,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getCustomerById: () =>
|
/*getCustomerById: () =>
|
||||||
buildGetCustomerByIdUseCase({
|
buildGetCustomerByIdUseCase({
|
||||||
finder,
|
finder,
|
||||||
fullSnapshotBuilder: snapshotBuilders.full,
|
fullSnapshotBuilder: snapshotBuilders.full,
|
||||||
@ -67,7 +76,7 @@ export function buildCustomersDependencies(params: ModuleParams): CustomersInter
|
|||||||
dtoMapper: inputMappers.createInputMapper,
|
dtoMapper: inputMappers.createInputMapper,
|
||||||
fullSnapshotBuilder: snapshotBuilders.full,
|
fullSnapshotBuilder: snapshotBuilders.full,
|
||||||
transactionManager,
|
transactionManager,
|
||||||
}),
|
}),*/
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
|
export * from "./customer-public-services";
|
||||||
export * from "./customers.di";
|
export * from "./customers.di";
|
||||||
@ -1,39 +1,22 @@
|
|||||||
import {
|
import {
|
||||||
type RequestWithAuth,
|
|
||||||
mockUser,
|
mockUser,
|
||||||
requireAuthenticated,
|
requireAuthenticated,
|
||||||
requireCompanyContext,
|
requireCompanyContext,
|
||||||
} from "@erp/auth/api";
|
} from "@erp/auth/api";
|
||||||
import { type ModuleParams, validateRequest } from "@erp/core/api";
|
import { type ModuleParams, RequestWithAuth, validateRequest } from "@erp/core/api";
|
||||||
import type { ILogger } from "@repo/rdx-logger";
|
import { type NextFunction, type Request, type Response, Router } from "express";
|
||||||
import { type Application, type NextFunction, type Request, type Response, Router } from "express";
|
|
||||||
import type { Sequelize } from "sequelize";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CreateCustomerRequestSchema,
|
CustomerListRequestSchema
|
||||||
CustomerListRequestSchema,
|
|
||||||
GetCustomerByIdRequestSchema,
|
|
||||||
UpdateCustomerByIdParamsRequestSchema,
|
|
||||||
UpdateCustomerByIdRequestSchema,
|
|
||||||
} from "../../../common/dto";
|
} from "../../../common/dto";
|
||||||
import { buildCustomerDependencies } from "../dependencies";
|
import type { CustomersInternalDeps } from "../di";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CreateCustomerController,
|
ListCustomersController
|
||||||
GetCustomerController,
|
|
||||||
ListCustomersController,
|
|
||||||
UpdateCustomerController,
|
|
||||||
} from "./controllers";
|
} from "./controllers";
|
||||||
|
|
||||||
export const customersRouter = (params: ModuleParams, deps: CustomerInternalDeps) => {
|
export const customersRouter = (params: ModuleParams, deps: CustomersInternalDeps) => {
|
||||||
const { app, baseRoutePath, logger } = params as {
|
const { app, config } = params;
|
||||||
app: Application;
|
|
||||||
database: Sequelize;
|
|
||||||
baseRoutePath: string;
|
|
||||||
logger: ILogger;
|
|
||||||
};
|
|
||||||
|
|
||||||
const deps = buildCustomerDependencies(params);
|
|
||||||
|
|
||||||
const router: Router = Router({ mergeParams: true });
|
const router: Router = Router({ mergeParams: true });
|
||||||
|
|
||||||
@ -62,49 +45,49 @@ export const customersRouter = (params: ModuleParams, deps: CustomerInternalDeps
|
|||||||
|
|
||||||
validateRequest(CustomerListRequestSchema, "params"),
|
validateRequest(CustomerListRequestSchema, "params"),
|
||||||
(req: Request, res: Response, next: NextFunction) => {
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
const useCase = deps.build.list();
|
const useCase = deps.useCases.listCustomers();
|
||||||
const controller = new ListCustomersController(useCase /*, deps.presenters.list */);
|
const controller = new ListCustomersController(useCase /*, deps.presenters.list */);
|
||||||
return controller.execute(req, res, next);
|
return controller.execute(req, res, next);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
/* router.get(
|
||||||
"/:customer_id",
|
"/:customer_id",
|
||||||
//checkTabContext,
|
//checkTabContext,
|
||||||
|
|
||||||
validateRequest(GetCustomerByIdRequestSchema, "params"),
|
validateRequest(GetCustomerByIdRequestSchema, "params"),
|
||||||
(req: Request, res: Response, next: NextFunction) => {
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
const useCase = deps.build.get();
|
const useCase = deps.useCases.get();
|
||||||
const controller = new GetCustomerController(useCase);
|
const controller = new GetCustomerController(useCase);
|
||||||
return controller.execute(req, res, next);
|
return controller.execute(req, res, next);
|
||||||
}
|
}
|
||||||
);
|
); */
|
||||||
|
|
||||||
router.post(
|
/* router.post(
|
||||||
"/",
|
"/",
|
||||||
//checkTabContext,
|
//checkTabContext,
|
||||||
|
|
||||||
validateRequest(CreateCustomerRequestSchema, "body"),
|
validateRequest(CreateCustomerRequestSchema, "body"),
|
||||||
(req: Request, res: Response, next: NextFunction) => {
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
const useCase = deps.build.create();
|
const useCase = deps.useCases.create();
|
||||||
const controller = new CreateCustomerController(useCase);
|
const controller = new CreateCustomerController(useCase);
|
||||||
return controller.execute(req, res, next);
|
return controller.execute(req, res, next);
|
||||||
}
|
}
|
||||||
);
|
); */
|
||||||
|
|
||||||
router.put(
|
/* router.put(
|
||||||
"/:customer_id",
|
"/:customer_id",
|
||||||
//checkTabContext,
|
//checkTabContext,
|
||||||
|
|
||||||
validateRequest(UpdateCustomerByIdParamsRequestSchema, "params"),
|
validateRequest(UpdateCustomerByIdParamsRequestSchema, "params"),
|
||||||
validateRequest(UpdateCustomerByIdRequestSchema, "body"),
|
validateRequest(UpdateCustomerByIdRequestSchema, "body"),
|
||||||
(req: Request, res: Response, next: NextFunction) => {
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
const useCase = deps.build.update();
|
const useCase = deps.useCases.update();
|
||||||
const controller = new UpdateCustomerController(useCase);
|
const controller = new UpdateCustomerController(useCase);
|
||||||
return controller.execute(req, res, next);
|
return controller.execute(req, res, next);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
*/
|
||||||
/*router.delete(
|
/*router.delete(
|
||||||
"/:customer_id",
|
"/:customer_id",
|
||||||
//checkTabContext,
|
//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 MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
||||||
import type { ICustomerDomainMapper } from "@erp/customers/api/application";
|
|
||||||
import {
|
import {
|
||||||
City,
|
City,
|
||||||
Country,
|
Country,
|
||||||
@ -28,10 +27,11 @@ import { Collection, Result } from "@repo/rdx-utils";
|
|||||||
import { Customer, CustomerStatus, type ICustomerProps } from "../../../domain";
|
import { Customer, CustomerStatus, type ICustomerProps } from "../../../domain";
|
||||||
import type { CustomerCreationAttributes, CustomerModel } from "../../sequelize";
|
import type { CustomerCreationAttributes, CustomerModel } from "../../sequelize";
|
||||||
|
|
||||||
export class CustomerDomainMapper
|
export class SequelizeCustomerDomainMapper extends SequelizeDomainMapper<
|
||||||
extends SequelizeDomainMapper<CustomerModel, CustomerCreationAttributes, Customer>
|
CustomerModel,
|
||||||
implements ICustomerDomainMapper
|
CustomerCreationAttributes,
|
||||||
{
|
Customer
|
||||||
|
> {
|
||||||
public mapToDomain(source: CustomerModel, params?: MapperParamsType): Result<Customer, Error> {
|
public mapToDomain(source: CustomerModel, params?: MapperParamsType): Result<Customer, Error> {
|
||||||
try {
|
try {
|
||||||
const errors: ValidationErrorDetail[] = [];
|
const errors: ValidationErrorDetail[] = [];
|
||||||
@ -1,2 +1,2 @@
|
|||||||
export * from "./domain";
|
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 { CustomerStatus } from "../../../domain";
|
||||||
import type { CustomerModel } from "../../sequelize";
|
import type { CustomerModel } from "../../sequelize";
|
||||||
|
|
||||||
export class CustomerSummaryMapper
|
export class SequelizeCustomerSummaryMapper
|
||||||
extends SequelizeQueryMapper<CustomerModel, CustomerSummary>
|
extends SequelizeQueryMapper<CustomerModel, CustomerSummary>
|
||||||
implements ICustomerSummaryMapper
|
implements ICustomerSummaryMapper
|
||||||
{
|
{
|
||||||
@ -7,11 +7,11 @@ import {
|
|||||||
import { type Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server";
|
import { type Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server";
|
||||||
import type { UniqueID } from "@repo/rdx-ddd";
|
import type { UniqueID } from "@repo/rdx-ddd";
|
||||||
import { type Collection, Result } from "@repo/rdx-utils";
|
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 { CustomerSummary, ICustomerRepository } from "../../../application";
|
||||||
import type { Customer } from "../../../domain";
|
import type { Customer } from "../../../domain";
|
||||||
import type { ICustomerDomainMapper, ICustomerListMapper } from "../../mappers";
|
import type { SequelizeCustomerDomainMapper, SequelizeCustomerSummaryMapper } from "../../mappers";
|
||||||
import { CustomerModel } from "../models/customer.model";
|
import { CustomerModel } from "../models/customer.model";
|
||||||
|
|
||||||
export class CustomerRepository
|
export class CustomerRepository
|
||||||
@ -19,8 +19,8 @@ export class CustomerRepository
|
|||||||
implements ICustomerRepository
|
implements ICustomerRepository
|
||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private readonly domainMapper: ICustomerDomainMapper,
|
private readonly domainMapper: SequelizeCustomerDomainMapper,
|
||||||
private readonly listMapper: ICustomerListMapper,
|
private readonly summaryMapper: SequelizeCustomerSummaryMapper,
|
||||||
database: Sequelize
|
database: Sequelize
|
||||||
) {
|
) {
|
||||||
super({ database });
|
super({ database });
|
||||||
@ -120,23 +120,42 @@ export class CustomerRepository
|
|||||||
async getByIdInCompany(
|
async getByIdInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
id: UniqueID,
|
id: UniqueID,
|
||||||
transaction?: Transaction
|
transaction?: Transaction,
|
||||||
|
options: FindOptions<InferAttributes<CustomerModel>> = {}
|
||||||
): Promise<Result<Customer, Error>> {
|
): Promise<Result<Customer, Error>> {
|
||||||
try {
|
try {
|
||||||
const mapper: ICustomerDomainMapper = this._registry.getDomainMapper({
|
// Normalización defensiva de order/include
|
||||||
resource: "customer",
|
const normalizedOrder = Array.isArray(options.order)
|
||||||
});
|
? options.order
|
||||||
|
: options.order
|
||||||
|
? [options.order]
|
||||||
|
: [];
|
||||||
|
|
||||||
const row = await CustomerModel.findOne({
|
const normalizedInclude = Array.isArray(options.include)
|
||||||
where: { id: id.toString(), company_id: companyId.toString() },
|
? 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,
|
transaction,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
const row = await CustomerModel.findOne(mergedOptions);
|
||||||
|
|
||||||
if (!row) {
|
if (!row) {
|
||||||
return Result.fail(new EntityNotFoundError("Customer", "id", id.toString()));
|
return Result.fail(new EntityNotFoundError("Customer", "id", id.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const customer = mapper.mapToDomain(row);
|
const customer = this.domainMapper.mapToDomain(row);
|
||||||
return customer;
|
return customer;
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
return Result.fail(translateSequelizeError(error));
|
return Result.fail(translateSequelizeError(error));
|
||||||
@ -156,7 +175,8 @@ export class CustomerRepository
|
|||||||
async findByCriteriaInCompany(
|
async findByCriteriaInCompany(
|
||||||
companyId: UniqueID,
|
companyId: UniqueID,
|
||||||
criteria: Criteria,
|
criteria: Criteria,
|
||||||
transaction?: Transaction
|
transaction?: Transaction,
|
||||||
|
options: FindOptions<InferAttributes<CustomerModel>> = {}
|
||||||
): Promise<Result<Collection<CustomerSummary>, Error>> {
|
): Promise<Result<Collection<CustomerSummary>, Error>> {
|
||||||
try {
|
try {
|
||||||
const criteriaConverter = new CriteriaToSequelizeConverter();
|
const criteriaConverter = new CriteriaToSequelizeConverter();
|
||||||
@ -182,18 +202,48 @@ export class CustomerRepository
|
|||||||
strictMode: true, // fuerza error si ORDER BY no permitido
|
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 = {
|
||||||
...query.where,
|
...query.where,
|
||||||
company_id: companyId.toString(),
|
company_id: companyId.toString(),
|
||||||
deleted_at: null,
|
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({
|
const { rows, count } = await CustomerModel.findAndCountAll({
|
||||||
...query,
|
...query,
|
||||||
transaction,
|
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) {
|
} catch (err: unknown) {
|
||||||
return Result.fail(translateSequelizeError(err));
|
return Result.fail(translateSequelizeError(err));
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user