v0.6.6
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
parent
9961383a9f
commit
4f331ceb14
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/auth",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/core",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/customer-invoices",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -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 }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
|
||||
<OrdersTable />
|
||||
{/* Explicación técnica */}
|
||||
<div className="mt-8 rounded border border-border bg-card p-3 sm:p-4 text-xs sm:text-sm text-muted-foreground space-y-2">
|
||||
<p className="font-semibold text-foreground">Estado de proforma</p>
|
||||
<ul className="list-disc list-inside space-y-1">
|
||||
<li>
|
||||
<ProformaStatusBadge status="draft" />
|
||||
<strong className="text-foreground">Borrador:</strong> Un{" "}
|
||||
<code className="rounded bg-muted px-1 text-xs">div</code> absoluto con{" "}
|
||||
<code className="rounded bg-muted px-1 text-xs">pointer-events: none</code> y{" "}
|
||||
<code className="rounded bg-muted px-1 text-xs">linear-gradient</code> se superpone
|
||||
encima del scroll.
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-foreground">Columna sticky:</strong> La celda de acciones usa{" "}
|
||||
<code className="rounded bg-muted px-1 text-xs">sticky right-0 z-20</code>.
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-foreground">Responsive:</strong> Columnas ocultas en móviles (
|
||||
<code className="rounded bg-muted px-1 text-xs">hidden sm:</code>,{" "}
|
||||
<code className="rounded bg-muted px-1 text-xs">hidden md:</code>,{" "}
|
||||
<code className="rounded bg-muted px-1 text-xs">hidden lg:</code>).
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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<TValue extends string = string> {
|
||||
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<ProformaTaxPercentageOption, ProformaTaxDefinition> =
|
||||
{
|
||||
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%" },
|
||||
];
|
||||
@ -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%" },
|
||||
];
|
||||
@ -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<TaxPercentageOption, ProformaTaxesDefinition> = {
|
||||
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%" },
|
||||
];
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
export interface ProformaTaxSummary {
|
||||
taxableAmount: number;
|
||||
|
||||
ivaCode: string;
|
||||
ivaPercentage: number;
|
||||
ivaCode: string | null;
|
||||
ivaPercentage: number | null;
|
||||
ivaAmount: number;
|
||||
|
||||
recCode: string | null;
|
||||
|
||||
@ -33,7 +33,7 @@ export interface Proforma {
|
||||
|
||||
taxes: ProformaTaxSummary[];
|
||||
|
||||
paymentMethod?: string;
|
||||
paymentMethodId: string | null;
|
||||
|
||||
subtotalAmount: number;
|
||||
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
import { PROFORMA_TAX_DEFINITIONS, type TaxPercentageOption } from "../constants";
|
||||
|
||||
export const getProformaRecPercentage = (taxPercentage: TaxPercentageOption): number => {
|
||||
return PROFORMA_TAX_DEFINITIONS[taxPercentage].recPercentage;
|
||||
};
|
||||
@ -1,3 +0,0 @@
|
||||
import { PROFORMA_RETENTION_OPTIONS } from "../constants";
|
||||
|
||||
export const getProformaRetentionOptions = () => PROFORMA_RETENTION_OPTIONS;
|
||||
@ -1,3 +0,0 @@
|
||||
import { PROFORMA_TAX_OPTIONS } from "../constants";
|
||||
|
||||
export const getProformaTaxOptions = () => PROFORMA_TAX_OPTIONS;
|
||||
@ -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";
|
||||
|
||||
@ -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;
|
||||
@ -23,7 +23,7 @@ export const mapProformaItemsToProformaItemsUpdateForm = (
|
||||
|
||||
itemDiscountPercentage: item.itemDiscountPercentage ?? null,
|
||||
|
||||
taxPercentage: item.ivaPercentage ?? null,
|
||||
taxPercentage: item.taxPercentage ?? null,
|
||||
recPercentage: item.recPercentage ?? null,
|
||||
};
|
||||
};
|
||||
|
||||
@ -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 | null | undefined>): 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,
|
||||
};
|
||||
};
|
||||
|
||||
@ -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<ProformaUpdateForm>) => {
|
||||
console.log(errors);
|
||||
console.error(errors);
|
||||
focusFirstInputFormError(form);
|
||||
|
||||
showWarningToast(
|
||||
|
||||
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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 }) => (
|
||||
<PercentageField
|
||||
inputClassName="border-none"
|
||||
@ -129,8 +129,8 @@ export const ProformaLineEditor = ({
|
||||
? [
|
||||
{
|
||||
id: "taxPercentage",
|
||||
header: t("form_fields.items.tax_percentage.label", "IVA"),
|
||||
headClassName: "w-[110px] text-right",
|
||||
header: t("form_fields.items.tax_percentage.label", "IVA (%)"),
|
||||
headClassName: "w-[110px] text-left",
|
||||
cell: ({ index }) => (
|
||||
<SelectField
|
||||
deserialize={(value) => (value === null || value === "" ? null : Number(value))}
|
||||
|
||||
@ -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 = ({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1">
|
||||
<ProformaTaxesCard />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col-reverse gap-3 border-t pt-4 sm:flex-row sm:justify-end">
|
||||
<Button disabled={isSubmitting} onClick={onReset} type="button" variant="outline">
|
||||
{t("common.reset", "Restablecer")}
|
||||
|
||||
@ -23,7 +23,12 @@ import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||
import { ReceiptTextIcon } from "lucide-react";
|
||||
|
||||
import { useTranslation } from "../../../../i18n";
|
||||
import { getProformaRetentionOptions, getProformaTaxOptions } from "../../../shared";
|
||||
import {
|
||||
getProformaRetentionOptions,
|
||||
getProformaTaxOptions,
|
||||
parseProformaRetentionPercentage,
|
||||
parseProformaTaxPercentage,
|
||||
} from "../../../shared";
|
||||
import type { UseUpdateProformaTaxControllerResult } from "../../controllers";
|
||||
|
||||
interface ProformaUpdateTaxEditorProps {
|
||||
@ -167,7 +172,13 @@ export const ProformaUpdateTaxEditor = ({
|
||||
inputClassName="bg-background"
|
||||
items={getProformaTaxOptions()}
|
||||
label={t("form_fields.proformas.default_tax_percentage.label", "IVA por defecto")}
|
||||
name="defaultTaxPercentage"
|
||||
name="taxPercentage"
|
||||
onChange={(value) => {
|
||||
const parsed = parseProformaTaxPercentage(value as number | null);
|
||||
if (parsed !== null) {
|
||||
taxCtrl.updateTaxPercentage(parsed);
|
||||
}
|
||||
}}
|
||||
placeholder={t(
|
||||
"form_fields.proformas.default_tax_percentage.placeholder",
|
||||
"Selecciona IVA"
|
||||
@ -198,13 +209,13 @@ export const ProformaUpdateTaxEditor = ({
|
||||
|
||||
{taxCtrl.hasRecPercentage ? (
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{PercentageHelper.formatPercent(taxCtrl.defaultRecPercentage ?? 0)}
|
||||
{PercentageHelper.formatPercent(taxCtrl.recPercentage ?? 0)}
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
}
|
||||
name="hasRecPercentage"
|
||||
onCheckedChange={taxCtrl.updateDefaultRecPercentage}
|
||||
onCheckedChange={taxCtrl.updateRecPercentage}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
|
||||
@ -223,7 +234,13 @@ export const ProformaUpdateTaxEditor = ({
|
||||
inputClassName="bg-background"
|
||||
items={getProformaRetentionOptions()}
|
||||
label={t("form_fields.proformas.retention_percentage.label", "Retención")}
|
||||
name="defaultRetentionPercentage"
|
||||
name="retentionPercentage"
|
||||
onChange={(value) => {
|
||||
const parsed = parseProformaRetentionPercentage(value as number | null);
|
||||
if (parsed !== null) {
|
||||
taxCtrl.updateRetentionPercentage(parsed);
|
||||
}
|
||||
}}
|
||||
placeholder={t(
|
||||
"form_fields.proformas.default_tax_percentage.placeholder",
|
||||
"Selecciona IVA"
|
||||
|
||||
@ -22,13 +22,13 @@ export const buildProformaUpdateDefault = (): ProformaUpdateForm => {
|
||||
taxRegimeCode: "01",
|
||||
|
||||
hasTaxPercentage: true,
|
||||
defaultTaxPercentage: 21,
|
||||
taxPercentage: 21,
|
||||
|
||||
hasRecPercentage: false,
|
||||
defaultRecPercentage: null,
|
||||
recPercentage: null,
|
||||
|
||||
hasRetentionPercentage: false,
|
||||
defaultRetentionPercentage: 15,
|
||||
retentionPercentage: 15,
|
||||
|
||||
paymentMethodId: null,
|
||||
|
||||
|
||||
@ -2,13 +2,17 @@ import { MoneyDTOHelper, PercentageDTOHelper, QuantityDTOHelper } from "@erp/cor
|
||||
import { toNullable } from "@repo/rdx-ddd";
|
||||
import { ObjectHelper } from "@repo/rdx-utils";
|
||||
|
||||
import { getProformaRecCode, getProformaRetentionCode, getProformaTaxCode } from "../../shared";
|
||||
import type { UpdateProformaByIdParams } from "../../shared/api";
|
||||
import type {
|
||||
ProformaItemUpdateForm,
|
||||
ProformaItemUpdatePatch,
|
||||
ProformaUpdateForm,
|
||||
ProformaUpdatePatch,
|
||||
} from "../entities";
|
||||
import type { ProformaItemUpdateForm, ProformaUpdateForm, ProformaUpdatePatch } from "../entities";
|
||||
|
||||
interface ProformaItemUpdateDTOContext {
|
||||
currencyCode: string;
|
||||
taxMode: ProformaUpdateForm["taxMode"];
|
||||
ivaCode: string | null;
|
||||
recCode: string | null;
|
||||
retentionCode: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convierte el patch del formulario de actualización de proforma
|
||||
@ -85,9 +89,8 @@ export const buildUpdateProformaByIdParams = (
|
||||
|
||||
// Si se han tocado los detalles, se envían todos
|
||||
if (shouldReplaceItems(patch)) {
|
||||
data.items = formData.items.map((item, index) =>
|
||||
toProformaItemUpdateDTO(item, index, formData)
|
||||
);
|
||||
const context = buildProformaItemUpdateDTOContext(formData);
|
||||
data.items = formData.items.map((item, index) => toProformaItemUpdateDTO(item, index, context));
|
||||
}
|
||||
|
||||
return {
|
||||
@ -97,41 +100,40 @@ export const buildUpdateProformaByIdParams = (
|
||||
};
|
||||
|
||||
const toProformaItemUpdateDTO = (
|
||||
item: ProformaItemUpdatePatch,
|
||||
item: ProformaItemUpdateForm,
|
||||
index: number,
|
||||
formData: ProformaUpdateForm
|
||||
context: ProformaItemUpdateDTOContext
|
||||
): NonNullable<UpdateProformaByIdParams["data"]["items"]>[number] => {
|
||||
const currencyCode = formData.currencyCode;
|
||||
|
||||
const quantity =
|
||||
item.quantity === null ? null : QuantityDTOHelper.fromNumberNulleable(item.quantity, 2);
|
||||
|
||||
const unit_amount =
|
||||
item.unitAmount === null
|
||||
? null
|
||||
: MoneyDTOHelper.fromNumberNulleable(item.unitAmount, currencyCode, 4);
|
||||
|
||||
const ivaPercentage = resolveLineIvaPercentage(item, formData);
|
||||
const recPercentage = resolveLineRecPercentage(item, formData);
|
||||
const retentionPercentage = resolveLineRetentionPercentage(formData);
|
||||
|
||||
return {
|
||||
position: index,
|
||||
|
||||
description: item.description,
|
||||
|
||||
quantity,
|
||||
unit_amount,
|
||||
quantity: toNullable(item.quantity, (value) => QuantityDTOHelper.fromNumberNulleable(value, 2)),
|
||||
|
||||
unit_amount: toNullable(item.unitAmount, (value) =>
|
||||
MoneyDTOHelper.fromNumberNulleable(value, context.currencyCode, 4)
|
||||
),
|
||||
|
||||
item_discount_percentage: toNullable(item.itemDiscountPercentage, (value) =>
|
||||
PercentageDTOHelper.fromNumber(value, 2)
|
||||
),
|
||||
|
||||
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)
|
||||
),
|
||||
iva_code: resolveItemIvaCode(item, context),
|
||||
rec_code: resolveItemRecCode(item, context),
|
||||
retention_code: context.retentionCode,
|
||||
};
|
||||
};
|
||||
|
||||
const buildProformaItemUpdateDTOContext = (
|
||||
formData: ProformaUpdateForm
|
||||
): ProformaItemUpdateDTOContext => {
|
||||
return {
|
||||
currencyCode: formData.currencyCode,
|
||||
taxMode: formData.taxMode,
|
||||
ivaCode: formData.taxMode === "single" ? resolveSingleIvaCode(formData) : null,
|
||||
recCode: formData.taxMode === "single" ? resolveSingleRecCode(formData) : null,
|
||||
retentionCode: resolveRetentionCode(formData),
|
||||
};
|
||||
};
|
||||
|
||||
@ -140,38 +142,56 @@ const shouldReplaceItems = (patch: ProformaUpdatePatch): boolean => {
|
||||
ObjectHelper.hasOwn(patch, "items") ||
|
||||
ObjectHelper.hasOwn(patch, "taxMode") ||
|
||||
ObjectHelper.hasOwn(patch, "hasTaxPercentage") ||
|
||||
ObjectHelper.hasOwn(patch, "defaultTaxPercentage") ||
|
||||
ObjectHelper.hasOwn(patch, "taxPercentage") ||
|
||||
ObjectHelper.hasOwn(patch, "hasRecPercentage") ||
|
||||
ObjectHelper.hasOwn(patch, "defaultRecPercentage")
|
||||
ObjectHelper.hasOwn(patch, "recPercentage") ||
|
||||
ObjectHelper.hasOwn(patch, "hasRetentionPercentage") ||
|
||||
ObjectHelper.hasOwn(patch, "retentionPercentage")
|
||||
);
|
||||
};
|
||||
|
||||
const resolveLineIvaPercentage = (
|
||||
item: ProformaItemUpdateForm,
|
||||
formData: ProformaUpdateForm
|
||||
): number | null => {
|
||||
if (formData.taxMode === "single") {
|
||||
return formData.hasTaxPercentage ? formData.defaultTaxPercentage : null;
|
||||
const resolveSingleIvaCode = (formData: ProformaUpdateForm): string | null => {
|
||||
if (!formData.hasTaxPercentage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return item.taxPercentage;
|
||||
return getProformaTaxCode(formData.taxPercentage);
|
||||
};
|
||||
|
||||
const resolveLineRecPercentage = (
|
||||
item: ProformaItemUpdateForm,
|
||||
formData: ProformaUpdateForm
|
||||
): number | null => {
|
||||
if (formData.taxMode === "single") {
|
||||
return formData.hasRecPercentage ? formData.defaultRecPercentage : null;
|
||||
const resolveSingleRecCode = (formData: ProformaUpdateForm): string | null => {
|
||||
if (!formData.hasRecPercentage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return item.recPercentage;
|
||||
return getProformaRecCode(formData.recPercentage);
|
||||
};
|
||||
|
||||
const resolveLineRetentionPercentage = (formData: ProformaUpdateForm): number | null => {
|
||||
return formData.hasRetentionPercentage ? formData.defaultRetentionPercentage : null;
|
||||
const resolveRetentionCode = (formData: ProformaUpdateForm): string | null => {
|
||||
if (!formData.hasRetentionPercentage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getProformaRetentionCode(formData.retentionPercentage);
|
||||
};
|
||||
|
||||
const toPercentageDTOOrNull = (value: number | null) => {
|
||||
return value === null ? null : PercentageDTOHelper.fromNumber(value, 2);
|
||||
const resolveItemIvaCode = (
|
||||
item: ProformaItemUpdateForm,
|
||||
context: ProformaItemUpdateDTOContext
|
||||
): string | null => {
|
||||
if (context.taxMode === "single") {
|
||||
return context.ivaCode;
|
||||
}
|
||||
|
||||
return getProformaTaxCode(item.taxPercentage);
|
||||
};
|
||||
|
||||
const resolveItemRecCode = (
|
||||
item: ProformaItemUpdateForm,
|
||||
context: ProformaItemUpdateDTOContext
|
||||
): string | null => {
|
||||
if (context.taxMode === "single") {
|
||||
return context.recCode;
|
||||
}
|
||||
|
||||
return getProformaRecCode(item.recPercentage);
|
||||
};
|
||||
|
||||
@ -5,21 +5,15 @@ import { calculateProformaTotalsFromLines } from "./calculations";
|
||||
|
||||
export interface CalculateProformaTotalsParams {
|
||||
globalDiscountPercentage: ProformaUpdateForm["globalDiscountPercentage"];
|
||||
|
||||
items: ProformaItemUpdateForm[];
|
||||
|
||||
hasRetentionPercentage: boolean;
|
||||
|
||||
retentionPercentage: number | null;
|
||||
}
|
||||
|
||||
export const calculateProformaTotals = ({
|
||||
globalDiscountPercentage,
|
||||
|
||||
items,
|
||||
|
||||
hasRetentionPercentage,
|
||||
|
||||
retentionPercentage,
|
||||
}: CalculateProformaTotalsParams): ProformaTotals => {
|
||||
const lines = mapProformaItemFormToProformaLineInputs(items, {
|
||||
@ -28,9 +22,7 @@ export const calculateProformaTotals = ({
|
||||
|
||||
return calculateProformaTotalsFromLines({
|
||||
lines,
|
||||
|
||||
globalDiscountPercentage,
|
||||
|
||||
retentionPercentage: hasRetentionPercentage ? retentionPercentage : null,
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@erp/customers",
|
||||
"description": "Customers",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/factuges",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@erp/supplier-invoices",
|
||||
"description": "Supplier invoices",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@erp/suppliers",
|
||||
"description": "Suppliers",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "uecko-erp-2025",
|
||||
"private": true,
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
"modules/*",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-criteria",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-ddd",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-logger",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-ui",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -36,6 +36,7 @@ type SelectFieldProps<TFormValues extends FieldValues> = {
|
||||
|
||||
orientation?: "vertical" | "horizontal" | "responsive";
|
||||
|
||||
onChange?: (value: unknown) => void;
|
||||
serialize?: (value: unknown) => string;
|
||||
deserialize?: (value: SelectFieldValue) => unknown;
|
||||
|
||||
@ -59,6 +60,7 @@ export function SelectField<TFormValues extends FieldValues>({
|
||||
|
||||
orientation = "vertical",
|
||||
|
||||
onChange,
|
||||
serialize,
|
||||
deserialize,
|
||||
|
||||
@ -102,7 +104,11 @@ export function SelectField<TFormValues extends FieldValues>({
|
||||
|
||||
<Select
|
||||
disabled={isDisabled}
|
||||
onValueChange={(value) => field.onChange(deserializeValue(value))}
|
||||
onValueChange={(value) =>
|
||||
onChange &&
|
||||
onChange(deserializeValue(value)) &&
|
||||
field.onChange(deserializeValue(value))
|
||||
}
|
||||
value={fieldValue}
|
||||
>
|
||||
<SelectTrigger
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-utils",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user