.
This commit is contained in:
parent
969f47357d
commit
8a0e776ce3
@ -18,8 +18,6 @@ import { InfrastructureUnavailableError } from "../../errors/infrastructure-unav
|
||||
* 👉 Este traductor pertenece a la infraestructura (persistencia)
|
||||
*/
|
||||
export function translateSequelizeError(err: unknown): Error {
|
||||
console.error(err);
|
||||
|
||||
// 1) Duplicados (índices únicos)
|
||||
if (err instanceof UniqueConstraintError) {
|
||||
// Tomamos el primer detalle (puede haber varios)
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { ProformaFactory } from "../factories";
|
||||
import type { IProformaRepository } from "../repositories";
|
||||
import { type IProformaCreator, type IProformaNumberGenerator, ProformaCreator } from "../services";
|
||||
|
||||
@ -7,11 +6,9 @@ export const buildProformaCreator = (params: {
|
||||
repository: IProformaRepository;
|
||||
}): IProformaCreator => {
|
||||
const { numberService, repository } = params;
|
||||
const factory = new ProformaFactory();
|
||||
|
||||
return new ProformaCreator({
|
||||
numberService,
|
||||
factory,
|
||||
repository,
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
export * from "./proforma-factory";
|
||||
export * from "./proforma-factory.interface";
|
||||
@ -1,17 +0,0 @@
|
||||
import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import type { Result } from "@repo/rdx-utils";
|
||||
|
||||
import type { IProformaCreateProps, Proforma } from "../../../domain";
|
||||
|
||||
export interface IProformaFactory {
|
||||
/**
|
||||
* Crea una proforma válida para una empresa a partir de props ya validadas.
|
||||
*
|
||||
* No persiste el agregado.
|
||||
*/
|
||||
createProforma(
|
||||
companyId: UniqueID,
|
||||
props: Omit<IProformaCreateProps, "companyId">,
|
||||
proformaId?: UniqueID
|
||||
): Result<Proforma, Error>;
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import type { Result } from "@repo/rdx-utils";
|
||||
|
||||
import { type IProformaCreateProps, Proforma } from "../../../domain";
|
||||
|
||||
import type { IProformaFactory } from "./proforma-factory.interface";
|
||||
|
||||
export class ProformaFactory implements IProformaFactory {
|
||||
createProforma(
|
||||
companyId: UniqueID,
|
||||
props: Omit<IProformaCreateProps, "companyId">,
|
||||
proformaId?: UniqueID
|
||||
): Result<Proforma, Error> {
|
||||
return Proforma.create({ ...props, companyId }, proformaId);
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@ import { Maybe, Result } from "@repo/rdx-utils";
|
||||
import type { CreateProformaItemRequestDTO, CreateProformaRequestDTO } from "../../../../common";
|
||||
import {
|
||||
type IProformaCreateProps,
|
||||
type IProformaItemProps,
|
||||
type IProformaItemCreateProps,
|
||||
InvoiceNumber,
|
||||
InvoicePaymentMethod,
|
||||
type InvoiceRecipient,
|
||||
@ -210,8 +210,8 @@ export class CreateProformaInputMapper /*implements ICreateProformaInputMapper*/
|
||||
globalDiscountPercentage: DiscountPercentage;
|
||||
errors: ValidationErrorDetail[];
|
||||
}
|
||||
): IProformaItemProps[] {
|
||||
const itemsProps: IProformaItemProps[] = [];
|
||||
): IProformaItemCreateProps[] {
|
||||
const itemsProps: IProformaItemCreateProps[] = [];
|
||||
|
||||
dto.items.forEach((item, index) => {
|
||||
const description = extractOrPushError(
|
||||
|
||||
@ -2,8 +2,7 @@ import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import type { Transaction } from "sequelize";
|
||||
|
||||
import type { IProformaCreateProps, Proforma } from "../../../domain";
|
||||
import type { IProformaFactory } from "../factories";
|
||||
import { type IProformaCreateProps, Proforma } from "../../../domain";
|
||||
import type { IProformaRepository } from "../repositories";
|
||||
|
||||
import type { IProformaNumberGenerator } from "./proforma-number-generator.interface";
|
||||
@ -21,18 +20,15 @@ export interface IProformaCreator {
|
||||
|
||||
type ProformaCreatorDeps = {
|
||||
numberService: IProformaNumberGenerator;
|
||||
factory: IProformaFactory;
|
||||
repository: IProformaRepository;
|
||||
};
|
||||
|
||||
export class ProformaCreator implements IProformaCreator {
|
||||
private readonly numberService: IProformaNumberGenerator;
|
||||
private readonly factory: IProformaFactory;
|
||||
private readonly repository: IProformaRepository;
|
||||
|
||||
constructor(deps: ProformaCreatorDeps) {
|
||||
this.numberService = deps.numberService;
|
||||
this.factory = deps.factory;
|
||||
this.repository = deps.repository;
|
||||
}
|
||||
|
||||
@ -55,20 +51,13 @@ export class ProformaCreator implements IProformaCreator {
|
||||
const invoiceNumber = numberResult.data;
|
||||
|
||||
// 2. Crear agregado
|
||||
const buildResult = this.factory.createProforma(
|
||||
companyId,
|
||||
{
|
||||
...props,
|
||||
invoiceNumber,
|
||||
},
|
||||
id
|
||||
);
|
||||
const proformaResult = Proforma.create({ ...props, invoiceNumber, companyId }, id);
|
||||
|
||||
if (buildResult.isFailure) {
|
||||
return Result.fail(buildResult.error);
|
||||
if (proformaResult.isFailure) {
|
||||
return Result.fail(proformaResult.error);
|
||||
}
|
||||
|
||||
const proforma = buildResult.data;
|
||||
const proforma = proformaResult.data;
|
||||
|
||||
// 3. Persistir
|
||||
const saveResult = await this.repository.create(proforma, transaction);
|
||||
|
||||
@ -18,12 +18,12 @@ export class ProformaFullSnapshotBuilder implements IProformaFullSnapshotBuilder
|
||||
private readonly taxesBuilder: IProformaTaxesFullSnapshotBuilder
|
||||
) {}
|
||||
|
||||
toOutput(invoice: Proforma): IProformaFullSnapshot {
|
||||
const items = this.itemsBuilder.toOutput(invoice.items);
|
||||
const recipient = this.recipientBuilder.toOutput(invoice);
|
||||
const taxes = this.taxesBuilder.toOutput(invoice.taxes());
|
||||
toOutput(proforma: Proforma): IProformaFullSnapshot {
|
||||
const items = this.itemsBuilder.toOutput(proforma.items);
|
||||
const recipient = this.recipientBuilder.toOutput(proforma);
|
||||
const taxes = this.taxesBuilder.toOutput(proforma.taxes());
|
||||
|
||||
const payment = invoice.paymentMethod.match(
|
||||
const payment = proforma.paymentMethod.match(
|
||||
(payment) => {
|
||||
const { id, payment_description } = payment.toObjectString();
|
||||
return {
|
||||
@ -34,27 +34,27 @@ export class ProformaFullSnapshotBuilder implements IProformaFullSnapshotBuilder
|
||||
() => undefined
|
||||
);
|
||||
|
||||
const allTotals = invoice.totals();
|
||||
const allTotals = proforma.totals();
|
||||
|
||||
return {
|
||||
id: invoice.id.toString(),
|
||||
company_id: invoice.companyId.toString(),
|
||||
id: proforma.id.toString(),
|
||||
company_id: proforma.companyId.toString(),
|
||||
|
||||
invoice_number: invoice.invoiceNumber.toString(),
|
||||
status: invoice.status.toPrimitive(),
|
||||
series: maybeToEmptyString(invoice.series, (value) => value.toString()),
|
||||
invoice_number: proforma.invoiceNumber.toString(),
|
||||
status: proforma.status.toPrimitive(),
|
||||
series: maybeToEmptyString(proforma.series, (value) => value.toString()),
|
||||
|
||||
invoice_date: invoice.invoiceDate.toDateString(),
|
||||
operation_date: maybeToEmptyString(invoice.operationDate, (value) => value.toDateString()),
|
||||
invoice_date: proforma.invoiceDate.toDateString(),
|
||||
operation_date: maybeToEmptyString(proforma.operationDate, (value) => value.toDateString()),
|
||||
|
||||
reference: maybeToEmptyString(invoice.reference, (value) => value.toString()),
|
||||
description: maybeToEmptyString(invoice.description, (value) => value.toString()),
|
||||
notes: maybeToEmptyString(invoice.notes, (value) => value.toString()),
|
||||
reference: maybeToEmptyString(proforma.reference, (value) => value.toString()),
|
||||
description: maybeToEmptyString(proforma.description, (value) => value.toString()),
|
||||
notes: maybeToEmptyString(proforma.notes, (value) => value.toString()),
|
||||
|
||||
language_code: invoice.languageCode.toString(),
|
||||
currency_code: invoice.currencyCode.toString(),
|
||||
language_code: proforma.languageCode.toString(),
|
||||
currency_code: proforma.currencyCode.toString(),
|
||||
|
||||
customer_id: invoice.customerId.toString(),
|
||||
customer_id: proforma.customerId.toString(),
|
||||
recipient,
|
||||
|
||||
payment_method: payment,
|
||||
@ -62,7 +62,7 @@ export class ProformaFullSnapshotBuilder implements IProformaFullSnapshotBuilder
|
||||
subtotal_amount: allTotals.subtotalAmount.toObjectString(),
|
||||
items_discount_amount: allTotals.itemDiscountAmount.toObjectString(),
|
||||
|
||||
global_discount_percentage: invoice.globalDiscountPercentage.toObjectString(),
|
||||
global_discount_percentage: proforma.globalDiscountPercentage.toObjectString(),
|
||||
global_discount_amount: allTotals.globalDiscountAmount.toObjectString(),
|
||||
|
||||
total_discount_amount: allTotals.totalDiscountAmount.toObjectString(),
|
||||
|
||||
@ -21,7 +21,7 @@ import {
|
||||
type ItemAmount,
|
||||
} from "../../common/value-objects";
|
||||
import {
|
||||
type IProformaItemProps,
|
||||
type IProformaItemCreateProps,
|
||||
type IProformaItems,
|
||||
ProformaItem,
|
||||
ProformaItems,
|
||||
@ -52,7 +52,7 @@ export interface IProformaCreateProps {
|
||||
|
||||
paymentMethod: Maybe<InvoicePaymentMethod>;
|
||||
|
||||
items: IProformaItemProps[];
|
||||
items: IProformaItemCreateProps[];
|
||||
globalDiscountPercentage: DiscountPercentage;
|
||||
}
|
||||
|
||||
@ -104,18 +104,32 @@ export type ProformaPatchProps = Partial<Omit<IProformaCreateProps, "companyId"
|
||||
//items?: ProformaItems;
|
||||
};
|
||||
|
||||
type InternalProformaProps = Omit<IProformaCreateProps, "items">;
|
||||
export type InternalProformaProps = Omit<IProformaCreateProps, "items">;
|
||||
|
||||
export class Proforma extends AggregateRoot<InternalProformaProps> implements IProforma {
|
||||
private readonly _items: ProformaItems;
|
||||
|
||||
protected constructor(props: InternalProformaProps, items: ProformaItems, id?: UniqueID) {
|
||||
super(props, id);
|
||||
this._items = items;
|
||||
}
|
||||
|
||||
// Creación funcional
|
||||
static create(props: IProformaCreateProps, id?: UniqueID): Result<Proforma, Error> {
|
||||
const internalItems = ProformaItems.create({
|
||||
items: [],
|
||||
languageCode: props.languageCode,
|
||||
currencyCode: props.currencyCode,
|
||||
globalDiscountPercentage: props.globalDiscountPercentage,
|
||||
});
|
||||
|
||||
const { items, ...internalProps } = props;
|
||||
const proforma = new Proforma(internalProps, id);
|
||||
const proforma = new Proforma(internalProps, internalItems, id);
|
||||
|
||||
const addItemsResult = proforma.initializeItems(items);
|
||||
const initializeResult = proforma.initializeItems(items);
|
||||
|
||||
if (addItemsResult.isFailure) {
|
||||
return Result.fail(addItemsResult.error);
|
||||
if (initializeResult.isFailure) {
|
||||
return Result.fail(initializeResult.error);
|
||||
}
|
||||
|
||||
// Reglas de negocio / validaciones
|
||||
@ -128,21 +142,25 @@ export class Proforma extends AggregateRoot<InternalProformaProps> implements IP
|
||||
}
|
||||
|
||||
// Rehidratación desde persistencia
|
||||
static rehydrate(props: InternalProformaProps, id: UniqueID): Proforma {
|
||||
return new Proforma(props, id);
|
||||
static rehydrate(props: InternalProformaProps, items: ProformaItems, id: UniqueID): Proforma {
|
||||
return new Proforma(props, items, id);
|
||||
}
|
||||
|
||||
private readonly _items: ProformaItems;
|
||||
private initializeItems(itemsProps: IProformaItemCreateProps[]): Result<void, Error> {
|
||||
for (const [index, itemProps] of itemsProps.entries()) {
|
||||
const itemResult = ProformaItem.create(itemProps);
|
||||
|
||||
protected constructor(props: InternalProformaProps, id?: UniqueID) {
|
||||
super(props, id);
|
||||
if (itemResult.isFailure) {
|
||||
return Result.fail(itemResult.error);
|
||||
}
|
||||
|
||||
this._items = ProformaItems.create({
|
||||
languageCode: props.languageCode,
|
||||
currencyCode: props.currencyCode,
|
||||
globalDiscountPercentage: props.globalDiscountPercentage,
|
||||
items: [],
|
||||
});
|
||||
const added = this._items.add(itemResult.data);
|
||||
|
||||
if (!added) {
|
||||
return Result.fail(new ProformaItemMismatch(index));
|
||||
}
|
||||
}
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
// Getters
|
||||
@ -206,7 +224,7 @@ export class Proforma extends AggregateRoot<InternalProformaProps> implements IP
|
||||
return this.props.globalDiscountPercentage;
|
||||
}
|
||||
|
||||
public get items(): IProformaItems {
|
||||
public get items(): ProformaItems {
|
||||
return this._items;
|
||||
}
|
||||
|
||||
@ -277,7 +295,7 @@ export class Proforma extends AggregateRoot<InternalProformaProps> implements IP
|
||||
return new ProformaTaxesCalculator(this.items).calculate();
|
||||
}
|
||||
|
||||
public addItem(props: IProformaItemProps): Result<void, Error> {
|
||||
public addItem(props: IProformaItemCreateProps): Result<void, Error> {
|
||||
const taxesResult = ProformaItemTaxes.create(props.taxes);
|
||||
if (taxesResult.isFailure) return Result.fail(taxesResult.error);
|
||||
|
||||
@ -318,23 +336,6 @@ export class Proforma extends AggregateRoot<InternalProformaProps> implements IP
|
||||
|
||||
// Helpers
|
||||
|
||||
private initializeItems(itemsProps: IProformaItemProps[]): Result<void, Error> {
|
||||
for (const [index, itemProps] of itemsProps.entries()) {
|
||||
const itemResult = ProformaItem.create(itemProps);
|
||||
|
||||
if (itemResult.isFailure) {
|
||||
return Result.fail(itemResult.error);
|
||||
}
|
||||
|
||||
const added = this._items.add(itemResult.data);
|
||||
|
||||
if (!added) {
|
||||
return Result.fail(new ProformaItemMismatch(index));
|
||||
}
|
||||
}
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Convierte un ItemAmount a InvoiceAmount (mantiene moneda y escala homogénea).
|
||||
*/
|
||||
|
||||
@ -25,7 +25,7 @@ import {
|
||||
*
|
||||
*/
|
||||
|
||||
export interface IProformaItemProps {
|
||||
export interface IProformaItemCreateProps {
|
||||
description: Maybe<ItemDescription>;
|
||||
|
||||
quantity: Maybe<ItemQuantity>; // Cantidad de unidades
|
||||
@ -87,22 +87,25 @@ export interface IProformaItem {
|
||||
isValued(): boolean; // Indica si el item tiene cantidad o precio (o ambos) para ser considerado "valorizado"
|
||||
}
|
||||
|
||||
type CreateProformaItemProps = IProformaItemProps;
|
||||
|
||||
type InternalProformaItemProps = Omit<IProformaItemProps, "taxes"> & {
|
||||
type InternalProformaItemProps = Omit<IProformaItemCreateProps, "taxes"> & {
|
||||
taxes: ProformaItemTaxes;
|
||||
};
|
||||
|
||||
export class ProformaItem extends DomainEntity<InternalProformaItemProps> implements IProformaItem {
|
||||
public static create(props: CreateProformaItemProps, id?: UniqueID): Result<ProformaItem, Error> {
|
||||
const taxesResult = ProformaItemTaxes.create(props.taxes);
|
||||
public static create(
|
||||
props: IProformaItemCreateProps,
|
||||
id?: UniqueID
|
||||
): Result<ProformaItem, Error> {
|
||||
const { taxes, ...internalProps } = props;
|
||||
|
||||
const taxesResult = ProformaItemTaxes.create(taxes);
|
||||
if (taxesResult.isFailure) {
|
||||
return Result.fail(taxesResult.error);
|
||||
}
|
||||
|
||||
const item = new ProformaItem(
|
||||
{
|
||||
...props,
|
||||
...internalProps,
|
||||
taxes: taxesResult.data,
|
||||
},
|
||||
id
|
||||
@ -155,7 +158,7 @@ export class ProformaItem extends DomainEntity<InternalProformaItemProps> implem
|
||||
return this.props.taxes;
|
||||
}
|
||||
|
||||
getProps(): IProformaItemProps {
|
||||
getProps(): IProformaItemCreateProps {
|
||||
return this.props;
|
||||
}
|
||||
|
||||
|
||||
@ -5,15 +5,10 @@ import { Collection, Result } from "@repo/rdx-utils";
|
||||
import { ProformaItemMismatch } from "../../errors";
|
||||
import { ProformaItemsTotalsCalculator } from "../../services/proforma-items-totals-calculator";
|
||||
|
||||
import type {
|
||||
IProformaItem,
|
||||
IProformaItemProps,
|
||||
IProformaItemTotals,
|
||||
ProformaItem,
|
||||
} from "./proforma-item.entity";
|
||||
import type { IProformaItem, IProformaItemTotals, ProformaItem } from "./proforma-item.entity";
|
||||
|
||||
export interface IProformaItemsProps {
|
||||
items: IProformaItemProps[];
|
||||
items?: ProformaItem[];
|
||||
|
||||
// Estos campos vienen de la cabecera,
|
||||
// pero se necesitan para cálculos y representaciones de la línea.
|
||||
@ -22,8 +17,19 @@ export interface IProformaItemsProps {
|
||||
currencyCode: CurrencyCode; // Para cálculos y formateos de moneda
|
||||
}
|
||||
|
||||
type CreateProformaProps = IProformaItemsProps;
|
||||
type InternalProformaProps = Omit<IProformaItemsProps, "items">;
|
||||
/*type ProformaItemCreateProps = {
|
||||
items: IProformaItemCreateProps[];
|
||||
|
||||
// Estos campos vienen de la cabecera,
|
||||
// pero se necesitan para cálculos y representaciones de la línea.
|
||||
globalDiscountPercentage: DiscountPercentage; // % descuento de la cabecera
|
||||
languageCode: LanguageCode; // Para formateos específicos de idioma
|
||||
currencyCode: CurrencyCode; // Para cálculos y formateos de moneda
|
||||
};
|
||||
|
||||
type InternalProformaProps = Omit<ProformaItemCreateProps, "items"> & {
|
||||
items: ProformaItem[];
|
||||
};*/
|
||||
|
||||
export interface IProformaItems {
|
||||
// OJO, no extendemos de Collection<IProformaItem> para no exponer
|
||||
@ -38,21 +44,23 @@ export interface IProformaItems {
|
||||
}
|
||||
|
||||
export class ProformaItems extends Collection<ProformaItem> implements IProformaItems {
|
||||
static create(props: CreateProformaProps): ProformaItems {
|
||||
return new ProformaItems(props);
|
||||
}
|
||||
|
||||
public readonly languageCode!: LanguageCode;
|
||||
public readonly currencyCode!: CurrencyCode;
|
||||
public readonly globalDiscountPercentage!: DiscountPercentage;
|
||||
|
||||
protected constructor(props: InternalProformaProps) {
|
||||
super([]);
|
||||
private constructor(props: IProformaItemsProps) {
|
||||
super(props.items ?? []);
|
||||
this.languageCode = props.languageCode;
|
||||
this.currencyCode = props.currencyCode;
|
||||
this.globalDiscountPercentage = props.globalDiscountPercentage;
|
||||
|
||||
this.ensureSameCurrencyAndLanguage(this.items);
|
||||
if (this.items.length > 0) {
|
||||
this.ensureSameContext(this.items);
|
||||
}
|
||||
}
|
||||
|
||||
static create(props: IProformaItemsProps): ProformaItems {
|
||||
return new ProformaItems(props);
|
||||
}
|
||||
|
||||
public add(item: ProformaItem): boolean {
|
||||
@ -105,10 +113,15 @@ export class ProformaItems extends Collection<ProformaItem> implements IProforma
|
||||
return new ProformaItemsTotalsCalculator(this).calculate();
|
||||
}
|
||||
|
||||
private ensureSameCurrencyAndLanguage(items: IProformaItem[]): void {
|
||||
private ensureSameContext(items: IProformaItem[]): void {
|
||||
for (const item of items) {
|
||||
if (!item.currencyCode.equals(this.currencyCode)) {
|
||||
throw new Error("[ProformaItems] All items must share the same currency.");
|
||||
const same =
|
||||
item.languageCode.equals(this.languageCode) &&
|
||||
item.currencyCode.equals(this.currencyCode) &&
|
||||
item.globalDiscountPercentage.equals(this.globalDiscountPercentage);
|
||||
|
||||
if (!same) {
|
||||
throw new Error("[ProformaItems] All items must share the same context.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ import { Maybe, Result } from "@repo/rdx-utils";
|
||||
import type { CreateProformaItemRequestDTO, CreateProformaRequestDTO } from "../../../../../common";
|
||||
import {
|
||||
type IProformaCreateProps,
|
||||
type IProformaItemProps,
|
||||
type IProformaItemCreateProps,
|
||||
InvoiceNumber,
|
||||
InvoicePaymentMethod,
|
||||
type InvoiceRecipient,
|
||||
@ -148,7 +148,7 @@ export class CreateProformaRequestMapper {
|
||||
);
|
||||
}
|
||||
|
||||
const proformaProps: Omit<IProformaCreateProps, "items"> & { items: IProformaItemProps[] } = {
|
||||
const proformaProps: Omit<IProformaCreateProps, "items"> & { items: IProformaItemCreateProps[] } = {
|
||||
companyId,
|
||||
status: defaultStatus!,
|
||||
|
||||
@ -181,7 +181,7 @@ export class CreateProformaRequestMapper {
|
||||
}
|
||||
}
|
||||
|
||||
private mapItems(items: CreateProformaItemRequestDTO[]): IProformaItemProps[] {
|
||||
private mapItems(items: CreateProformaItemRequestDTO[]): IProformaItemCreateProps[] {
|
||||
const proformaItems = CustomerInvoiceItems.create({
|
||||
currencyCode: this.currencyCode!,
|
||||
languageCode: this.languageCode!,
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
import { Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
|
||||
|
||||
import {
|
||||
type IProformaCreateProps,
|
||||
type InternalProformaProps,
|
||||
InvoiceNumber,
|
||||
InvoicePaymentMethod,
|
||||
InvoiceSerie,
|
||||
@ -67,7 +67,7 @@ export class SequelizeProformaDomainMapper extends SequelizeDomainMapper<
|
||||
const status = extractOrPushError(InvoiceStatus.create(raw.status), "status", errors);
|
||||
|
||||
const series = extractOrPushError(
|
||||
maybeFromNullableResult(raw.series, (v) => InvoiceSerie.create(v)),
|
||||
maybeFromNullableResult(raw.series, (value) => InvoiceSerie.create(value)),
|
||||
"series",
|
||||
errors
|
||||
);
|
||||
@ -86,7 +86,7 @@ export class SequelizeProformaDomainMapper extends SequelizeDomainMapper<
|
||||
);
|
||||
|
||||
const operationDate = extractOrPushError(
|
||||
maybeFromNullableResult(raw.operation_date, (v) => UtcDate.createFromISO(v)),
|
||||
maybeFromNullableResult(raw.operation_date, (value) => UtcDate.createFromISO(value)),
|
||||
"operation_date",
|
||||
errors
|
||||
);
|
||||
@ -195,11 +195,15 @@ export class SequelizeProformaDomainMapper extends SequelizeDomainMapper<
|
||||
});
|
||||
|
||||
// 3) Items (colección)
|
||||
const itemsResults = this._itemsMapper.mapToDomainCollection(raw.items, raw.items.length, {
|
||||
errors,
|
||||
parent: attributes,
|
||||
...params,
|
||||
});
|
||||
const itemCollectionResults = this._itemsMapper.mapToDomainCollection(
|
||||
raw.items,
|
||||
raw.items.length,
|
||||
{
|
||||
errors,
|
||||
parent: attributes,
|
||||
...params,
|
||||
}
|
||||
);
|
||||
|
||||
// 4) Si hubo errores de mapeo, devolvemos colección de validación
|
||||
if (errors.length > 0) {
|
||||
@ -209,15 +213,14 @@ export class SequelizeProformaDomainMapper extends SequelizeDomainMapper<
|
||||
}
|
||||
|
||||
// 6) Construcción del agregado (Dominio)
|
||||
|
||||
const items = ProformaItems.create({
|
||||
languageCode: attributes.languageCode!,
|
||||
currencyCode: attributes.currencyCode!,
|
||||
globalDiscountPercentage: attributes.globalDiscountPercentage!,
|
||||
items: itemsResults.data.getAll(),
|
||||
items: itemCollectionResults.data.getAll(),
|
||||
});
|
||||
|
||||
const invoiceProps: IProformaCreateProps = {
|
||||
const invoiceProps: InternalProformaProps = {
|
||||
companyId: attributes.companyId!,
|
||||
|
||||
status: attributes.status!,
|
||||
@ -239,21 +242,12 @@ export class SequelizeProformaDomainMapper extends SequelizeDomainMapper<
|
||||
globalDiscountPercentage: attributes.globalDiscountPercentage!,
|
||||
|
||||
paymentMethod: attributes.paymentMethod!,
|
||||
|
||||
items,
|
||||
};
|
||||
|
||||
const createResult = Proforma.create(invoiceProps, attributes.invoiceId);
|
||||
const invoiceId = attributes.invoiceId!;
|
||||
const proforma = Proforma.rehydrate(invoiceProps, items, invoiceId);
|
||||
|
||||
if (createResult.isFailure) {
|
||||
return Result.fail(
|
||||
new ValidationErrorCollection("Customer invoice entity creation failed", [
|
||||
{ path: "invoice", message: createResult.error.message },
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
return Result.ok(createResult.data);
|
||||
return Result.ok(proforma);
|
||||
} catch (err: unknown) {
|
||||
return Result.fail(err as Error);
|
||||
}
|
||||
@ -312,7 +306,7 @@ export class SequelizeProformaDomainMapper extends SequelizeDomainMapper<
|
||||
|
||||
const allAmounts = source.totals(); // Da los totales ya calculados
|
||||
|
||||
const invoiceValues: Partial<CustomerInvoiceCreationAttributes> = {
|
||||
const proformaValues: Partial<CustomerInvoiceCreationAttributes> = {
|
||||
// Identificación
|
||||
id: source.id.toPrimitive(),
|
||||
company_id: source.companyId.toPrimitive(),
|
||||
@ -360,6 +354,15 @@ export class SequelizeProformaDomainMapper extends SequelizeDomainMapper<
|
||||
taxable_amount_value: allAmounts.taxableAmount.value,
|
||||
taxable_amount_scale: allAmounts.taxableAmount.scale,
|
||||
|
||||
iva_amount_value: allAmounts.ivaAmount.value,
|
||||
iva_amount_scale: allAmounts.ivaAmount.scale,
|
||||
|
||||
rec_amount_value: allAmounts.recAmount.value,
|
||||
rec_amount_scale: allAmounts.recAmount.scale,
|
||||
|
||||
retention_amount_value: allAmounts.retentionAmount.value,
|
||||
retention_amount_scale: allAmounts.retentionAmount.scale,
|
||||
|
||||
taxes_amount_value: allAmounts.taxesAmount.value,
|
||||
taxes_amount_scale: allAmounts.taxesAmount.scale,
|
||||
|
||||
@ -374,7 +377,7 @@ export class SequelizeProformaDomainMapper extends SequelizeDomainMapper<
|
||||
};
|
||||
|
||||
return Result.ok<CustomerInvoiceCreationAttributes>(
|
||||
invoiceValues as CustomerInvoiceCreationAttributes
|
||||
proformaValues as CustomerInvoiceCreationAttributes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ import { Result } from "@repo/rdx-utils";
|
||||
|
||||
import {
|
||||
type IProformaCreateProps,
|
||||
type IProformaItemProps,
|
||||
type IProformaItemCreateProps,
|
||||
ItemAmount,
|
||||
ItemDescription,
|
||||
ItemQuantity,
|
||||
@ -54,7 +54,7 @@ export class SequelizeProformaItemDomainMapper extends SequelizeDomainMapper<
|
||||
private mapAttributesToDomain(
|
||||
raw: CustomerInvoiceItemModel,
|
||||
params?: MapperParamsType
|
||||
): Partial<IProformaItemProps & ProformaItemTaxesProps> & { itemId?: UniqueID } {
|
||||
): Partial<IProformaItemCreateProps & ProformaItemTaxesProps> & { itemId?: UniqueID } {
|
||||
const { errors, index, parent } = params as {
|
||||
index: number;
|
||||
errors: ValidationErrorDetail[];
|
||||
@ -160,7 +160,8 @@ export class SequelizeProformaItemDomainMapper extends SequelizeDomainMapper<
|
||||
});
|
||||
|
||||
// 2) Construcción del elemento de dominio
|
||||
const createResult = ProformaItem.create(
|
||||
const itemId = attributes.itemId!;
|
||||
const newItem = ProformaItem.rehydrate(
|
||||
{
|
||||
languageCode: attributes.languageCode!,
|
||||
currencyCode: attributes.currencyCode!,
|
||||
@ -171,18 +172,18 @@ export class SequelizeProformaItemDomainMapper extends SequelizeDomainMapper<
|
||||
globalDiscountPercentage: attributes.globalDiscountPercentage!,
|
||||
taxes: taxesResult.data,
|
||||
},
|
||||
attributes.itemId
|
||||
itemId
|
||||
);
|
||||
|
||||
if (createResult.isFailure) {
|
||||
/*if (createResult.isFailure) {
|
||||
return Result.fail(
|
||||
new ValidationErrorCollection("Invoice item entity creation failed", [
|
||||
{ path: `items[${index}]`, message: createResult.error.message },
|
||||
])
|
||||
);
|
||||
}
|
||||
}*/
|
||||
|
||||
return createResult;
|
||||
return Result.ok(newItem);
|
||||
}
|
||||
|
||||
public mapToPersistence(
|
||||
|
||||
@ -365,8 +365,6 @@ export class ProformaRepository
|
||||
? [options.include]
|
||||
: [];
|
||||
|
||||
console.log(query.where);
|
||||
|
||||
query.where = {
|
||||
...query.where,
|
||||
...(options.where ?? {}),
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { JsonTaxCatalogProvider } from "@erp/core";
|
||||
import { DiscountPercentage, Tax } from "@erp/core/api";
|
||||
import {
|
||||
type IProformaItemProps,
|
||||
type IProformaItemCreateProps,
|
||||
InvoicePaymentMethod,
|
||||
InvoiceSerie,
|
||||
ItemAmount,
|
||||
@ -9,7 +9,6 @@ import {
|
||||
ItemQuantity,
|
||||
type ProformaItemTaxesProps,
|
||||
} from "@erp/customer-invoices/api/domain";
|
||||
import type { CustomerTaxesProps } from "@erp/customers/api/domain";
|
||||
import {
|
||||
City,
|
||||
Country,
|
||||
@ -91,7 +90,7 @@ export interface IProformaFromFactuGESProps {
|
||||
|
||||
paymentMethod: Maybe<InvoicePaymentMethod>;
|
||||
|
||||
items: IProformaItemProps[];
|
||||
items: IProformaItemCreateProps[];
|
||||
globalDiscountPercentage: DiscountPercentage;
|
||||
};
|
||||
}
|
||||
@ -409,29 +408,6 @@ export class CreateProformaFromFactugesInputMapper
|
||||
return customerProps;
|
||||
}
|
||||
|
||||
private mapCustomerTaxesProps(
|
||||
dto: CreateProformaFromFactugesRequestDTO["customer"],
|
||||
params: {
|
||||
errors: ValidationErrorDetail[];
|
||||
}
|
||||
): CustomerTaxesProps {
|
||||
const { errors } = params;
|
||||
|
||||
const iva = extractOrPushError(
|
||||
maybeFromNullableResult("iva_21", (value) => Tax.createFromCode(value, this.taxCatalog)),
|
||||
"iva_21",
|
||||
errors
|
||||
);
|
||||
|
||||
this.throwIfValidationErrors(errors);
|
||||
|
||||
return {
|
||||
iva: iva!,
|
||||
rec: Maybe.none(),
|
||||
retention: Maybe.none(),
|
||||
};
|
||||
}
|
||||
|
||||
private mapItemsProps(
|
||||
dto: CreateProformaFromFactugesRequestDTO,
|
||||
params: {
|
||||
@ -440,8 +416,8 @@ export class CreateProformaFromFactugesInputMapper
|
||||
globalDiscountPercentage: DiscountPercentage;
|
||||
errors: ValidationErrorDetail[];
|
||||
}
|
||||
): IProformaItemProps[] {
|
||||
const itemsProps: IProformaItemProps[] = [];
|
||||
): IProformaItemCreateProps[] {
|
||||
const itemsProps: IProformaItemCreateProps[] = [];
|
||||
|
||||
dto.items.forEach((item, index) => {
|
||||
const description = extractOrPushError(
|
||||
@ -482,15 +458,18 @@ export class CreateProformaFromFactugesInputMapper
|
||||
this.throwIfValidationErrors(params.errors);
|
||||
|
||||
itemsProps.push({
|
||||
description: description!,
|
||||
|
||||
quantity: quantity!,
|
||||
unitAmount: unitAmount!,
|
||||
|
||||
itemDiscountPercentage: discountPercentage!,
|
||||
|
||||
taxes,
|
||||
|
||||
globalDiscountPercentage: params.globalDiscountPercentage,
|
||||
languageCode: params.languageCode,
|
||||
currencyCode: params.currencyCode,
|
||||
|
||||
description: description!,
|
||||
quantity: quantity!,
|
||||
unitAmount: unitAmount!,
|
||||
itemDiscountPercentage: discountPercentage!,
|
||||
taxes,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user