Facturas de cliente

This commit is contained in:
David Arranz 2025-10-12 21:09:56 +02:00
parent ef8a20d296
commit 27a5e30d37
5 changed files with 33 additions and 18 deletions

View File

@ -39,7 +39,7 @@ export function AmountInputField<T extends FieldValues>({
<FormControl>
<AmountInput
id={inputId}
value={field.value}
value={field.value ?? ""}
onChange={field.onChange}
{...inputProps}
/>

View File

@ -5,8 +5,8 @@ import { InputEmptyMode, InputReadOnlyMode } from './quantity-input';
export type AmountInputProps = {
value: number | "" | string; // "" → no mostrar nada; string puede venir con separadores
onChange: (next: number | "") => void;
value: number | string; // "" → no mostrar nada; string puede venir con separadores
onChange: (next: number | string) => void;
readOnly?: boolean;
readOnlyMode?: InputReadOnlyMode; // default "textlike-input"
id?: string;

View File

@ -28,25 +28,27 @@ export function QuantityInputField<TFormValues extends FieldValues>({
<FormField
control={control}
name={name}
render={({ field }) => (
<FormItem>
render={({ field }) => {
const { value, onChange } = field;
console.log(value);
return <FormItem>
{label ? (
<FormLabel htmlFor={inputId}>
{label} {required ? <span aria-hidden="true">*</span> : null}
{label} {required ? <span aria-hidden='true'>*</span> : null}
</FormLabel>
) : null}
<FormControl>
<QuantityInput
id={inputId}
value={field.value}
onChange={field.onChange}
value={value}
onChange={onChange}
{...inputProps}
/>
</FormControl>
{description ? <FormDescription>{description}</FormDescription> : null}
<FormMessage />
</FormItem>
)}
}}
/>
);
}

View File

@ -110,9 +110,12 @@ export function useInvoiceAutoRecalc(
// Suscripción reactiva a cambios del formulario
React.useEffect(() => {
console.log("recalculo algo?");
if (!isDirty || isLoading || isSubmitting) return;
const subscription = watch(async (formData, { name, type }) => {
const subscription = watch((formData, { name, type }) => {
console.log(name, type);
const items = (formData?.items || []) as InvoiceItemFormData[];
if (items.length === 0) return;
@ -126,7 +129,7 @@ export function useInvoiceAutoRecalc(
setInvoiceTotals(form, invoiceTotals);
// 3) valida una vez (opcional)
await trigger([
trigger([
"subtotal_amount",
"discount_amount",
"taxable_amount",
@ -137,19 +140,25 @@ export function useInvoiceAutoRecalc(
// 2. Cambio puntual de una línea
if (name?.startsWith("items.") && type === "change") {
console.log("2. items!");
const index = Number(name.split(".")[1]);
const field = name.split(".")[2];
if (["quantity", "unit_amount", "discount_percentage", "tax_codes"].includes(field)) {
console.log("2.1. recalculo items!");
const item = items[index] as InvoiceItemFormData;
const prevTotals = itemCache.current.get(index);
const newTotals = calculateItemTotals(item);
console.log(prevTotals, newTotals);
// Si no hay cambios en los totales, no tocamos nada
const itemHasChanges =
prevTotals && JSON.stringify(prevTotals) !== JSON.stringify(newTotals);
prevTotals || JSON.stringify(prevTotals) !== JSON.stringify(newTotals);
if (!itemHasChanges) {
console.log("No hay cambios, me voy!!!");
return;
}
@ -173,7 +182,7 @@ export function useInvoiceAutoRecalc(
setInvoiceTotals(form, invoiceTotals);
// 3) valida una vez (opcional)
await trigger([
trigger([
"items",
"subtotal_amount",
"discount_amount",
@ -188,10 +197,15 @@ export function useInvoiceAutoRecalc(
return () => subscription.unsubscribe();
}, [
watch,
trigger,
setValue,
getValues,
isDirty,
isLoading,
isSubmitting,
itemCache,
setInvoiceItemTotals,
setInvoiceTotals,
calculateItemTotals,
calculateInvoiceTotals,
]);

View File

@ -1,15 +1,14 @@
import { NumericStringSchema } from "@erp/core";
import { z } from "zod/v4";
export const InvoiceItemFormSchema = z.object({
is_non_valued: z.boolean(),
description: z.string().max(2000).optional().default(""),
quantity: NumericStringSchema.optional(),
unit_amount: NumericStringSchema.optional(),
quantity: z.any(), //NumericStringSchema.optional(),
unit_amount: z.any(), //NumericStringSchema.optional(),
subtotal_amount: z.number(),
discount_percentage: NumericStringSchema.optional(),
subtotal_amount: z.any(), //z.number(),
discount_percentage: z.any(), //NumericStringSchema.optional(),
discount_amount: z.number(),
taxable_amount: z.number(),