.
This commit is contained in:
parent
3b77fb7cb8
commit
5d5ec76ad6
@ -12,7 +12,7 @@ import {
|
|||||||
extractOrPushError,
|
extractOrPushError,
|
||||||
maybeFromNullableResult,
|
maybeFromNullableResult,
|
||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Maybe, NumberHelper, 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 {
|
import {
|
||||||
@ -21,7 +21,6 @@ import {
|
|||||||
ItemDescription,
|
ItemDescription,
|
||||||
ItemQuantity,
|
ItemQuantity,
|
||||||
type ProformaItemPatchProps,
|
type ProformaItemPatchProps,
|
||||||
ProformaItemTaxes,
|
|
||||||
type ProformaItemTaxesProps,
|
type ProformaItemTaxesProps,
|
||||||
type ProformaPatchProps,
|
type ProformaPatchProps,
|
||||||
} from "../../../domain";
|
} from "../../../domain";
|
||||||
@ -167,9 +166,7 @@ export class UpdateProformaInputMapper implements IUpdateProformaInputMapper {
|
|||||||
|
|
||||||
toPatchField(dto.global_discount_percentage).ifSet((globalDiscountPercentage) => {
|
toPatchField(dto.global_discount_percentage).ifSet((globalDiscountPercentage) => {
|
||||||
proformaPatchProps.globalDiscountPercentage = extractOrPushError(
|
proformaPatchProps.globalDiscountPercentage = extractOrPushError(
|
||||||
DiscountPercentage.create({
|
DiscountPercentage.fromObjectString(globalDiscountPercentage),
|
||||||
value: NumberHelper.toSafeNumber(globalDiscountPercentage.value),
|
|
||||||
}),
|
|
||||||
"global_discount_percentage",
|
"global_discount_percentage",
|
||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
@ -226,7 +223,7 @@ export class UpdateProformaInputMapper implements IUpdateProformaInputMapper {
|
|||||||
params.errors
|
params.errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const taxes = this.mapTaxesProps(item.taxes, {
|
const taxes = this.mapTaxesProps(item, {
|
||||||
itemIndex: index,
|
itemIndex: index,
|
||||||
errors: params.errors,
|
errors: params.errors,
|
||||||
});
|
});
|
||||||
@ -243,38 +240,34 @@ export class UpdateProformaInputMapper implements IUpdateProformaInputMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private mapTaxesProps(
|
private mapTaxesProps(
|
||||||
taxesDTO: NonNullable<UpdateProformaByIdRequestDTO["items"]>[number]["taxes"],
|
itemDTO: NonNullable<UpdateProformaByIdRequestDTO["items"]>[number],
|
||||||
params: { itemIndex: number; errors: ValidationErrorDetail[] }
|
params: { itemIndex: number; errors: ValidationErrorDetail[] }
|
||||||
): ProformaItemTaxesProps {
|
): ProformaItemTaxesProps {
|
||||||
const parts = taxesDTO.split(";");
|
const iva = extractOrPushError(
|
||||||
if (parts.length !== 3) {
|
maybeFromNullableResult(itemDTO.iva_code, (v) => Tax.createFromCode(v, this.taxCatalog)),
|
||||||
params.errors.push({
|
`items[${params.itemIndex}].iva_code`,
|
||||||
path: `items[${params.itemIndex}].taxes`,
|
|
||||||
message: "Tax combination must contain exactly three elements",
|
|
||||||
});
|
|
||||||
return ProformaItemTaxes.empty().getProps();
|
|
||||||
}
|
|
||||||
|
|
||||||
const [ivaCode, recCode, retentionCode] = parts;
|
|
||||||
|
|
||||||
const iva = this.mapTaxCode(ivaCode, `items[${params.itemIndex}].taxes.iva`, params.errors);
|
|
||||||
const rec = this.mapTaxCode(recCode, `items[${params.itemIndex}].taxes.rec`, params.errors);
|
|
||||||
const retention = this.mapTaxCode(
|
|
||||||
retentionCode,
|
|
||||||
`items[${params.itemIndex}].taxes.retention`,
|
|
||||||
params.errors
|
params.errors
|
||||||
);
|
);
|
||||||
|
|
||||||
return ProformaItemTaxes.create({ iva, rec, retention }).data.getProps();
|
const rec = extractOrPushError(
|
||||||
}
|
maybeFromNullableResult(itemDTO.rec_code, (v) => Tax.createFromCode(v, this.taxCatalog)),
|
||||||
|
`items[${params.itemIndex}].rec_code`,
|
||||||
|
params.errors
|
||||||
|
);
|
||||||
|
|
||||||
private mapTaxCode(code: string, path: string, errors: ValidationErrorDetail[]): Maybe<Tax> {
|
const retention = extractOrPushError(
|
||||||
if (code === "#") {
|
maybeFromNullableResult(itemDTO.retention_code, (v) =>
|
||||||
return Maybe.none();
|
Tax.createFromCode(v, this.taxCatalog)
|
||||||
}
|
),
|
||||||
|
`items[${params.itemIndex}].retention_code`,
|
||||||
|
params.errors
|
||||||
|
);
|
||||||
|
|
||||||
const tax = extractOrPushError(Tax.createFromCode(code, this.taxCatalog), path, errors);
|
return {
|
||||||
return tax ? Maybe.some(tax) : Maybe.none();
|
iva: iva!,
|
||||||
|
rec: rec!,
|
||||||
|
retention: retention!,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private throwIfValidationErrors(errors: ValidationErrorDetail[]): void {
|
private throwIfValidationErrors(errors: ValidationErrorDetail[]): void {
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
import type { ISnapshotBuilder } from "@erp/core/api";
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
import type { ProformaItemDetailDTO } from "@erp/customer-invoices/common";
|
import type { ProformaItemDetailDTO } from "@erp/customer-invoices/common";
|
||||||
import {
|
import { maybeToNullable } from "@repo/rdx-ddd";
|
||||||
maybeToEmptyPercentageObjectString,
|
|
||||||
maybeToEmptyString,
|
|
||||||
maybeToNullable,
|
|
||||||
} from "@repo/rdx-ddd";
|
|
||||||
|
|
||||||
import { ItemAmount, type ProformaItem, type ProformaItems } from "../../../../domain";
|
import { ItemAmount, type ProformaItem, type ProformaItems } from "../../../../domain";
|
||||||
|
|
||||||
@ -52,20 +48,26 @@ export class ProformaItemsFullSnapshotBuilder implements IProformaItemsFullSnaps
|
|||||||
? allAmounts.taxableAmount.toObjectString()
|
? allAmounts.taxableAmount.toObjectString()
|
||||||
: ItemAmount.zero(currencyCode).toObjectString(),
|
: ItemAmount.zero(currencyCode).toObjectString(),
|
||||||
|
|
||||||
iva_code: maybeToEmptyString(proformaItem.ivaCode()),
|
iva_code: maybeToNullable(proformaItem.ivaCode(), (value) => value.toString()),
|
||||||
iva_percentage: maybeToEmptyPercentageObjectString(proformaItem.ivaPercentage()),
|
iva_percentage: maybeToNullable(proformaItem.ivaPercentage(), (value) =>
|
||||||
|
value.toObjectString()
|
||||||
|
),
|
||||||
iva_amount: isValued
|
iva_amount: isValued
|
||||||
? allAmounts.ivaAmount.toObjectString()
|
? allAmounts.ivaAmount.toObjectString()
|
||||||
: ItemAmount.zero(currencyCode).toObjectString(),
|
: ItemAmount.zero(currencyCode).toObjectString(),
|
||||||
|
|
||||||
rec_code: maybeToEmptyString(proformaItem.recCode()),
|
rec_code: maybeToNullable(proformaItem.recCode(), (value) => value.toString()),
|
||||||
rec_percentage: maybeToEmptyPercentageObjectString(proformaItem.recPercentage()),
|
rec_percentage: maybeToNullable(proformaItem.recPercentage(), (value) =>
|
||||||
|
value.toObjectString()
|
||||||
|
),
|
||||||
rec_amount: isValued
|
rec_amount: isValued
|
||||||
? allAmounts.recAmount.toObjectString()
|
? allAmounts.recAmount.toObjectString()
|
||||||
: ItemAmount.zero(currencyCode).toObjectString(),
|
: ItemAmount.zero(currencyCode).toObjectString(),
|
||||||
|
|
||||||
retention_code: maybeToEmptyString(proformaItem.retentionCode()),
|
retention_code: maybeToNullable(proformaItem.retentionCode(), (value) => value.toString()),
|
||||||
retention_percentage: maybeToEmptyPercentageObjectString(proformaItem.retentionPercentage()),
|
retention_percentage: maybeToNullable(proformaItem.retentionPercentage(), (value) =>
|
||||||
|
value.toObjectString()
|
||||||
|
),
|
||||||
retention_amount: isValued
|
retention_amount: isValued
|
||||||
? allAmounts.retentionAmount.toObjectString()
|
? allAmounts.retentionAmount.toObjectString()
|
||||||
: ItemAmount.zero(currencyCode).toObjectString(),
|
: ItemAmount.zero(currencyCode).toObjectString(),
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type { ISnapshotBuilder } from "@erp/core/api";
|
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||||
import { maybeToEmptyPercentageObjectString, maybeToEmptyString } from "@repo/rdx-ddd";
|
import { maybeToNullable } from "@repo/rdx-ddd";
|
||||||
import type { Collection } from "@repo/rdx-utils";
|
import type { Collection } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { TaxesBreakdownDTO } from "../../../../../common";
|
import type { TaxesBreakdownDTO } from "../../../../../common";
|
||||||
@ -13,16 +13,18 @@ export class ProformaTaxesFullSnapshotBuilder implements IProformaTaxesFullSnaps
|
|||||||
return {
|
return {
|
||||||
taxable_amount: proformaTax.taxableAmount.toObjectString(),
|
taxable_amount: proformaTax.taxableAmount.toObjectString(),
|
||||||
|
|
||||||
iva_code: maybeToEmptyString(proformaTax.ivaCode),
|
iva_code: maybeToNullable(proformaTax.ivaCode, (value) => value.toString()),
|
||||||
iva_percentage: maybeToEmptyPercentageObjectString(proformaTax.ivaPercentage),
|
iva_percentage: maybeToNullable(proformaTax.ivaPercentage, (value) => value.toObjectString()),
|
||||||
iva_amount: proformaTax.ivaAmount.toObjectString(),
|
iva_amount: proformaTax.ivaAmount.toObjectString(),
|
||||||
|
|
||||||
rec_code: maybeToEmptyString(proformaTax.recCode),
|
rec_code: maybeToNullable(proformaTax.recCode, (value) => value.toString()),
|
||||||
rec_percentage: maybeToEmptyPercentageObjectString(proformaTax.recPercentage),
|
rec_percentage: maybeToNullable(proformaTax.recPercentage, (value) => value.toObjectString()),
|
||||||
rec_amount: proformaTax.recAmount.toObjectString(),
|
rec_amount: proformaTax.recAmount.toObjectString(),
|
||||||
|
|
||||||
retention_code: maybeToEmptyString(proformaTax.retentionCode),
|
retention_code: maybeToNullable(proformaTax.retentionCode, (value) => value.toString()),
|
||||||
retention_percentage: maybeToEmptyPercentageObjectString(proformaTax.retentionPercentage),
|
retention_percentage: maybeToNullable(proformaTax.retentionPercentage, (value) =>
|
||||||
|
value.toObjectString()
|
||||||
|
),
|
||||||
retention_amount: proformaTax.retentionAmount.toObjectString(),
|
retention_amount: proformaTax.retentionAmount.toObjectString(),
|
||||||
|
|
||||||
taxes_amount: proformaTax.taxesAmount.toObjectString(),
|
taxes_amount: proformaTax.taxesAmount.toObjectString(),
|
||||||
|
|||||||
@ -40,15 +40,15 @@ export interface IProformaItems {
|
|||||||
totals(): IProformaItemTotals;
|
totals(): IProformaItemTotals;
|
||||||
taxes(): Collection<IProformaTaxTotals>;
|
taxes(): Collection<IProformaTaxTotals>;
|
||||||
|
|
||||||
readonly globalDiscountPercentage: DiscountPercentage; // % descuento de la cabecera
|
globalDiscountPercentage: DiscountPercentage; // % descuento de la cabecera
|
||||||
readonly languageCode: LanguageCode; // Para formateos específicos de idioma
|
languageCode: LanguageCode; // Para formateos específicos de idioma
|
||||||
readonly currencyCode: CurrencyCode; // Para cálculos y formateos de moneda
|
currencyCode: CurrencyCode; // Para cálculos y formateos de moneda
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProformaItems extends Collection<ProformaItem> implements IProformaItems {
|
export class ProformaItems extends Collection<ProformaItem> implements IProformaItems {
|
||||||
public readonly languageCode!: LanguageCode;
|
public languageCode!: LanguageCode;
|
||||||
public readonly currencyCode!: CurrencyCode;
|
public currencyCode!: CurrencyCode;
|
||||||
public readonly globalDiscountPercentage!: DiscountPercentage;
|
public globalDiscountPercentage!: DiscountPercentage;
|
||||||
|
|
||||||
private constructor(props: IProformaItemsProps) {
|
private constructor(props: IProformaItemsProps) {
|
||||||
super(props.items ?? []);
|
super(props.items ?? []);
|
||||||
@ -67,6 +67,22 @@ export class ProformaItems extends Collection<ProformaItem> implements IProforma
|
|||||||
}
|
}
|
||||||
|
|
||||||
public add(item: ProformaItem): boolean {
|
public add(item: ProformaItem): boolean {
|
||||||
|
console.log("adding item", {
|
||||||
|
item: {
|
||||||
|
description: item.description.getOrUndefined()?.toString(),
|
||||||
|
quantity: item.quantity.getOrUndefined()?.toString(),
|
||||||
|
unitAmount: item.unitAmount.getOrUndefined()?.toObjectString(),
|
||||||
|
itemDiscountPercentage: item.itemDiscountPercentage.getOrUndefined()?.toObjectString(),
|
||||||
|
ivaPercentage: item.ivaPercentage.toString(),
|
||||||
|
recPercentage: item.recPercentage.toString(),
|
||||||
|
languageCode: item.languageCode.code,
|
||||||
|
currencyCode: item.currencyCode.code,
|
||||||
|
globalDiscountPercentage: item.globalDiscountPercentage.toObjectString(),
|
||||||
|
},
|
||||||
|
languageCode: this.languageCode,
|
||||||
|
currencyCode: this.currencyCode,
|
||||||
|
globalDiscountPercentage: this.globalDiscountPercentage,
|
||||||
|
});
|
||||||
const same =
|
const same =
|
||||||
this.languageCode.equals(item.languageCode) &&
|
this.languageCode.equals(item.languageCode) &&
|
||||||
this.currencyCode.equals(item.currencyCode) &&
|
this.currencyCode.equals(item.currencyCode) &&
|
||||||
|
|||||||
@ -8,11 +8,10 @@ import {
|
|||||||
} from "@erp/core";
|
} from "@erp/core";
|
||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
import { ItemPositionSchema, TaxCombinationCodeSchema } from "../../shared";
|
import { ItemPositionSchema } from "../../shared";
|
||||||
|
|
||||||
export const UpdateProformaItemRequestSchema = z.object({
|
export const UpdateProformaItemRequestSchema = z.object({
|
||||||
position: ItemPositionSchema,
|
position: ItemPositionSchema,
|
||||||
is_valued: z.boolean(),
|
|
||||||
|
|
||||||
description: z.string().nullable(),
|
description: z.string().nullable(),
|
||||||
|
|
||||||
@ -21,7 +20,14 @@ export const UpdateProformaItemRequestSchema = z.object({
|
|||||||
|
|
||||||
item_discount_percentage: PercentageSchema.nullable(),
|
item_discount_percentage: PercentageSchema.nullable(),
|
||||||
|
|
||||||
taxes: TaxCombinationCodeSchema,
|
iva_code: z.string().nullable().optional(),
|
||||||
|
iva_percentage: PercentageSchema.nullable().optional(),
|
||||||
|
|
||||||
|
rec_code: z.string().nullable().optional(),
|
||||||
|
rec_percentage: PercentageSchema.nullable().optional(),
|
||||||
|
|
||||||
|
retention_code: z.string().nullable().optional(),
|
||||||
|
retention_percentage: PercentageSchema.nullable().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const UpdateProformaByIdParamsRequestSchema = z.object({
|
export const UpdateProformaByIdParamsRequestSchema = z.object({
|
||||||
@ -47,6 +53,8 @@ export const UpdateProformaByIdRequestSchema = z.object({
|
|||||||
|
|
||||||
payment_method_id: z.uuid().nullable().optional(),
|
payment_method_id: z.uuid().nullable().optional(),
|
||||||
|
|
||||||
|
// retención como código??? retencion_15
|
||||||
|
|
||||||
items: z.array(UpdateProformaItemRequestSchema).optional(),
|
items: z.array(UpdateProformaItemRequestSchema).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -4,16 +4,16 @@ import { z } from "zod/v4";
|
|||||||
export const TaxesBreakdownSchema = z.object({
|
export const TaxesBreakdownSchema = z.object({
|
||||||
taxable_amount: MoneySchema,
|
taxable_amount: MoneySchema,
|
||||||
|
|
||||||
iva_code: z.string(),
|
iva_code: z.string().nullable(),
|
||||||
iva_percentage: PercentageSchema,
|
iva_percentage: PercentageSchema.nullable(),
|
||||||
iva_amount: MoneySchema,
|
iva_amount: MoneySchema,
|
||||||
|
|
||||||
rec_code: z.string().nullable(),
|
rec_code: z.string().nullable(),
|
||||||
rec_percentage: PercentageSchema,
|
rec_percentage: PercentageSchema.nullable(),
|
||||||
rec_amount: MoneySchema,
|
rec_amount: MoneySchema,
|
||||||
|
|
||||||
retention_code: z.string().nullable(),
|
retention_code: z.string().nullable(),
|
||||||
retention_percentage: PercentageSchema,
|
retention_percentage: PercentageSchema.nullable(),
|
||||||
retention_amount: MoneySchema,
|
retention_amount: MoneySchema,
|
||||||
|
|
||||||
taxes_amount: MoneySchema,
|
taxes_amount: MoneySchema,
|
||||||
|
|||||||
@ -162,8 +162,6 @@ export const useUpdateProformaController = (
|
|||||||
|
|
||||||
console.log("Enviando actualización con params:", params);
|
console.log("Enviando actualización con params:", params);
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Enviamos cambios al servidor
|
// Enviamos cambios al servidor
|
||||||
const updated = await mutateAsync(params);
|
const updated = await mutateAsync(params);
|
||||||
|
|||||||
@ -23,4 +23,7 @@ export interface ProformaItemUpdatePatch {
|
|||||||
unitAmount: number | null;
|
unitAmount: number | null;
|
||||||
|
|
||||||
itemDiscountPercentage: number | null;
|
itemDiscountPercentage: number | null;
|
||||||
|
|
||||||
|
taxPercentage: number | null;
|
||||||
|
recPercentage: number | null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,19 @@ export type ProformaUpdatePatch = {
|
|||||||
|
|
||||||
globalDiscountPercentage?: number;
|
globalDiscountPercentage?: number;
|
||||||
|
|
||||||
paymentMehodId?: string;
|
taxMode?: string;
|
||||||
|
taxRegimeCode?: string | null;
|
||||||
|
|
||||||
|
hasTaxPercentage?: boolean;
|
||||||
|
defaultTaxPercentage?: number | null;
|
||||||
|
|
||||||
|
hasRecPercentage?: boolean;
|
||||||
|
defaultRecPercentage?: number | null;
|
||||||
|
|
||||||
|
hasRetentionPercentage?: boolean;
|
||||||
|
defaultRetentionPercentage?: number | null;
|
||||||
|
|
||||||
|
paymentMethodId?: string | null;
|
||||||
|
|
||||||
languageCode?: string;
|
languageCode?: string;
|
||||||
currencyCode?: string;
|
currencyCode?: string;
|
||||||
|
|||||||
@ -4,10 +4,12 @@ import {
|
|||||||
LineDescriptionField,
|
LineDescriptionField,
|
||||||
PercentageField,
|
PercentageField,
|
||||||
QuantityField,
|
QuantityField,
|
||||||
|
SelectField,
|
||||||
} from "@repo/rdx-ui/components";
|
} from "@repo/rdx-ui/components";
|
||||||
import { MoneyHelper } from "@repo/rdx-utils";
|
import { MoneyHelper } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import { useTranslation } from "../../../../i18n";
|
import { useTranslation } from "../../../../i18n";
|
||||||
|
import { getProformaTaxOptions } from "../../../shared";
|
||||||
import type {
|
import type {
|
||||||
ProformaItemAmounts,
|
ProformaItemAmounts,
|
||||||
ProformaItemField,
|
ProformaItemField,
|
||||||
@ -130,11 +132,12 @@ export const ProformaLineEditor = ({
|
|||||||
header: t("form_fields.items.tax_percentage.label", "IVA"),
|
header: t("form_fields.items.tax_percentage.label", "IVA"),
|
||||||
headClassName: "w-[110px] text-right",
|
headClassName: "w-[110px] text-right",
|
||||||
cell: ({ index }) => (
|
cell: ({ index }) => (
|
||||||
<PercentageField
|
<SelectField
|
||||||
|
deserialize={(value) => (value === null || value === "" ? null : Number(value))}
|
||||||
inputClassName="border-none"
|
inputClassName="border-none"
|
||||||
maxFractionDigits={2}
|
items={getProformaTaxOptions()}
|
||||||
minFractionDigits={0}
|
|
||||||
name={`items.${index}.taxPercentage`}
|
name={`items.${index}.taxPercentage`}
|
||||||
|
serialize={(value) => (typeof value === "number" ? String(value) : "")}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
} satisfies LineEditorColumn<ProformaItemField>,
|
} satisfies LineEditorColumn<ProformaItemField>,
|
||||||
|
|||||||
@ -1,8 +1,14 @@
|
|||||||
import { MoneyDTOHelper, PercentageDTOHelper, QuantityDTOHelper } from "@erp/core";
|
import { MoneyDTOHelper, PercentageDTOHelper, QuantityDTOHelper } from "@erp/core";
|
||||||
|
import { toNullable } from "@repo/rdx-ddd";
|
||||||
import { ObjectHelper } from "@repo/rdx-utils";
|
import { ObjectHelper } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import type { UpdateProformaByIdParams } from "../../shared/api";
|
import type { UpdateProformaByIdParams } from "../../shared/api";
|
||||||
import type { ProformaItemUpdatePatch, ProformaUpdateForm, ProformaUpdatePatch } from "../entities";
|
import type {
|
||||||
|
ProformaItemUpdateForm,
|
||||||
|
ProformaItemUpdatePatch,
|
||||||
|
ProformaUpdateForm,
|
||||||
|
ProformaUpdatePatch,
|
||||||
|
} from "../entities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convierte el patch del formulario de actualización de proforma
|
* Convierte el patch del formulario de actualización de proforma
|
||||||
@ -28,11 +34,6 @@ export const buildUpdateProformaByIdParams = (
|
|||||||
throw new Error("proformaId is required");
|
throw new Error("proformaId is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
//const currencyCode = formData.currencyCode;
|
|
||||||
//const languageCode = formData.languageCode;
|
|
||||||
|
|
||||||
console.log("PATCH => ", patch);
|
|
||||||
|
|
||||||
const data: UpdateProformaByIdParams["data"] = {};
|
const data: UpdateProformaByIdParams["data"] = {};
|
||||||
|
|
||||||
if (ObjectHelper.hasOwn(patch, "series")) {
|
if (ObjectHelper.hasOwn(patch, "series")) {
|
||||||
@ -63,13 +64,6 @@ export const buildUpdateProformaByIdParams = (
|
|||||||
data.notes = patch.notes;
|
data.notes = patch.notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ObjectHelper.hasOwn(patch, "globalDiscountPercentage")) {
|
|
||||||
data.global_discount_percentage = PercentageDTOHelper.fromNumber(
|
|
||||||
patch.globalDiscountPercentage!,
|
|
||||||
2
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ObjectHelper.hasOwn(patch, "languageCode")) {
|
if (ObjectHelper.hasOwn(patch, "languageCode")) {
|
||||||
data.language_code = patch.languageCode;
|
data.language_code = patch.languageCode;
|
||||||
}
|
}
|
||||||
@ -78,11 +72,23 @@ export const buildUpdateProformaByIdParams = (
|
|||||||
data.currency_code = patch.currencyCode;
|
data.currency_code = patch.currencyCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ObjectHelper.hasOwn(patch, "items")) {
|
if (ObjectHelper.hasOwn(patch, "paymentMethodId")) {
|
||||||
data.items = patch.items?.map((item, index) => toProformaItemUpdateDTO(item, index, formData));
|
data.payment_method_id = patch.paymentMethodId;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("DATA => ", data);
|
if (ObjectHelper.hasOwn(patch, "globalDiscountPercentage")) {
|
||||||
|
data.global_discount_percentage = PercentageDTOHelper.fromNumber(
|
||||||
|
patch.globalDiscountPercentage!,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si se han tocado los detalles, se envían todos
|
||||||
|
if (shouldReplaceItems(patch)) {
|
||||||
|
data.items = formData.items.map((item, index) =>
|
||||||
|
toProformaItemUpdateDTO(item, index, formData)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
@ -92,11 +98,10 @@ export const buildUpdateProformaByIdParams = (
|
|||||||
|
|
||||||
const toProformaItemUpdateDTO = (
|
const toProformaItemUpdateDTO = (
|
||||||
item: ProformaItemUpdatePatch,
|
item: ProformaItemUpdatePatch,
|
||||||
_index: number,
|
index: number,
|
||||||
formData: ProformaUpdateForm
|
formData: ProformaUpdateForm
|
||||||
): NonNullable<UpdateProformaByIdParams["data"]["items"]>[number] => {
|
): NonNullable<UpdateProformaByIdParams["data"]["items"]>[number] => {
|
||||||
const currencyCode = formData.currencyCode;
|
const currencyCode = formData.currencyCode;
|
||||||
//const languageCode = formData.languageCode;
|
|
||||||
|
|
||||||
const quantity =
|
const quantity =
|
||||||
item.quantity === null ? null : QuantityDTOHelper.fromNumberNulleable(item.quantity, 2);
|
item.quantity === null ? null : QuantityDTOHelper.fromNumberNulleable(item.quantity, 2);
|
||||||
@ -106,22 +111,67 @@ const toProformaItemUpdateDTO = (
|
|||||||
? null
|
? null
|
||||||
: MoneyDTOHelper.fromNumberNulleable(item.unitAmount, currencyCode, 4);
|
: MoneyDTOHelper.fromNumberNulleable(item.unitAmount, currencyCode, 4);
|
||||||
|
|
||||||
const is_valued = item.isValued;
|
const ivaPercentage = resolveLineIvaPercentage(item, formData);
|
||||||
|
const recPercentage = resolveLineRecPercentage(item, formData);
|
||||||
|
const retentionPercentage = resolveLineRetentionPercentage(formData);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
position: item.position,
|
position: index,
|
||||||
is_valued,
|
|
||||||
|
|
||||||
description: item.description,
|
description: item.description,
|
||||||
|
|
||||||
quantity,
|
quantity,
|
||||||
unit_amount,
|
unit_amount,
|
||||||
|
|
||||||
item_discount_percentage:
|
item_discount_percentage: toNullable(item.itemDiscountPercentage, (value) =>
|
||||||
item.itemDiscountPercentage === null
|
PercentageDTOHelper.fromNumber(value, 2)
|
||||||
? null
|
),
|
||||||
: PercentageDTOHelper.fromNumber(item.itemDiscountPercentage),
|
|
||||||
|
|
||||||
taxes: "#;#;#", // TODO: CAMBIAR!!!!
|
iva_percentage: toNullable(ivaPercentage, (value) => PercentageDTOHelper.fromNumber(value, 2)),
|
||||||
|
rec_percentage: toNullable(recPercentage, (value) => PercentageDTOHelper.fromNumber(value, 2)),
|
||||||
|
retention_percentage: toNullable(retentionPercentage, (value) =>
|
||||||
|
PercentageDTOHelper.fromNumber(value, 2)
|
||||||
|
),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const shouldReplaceItems = (patch: ProformaUpdatePatch): boolean => {
|
||||||
|
return (
|
||||||
|
ObjectHelper.hasOwn(patch, "items") ||
|
||||||
|
ObjectHelper.hasOwn(patch, "taxMode") ||
|
||||||
|
ObjectHelper.hasOwn(patch, "hasTaxPercentage") ||
|
||||||
|
ObjectHelper.hasOwn(patch, "defaultTaxPercentage") ||
|
||||||
|
ObjectHelper.hasOwn(patch, "hasRecPercentage") ||
|
||||||
|
ObjectHelper.hasOwn(patch, "defaultRecPercentage")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveLineIvaPercentage = (
|
||||||
|
item: ProformaItemUpdateForm,
|
||||||
|
formData: ProformaUpdateForm
|
||||||
|
): number | null => {
|
||||||
|
if (formData.taxMode === "single") {
|
||||||
|
return formData.hasTaxPercentage ? formData.defaultTaxPercentage : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.taxPercentage;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveLineRecPercentage = (
|
||||||
|
item: ProformaItemUpdateForm,
|
||||||
|
formData: ProformaUpdateForm
|
||||||
|
): number | null => {
|
||||||
|
if (formData.taxMode === "single") {
|
||||||
|
return formData.hasRecPercentage ? formData.defaultRecPercentage : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.recPercentage;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveLineRetentionPercentage = (formData: ProformaUpdateForm): number | null => {
|
||||||
|
return formData.hasRetentionPercentage ? formData.defaultRetentionPercentage : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toPercentageDTOOrNull = (value: number | null) => {
|
||||||
|
return value === null ? null : PercentageDTOHelper.fromNumber(value, 2);
|
||||||
|
};
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
import type { CommercialDocumentLineAmounts, CommercialDocumentLineInput } from "../../entities";
|
|
||||||
|
|
||||||
import { percentageAmount, roundMoney, toCalculationNumber } from "./money-calculation";
|
|
||||||
|
|
||||||
export const calculateCommercialDocumentLineAmounts = (
|
|
||||||
line?: CommercialDocumentLineInput
|
|
||||||
): CommercialDocumentLineAmounts => {
|
|
||||||
if (!line) {
|
|
||||||
return {
|
|
||||||
grossAmount: 0,
|
|
||||||
itemDiscountAmount: 0,
|
|
||||||
taxableBaseBeforeGlobalDiscount: 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const quantity = toCalculationNumber(line.quantity);
|
|
||||||
const unitAmount = toCalculationNumber(line.unitAmount);
|
|
||||||
const itemDiscountPercentage = toCalculationNumber(line.itemDiscountPercentage);
|
|
||||||
|
|
||||||
const grossAmount = quantity * unitAmount;
|
|
||||||
const itemDiscountAmount = percentageAmount(grossAmount, itemDiscountPercentage);
|
|
||||||
|
|
||||||
return {
|
|
||||||
grossAmount: roundMoney(grossAmount),
|
|
||||||
itemDiscountAmount: roundMoney(itemDiscountAmount),
|
|
||||||
taxableBaseBeforeGlobalDiscount: roundMoney(grossAmount - itemDiscountAmount),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
import type { CommercialDocumentLineInput } from "../../entities";
|
|
||||||
|
|
||||||
import { calculateCommercialDocumentLineAmounts } from "./calculate-commercial-document-line-amounts";
|
|
||||||
import { roundMoney } from "./money-calculation";
|
|
||||||
|
|
||||||
export interface CommercialDocumentLinesTotals {
|
|
||||||
subtotalBeforeDiscounts: number;
|
|
||||||
lineDiscountTotal: number;
|
|
||||||
taxableBaseBeforeGlobalDiscount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const calculateCommercialDocumentLinesTotals = (
|
|
||||||
lines: CommercialDocumentLineInput[]
|
|
||||||
): CommercialDocumentLinesTotals => {
|
|
||||||
return lines.reduce<CommercialDocumentLinesTotals>(
|
|
||||||
(acc, line) => {
|
|
||||||
const amounts = calculateCommercialDocumentLineAmounts(line);
|
|
||||||
|
|
||||||
return {
|
|
||||||
subtotalBeforeDiscounts: roundMoney(acc.subtotalBeforeDiscounts + amounts.grossAmount),
|
|
||||||
lineDiscountTotal: roundMoney(acc.lineDiscountTotal + amounts.itemDiscountAmount),
|
|
||||||
taxableBaseBeforeGlobalDiscount: roundMoney(
|
|
||||||
acc.taxableBaseBeforeGlobalDiscount + amounts.taxableBaseBeforeGlobalDiscount
|
|
||||||
),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
{
|
|
||||||
subtotalBeforeDiscounts: 0,
|
|
||||||
lineDiscountTotal: 0,
|
|
||||||
taxableBaseBeforeGlobalDiscount: 0,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
import type {
|
|
||||||
CommercialDocumentLineInput,
|
|
||||||
CommercialDocumentTaxBreakdownLine,
|
|
||||||
} from "../../entities";
|
|
||||||
|
|
||||||
import { calculateCommercialDocumentLineAmounts } from "./calculate-commercial-document-line-amounts";
|
|
||||||
import { calculateProportionalDiscount } from "./calculate-proportional-discount";
|
|
||||||
import { percentageAmount, roundMoney, toCalculationNumber } from "./money-calculation";
|
|
||||||
|
|
||||||
interface CalculateCommercialDocumentTaxBreakdownParams {
|
|
||||||
lines: CommercialDocumentLineInput[];
|
|
||||||
taxableBaseBeforeGlobalDiscount: number;
|
|
||||||
globalDiscountAmount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const calculateCommercialDocumentTaxBreakdown = ({
|
|
||||||
lines,
|
|
||||||
taxableBaseBeforeGlobalDiscount,
|
|
||||||
globalDiscountAmount,
|
|
||||||
}: CalculateCommercialDocumentTaxBreakdownParams): CommercialDocumentTaxBreakdownLine[] => {
|
|
||||||
const taxMap = new Map<number, { taxableBase: number; taxAmount: number }>();
|
|
||||||
|
|
||||||
for (const line of lines) {
|
|
||||||
const lineAmounts = calculateCommercialDocumentLineAmounts(line);
|
|
||||||
|
|
||||||
const proportionalGlobalDiscount = calculateProportionalDiscount(
|
|
||||||
lineAmounts.taxableBaseBeforeGlobalDiscount,
|
|
||||||
taxableBaseBeforeGlobalDiscount,
|
|
||||||
globalDiscountAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
const lineTaxableBase =
|
|
||||||
lineAmounts.taxableBaseBeforeGlobalDiscount - proportionalGlobalDiscount;
|
|
||||||
|
|
||||||
const taxPercentage = toCalculationNumber(line.taxPercentage);
|
|
||||||
const taxAmount = percentageAmount(lineTaxableBase, taxPercentage);
|
|
||||||
|
|
||||||
const current = taxMap.get(taxPercentage) ?? {
|
|
||||||
taxableBase: 0,
|
|
||||||
taxAmount: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
taxMap.set(taxPercentage, {
|
|
||||||
taxableBase: current.taxableBase + lineTaxableBase,
|
|
||||||
taxAmount: current.taxAmount + taxAmount,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.from(taxMap.entries())
|
|
||||||
.sort(([a], [b]) => a - b)
|
|
||||||
.map(([taxPercentage, value]) => ({
|
|
||||||
taxPercentage,
|
|
||||||
taxableBase: roundMoney(value.taxableBase),
|
|
||||||
taxAmount: roundMoney(value.taxAmount),
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
import type { CommercialDocumentLineInput, CommercialDocumentTotals } from "../../entities";
|
|
||||||
|
|
||||||
import { calculateCommercialDocumentLinesTotals } from "./calculate-commercial-document-lines-totals";
|
|
||||||
import { calculateCommercialDocumentTaxBreakdown } from "./calculate-commercial-document-tax-breakdown";
|
|
||||||
import { percentageAmount, roundMoney, toCalculationNumber } from "./money-calculation";
|
|
||||||
|
|
||||||
interface CalculateCommercialDocumentTotalsParams {
|
|
||||||
lines: CommercialDocumentLineInput[];
|
|
||||||
globalDiscountPercentage: number | null | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const calculateCommercialDocumentTotals = ({
|
|
||||||
lines,
|
|
||||||
globalDiscountPercentage,
|
|
||||||
}: CalculateCommercialDocumentTotalsParams): CommercialDocumentTotals => {
|
|
||||||
const normalizedGlobalDiscountPercentage = toCalculationNumber(globalDiscountPercentage);
|
|
||||||
|
|
||||||
const linesTotals = calculateCommercialDocumentLinesTotals(lines);
|
|
||||||
|
|
||||||
const globalDiscountAmount = percentageAmount(
|
|
||||||
linesTotals.taxableBaseBeforeGlobalDiscount,
|
|
||||||
normalizedGlobalDiscountPercentage
|
|
||||||
);
|
|
||||||
|
|
||||||
const taxableBase = linesTotals.taxableBaseBeforeGlobalDiscount - globalDiscountAmount;
|
|
||||||
|
|
||||||
const taxBreakdown = calculateCommercialDocumentTaxBreakdown({
|
|
||||||
lines,
|
|
||||||
taxableBaseBeforeGlobalDiscount: linesTotals.taxableBaseBeforeGlobalDiscount,
|
|
||||||
globalDiscountAmount,
|
|
||||||
});
|
|
||||||
|
|
||||||
const taxTotal = taxBreakdown.reduce((total, tax) => total + tax.taxAmount, 0);
|
|
||||||
|
|
||||||
return {
|
|
||||||
subtotalBeforeDiscounts: roundMoney(linesTotals.subtotalBeforeDiscounts),
|
|
||||||
|
|
||||||
lineDiscountTotal: roundMoney(linesTotals.lineDiscountTotal),
|
|
||||||
globalDiscountPercentage: normalizedGlobalDiscountPercentage,
|
|
||||||
globalDiscountAmount: roundMoney(globalDiscountAmount),
|
|
||||||
|
|
||||||
taxableBase: roundMoney(taxableBase),
|
|
||||||
|
|
||||||
taxBreakdown,
|
|
||||||
taxTotal: roundMoney(taxTotal),
|
|
||||||
|
|
||||||
total: roundMoney(taxableBase + taxTotal),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -83,16 +83,17 @@ function serializeMaybe<T, R>(maybe: Maybe<T>, onNone: R, map: (value: T) => R):
|
|||||||
*/
|
*/
|
||||||
export function toNullable<V, R>(input: null | undefined, map: (t: V) => R): null;
|
export function toNullable<V, R>(input: null | undefined, map: (t: V) => R): null;
|
||||||
export function toNullable<V, R>(input: Maybe<V>, map: (t: V) => R): R | null;
|
export function toNullable<V, R>(input: Maybe<V>, map: (t: V) => R): R | null;
|
||||||
|
export function toNullable<V, R>(input: V | null, map: (t: V) => R): R | null;
|
||||||
export function toNullable<V, R>(input: V, map: (t: V) => R): R;
|
export function toNullable<V, R>(input: V, map: (t: V) => R): R;
|
||||||
export function toNullable<V, R>(
|
export function toNullable<V, R>(
|
||||||
input: V | Maybe<V> | null | undefined,
|
input: V | Maybe<V> | null | undefined,
|
||||||
map: (t: V) => R
|
map: (t: V) => R
|
||||||
): R | null {
|
): R | null {
|
||||||
if (isMaybe<V>(input)) {
|
if (isMaybe<V>(input)) {
|
||||||
return maybeToNullable(input, map);
|
return maybeToNullable<V, R>(input, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapNullable(input, map);
|
return mapNullable<V, R>(input, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user