diff --git a/modules/customer-invoices/src/api/application/proformas/mappers/update-proforma-input.mapper.ts b/modules/customer-invoices/src/api/application/proformas/mappers/update-proforma-input.mapper.ts index 752b5d37..2a9c7ae0 100644 --- a/modules/customer-invoices/src/api/application/proformas/mappers/update-proforma-input.mapper.ts +++ b/modules/customer-invoices/src/api/application/proformas/mappers/update-proforma-input.mapper.ts @@ -12,7 +12,7 @@ import { extractOrPushError, maybeFromNullableResult, } 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 { @@ -21,7 +21,6 @@ import { ItemDescription, ItemQuantity, type ProformaItemPatchProps, - ProformaItemTaxes, type ProformaItemTaxesProps, type ProformaPatchProps, } from "../../../domain"; @@ -167,9 +166,7 @@ export class UpdateProformaInputMapper implements IUpdateProformaInputMapper { toPatchField(dto.global_discount_percentage).ifSet((globalDiscountPercentage) => { proformaPatchProps.globalDiscountPercentage = extractOrPushError( - DiscountPercentage.create({ - value: NumberHelper.toSafeNumber(globalDiscountPercentage.value), - }), + DiscountPercentage.fromObjectString(globalDiscountPercentage), "global_discount_percentage", errors ); @@ -226,7 +223,7 @@ export class UpdateProformaInputMapper implements IUpdateProformaInputMapper { params.errors ); - const taxes = this.mapTaxesProps(item.taxes, { + const taxes = this.mapTaxesProps(item, { itemIndex: index, errors: params.errors, }); @@ -243,38 +240,34 @@ export class UpdateProformaInputMapper implements IUpdateProformaInputMapper { } private mapTaxesProps( - taxesDTO: NonNullable[number]["taxes"], + itemDTO: NonNullable[number], params: { itemIndex: number; errors: ValidationErrorDetail[] } ): ProformaItemTaxesProps { - const parts = taxesDTO.split(";"); - if (parts.length !== 3) { - params.errors.push({ - 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`, + const iva = extractOrPushError( + maybeFromNullableResult(itemDTO.iva_code, (v) => Tax.createFromCode(v, this.taxCatalog)), + `items[${params.itemIndex}].iva_code`, 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 { - if (code === "#") { - return Maybe.none(); - } + const retention = extractOrPushError( + maybeFromNullableResult(itemDTO.retention_code, (v) => + Tax.createFromCode(v, this.taxCatalog) + ), + `items[${params.itemIndex}].retention_code`, + params.errors + ); - const tax = extractOrPushError(Tax.createFromCode(code, this.taxCatalog), path, errors); - return tax ? Maybe.some(tax) : Maybe.none(); + return { + iva: iva!, + rec: rec!, + retention: retention!, + }; } private throwIfValidationErrors(errors: ValidationErrorDetail[]): void { diff --git a/modules/customer-invoices/src/api/application/proformas/snapshot-builders/full/proforma-items-full-snapshot-builder.ts b/modules/customer-invoices/src/api/application/proformas/snapshot-builders/full/proforma-items-full-snapshot-builder.ts index 080fe87b..362ab1e3 100644 --- a/modules/customer-invoices/src/api/application/proformas/snapshot-builders/full/proforma-items-full-snapshot-builder.ts +++ b/modules/customer-invoices/src/api/application/proformas/snapshot-builders/full/proforma-items-full-snapshot-builder.ts @@ -1,10 +1,6 @@ import type { ISnapshotBuilder } from "@erp/core/api"; import type { ProformaItemDetailDTO } from "@erp/customer-invoices/common"; -import { - maybeToEmptyPercentageObjectString, - maybeToEmptyString, - maybeToNullable, -} from "@repo/rdx-ddd"; +import { maybeToNullable } from "@repo/rdx-ddd"; import { ItemAmount, type ProformaItem, type ProformaItems } from "../../../../domain"; @@ -52,20 +48,26 @@ export class ProformaItemsFullSnapshotBuilder implements IProformaItemsFullSnaps ? allAmounts.taxableAmount.toObjectString() : ItemAmount.zero(currencyCode).toObjectString(), - iva_code: maybeToEmptyString(proformaItem.ivaCode()), - iva_percentage: maybeToEmptyPercentageObjectString(proformaItem.ivaPercentage()), + iva_code: maybeToNullable(proformaItem.ivaCode(), (value) => value.toString()), + iva_percentage: maybeToNullable(proformaItem.ivaPercentage(), (value) => + value.toObjectString() + ), iva_amount: isValued ? allAmounts.ivaAmount.toObjectString() : ItemAmount.zero(currencyCode).toObjectString(), - rec_code: maybeToEmptyString(proformaItem.recCode()), - rec_percentage: maybeToEmptyPercentageObjectString(proformaItem.recPercentage()), + rec_code: maybeToNullable(proformaItem.recCode(), (value) => value.toString()), + rec_percentage: maybeToNullable(proformaItem.recPercentage(), (value) => + value.toObjectString() + ), rec_amount: isValued ? allAmounts.recAmount.toObjectString() : ItemAmount.zero(currencyCode).toObjectString(), - retention_code: maybeToEmptyString(proformaItem.retentionCode()), - retention_percentage: maybeToEmptyPercentageObjectString(proformaItem.retentionPercentage()), + retention_code: maybeToNullable(proformaItem.retentionCode(), (value) => value.toString()), + retention_percentage: maybeToNullable(proformaItem.retentionPercentage(), (value) => + value.toObjectString() + ), retention_amount: isValued ? allAmounts.retentionAmount.toObjectString() : ItemAmount.zero(currencyCode).toObjectString(), diff --git a/modules/customer-invoices/src/api/application/proformas/snapshot-builders/full/proforma-taxes-full-snapshot-builder.ts b/modules/customer-invoices/src/api/application/proformas/snapshot-builders/full/proforma-taxes-full-snapshot-builder.ts index 072fb951..5d882fa9 100644 --- a/modules/customer-invoices/src/api/application/proformas/snapshot-builders/full/proforma-taxes-full-snapshot-builder.ts +++ b/modules/customer-invoices/src/api/application/proformas/snapshot-builders/full/proforma-taxes-full-snapshot-builder.ts @@ -1,5 +1,5 @@ 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 { TaxesBreakdownDTO } from "../../../../../common"; @@ -13,16 +13,18 @@ export class ProformaTaxesFullSnapshotBuilder implements IProformaTaxesFullSnaps return { taxable_amount: proformaTax.taxableAmount.toObjectString(), - iva_code: maybeToEmptyString(proformaTax.ivaCode), - iva_percentage: maybeToEmptyPercentageObjectString(proformaTax.ivaPercentage), + iva_code: maybeToNullable(proformaTax.ivaCode, (value) => value.toString()), + iva_percentage: maybeToNullable(proformaTax.ivaPercentage, (value) => value.toObjectString()), iva_amount: proformaTax.ivaAmount.toObjectString(), - rec_code: maybeToEmptyString(proformaTax.recCode), - rec_percentage: maybeToEmptyPercentageObjectString(proformaTax.recPercentage), + rec_code: maybeToNullable(proformaTax.recCode, (value) => value.toString()), + rec_percentage: maybeToNullable(proformaTax.recPercentage, (value) => value.toObjectString()), rec_amount: proformaTax.recAmount.toObjectString(), - retention_code: maybeToEmptyString(proformaTax.retentionCode), - retention_percentage: maybeToEmptyPercentageObjectString(proformaTax.retentionPercentage), + retention_code: maybeToNullable(proformaTax.retentionCode, (value) => value.toString()), + retention_percentage: maybeToNullable(proformaTax.retentionPercentage, (value) => + value.toObjectString() + ), retention_amount: proformaTax.retentionAmount.toObjectString(), taxes_amount: proformaTax.taxesAmount.toObjectString(), diff --git a/modules/customer-invoices/src/api/domain/proformas/entities/proforma-items/proforma-items.collection.ts b/modules/customer-invoices/src/api/domain/proformas/entities/proforma-items/proforma-items.collection.ts index 6561cee3..45e0339b 100644 --- a/modules/customer-invoices/src/api/domain/proformas/entities/proforma-items/proforma-items.collection.ts +++ b/modules/customer-invoices/src/api/domain/proformas/entities/proforma-items/proforma-items.collection.ts @@ -40,15 +40,15 @@ export interface IProformaItems { totals(): IProformaItemTotals; taxes(): Collection; - readonly globalDiscountPercentage: DiscountPercentage; // % descuento de la cabecera - readonly languageCode: LanguageCode; // Para formateos específicos de idioma - readonly currencyCode: CurrencyCode; // Para cálculos y formateos de moneda + globalDiscountPercentage: DiscountPercentage; // % descuento de la cabecera + languageCode: LanguageCode; // Para formateos específicos de idioma + currencyCode: CurrencyCode; // Para cálculos y formateos de moneda } export class ProformaItems extends Collection implements IProformaItems { - public readonly languageCode!: LanguageCode; - public readonly currencyCode!: CurrencyCode; - public readonly globalDiscountPercentage!: DiscountPercentage; + public languageCode!: LanguageCode; + public currencyCode!: CurrencyCode; + public globalDiscountPercentage!: DiscountPercentage; private constructor(props: IProformaItemsProps) { super(props.items ?? []); @@ -67,6 +67,22 @@ export class ProformaItems extends Collection implements IProforma } 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 = this.languageCode.equals(item.languageCode) && this.currencyCode.equals(item.currencyCode) && diff --git a/modules/customer-invoices/src/common/dto/request/proformas/update-proforma-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/proformas/update-proforma-by-id.request.dto.ts index b1629298..16bc3e90 100644 --- a/modules/customer-invoices/src/common/dto/request/proformas/update-proforma-by-id.request.dto.ts +++ b/modules/customer-invoices/src/common/dto/request/proformas/update-proforma-by-id.request.dto.ts @@ -8,11 +8,10 @@ import { } from "@erp/core"; import { z } from "zod/v4"; -import { ItemPositionSchema, TaxCombinationCodeSchema } from "../../shared"; +import { ItemPositionSchema } from "../../shared"; export const UpdateProformaItemRequestSchema = z.object({ position: ItemPositionSchema, - is_valued: z.boolean(), description: z.string().nullable(), @@ -21,7 +20,14 @@ export const UpdateProformaItemRequestSchema = z.object({ 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({ @@ -47,6 +53,8 @@ export const UpdateProformaByIdRequestSchema = z.object({ payment_method_id: z.uuid().nullable().optional(), + // retención como código??? retencion_15 + items: z.array(UpdateProformaItemRequestSchema).optional(), }); diff --git a/modules/customer-invoices/src/common/dto/shared/taxes-breakdown.dto.ts b/modules/customer-invoices/src/common/dto/shared/taxes-breakdown.dto.ts index 45dff20c..12b9359b 100644 --- a/modules/customer-invoices/src/common/dto/shared/taxes-breakdown.dto.ts +++ b/modules/customer-invoices/src/common/dto/shared/taxes-breakdown.dto.ts @@ -4,16 +4,16 @@ import { z } from "zod/v4"; export const TaxesBreakdownSchema = z.object({ taxable_amount: MoneySchema, - iva_code: z.string(), - iva_percentage: PercentageSchema, + iva_code: z.string().nullable(), + iva_percentage: PercentageSchema.nullable(), iva_amount: MoneySchema, rec_code: z.string().nullable(), - rec_percentage: PercentageSchema, + rec_percentage: PercentageSchema.nullable(), rec_amount: MoneySchema, retention_code: z.string().nullable(), - retention_percentage: PercentageSchema, + retention_percentage: PercentageSchema.nullable(), retention_amount: MoneySchema, taxes_amount: MoneySchema, diff --git a/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-controller.ts b/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-controller.ts index 2cc48c11..ae149e00 100644 --- a/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-controller.ts +++ b/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-controller.ts @@ -162,8 +162,6 @@ export const useUpdateProformaController = ( console.log("Enviando actualización con params:", params); - return; - try { // Enviamos cambios al servidor const updated = await mutateAsync(params); diff --git a/modules/customer-invoices/src/web/proformas/update/entities/proforma-item-update-patch.entity.ts b/modules/customer-invoices/src/web/proformas/update/entities/proforma-item-update-patch.entity.ts index 5247f784..f6615522 100644 --- a/modules/customer-invoices/src/web/proformas/update/entities/proforma-item-update-patch.entity.ts +++ b/modules/customer-invoices/src/web/proformas/update/entities/proforma-item-update-patch.entity.ts @@ -23,4 +23,7 @@ export interface ProformaItemUpdatePatch { unitAmount: number | null; itemDiscountPercentage: number | null; + + taxPercentage: number | null; + recPercentage: number | null; } diff --git a/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-patch.entity.ts b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-patch.entity.ts index 798109c7..5225776a 100644 --- a/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-patch.entity.ts +++ b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-patch.entity.ts @@ -25,7 +25,19 @@ export type ProformaUpdatePatch = { 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; currencyCode?: string; diff --git a/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-line-editor.tsx b/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-line-editor.tsx index 3312133e..dca33eb1 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-line-editor.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-line-editor.tsx @@ -4,10 +4,12 @@ import { LineDescriptionField, PercentageField, QuantityField, + SelectField, } from "@repo/rdx-ui/components"; import { MoneyHelper } from "@repo/rdx-utils"; import { useTranslation } from "../../../../i18n"; +import { getProformaTaxOptions } from "../../../shared"; import type { ProformaItemAmounts, ProformaItemField, @@ -130,11 +132,12 @@ export const ProformaLineEditor = ({ header: t("form_fields.items.tax_percentage.label", "IVA"), headClassName: "w-[110px] text-right", cell: ({ index }) => ( - (value === null || value === "" ? null : Number(value))} inputClassName="border-none" - maxFractionDigits={2} - minFractionDigits={0} + items={getProformaTaxOptions()} name={`items.${index}.taxPercentage`} + serialize={(value) => (typeof value === "number" ? String(value) : "")} /> ), } satisfies LineEditorColumn, diff --git a/modules/customer-invoices/src/web/proformas/update/utils/build-update-proforma-by-id-params.ts b/modules/customer-invoices/src/web/proformas/update/utils/build-update-proforma-by-id-params.ts index b97c0f98..f3d64f70 100644 --- a/modules/customer-invoices/src/web/proformas/update/utils/build-update-proforma-by-id-params.ts +++ b/modules/customer-invoices/src/web/proformas/update/utils/build-update-proforma-by-id-params.ts @@ -1,8 +1,14 @@ import { MoneyDTOHelper, PercentageDTOHelper, QuantityDTOHelper } from "@erp/core"; +import { toNullable } from "@repo/rdx-ddd"; import { ObjectHelper } from "@repo/rdx-utils"; 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 @@ -28,11 +34,6 @@ export const buildUpdateProformaByIdParams = ( throw new Error("proformaId is required"); } - //const currencyCode = formData.currencyCode; - //const languageCode = formData.languageCode; - - console.log("PATCH => ", patch); - const data: UpdateProformaByIdParams["data"] = {}; if (ObjectHelper.hasOwn(patch, "series")) { @@ -63,13 +64,6 @@ export const buildUpdateProformaByIdParams = ( data.notes = patch.notes; } - if (ObjectHelper.hasOwn(patch, "globalDiscountPercentage")) { - data.global_discount_percentage = PercentageDTOHelper.fromNumber( - patch.globalDiscountPercentage!, - 2 - ); - } - if (ObjectHelper.hasOwn(patch, "languageCode")) { data.language_code = patch.languageCode; } @@ -78,11 +72,23 @@ export const buildUpdateProformaByIdParams = ( data.currency_code = patch.currencyCode; } - if (ObjectHelper.hasOwn(patch, "items")) { - data.items = patch.items?.map((item, index) => toProformaItemUpdateDTO(item, index, formData)); + if (ObjectHelper.hasOwn(patch, "paymentMethodId")) { + 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 { id, @@ -92,11 +98,10 @@ export const buildUpdateProformaByIdParams = ( const toProformaItemUpdateDTO = ( item: ProformaItemUpdatePatch, - _index: number, + index: number, formData: ProformaUpdateForm ): NonNullable[number] => { const currencyCode = formData.currencyCode; - //const languageCode = formData.languageCode; const quantity = item.quantity === null ? null : QuantityDTOHelper.fromNumberNulleable(item.quantity, 2); @@ -106,22 +111,67 @@ const toProformaItemUpdateDTO = ( ? null : 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 { - position: item.position, - is_valued, + position: index, description: item.description, quantity, unit_amount, - item_discount_percentage: - item.itemDiscountPercentage === null - ? null - : PercentageDTOHelper.fromNumber(item.itemDiscountPercentage), + item_discount_percentage: toNullable(item.itemDiscountPercentage, (value) => + PercentageDTOHelper.fromNumber(value, 2) + ), - 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); +}; diff --git a/modules/customer-invoices/src/web/proformas/update/utils/calculations/calculate-commercial-document-line-amounts.ts b/modules/customer-invoices/src/web/proformas/update/utils/calculations/calculate-commercial-document-line-amounts.ts deleted file mode 100644 index 57144408..00000000 --- a/modules/customer-invoices/src/web/proformas/update/utils/calculations/calculate-commercial-document-line-amounts.ts +++ /dev/null @@ -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), - }; -}; diff --git a/modules/customer-invoices/src/web/proformas/update/utils/calculations/calculate-commercial-document-lines-totals.ts b/modules/customer-invoices/src/web/proformas/update/utils/calculations/calculate-commercial-document-lines-totals.ts deleted file mode 100644 index 1108bca2..00000000 --- a/modules/customer-invoices/src/web/proformas/update/utils/calculations/calculate-commercial-document-lines-totals.ts +++ /dev/null @@ -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( - (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, - } - ); -}; diff --git a/modules/customer-invoices/src/web/proformas/update/utils/calculations/calculate-commercial-document-tax-breakdown.ts b/modules/customer-invoices/src/web/proformas/update/utils/calculations/calculate-commercial-document-tax-breakdown.ts deleted file mode 100644 index 1f1feab4..00000000 --- a/modules/customer-invoices/src/web/proformas/update/utils/calculations/calculate-commercial-document-tax-breakdown.ts +++ /dev/null @@ -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(); - - 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), - })); -}; diff --git a/modules/customer-invoices/src/web/proformas/update/utils/calculations/calculate-commercial-document-totals.ts b/modules/customer-invoices/src/web/proformas/update/utils/calculations/calculate-commercial-document-totals.ts deleted file mode 100644 index c895ed0f..00000000 --- a/modules/customer-invoices/src/web/proformas/update/utils/calculations/calculate-commercial-document-totals.ts +++ /dev/null @@ -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), - }; -}; diff --git a/packages/rdx-ddd/src/helpers/normalizers.ts b/packages/rdx-ddd/src/helpers/normalizers.ts index e9f348d4..204b9cdb 100644 --- a/packages/rdx-ddd/src/helpers/normalizers.ts +++ b/packages/rdx-ddd/src/helpers/normalizers.ts @@ -83,16 +83,17 @@ function serializeMaybe(maybe: Maybe, onNone: R, map: (value: T) => R): */ export function toNullable(input: null | undefined, map: (t: V) => R): null; export function toNullable(input: Maybe, map: (t: V) => R): R | null; +export function toNullable(input: V | null, map: (t: V) => R): R | null; export function toNullable(input: V, map: (t: V) => R): R; export function toNullable( input: V | Maybe | null | undefined, map: (t: V) => R ): R | null { if (isMaybe(input)) { - return maybeToNullable(input, map); + return maybeToNullable(input, map); } - return mapNullable(input, map); + return mapNullable(input, map); } /**