Importación desde FactuGES

This commit is contained in:
David Arranz 2026-03-25 14:26:10 +01:00
parent a4615e8bc4
commit 2ae118d1ff
4 changed files with 235 additions and 101 deletions

View File

@ -170,7 +170,7 @@ export class ProformaItem extends DomainEntity<InternalProformaItemProps> implem
// Todos a 4 decimales // Todos a 4 decimales
public isValued(): boolean { public isValued(): boolean {
return this.props.quantity.isSome() || this.props.unitAmount.isSome(); return this.props.quantity.isSome() && this.props.unitAmount.isSome();
} }
public subtotalAmount(): ItemAmount { public subtotalAmount(): ItemAmount {

View File

@ -1,7 +1,7 @@
import type { JsonTaxCatalogProvider } from "@erp/core"; import type { JsonTaxCatalogProvider } from "@erp/core";
import { DiscountPercentage, Tax } from "@erp/core/api"; import { DiscountPercentage, Tax } from "@erp/core/api";
import { import {
type IProformaItemCreateProps, InvoiceAmount,
InvoiceSerie, InvoiceSerie,
ItemAmount, ItemAmount,
ItemDescription, ItemDescription,
@ -39,73 +39,86 @@ import type {
CreateProformaItemFromFactugesRequestDTO, CreateProformaItemFromFactugesRequestDTO,
} from "../../../common"; } from "../../../common";
export interface IProformaFromFactuGESProps { export type ProformaCustomerLookup = {
customerLookup: {
tin: TINNumber; tin: TINNumber;
}; };
paymentLookup: { export type ProformaPaymentLookup = {
factuges_id: string; factuges_id: string;
}; };
customerDraft: { export type ProformaCustomerDraft = {
//reference: Maybe<Name>;
isCompany: boolean; isCompany: boolean;
name: Name; name: Name;
//tradeName: Maybe<Name>;
tin: TINNumber; tin: TINNumber;
address: PostalAddressProps; address: PostalAddressProps;
emailPrimary: Maybe<EmailAddress>; emailPrimary: Maybe<EmailAddress>;
emailSecondary: Maybe<EmailAddress>; emailSecondary: Maybe<EmailAddress>;
phonePrimary: Maybe<PhoneNumber>; phonePrimary: Maybe<PhoneNumber>;
phoneSecondary: Maybe<PhoneNumber>; phoneSecondary: Maybe<PhoneNumber>;
mobilePrimary: Maybe<PhoneNumber>; mobilePrimary: Maybe<PhoneNumber>;
mobileSecondary: Maybe<PhoneNumber>; mobileSecondary: Maybe<PhoneNumber>;
//fax: Maybe<PhoneNumber>;
website: Maybe<URLAddress>; website: Maybe<URLAddress>;
//legalRecord: Maybe<TextValue>;
//defaultTaxes: CustomerTaxesProps;
languageCode: LanguageCode; languageCode: LanguageCode;
currencyCode: CurrencyCode; currencyCode: CurrencyCode;
}; };
proformaDraft: { export type ProformaDraftItem = {
series: Maybe<InvoiceSerie>; position: string;
description: Maybe<ItemDescription>;
quantity: Maybe<ItemQuantity>;
unitAmount: Maybe<ItemAmount>;
subtotalAmount: Maybe<ItemAmount>;
itemDiscountPercentage: Maybe<DiscountPercentage>;
itemDiscountAmount: Maybe<ItemAmount>;
globalDiscountPercentage: DiscountPercentage;
globalDiscountAmount: Maybe<ItemAmount>;
totalDiscountAmount: Maybe<ItemAmount>;
taxableAmount: Maybe<ItemAmount>;
taxes: ProformaItemTaxesProps;
taxesAmount: Maybe<ItemAmount>;
totalAmount: Maybe<ItemAmount>;
languageCode: LanguageCode;
currencyCode: CurrencyCode;
};
export type ProformaDraft = {
series: Maybe<InvoiceSerie>;
invoiceDate: UtcDate; invoiceDate: UtcDate;
operationDate: Maybe<UtcDate>; operationDate: Maybe<UtcDate>;
reference: Maybe<string>; reference: Maybe<string>;
description: Maybe<string>; description: Maybe<string>;
notes: Maybe<TextValue>; notes: Maybe<TextValue>;
languageCode: LanguageCode; languageCode: LanguageCode;
currencyCode: CurrencyCode; currencyCode: CurrencyCode;
subtotalAmount: Maybe<ItemAmount>;
items: IProformaItemCreateProps[];
globalDiscountPercentage: DiscountPercentage; globalDiscountPercentage: DiscountPercentage;
itemsDiscountAmount: Maybe<ItemAmount>;
taxableAmount: Maybe<ItemAmount>;
taxes: ProformaItemTaxesProps;
taxesAmount: Maybe<ItemAmount>;
totalAmount: Maybe<ItemAmount>;
items: ProformaDraftItem[];
}; };
paymentDraft: { export type ProformaPaymentDraft = {
factuges_id: string; factuges_id: string;
description: string; description: string;
}; };
}
export type FactugesProformaPayload = {
customerLookup: ProformaCustomerLookup;
paymentLookup: ProformaPaymentLookup;
customerDraft: ProformaCustomerDraft;
proformaDraft: ProformaDraft;
paymentDraft: ProformaPaymentDraft;
};
export interface ICreateProformaFromFactugesInputMapper { export interface ICreateProformaFromFactugesInputMapper {
map( map(
dto: CreateProformaFromFactugesRequestDTO, dto: CreateProformaFromFactugesRequestDTO,
params: { companyId: UniqueID } params: { companyId: UniqueID }
): Result<IProformaFromFactuGESProps>; ): Result<FactugesProformaPayload>;
} }
export class CreateProformaFromFactugesInputMapper export class CreateProformaFromFactugesInputMapper
@ -120,7 +133,7 @@ export class CreateProformaFromFactugesInputMapper
public map( public map(
dto: CreateProformaFromFactugesRequestDTO, dto: CreateProformaFromFactugesRequestDTO,
params: { companyId: UniqueID } params: { companyId: UniqueID }
): Result<IProformaFromFactuGESProps> { ): Result<FactugesProformaPayload> {
try { try {
const errors: ValidationErrorDetail[] = []; const errors: ValidationErrorDetail[] = [];
const { companyId } = params; const { companyId } = params;
@ -172,7 +185,7 @@ export class CreateProformaFromFactugesInputMapper
currencyCode: CurrencyCode; currencyCode: CurrencyCode;
errors: ValidationErrorDetail[]; errors: ValidationErrorDetail[];
} }
): IProformaFromFactuGESProps["paymentDraft"] { ): ProformaPaymentDraft {
const errors: ValidationErrorDetail[] = []; const errors: ValidationErrorDetail[] = [];
const { companyId } = params; const { companyId } = params;
@ -189,7 +202,7 @@ export class CreateProformaFromFactugesInputMapper
currencyCode: CurrencyCode; currencyCode: CurrencyCode;
errors: ValidationErrorDetail[]; errors: ValidationErrorDetail[];
} }
): IProformaFromFactuGESProps["proformaDraft"] { ): ProformaDraft {
const errors: ValidationErrorDetail[] = []; const errors: ValidationErrorDetail[] = [];
const { companyId } = params; const { companyId } = params;
@ -230,7 +243,7 @@ export class CreateProformaFromFactugesInputMapper
); );
const description = extractOrPushError( const description = extractOrPushError(
maybeFromNullableResult(dto.reference, (value) => Result.ok(String(value))), maybeFromNullableResult(dto.description, (value) => Result.ok(String(value))),
"description", "description",
errors errors
); );
@ -241,12 +254,6 @@ export class CreateProformaFromFactugesInputMapper
errors errors
); );
const globalDiscountPercentage = extractOrPushError(
DiscountPercentage.create({ value: Number(dto.global_discount_percentage_value) }),
"global_discount_percentage_value",
params.errors
);
const languageCode = extractOrPushError( const languageCode = extractOrPushError(
LanguageCode.create(dto.customer.language_code), LanguageCode.create(dto.customer.language_code),
"language_code", "language_code",
@ -255,6 +262,59 @@ export class CreateProformaFromFactugesInputMapper
const currencyCode = CurrencyCode.fromEUR(); const currencyCode = CurrencyCode.fromEUR();
const subtotalAmount = extractOrPushError(
maybeFromNullableResult(dto.subtotal_amount_value, (value) =>
InvoiceAmount.create({ value: Number(value) })
),
"subtotal_amount_value",
params.errors
);
const globalDiscountPercentage = extractOrPushError(
DiscountPercentage.create({ value: Number(dto.global_discount_percentage_value) }),
"global_discount_percentage_value",
params.errors
);
const itemsDiscountAmount = extractOrPushError(
maybeFromNullableResult(dto.discount_amount_value, (value) =>
InvoiceAmount.create({ value: Number(value) })
),
"discount_amount_value",
params.errors
);
const taxableAmount = extractOrPushError(
maybeFromNullableResult(dto.taxable_amount_value, (value) =>
InvoiceAmount.create({ value: Number(value) })
),
"taxable_amount_value",
params.errors
);
// TODO: Determinar cómo calcular los impuestos de la cabecera de la proforma
const taxes: ProformaItemTaxesProps = {
iva: Maybe.none(),
retention: Maybe.none(),
rec: Maybe.none(),
};
const taxesAmount = extractOrPushError(
maybeFromNullableResult(dto.taxes_amount_value, (value) =>
InvoiceAmount.create({ value: Number(value) })
),
"taxes_amount_value",
params.errors
);
const totalAmount = extractOrPushError(
maybeFromNullableResult(dto.total_amount_value, (value) =>
InvoiceAmount.create({ value: Number(value) })
),
"total_amount_value",
params.errors
);
const itemsProps = this.mapItemsProps(dto, { const itemsProps = this.mapItemsProps(dto, {
languageCode: languageCode!, languageCode: languageCode!,
currencyCode: currencyCode, currencyCode: currencyCode,
@ -262,7 +322,7 @@ export class CreateProformaFromFactugesInputMapper
errors, errors,
}); });
const props: IProformaFromFactuGESProps["proformaDraft"] = { const props: ProformaDraft = {
//companyId, //companyId,
//status: defaultStatus, //status: defaultStatus,
@ -282,9 +342,16 @@ export class CreateProformaFromFactugesInputMapper
languageCode: languageCode!, languageCode: languageCode!,
currencyCode: currencyCode!, currencyCode: currencyCode!,
subtotalAmount: subtotalAmount!,
globalDiscountPercentage: globalDiscountPercentage!, globalDiscountPercentage: globalDiscountPercentage!,
itemsDiscountAmount: itemsDiscountAmount!,
items: itemsProps, // ← IProformaItemProps[] taxableAmount: taxableAmount!,
taxes: taxes,
taxesAmount: taxesAmount!,
totalAmount: totalAmount!,
items: itemsProps,
}; };
return props; return props;
@ -297,7 +364,7 @@ export class CreateProformaFromFactugesInputMapper
currencyCode: CurrencyCode; currencyCode: CurrencyCode;
errors: ValidationErrorDetail[]; errors: ValidationErrorDetail[];
} }
): IProformaFromFactuGESProps["customerDraft"] { ): ProformaCustomerDraft {
const { errors, currencyCode } = params; const { errors, currencyCode } = params;
const isCompany = dto.is_company === "1"; const isCompany = dto.is_company === "1";
@ -397,7 +464,7 @@ export class CreateProformaFromFactugesInputMapper
errors, errors,
});*/ });*/
const customerProps: IProformaFromFactuGESProps["customerDraft"] = { const customerProps: ProformaCustomerDraft = {
//companyId, //companyId,
//status: status!, //status: status!,
@ -440,10 +507,12 @@ export class CreateProformaFromFactugesInputMapper
globalDiscountPercentage: DiscountPercentage; globalDiscountPercentage: DiscountPercentage;
errors: ValidationErrorDetail[]; errors: ValidationErrorDetail[];
} }
): IProformaItemCreateProps[] { ): ProformaDraftItem[] {
const itemsProps: IProformaItemCreateProps[] = []; const itemsProps: ProformaDraftItem[] = [];
dto.items.forEach((item, index) => { dto.items.forEach((item, index) => {
const position = String(item.position);
const description = extractOrPushError( const description = extractOrPushError(
maybeFromNullableResult(item.description, (value) => ItemDescription.create(value)), maybeFromNullableResult(item.description, (value) => ItemDescription.create(value)),
`items[${index}].description`, `items[${index}].description`,
@ -454,19 +523,27 @@ export class CreateProformaFromFactugesInputMapper
maybeFromNullableResult(item.quantity_value, (value) => maybeFromNullableResult(item.quantity_value, (value) =>
ItemQuantity.create({ value: Number(value) }) ItemQuantity.create({ value: Number(value) })
), ),
"items[$index].quantity_value", `items[${index}].quantity_value`,
params.errors params.errors
); );
const unitAmount = extractOrPushError( const unitAmount = extractOrPushError(
maybeFromNullableResult(item.unit_value, (value) => maybeFromNullableResult(item.unit_amount_value, (value) =>
ItemAmount.create({ value: Number(value) }) ItemAmount.create({ value: Number(value) })
), ),
`items[${index}].unit_value`, `items[${index}].unit_amount_value`,
params.errors params.errors
); );
const discountPercentage = extractOrPushError( const subtotalAmount = extractOrPushError(
maybeFromNullableResult(item.subtotal_amount_value, (value) =>
ItemAmount.create({ value: Number(value) })
),
`items[${index}].subtotal_amount_value`,
params.errors
);
const itemDiscountPercentage = extractOrPushError(
maybeFromNullableResult(item.item_discount_percentage_value, (value) => maybeFromNullableResult(item.item_discount_percentage_value, (value) =>
DiscountPercentage.create({ value: Number(value) }) DiscountPercentage.create({ value: Number(value) })
), ),
@ -474,35 +551,93 @@ export class CreateProformaFromFactugesInputMapper
params.errors params.errors
); );
const taxes = this.mapItempTaxesProps(item, { const itemDiscountAmount = extractOrPushError(
maybeFromNullableResult(item.item_discount_amount_value, (value) =>
ItemAmount.create({ value: Number(value) })
),
`items[${index}].item_discount_amount_value`,
params.errors
);
const globalDiscountAmount = extractOrPushError(
maybeFromNullableResult(item.global_discount_amount_value, (value) =>
ItemAmount.create({ value: Number(value) })
),
`items[${index}].global_discount_amount_value`,
params.errors
);
const totalDiscountAmount = extractOrPushError(
maybeFromNullableResult(item.total_discount_amount_value, (value) =>
ItemAmount.create({ value: Number(value) })
),
`items[${index}].total_discount_amount_value`,
params.errors
);
const taxableAmount = extractOrPushError(
maybeFromNullableResult(item.taxable_amount_value, (value) =>
ItemAmount.create({ value: Number(value) })
),
`items[${index}].taxable_amount_value`,
params.errors
);
const taxesAmount = extractOrPushError(
maybeFromNullableResult(item.taxes_amount_value, (value) =>
ItemAmount.create({ value: Number(value) })
),
`items[${index}].taxes_amount_value`,
params.errors
);
const totalAmount = extractOrPushError(
maybeFromNullableResult(item.total_amount_value, (value) =>
ItemAmount.create({ value: Number(value) })
),
`items[${index}].total_amount_value`,
params.errors
);
const taxes = this.mapItemTaxesProps(item, {
itemIndex: index, itemIndex: index,
errors: params.errors, errors: params.errors,
}); });
this.throwIfValidationErrors(params.errors); const _item: ProformaDraftItem = {
position,
itemsProps.push({
description: description!, description: description!,
quantity: quantity!, quantity: quantity!,
unitAmount: unitAmount!, unitAmount: unitAmount!,
subtotalAmount: subtotalAmount!,
itemDiscountPercentage: discountPercentage!, itemDiscountPercentage: itemDiscountPercentage!,
itemDiscountAmount: itemDiscountAmount!,
taxes,
globalDiscountPercentage: params.globalDiscountPercentage, globalDiscountPercentage: params.globalDiscountPercentage,
globalDiscountAmount: globalDiscountAmount!,
totalDiscountAmount: totalDiscountAmount!,
taxableAmount: taxableAmount!,
taxes,
taxesAmount: taxesAmount!,
totalAmount: totalAmount!,
languageCode: params.languageCode, languageCode: params.languageCode,
currencyCode: params.currencyCode, currencyCode: params.currencyCode,
};
itemsProps.push(_item);
}); });
});
this.throwIfValidationErrors(params.errors);
return itemsProps; return itemsProps;
} }
/* Devuelve las propiedades de los impustos de una línea de detalle */ /* Devuelve las propiedades de los impustos de una línea de detalle */
private mapItempTaxesProps( private mapItemTaxesProps(
itemDTO: CreateProformaItemFromFactugesRequestDTO, itemDTO: CreateProformaItemFromFactugesRequestDTO,
params: { itemIndex: number; errors: ValidationErrorDetail[] } params: { itemIndex: number; errors: ValidationErrorDetail[] }
): ProformaItemTaxesProps { ): ProformaItemTaxesProps {

View File

@ -18,10 +18,7 @@ import { Maybe, Result } from "@repo/rdx-utils";
import type { Transaction } from "sequelize"; import type { Transaction } from "sequelize";
import type { CreateProformaFromFactugesRequestDTO } from "../../../common"; import type { CreateProformaFromFactugesRequestDTO } from "../../../common";
import type { import type { FactugesProformaPayload, ICreateProformaFromFactugesInputMapper } from "../mappers";
ICreateProformaFromFactugesInputMapper,
IProformaFromFactuGESProps,
} from "../mappers";
import paymentsCatalog from "./payments.json"; import paymentsCatalog from "./payments.json";
@ -139,6 +136,8 @@ export class CreateProformaFromFactugesUseCase {
const snapshot = readResult.data; const snapshot = readResult.data;
//const comparisonResults = this.compare()
const result = { const result = {
customer_id: customer.id.toString(), customer_id: customer.id.toString(),
proforma_id: snapshot.id.toString(), proforma_id: snapshot.id.toString(),
@ -152,7 +151,7 @@ export class CreateProformaFromFactugesUseCase {
} }
private buildProformaCreateProps(deps: { private buildProformaCreateProps(deps: {
proformaDraft: IProformaFromFactuGESProps["proformaDraft"]; proformaDraft: FactugesProformaPayload["proformaDraft"];
customerId: UniqueID; customerId: UniqueID;
payment: FakePaymentMethod; payment: FakePaymentMethod;
context: { context: {
@ -193,8 +192,8 @@ export class CreateProformaFromFactugesUseCase {
* @returns `Result` con el cliente resuelto o el error producido. * @returns `Result` con el cliente resuelto o el error producido.
*/ */
private async resolveCustomer( private async resolveCustomer(
customerLookup: IProformaFromFactuGESProps["customerLookup"], customerLookup: FactugesProformaPayload["customerLookup"],
customerDraft: IProformaFromFactuGESProps["customerDraft"], customerDraft: FactugesProformaPayload["customerDraft"],
context: { context: {
companyId: UniqueID; companyId: UniqueID;
transaction: Transaction; transaction: Transaction;
@ -227,8 +226,8 @@ export class CreateProformaFromFactugesUseCase {
} }
private async resolvePayment( private async resolvePayment(
paymentLookup: IProformaFromFactuGESProps["paymentLookup"], paymentLookup: FactugesProformaPayload["paymentLookup"],
paymentDraft: IProformaFromFactuGESProps["paymentDraft"], paymentDraft: FactugesProformaPayload["paymentDraft"],
context: { context: {
companyId: UniqueID; companyId: UniqueID;
transaction: Transaction; transaction: Transaction;
@ -254,7 +253,7 @@ export class CreateProformaFromFactugesUseCase {
} }
private buildCustomerCreateProps( private buildCustomerCreateProps(
customerDraft: IProformaFromFactuGESProps["customerDraft"], customerDraft: FactugesProformaPayload["customerDraft"],
context: { context: {
companyId: UniqueID; companyId: UniqueID;
transaction: Transaction; transaction: Transaction;

View File

@ -5,9 +5,9 @@ export const CreateProformaItemFromFactugesRequestSchema = z.object({
position: z.string(), position: z.string(),
description: z.string().default(""), description: z.string().default(""),
quantity_value: NumericStringSchema.default(""), // Ya viene escalado quantity_value: NumericStringSchema.default(""), // Ya viene escalado
unit_value: NumericStringSchema.default(""), unit_amount_value: NumericStringSchema.default(""),
subtotal_amuount_value: NumericStringSchema.default(""), subtotal_amount_value: NumericStringSchema.default(""),
item_discount_percentage_value: NumericStringSchema.default(""), item_discount_percentage_value: NumericStringSchema.default(""),
item_discount_amount_value: NumericStringSchema.default(""), item_discount_amount_value: NumericStringSchema.default(""),
@ -17,7 +17,7 @@ export const CreateProformaItemFromFactugesRequestSchema = z.object({
total_discount_amount_value: NumericStringSchema.default(""), total_discount_amount_value: NumericStringSchema.default(""),
taxable_amount_value: NumericStringSchema.default(""), taxable_amount_value: NumericStringSchema.default(""),
total_value: NumericStringSchema.default(""), total_amount_value: NumericStringSchema.default(""),
iva_code: z.string().default(""), iva_code: z.string().default(""),
iva_percentage_value: NumericStringSchema.default(""), iva_percentage_value: NumericStringSchema.default(""),