diff --git a/apps/server/package.json b/apps/server/package.json index 5176d89d..3b49c93a 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,6 +1,6 @@ { "name": "@erp/factuges-server", - "version": "0.6.5", + "version": "0.6.6", "private": true, "scripts": { "build": "tsup src/index.ts --config tsup.config.ts", diff --git a/apps/web/package.json b/apps/web/package.json index f576740f..5ea54656 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,7 +1,7 @@ { "name": "@erp/factuges-web", "private": true, - "version": "0.6.5", + "version": "0.6.6", "type": "module", "scripts": { "dev": "vite --host --clearScreen false", diff --git a/modules/auth/package.json b/modules/auth/package.json index f66e3629..f4f13dfa 100644 --- a/modules/auth/package.json +++ b/modules/auth/package.json @@ -1,6 +1,6 @@ { "name": "@erp/auth", - "version": "0.6.5", + "version": "0.6.6", "private": true, "type": "module", "sideEffects": false, diff --git a/modules/core/package.json b/modules/core/package.json index d2db661f..2f472194 100644 --- a/modules/core/package.json +++ b/modules/core/package.json @@ -1,6 +1,6 @@ { "name": "@erp/core", - "version": "0.6.5", + "version": "0.6.6", "private": true, "type": "module", "sideEffects": false, diff --git a/modules/core/src/common/catalogs/taxes/spain-tax-catalog.json b/modules/core/src/common/catalogs/taxes/spain-tax-catalog.json index 1d4be6f9..020bb32f 100644 --- a/modules/core/src/common/catalogs/taxes/spain-tax-catalog.json +++ b/modules/core/src/common/catalogs/taxes/spain-tax-catalog.json @@ -118,7 +118,7 @@ }, { "name": "Retenc. 35%", - "code": "retencion_35", + "code": "retention_35", "value": "3500", "scale": "2", "group": "retention", @@ -127,7 +127,7 @@ }, { "name": "Retenc. 19%", - "code": "retencion_19", + "code": "retention_19", "value": "1900", "scale": "2", "group": "retention", @@ -136,7 +136,7 @@ }, { "name": "Retenc. 15%", - "code": "retencion_15", + "code": "retention_15", "value": "1500", "scale": "2", "group": "retention", @@ -145,7 +145,7 @@ }, { "name": "Retenc. 7%", - "code": "retencion_7", + "code": "retention_7", "value": "700", "scale": "2", "group": "retention", @@ -154,7 +154,7 @@ }, { "name": "Retenc. 2%", - "code": "retencion_2", + "code": "retention_2", "value": "200", "scale": "2", "group": "retention", diff --git a/modules/customer-invoices/package.json b/modules/customer-invoices/package.json index d9e08b30..3f422270 100644 --- a/modules/customer-invoices/package.json +++ b/modules/customer-invoices/package.json @@ -1,6 +1,6 @@ { "name": "@erp/customer-invoices", - "version": "0.6.5", + "version": "0.6.6", "private": true, "type": "module", "sideEffects": false, 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 2a9c7ae0..0ac9eb1a 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 @@ -188,7 +188,8 @@ export class UpdateProformaInputMapper implements IUpdateProformaInputMapper { return Result.ok(proformaPatchProps); } catch (err: unknown) { - return Result.fail(new DomainError("Proforma props mapping failed", { cause: err })); + console.error(err); + return Result.fail(new DomainError("Proforma props mapping failed (update)", { cause: err })); } } diff --git a/modules/customer-invoices/src/web/proformas/list/ui/pages/list-proformas-page.tsx b/modules/customer-invoices/src/web/proformas/list/ui/pages/list-proformas-page.tsx index ce1f35c5..caa78309 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/pages/list-proformas-page.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/pages/list-proformas-page.tsx @@ -24,8 +24,7 @@ import { prepareIssueProformaTarget } from "../../../issue-proforma/utils"; import type { ProformaListRow } from "../../../shared"; import { useListProformasPageController } from "../../controllers"; import { ProformaSummaryPanel, ProformasGrid, useProformasGridColumns } from "../blocks"; - -import { OrdersTable } from "./orders-table"; +import { ProformaStatusBadge } from "../components"; export const ListProformasPage = () => { const { t } = useTranslation(); @@ -86,7 +85,30 @@ export const ListProformasPage = () => { pageSize={listCtrl.pageSize} /> - + {/* Explicación técnica */} +
+

Estado de proforma

+ +
); diff --git a/modules/customer-invoices/src/web/proformas/shared/adapters/get-proforma-by-id.adapter.ts b/modules/customer-invoices/src/web/proformas/shared/adapters/get-proforma-by-id.adapter.ts index 10fbbe18..d17c6ae5 100644 --- a/modules/customer-invoices/src/web/proformas/shared/adapters/get-proforma-by-id.adapter.ts +++ b/modules/customer-invoices/src/web/proformas/shared/adapters/get-proforma-by-id.adapter.ts @@ -43,7 +43,7 @@ export const GetProformaByIdAdapter = { recipient: mapRecipient(dto), taxes: dto.taxes.map(mapTaxSummary), - paymentMethod: dto.payment_method?.id, + paymentMethodId: dto.payment_method?.id ?? null, subtotalAmount: MoneyDTOHelper.toNumber(dto.subtotal_amount), @@ -85,24 +85,20 @@ const mapItem = (dto: GetProformaByIdResponseDTO["items"][number]): ProformaItem taxableAmount: MoneyDTOHelper.toNumber(dto.taxable_amount), - ivaCode: dto.iva_code, - ivaPercentage: PercentageDTOHelper.toNumber(dto.iva_percentage), - ivaAmount: MoneyDTOHelper.toNumber(dto.iva_amount), + taxCode: dto.iva_code, + taxPercentage: PercentageDTOHelper.toNumericNulleable(dto.iva_percentage), + taxAmount: MoneyDTOHelper.toNumber(dto.iva_amount), recCode: dto.rec_code, - recPercentage: PercentageDTOHelper.toNumber(dto.rec_percentage), + recPercentage: PercentageDTOHelper.toNumericNulleable(dto.rec_percentage), recAmount: MoneyDTOHelper.toNumber(dto.rec_amount), retentionCode: dto.retention_code, - retentionPercentage: PercentageDTOHelper.toNumber(dto.retention_percentage), + retentionPercentage: PercentageDTOHelper.toNumericNulleable(dto.retention_percentage), retentionAmount: MoneyDTOHelper.toNumber(dto.retention_amount), taxesAmount: MoneyDTOHelper.toNumber(dto.taxes_amount), totalAmount: MoneyDTOHelper.toNumber(dto.total_amount), - - // Nota: el DTO de detalle actual no incluye `taxes` por línea. - // Dejamos fallback explícito para no inventar parseos. - taxes: "", }; return item; diff --git a/modules/customer-invoices/src/web/proformas/shared/constants/index.ts b/modules/customer-invoices/src/web/proformas/shared/constants/index.ts index a61736f2..4559014d 100644 --- a/modules/customer-invoices/src/web/proformas/shared/constants/index.ts +++ b/modules/customer-invoices/src/web/proformas/shared/constants/index.ts @@ -1,3 +1,2 @@ export * from "./payment-method-options.constants"; -export * from "./retentions-options.constants"; -export * from "./taxes-options.constants"; +export * from "./proforma-fiscal-options.constants"; diff --git a/modules/customer-invoices/src/web/proformas/shared/constants/proforma-fiscal-options.constants.ts b/modules/customer-invoices/src/web/proformas/shared/constants/proforma-fiscal-options.constants.ts new file mode 100644 index 00000000..66a174f4 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/constants/proforma-fiscal-options.constants.ts @@ -0,0 +1,90 @@ +export type ProformaTaxPercentageOption = 21 | 10 | 4 | 0; +export type ProformaRecPercentageOption = 5.2 | 1.4 | 0.5 | 0; +export type ProformaRetentionPercentageOption = 15 | 7; + +export interface ProformaSelectOption { + value: TValue; + label: string; +} + +export interface ProformaTaxDefinition { + taxCode: string; + taxPercentage: ProformaTaxPercentageOption; + taxLabel: string; + recCode: string; + recPercentage: ProformaRecPercentageOption; +} + +export interface ProformaRetentionDefinition { + retentionCode: string; + retentionPercentage: ProformaRetentionPercentageOption; + retentionLabel: string; +} + +export const PROFORMA_TAX_DEFINITIONS: Record = + { + 21: { + taxCode: "iva_21", + taxPercentage: 21, + taxLabel: "General", + recCode: "rec_5_2", + recPercentage: 5.2, + }, + 10: { + taxCode: "iva_10", + taxPercentage: 10, + taxLabel: "Reducido", + recCode: "rec_1_4", + recPercentage: 1.4, + }, + 4: { + taxCode: "iva_4", + taxPercentage: 4, + taxLabel: "Superreducido", + recCode: "rec_0_5", + recPercentage: 0.5, + }, + 0: { + taxCode: "iva_0", + taxPercentage: 0, + taxLabel: "Exento", + recCode: "rec_0", + recPercentage: 0, + }, + } as const; + +export const PROFORMA_RETENTION_DEFINITIONS: Record< + ProformaRetentionPercentageOption, + ProformaRetentionDefinition +> = { + 15: { + retentionCode: "retention_15", + retentionPercentage: 15, + retentionLabel: "General", + }, + 7: { + retentionCode: "retention_7", + retentionPercentage: 7, + retentionLabel: "Inicio actividad", + }, +} as const; + +export const PROFORMA_TAX_OPTIONS: ProformaSelectOption<`${ProformaTaxPercentageOption}`>[] = [ + { value: "0", label: "0%" }, + { value: "4", label: "4%" }, + { value: "10", label: "10%" }, + { value: "21", label: "21%" }, +]; + +export const PROFORMA_REC_OPTIONS: ProformaSelectOption<`${ProformaRecPercentageOption}`>[] = [ + { value: "0", label: "0%" }, + { value: "0.5", label: "0,5%" }, + { value: "1.4", label: "1,4%" }, + { value: "5.2", label: "5,2%" }, +]; + +export const PROFORMA_RETENTION_OPTIONS: ProformaSelectOption<`${ProformaRetentionPercentageOption}`>[] = + [ + { value: "15", label: "15%" }, + { value: "7", label: "7%" }, + ]; diff --git a/modules/customer-invoices/src/web/proformas/shared/constants/retentions-options.constants.ts b/modules/customer-invoices/src/web/proformas/shared/constants/retentions-options.constants.ts deleted file mode 100644 index fe3a90ff..00000000 --- a/modules/customer-invoices/src/web/proformas/shared/constants/retentions-options.constants.ts +++ /dev/null @@ -1,25 +0,0 @@ -export type RetentionPercentageOption = 15 | 7; - -export interface ProformaRetentionsDefinition { - retentionPercentage: RetentionPercentageOption; - retentionLabel: string; -} - -export const PROFORMA_RETENTION_DEFINITIONS: Record< - RetentionPercentageOption, - ProformaRetentionsDefinition -> = { - 15: { - retentionPercentage: 15, - retentionLabel: "General", - }, - 7: { - retentionPercentage: 7, - retentionLabel: "Reducido", - }, -}; - -export const PROFORMA_RETENTION_OPTIONS = [ - { value: "15", label: "15%" }, - { value: "7", label: "7%" }, -]; diff --git a/modules/customer-invoices/src/web/proformas/shared/constants/taxes-options.constants.ts b/modules/customer-invoices/src/web/proformas/shared/constants/taxes-options.constants.ts deleted file mode 100644 index f5a92738..00000000 --- a/modules/customer-invoices/src/web/proformas/shared/constants/taxes-options.constants.ts +++ /dev/null @@ -1,37 +0,0 @@ -export type TaxPercentageOption = 21 | 10 | 4 | 0; - -export interface ProformaTaxesDefinition { - taxPercentage: TaxPercentageOption; - taxLabel: string; - recPercentage: number; -} - -export const PROFORMA_TAX_DEFINITIONS: Record = { - 21: { - taxPercentage: 21, - taxLabel: "General", - recPercentage: 5.2, - }, - 10: { - taxPercentage: 10, - taxLabel: "Reducido", - recPercentage: 1.4, - }, - 4: { - taxPercentage: 4, - taxLabel: "Superreducido", - recPercentage: 0.5, - }, - 0: { - taxPercentage: 0, - taxLabel: "Exento", - recPercentage: 0, - }, -}; - -export const PROFORMA_TAX_OPTIONS = [ - { value: "0", label: "0%" }, - { value: "4", label: "4%" }, - { value: "10", label: "10%" }, - { value: "21", label: "21%" }, -]; diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-item.entity.ts b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-item.entity.ts index d4b00d1a..ee3f0955 100644 --- a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-item.entity.ts +++ b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-item.entity.ts @@ -24,21 +24,19 @@ export interface ProformaItem { taxableAmount: number; - ivaCode: string; - ivaPercentage: number; - ivaAmount: number; + taxCode: string | null; + taxPercentage: number | null; + taxAmount: number; recCode: string | null; - recPercentage: number; + recPercentage: number | null; recAmount: number; retentionCode: string | null; - retentionPercentage: number; + retentionPercentage: number | null; retentionAmount: number; taxesAmount: number; totalAmount: number; - - taxes: string; } diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-tax-summary.entity.ts b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-tax-summary.entity.ts index 75a3b319..48d4628c 100644 --- a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-tax-summary.entity.ts +++ b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-tax-summary.entity.ts @@ -7,8 +7,8 @@ export interface ProformaTaxSummary { taxableAmount: number; - ivaCode: string; - ivaPercentage: number; + ivaCode: string | null; + ivaPercentage: number | null; ivaAmount: number; recCode: string | null; diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/proforma.entity.ts b/modules/customer-invoices/src/web/proformas/shared/entities/proforma.entity.ts index a87b6314..127db1bb 100644 --- a/modules/customer-invoices/src/web/proformas/shared/entities/proforma.entity.ts +++ b/modules/customer-invoices/src/web/proformas/shared/entities/proforma.entity.ts @@ -33,7 +33,7 @@ export interface Proforma { taxes: ProformaTaxSummary[]; - paymentMethod?: string; + paymentMethodId: string | null; subtotalAmount: number; diff --git a/modules/customer-invoices/src/web/proformas/shared/utils/get-proforma-rec-percentage.ts b/modules/customer-invoices/src/web/proformas/shared/utils/get-proforma-rec-percentage.ts deleted file mode 100644 index beab3c97..00000000 --- a/modules/customer-invoices/src/web/proformas/shared/utils/get-proforma-rec-percentage.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { PROFORMA_TAX_DEFINITIONS, type TaxPercentageOption } from "../constants"; - -export const getProformaRecPercentage = (taxPercentage: TaxPercentageOption): number => { - return PROFORMA_TAX_DEFINITIONS[taxPercentage].recPercentage; -}; diff --git a/modules/customer-invoices/src/web/proformas/shared/utils/get-proforma-retention-options.ts b/modules/customer-invoices/src/web/proformas/shared/utils/get-proforma-retention-options.ts deleted file mode 100644 index ba66c165..00000000 --- a/modules/customer-invoices/src/web/proformas/shared/utils/get-proforma-retention-options.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { PROFORMA_RETENTION_OPTIONS } from "../constants"; - -export const getProformaRetentionOptions = () => PROFORMA_RETENTION_OPTIONS; diff --git a/modules/customer-invoices/src/web/proformas/shared/utils/get-proforma-tax-options.ts b/modules/customer-invoices/src/web/proformas/shared/utils/get-proforma-tax-options.ts deleted file mode 100644 index ab9ce945..00000000 --- a/modules/customer-invoices/src/web/proformas/shared/utils/get-proforma-tax-options.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { PROFORMA_TAX_OPTIONS } from "../constants"; - -export const getProformaTaxOptions = () => PROFORMA_TAX_OPTIONS; diff --git a/modules/customer-invoices/src/web/proformas/shared/utils/index.ts b/modules/customer-invoices/src/web/proformas/shared/utils/index.ts index 0b83e259..f9329bc3 100644 --- a/modules/customer-invoices/src/web/proformas/shared/utils/index.ts +++ b/modules/customer-invoices/src/web/proformas/shared/utils/index.ts @@ -1,3 +1 @@ -export * from "./get-proforma-rec-percentage"; -export * from "./get-proforma-retention-options"; -export * from "./get-proforma-tax-options"; +export * from "./proforma-fiscal-options.utils"; diff --git a/modules/customer-invoices/src/web/proformas/shared/utils/proforma-fiscal-options.utils.ts b/modules/customer-invoices/src/web/proformas/shared/utils/proforma-fiscal-options.utils.ts new file mode 100644 index 00000000..67737b37 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/utils/proforma-fiscal-options.utils.ts @@ -0,0 +1,96 @@ +import { PercentageHelper, isNullishOrEmpty } from "@repo/rdx-utils"; + +import { + PROFORMA_REC_OPTIONS, + PROFORMA_RETENTION_DEFINITIONS, + PROFORMA_RETENTION_OPTIONS, + PROFORMA_TAX_DEFINITIONS, + PROFORMA_TAX_OPTIONS, + type ProformaRecPercentageOption, + type ProformaRetentionPercentageOption, + type ProformaTaxPercentageOption, +} from "../constants"; + +export const parseProformaTaxPercentage = ( + value: number | string | null | undefined +): ProformaTaxPercentageOption | null => { + if (isNullishOrEmpty(value)) { + return null; + } + + const numericValue = Number(value); + const normalizedValue = PercentageHelper.normalizePercentage(numericValue); + + if (normalizedValue in PROFORMA_TAX_DEFINITIONS) { + return normalizedValue as ProformaTaxPercentageOption; + } + + return null; +}; + +export const parseProformaRetentionPercentage = ( + value: number | string | null | undefined +): ProformaRetentionPercentageOption | null => { + if (isNullishOrEmpty(value)) { + return null; + } + + const numericValue = Number(value); + const normalizedValue = PercentageHelper.normalizePercentage(numericValue); + + if (normalizedValue in PROFORMA_RETENTION_DEFINITIONS) { + return normalizedValue as ProformaRetentionPercentageOption; + } + + return null; +}; + +export const getProformaTaxCode = ( + taxPercentage: number | string | null | undefined +): string | null => { + const parsedTaxPercentage = parseProformaTaxPercentage(taxPercentage); + + if (parsedTaxPercentage === null) return null; + + return PROFORMA_TAX_DEFINITIONS[parsedTaxPercentage].taxCode; +}; + +export const getProformaRecPercentage = ( + taxPercentage: ProformaTaxPercentageOption | null | undefined +): ProformaRecPercentageOption | null => { + if (taxPercentage === null || taxPercentage === undefined) return null; + + return PROFORMA_TAX_DEFINITIONS[taxPercentage].recPercentage; +}; + +export const getProformaRecCode = ( + recPercentage: number | string | null | undefined +): string | null => { + if (recPercentage === null || recPercentage === undefined || recPercentage === "") { + return null; + } + + const numericValue = Number(recPercentage); + + const definition = Object.values(PROFORMA_TAX_DEFINITIONS).find( + (taxDefinition) => taxDefinition.recPercentage === numericValue + ); + + return definition?.recCode ?? null; +}; + +export const getProformaRetentionCode = ( + retentionPercentage: number | string | null | undefined +): string | null => { + const parsedRetentionPercentage = parseProformaRetentionPercentage(retentionPercentage); + + if (parsedRetentionPercentage === null) return null; + + return PROFORMA_RETENTION_DEFINITIONS[parsedRetentionPercentage].retentionCode; +}; + +export const getProformaTaxOptions = () => PROFORMA_TAX_OPTIONS; + +export const getProformaRecOptions = () => PROFORMA_REC_OPTIONS; + +export const getProformaRetentionOptions = () => PROFORMA_RETENTION_OPTIONS; diff --git a/modules/customer-invoices/src/web/proformas/update/adapters/map-proforma-items-to-proforma-items-update-form.adapter.ts b/modules/customer-invoices/src/web/proformas/update/adapters/map-proforma-items-to-proforma-items-update-form.adapter.ts index e5aa0317..be78eaac 100644 --- a/modules/customer-invoices/src/web/proformas/update/adapters/map-proforma-items-to-proforma-items-update-form.adapter.ts +++ b/modules/customer-invoices/src/web/proformas/update/adapters/map-proforma-items-to-proforma-items-update-form.adapter.ts @@ -23,7 +23,7 @@ export const mapProformaItemsToProformaItemsUpdateForm = ( itemDiscountPercentage: item.itemDiscountPercentage ?? null, - taxPercentage: item.ivaPercentage ?? null, + taxPercentage: item.taxPercentage ?? null, recPercentage: item.recPercentage ?? null, }; }; diff --git a/modules/customer-invoices/src/web/proformas/update/adapters/map-proforma-to-proforma-update-form.adapter.ts b/modules/customer-invoices/src/web/proformas/update/adapters/map-proforma-to-proforma-update-form.adapter.ts index a99502da..d42e3b36 100644 --- a/modules/customer-invoices/src/web/proformas/update/adapters/map-proforma-to-proforma-update-form.adapter.ts +++ b/modules/customer-invoices/src/web/proformas/update/adapters/map-proforma-to-proforma-update-form.adapter.ts @@ -1,34 +1,24 @@ -import { PercentageHelper } from "@repo/rdx-utils"; - -import type { Proforma, ProformaItem } from "../../shared"; +import type { Proforma } from "../../shared"; import type { ProformaTaxMode, ProformaUpdateForm } from "../entities"; import { buildProformaUpdateDefault } from "../utils"; import { mapProformaItemsToProformaItemsUpdateForm } from "./map-proforma-items-to-proforma-items-update-form.adapter"; +interface FiscalDefaults { + taxMode: ProformaTaxMode; + + defaultTaxPercentage: number | null; + defaultRecPercentage: number | null; + defaultRetentionPercentage: number | null; +} + /** * Mapea una proforma a un formulario de actualización de proforma. */ + export const mapProformaToProformaUpdateForm = (proforma: Proforma): ProformaUpdateForm => { const proformaDefaults = buildProformaUpdateDefault(); - - const taxMode = inferProformaTaxMode(proforma.items); - - const defaultTaxSummary = proforma.taxes[0]; - const firstTaxableItem = getFirstTaxableItem(proforma.items); - - const defaultTaxPercentage = - defaultTaxSummary?.ivaPercentage ?? - firstTaxableItem?.ivaPercentage ?? - proformaDefaults.defaultTaxPercentage; - - const defaultRecPercentage = - defaultTaxSummary?.recPercentage ?? firstTaxableItem?.recPercentage ?? null; - - const defaultRetentionPercentage = - defaultTaxSummary?.retentionPercentage ?? firstTaxableItem?.retentionPercentage ?? null; - - console.log({ defaultTaxPercentage, defaultRecPercentage, defaultRetentionPercentage, taxMode }); + const fiscalDefaults = resolveFiscalDefaults(proforma, proformaDefaults); return { series: proforma.series ?? proformaDefaults.series, @@ -48,55 +38,56 @@ export const mapProformaToProformaUpdateForm = (proforma: Proforma): ProformaUpd globalDiscountPercentage: proforma.globalDiscountPercentage ?? proformaDefaults.globalDiscountPercentage, - taxMode, - taxRegimeCode: proformaDefaults.taxRegimeCode, + taxMode: fiscalDefaults.taxMode, + taxRegimeCode: "01", //taxRegimeCode: proforma.taxRegimeCode ?? proformaDefaults.taxRegimeCode, // TODO: implementar en API - hasTaxPercentage: PercentageHelper.hasPositivePercentage(defaultTaxPercentage), - defaultTaxPercentage: defaultTaxPercentage, + hasTaxPercentage: fiscalDefaults.defaultTaxPercentage !== null, + taxPercentage: fiscalDefaults.defaultTaxPercentage, - hasRecPercentage: PercentageHelper.hasPositivePercentage(defaultRecPercentage), - defaultRecPercentage: defaultRecPercentage, + hasRecPercentage: fiscalDefaults.defaultRecPercentage !== null, + recPercentage: fiscalDefaults.defaultRecPercentage, - hasRetentionPercentage: PercentageHelper.hasPositivePercentage(defaultRetentionPercentage), - defaultRetentionPercentage: defaultRetentionPercentage, + hasRetentionPercentage: fiscalDefaults.defaultRetentionPercentage !== null, + retentionPercentage: fiscalDefaults.defaultRetentionPercentage, - paymentMethodId: proforma.paymentMethod ?? proformaDefaults.paymentMethodId, + paymentMethodId: proforma.paymentMethodId ?? proformaDefaults.paymentMethodId, items: proforma.items.map(mapProformaItemsToProformaItemsUpdateForm), }; }; -const getFirstTaxableItem = (items: ProformaItem[]): ProformaItem | undefined => { - return items.find((item) => item.isValued) ?? items[0]; -}; +const resolveFiscalDefaults = ( + proforma: Proforma, + proformaDefaults: ProformaUpdateForm +): FiscalDefaults => { + // Caso habitual: una sola combinación de impuestos + if (proforma.taxes.length === 1) { + const taxSummary = proforma.taxes[0]; -const inferProformaTaxMode = (items: ProformaItem[]): ProformaTaxMode => { - const comparableItems = items.filter((item) => item.isValued); - - if (comparableItems.length === 0) { - return "single"; + return { + taxMode: "single", + defaultTaxPercentage: taxSummary.ivaCode === null ? null : taxSummary.ivaPercentage, + defaultRecPercentage: taxSummary.recCode === null ? null : taxSummary.recPercentage, + defaultRetentionPercentage: + taxSummary.retentionCode === null ? null : taxSummary.retentionPercentage, + }; } - const sourceItems = comparableItems; + // Caso excepcional: proforma sin impuestos + if (proforma.taxes.length === 0) { + return { + taxMode: "single", + defaultTaxPercentage: proformaDefaults.taxPercentage, + defaultRecPercentage: proformaDefaults.recPercentage, + defaultRetentionPercentage: proformaDefaults.retentionPercentage, + }; + } - const ivaPercentages = uniquePercentageValues(sourceItems.map((item) => item.ivaPercentage)); - const recPercentages = uniquePercentageValues(sourceItems.map((item) => item.recPercentage)); - const retentionPercentages = uniquePercentageValues( - sourceItems.map((item) => item.retentionPercentage) - ); - - const hasSingleTaxSetup = - ivaPercentages.length <= 1 && recPercentages.length <= 1 && retentionPercentages.length <= 1; - - return hasSingleTaxSetup ? "single" : "perLine"; -}; - -const uniquePercentageValues = (values: Array): number[] => { - return Array.from( - new Set( - values - .filter((value): value is number => value !== null && value !== undefined) - .map((value) => PercentageHelper.normalizePercentage(value)) - ) - ); + // Proforma con varias combinaciones de impuestos + return { + taxMode: "perLine", + defaultTaxPercentage: proformaDefaults.taxPercentage, + defaultRecPercentage: proformaDefaults.recPercentage, + defaultRetentionPercentage: proformaDefaults.retentionPercentage, + }; }; 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 ae149e00..18aa1df5 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 @@ -156,12 +156,8 @@ export const useUpdateProformaController = ( return; } - console.log("Parche de actualización construido:", patchData); - const params = buildUpdateProformaByIdParams(proformaId, patchData, formData); - console.log("Enviando actualización con params:", params); - try { // Enviamos cambios al servidor const updated = await mutateAsync(params); @@ -224,7 +220,7 @@ export const useUpdateProformaController = ( } }, (errors: FieldErrors) => { - console.log(errors); + console.error(errors); focusFirstInputFormError(form); showWarningToast( diff --git a/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-tax-controller.ts b/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-tax-controller.ts index 8886529a..d4484829 100644 --- a/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-tax-controller.ts +++ b/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-tax-controller.ts @@ -2,8 +2,8 @@ import { useCallback, useEffect, useRef } from "react"; import { type UseFormReturn, useWatch } from "react-hook-form"; import { - type RetentionPercentageOption, - type TaxPercentageOption, + type ProformaRetentionPercentageOption, + type ProformaTaxPercentageOption, getProformaRecPercentage, } from "../../shared"; import type { ProformaTaxMode, ProformaUpdateForm } from "../entities"; @@ -16,13 +16,13 @@ export interface UseUpdateProformaTaxControllerResult { taxMode: ProformaTaxMode; hasTaxPercentage: boolean; - defaultTaxPercentage: number | null; + taxPercentage: number | null; hasRecPercentage: boolean; - defaultRecPercentage: number | null; + recPercentage: number | null; hasRetentionPercentage: boolean; - defaultRetentionPercentage: number | null; + retentionPercentage: number | null; usesSingleTax: boolean; usesPerLineTax: boolean; @@ -30,20 +30,20 @@ export interface UseUpdateProformaTaxControllerResult { enablePerLineTaxes: () => void; disablePerLineTaxes: () => void; - updateDefaultTaxPercentage: (newTaxPercentage: RetentionPercentageOption) => void; - updateDefaultRecPercentage: (enabled: boolean) => void; - updateDefaultRetentionPercentage: (enabled: boolean) => void; + updateTaxPercentage: (newTaxPercentage: ProformaTaxPercentageOption) => void; + updateRecPercentage: (enabled: boolean) => void; + updateRetentionPercentage: (newRetentionPercentage: ProformaRetentionPercentageOption) => void; } const resolveRecPercentage = ( enabled: boolean, - taxPercentage: RetentionPercentageOption | null + taxPercentage: ProformaTaxPercentageOption | null ): number | null => { if (!enabled || taxPercentage === null) { return null; } - return getProformaRecPercentage(taxPercentage as TaxPercentageOption); + return getProformaRecPercentage(taxPercentage as ProformaTaxPercentageOption); }; export const useUpdateProformaTaxController = ({ @@ -54,14 +54,13 @@ export const useUpdateProformaTaxController = ({ const taxMode = useWatch({ control, name: "taxMode" }); const hasTaxPercentage = useWatch({ control, name: "hasTaxPercentage" }) ?? false; - const defaultTaxPercentage = useWatch({ control, name: "defaultTaxPercentage" }) ?? null; + const taxPercentage = useWatch({ control, name: "taxPercentage" }) ?? null; const hasRecPercentage = useWatch({ control, name: "hasRecPercentage" }) ?? false; - const defaultRecPercentage = useWatch({ control, name: "defaultRecPercentage" }) ?? null; + const recPercentage = useWatch({ control, name: "recPercentage" }) ?? null; const hasRetentionPercentage = useWatch({ control, name: "hasRetentionPercentage" }) ?? false; - const defaultRetentionPercentage = - useWatch({ control, name: "defaultRetentionPercentage" }) ?? null; + const retentionPercentage = useWatch({ control, name: "retentionPercentage" }) ?? null; const hasMountedRef = useRef(false); @@ -72,14 +71,14 @@ export const useUpdateProformaTaxController = ({ const nextRecPercentage = resolveRecPercentage( hasRecPercentage, - defaultTaxPercentage as RetentionPercentageOption | null + taxPercentage as ProformaTaxPercentageOption | null ); const shouldMarkDirty = hasMountedRef.current; currentItems.forEach((item, index) => { - if (item.taxPercentage !== defaultTaxPercentage) { - setValue(`items.${index}.taxPercentage`, defaultTaxPercentage, { + if (item.taxPercentage !== taxPercentage) { + setValue(`items.${index}.taxPercentage`, taxPercentage, { shouldDirty: shouldMarkDirty, shouldTouch: false, shouldValidate: true, @@ -96,7 +95,7 @@ export const useUpdateProformaTaxController = ({ }); hasMountedRef.current = true; - }, [defaultTaxPercentage, getValues, hasRecPercentage, setValue, taxMode]); + }, [taxPercentage, getValues, hasRecPercentage, setValue, taxMode]); const enablePerLineTaxes = useCallback(() => { setValue("taxMode", "perLine", { @@ -114,20 +113,25 @@ export const useUpdateProformaTaxController = ({ }); }, [setValue]); - const updateDefaultTaxPercentage = useCallback( - (newTaxPercentage: RetentionPercentageOption): void => { - setValue("defaultTaxPercentage", newTaxPercentage, { + const updateTaxPercentage = useCallback( + (newTaxPercentage: ProformaTaxPercentageOption): void => { + setValue("taxPercentage", newTaxPercentage, { shouldDirty: true, shouldTouch: true, shouldValidate: true, }); + setValue("hasTaxPercentage", true, { + shouldDirty: true, + shouldValidate: true, + }); + const isRecPercentageEnabled = getValues("hasRecPercentage"); if (isRecPercentageEnabled) { setValue( - "defaultRecPercentage", - getProformaRecPercentage(newTaxPercentage as TaxPercentageOption), + "recPercentage", + getProformaRecPercentage(newTaxPercentage as ProformaTaxPercentageOption), { shouldDirty: true, shouldValidate: true, @@ -138,7 +142,7 @@ export const useUpdateProformaTaxController = ({ [getValues, setValue] ); - const updateDefaultRecPercentage = useCallback( + const updateRecPercentage = useCallback( (enabled: boolean): void => { setValue("hasRecPercentage", enabled, { shouldDirty: true, @@ -146,9 +150,9 @@ export const useUpdateProformaTaxController = ({ shouldValidate: true, }); - const taxPercentage = getValues("defaultTaxPercentage") as RetentionPercentageOption | null; + const taxPercentage = getValues("taxPercentage") as ProformaTaxPercentageOption | null; - setValue("defaultRecPercentage", resolveRecPercentage(enabled, taxPercentage), { + setValue("recPercentage", resolveRecPercentage(enabled, taxPercentage), { shouldDirty: true, shouldValidate: true, }); @@ -156,13 +160,18 @@ export const useUpdateProformaTaxController = ({ [getValues, setValue] ); - const updateDefaultRetentionPercentage = useCallback( - (enabled: boolean): void => { - setValue("hasRetentionPercentage", enabled, { + const updateRetentionPercentage = useCallback( + (newRetentionPercentage: ProformaRetentionPercentageOption): void => { + setValue("retentionPercentage", newRetentionPercentage, { shouldDirty: true, shouldTouch: true, shouldValidate: true, }); + + setValue("hasRetentionPercentage", true, { + shouldDirty: true, + shouldValidate: true, + }); }, [setValue] ); @@ -171,13 +180,13 @@ export const useUpdateProformaTaxController = ({ taxMode, hasTaxPercentage, - defaultTaxPercentage, + taxPercentage, hasRecPercentage, - defaultRecPercentage, + recPercentage, hasRetentionPercentage, - defaultRetentionPercentage, + retentionPercentage, usesSingleTax: taxMode === "single", usesPerLineTax: taxMode === "perLine", @@ -185,8 +194,8 @@ export const useUpdateProformaTaxController = ({ enablePerLineTaxes, disablePerLineTaxes, - updateDefaultTaxPercentage, - updateDefaultRecPercentage, - updateDefaultRetentionPercentage, + updateTaxPercentage, + updateRecPercentage, + updateRetentionPercentage, }; }; diff --git a/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-totals-controller.ts b/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-totals-controller.ts index f5d0de8c..c9e8a5dd 100644 --- a/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-totals-controller.ts +++ b/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-totals-controller.ts @@ -21,7 +21,7 @@ export const useUpdateProformaTotalsController = ({ const items = useWatch({ control, name: "items" }); const hasRetentionPercentage = useWatch({ control, name: "hasRetentionPercentage" }); - const retentionPercentage = useWatch({ control, name: "defaultRetentionPercentage" }); + const retentionPercentage = useWatch({ control, name: "retentionPercentage" }); const totals = useMemo(() => { return calculateProformaTotals({ diff --git a/modules/customer-invoices/src/web/proformas/update/entities/index.ts b/modules/customer-invoices/src/web/proformas/update/entities/index.ts index b890ad17..98aea30f 100644 --- a/modules/customer-invoices/src/web/proformas/update/entities/index.ts +++ b/modules/customer-invoices/src/web/proformas/update/entities/index.ts @@ -5,4 +5,3 @@ export * from "./proforma-item-update-patch.entity"; export * from "./proforma-update-form.entity"; export * from "./proforma-update-form.schema"; export * from "./proforma-update-patch.entity"; -export * from "./proforma-update-totals.entity"; diff --git a/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.entity.ts b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.entity.ts index 4e3db326..16c38e83 100644 --- a/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.entity.ts +++ b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.entity.ts @@ -38,13 +38,13 @@ export interface ProformaUpdateForm { taxRegimeCode: string | null; hasTaxPercentage: boolean; - defaultTaxPercentage: number | null; + taxPercentage: number | null; hasRecPercentage: boolean; - defaultRecPercentage: number | null; + recPercentage: number | null; hasRetentionPercentage: boolean; - defaultRetentionPercentage: number | null; + retentionPercentage: number | null; paymentMethodId: string | null; diff --git a/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.schema.ts b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.schema.ts index 1c5dd5bd..111c475a 100644 --- a/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.schema.ts +++ b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.schema.ts @@ -39,13 +39,13 @@ export const ProformaUpdateFormSchema = z taxRegimeCode: z.string(), hasTaxPercentage: z.boolean(), - defaultTaxPercentage: z.number().nullable(), + taxPercentage: z.number().nullable(), hasRecPercentage: z.boolean(), - defaultRecPercentage: z.number().nullable(), + recPercentage: z.number().nullable(), hasRetentionPercentage: z.boolean(), - defaultRetentionPercentage: z.number().nullable(), + retentionPercentage: z.number().nullable(), paymentMethodId: z.string().nullable(), @@ -55,7 +55,7 @@ export const ProformaUpdateFormSchema = z (formValues) => { if ( formValues.hasRetentionPercentage && - !PercentageHelper.hasPositivePercentage(formValues.defaultRetentionPercentage) + !PercentageHelper.hasPositivePercentage(formValues.retentionPercentage) ) { return false; } 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 5225776a..a1609159 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 @@ -29,13 +29,13 @@ export type ProformaUpdatePatch = { taxRegimeCode?: string | null; hasTaxPercentage?: boolean; - defaultTaxPercentage?: number | null; + taxPercentage?: number | null; hasRecPercentage?: boolean; - defaultRecPercentage?: number | null; + recPercentage?: number | null; hasRetentionPercentage?: boolean; - defaultRetentionPercentage?: number | null; + retentionPercentage?: number | null; paymentMethodId?: string | null; diff --git a/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-totals.entity.ts b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-totals.entity.ts deleted file mode 100644 index 3119a381..00000000 --- a/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-totals.entity.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { ProformaHeaderTotals } from "./proforma-calculation.entity"; - -export interface ProformaTaxBreakdownLine { - taxableBase: number; - ivaPercentage: number; - ivaAmount: number; - recPercentage: number | null; - recAmount: number; -} - -export interface ProformaTotals extends ProformaHeaderTotals { - recPercentage: number | null; - recAmount: number; - - retentionPercentage: number | null; - retentionAmount: number; -} 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 dca33eb1..eead15fb 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 @@ -113,8 +113,8 @@ export const ProformaLineEditor = ({ }, { id: "itemDiscountPercentage", - header: t("form_fields.items.discount_percentage.label", "% Dto"), - headClassName: "w-[100px] text-right", + header: t("form_fields.items.discount_percentage.label", "Dto (%)"), + headClassName: "w-[100px] text-left", cell: ({ index }) => ( ( (value === null || value === "" ? null : Number(value))} diff --git a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-editor-form.tsx b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-editor-form.tsx index da81d27a..4f4fa0e4 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-editor-form.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/editors/proforma-update-editor-form.tsx @@ -15,7 +15,6 @@ import type { } from "../../controllers"; import { ProformaTotalsSummary } from "../blocks"; -import { ProformaTaxesCard } from "./proforma-taxes-card"; import { ProformaUpdateHeaderEditor } from "./proforma-update-header-editor"; import { ProformaUpdateItemsEditor } from "./proforma-update-items-editor"; import { ProformaUpdateTaxEditor } from "./proforma-update-tax-editor"; @@ -95,10 +94,6 @@ export const ProformaUpdateEditorForm = ({ /> -
- -
-