Facturas de cliente

This commit is contained in:
David Arranz 2025-10-07 10:17:35 +02:00
parent d4a4ab7a31
commit f35b7b098a
4 changed files with 68 additions and 47 deletions

View File

@ -5,7 +5,8 @@
"duplicate_row": "Duplicate", "duplicate_row": "Duplicate",
"insert_row_above": "Insert row above", "insert_row_above": "Insert row above",
"insert_row_below": "Insert row below", "insert_row_below": "Insert row below",
"remove_row": "Remove" "remove_row": "Remove",
"actions": "Actions"
}, },
"catalog": { "catalog": {
"status": { "status": {
@ -110,7 +111,7 @@
"placeholder": "Description of the invoice", "placeholder": "Description of the invoice",
"description": "General description of the invoice" "description": "General description of the invoice"
}, },
"subtotal_price": { "subtotal_amount": {
"label": "Subtotal", "label": "Subtotal",
"placeholder": "", "placeholder": "",
"desc": "Invoice subtotal" "desc": "Invoice subtotal"
@ -120,12 +121,12 @@
"placeholder": "", "placeholder": "",
"desc": "Percentage discount" "desc": "Percentage discount"
}, },
"discount_price": { "discount_amount": {
"label": "Discount price", "label": "Discount price",
"placeholder": "", "placeholder": "",
"desc": "Percentage discount price" "desc": "Percentage discount price"
}, },
"total_price": { "total_amount": {
"label": "Total price", "label": "Total price",
"placeholder": "", "placeholder": "",
"desc": "Invoice total price" "desc": "Invoice total price"
@ -135,7 +136,7 @@
"placeholder": "Additional notes about the invoice", "placeholder": "Additional notes about the invoice",
"description": "Additional notes that can be included in the invoice" "description": "Additional notes that can be included in the invoice"
}, },
"items": { "item": {
"quantity": { "quantity": {
"label": "Quantity", "label": "Quantity",
"placeholder": "", "placeholder": "",
@ -146,40 +147,40 @@
"placeholder": "", "placeholder": "",
"description": "" "description": ""
}, },
"unit_price": { "unit_amount": {
"label": "Unit price", "label": "Unit price",
"placeholder": "", "placeholder": "",
"description": "Item unit price" "description": "Item unit price"
}, },
"subtotal_price": { "subtotal_amount": {
"label": "Subtotal", "label": "Subtotal",
"placeholder": "", "placeholder": "",
"description": "" "description": ""
}, },
"discount": { "discount_percentage": {
"label": "Dto (%)", "label": "Dto (%)",
"placeholder": "", "placeholder": "",
"description": "Percentage discount" "description": "Percentage discount"
}, },
"discount_price": { "discount_amount": {
"label": "Discount price", "label": "Discount price",
"placeholder": "", "placeholder": "",
"desc": "Percentage discount price" "description": "Percentage discount price"
}, },
"taxes": { "tax_codes": {
"label": "Taxes", "label": "Taxes",
"placeholder": "", "placeholder": "",
"desc": "Taxes" "description": "Taxes"
}, },
"taxes_price": { "taxes_amount": {
"label": "Taxes price", "label": "Taxes price",
"placeholder": "", "placeholder": "",
"desc": "Percentage taxes price" "description": "Percentage taxes price"
}, },
"total_price": { "total_amount": {
"label": "Total price", "label": "Total",
"placeholder": "", "placeholder": "",
"description": "Total price with percentage discount" "description": "Invoice line total"
} }
} }
}, },
@ -189,6 +190,15 @@
"placeholder": "Select taxes", "placeholder": "Select taxes",
"description": "Select the taxes to apply to the invoice items", "description": "Select the taxes to apply to the invoice items",
"invalid_tax_selection": "Invalid tax selection. Please select a valid tax." "invalid_tax_selection": "Invalid tax selection. Please select a valid tax."
},
"hover_card_totals_summary": {
"label": "Breakdown of the amount",
"fields": {
"subtotal_amount": "Subtotal",
"discount_percentage": "Discount",
"taxable_amount": "Taxable base",
"total_amount": "Total"
}
} }
} }
} }

View File

@ -5,7 +5,8 @@
"duplicate_row": "Duplicar", "duplicate_row": "Duplicar",
"insert_row_above": "Insertar fila arriba", "insert_row_above": "Insertar fila arriba",
"insert_row_below": "Insertar fila abajo", "insert_row_below": "Insertar fila abajo",
"remove_row": "Eliminar" "remove_row": "Eliminar",
"actions": "Acciones"
}, },
"catalog": { "catalog": {
"status": { "status": {
@ -102,7 +103,7 @@
"placeholder": "Descripción de la factura", "placeholder": "Descripción de la factura",
"description": "Descripción general de la factura" "description": "Descripción general de la factura"
}, },
"subtotal_price": { "subtotal_amount": {
"label": "Subtotal", "label": "Subtotal",
"placeholder": "", "placeholder": "",
"desc": "Subtotal de la factura" "desc": "Subtotal de la factura"
@ -112,12 +113,12 @@
"placeholder": "", "placeholder": "",
"desc": "Porcentaje de descuento" "desc": "Porcentaje de descuento"
}, },
"discount_price": { "discount_amount": {
"label": "Importe del descuento", "label": "Importe del descuento",
"placeholder": "", "placeholder": "",
"desc": "Importe del descuento porcentual" "desc": "Importe del descuento porcentual"
}, },
"total_price": { "total_amount": {
"label": "Precio total", "label": "Precio total",
"placeholder": "", "placeholder": "",
"desc": "Precio total de la factura" "desc": "Precio total de la factura"
@ -127,7 +128,7 @@
"placeholder": "Notas adicionales sobre la factura", "placeholder": "Notas adicionales sobre la factura",
"description": "Notas adicionales que se pueden incluir en la factura" "description": "Notas adicionales que se pueden incluir en la factura"
}, },
"items": { "item": {
"quantity": { "quantity": {
"label": "Cantidad", "label": "Cantidad",
"placeholder": "", "placeholder": "",
@ -138,37 +139,37 @@
"placeholder": "", "placeholder": "",
"description": "" "description": ""
}, },
"unit_price": { "unit_amount": {
"label": "Precio unitario", "label": "Precio unitario",
"placeholder": "", "placeholder": "",
"description": "Precio unitario del producto" "description": "Precio unitario del producto"
}, },
"subtotal_price": { "subtotal_amount": {
"label": "Subtotal", "label": "Subtotal",
"placeholder": "", "placeholder": "",
"description": "" "description": ""
}, },
"discount": { "discount_percentage": {
"label": "Dto (%)", "label": "Dto (%)",
"placeholder": "", "placeholder": "",
"description": "Porcentaje de descuento" "description": "Porcentaje de descuento"
}, },
"discount_price": { "discount_amount": {
"label": "Importe del descuento", "label": "Importe del descuento",
"placeholder": "", "placeholder": "",
"desc": "Importe del descuento porcentual" "description": "Importe del descuento porcentual"
}, },
"taxes": { "tax_codes": {
"label": "Impuestos", "label": "Impuestos",
"placeholder": "", "placeholder": "",
"desc": "Impuestos" "description": "Impuestos"
}, },
"taxes_price": { "taxes_amount": {
"label": "Importe impuestos", "label": "Importe impuestos",
"placeholder": "", "placeholder": "",
"desc": "Importe porcentual de los impuestos" "description": "Importe porcentual de los impuestos"
}, },
"total_price": { "total_amount": {
"label": "Precio total", "label": "Precio total",
"placeholder": "", "placeholder": "",
"description": "Precio total con descuento porcentual" "description": "Precio total con descuento porcentual"
@ -181,6 +182,15 @@
"placeholder": "Selecciona impuestos", "placeholder": "Selecciona impuestos",
"description": "Selecciona los impuestos a aplicar a los artículos de la factura", "description": "Selecciona los impuestos a aplicar a los artículos de la factura",
"invalid_tax_selection": "Selección de impuestos no válida. Por favor, selecciona un impuesto válido." "invalid_tax_selection": "Selección de impuestos no válida. Por favor, selecciona un impuesto válido."
},
"hover_card_totals_summary": {
"label": "Desglose del importe",
"fields": {
"subtotal_amount": "Subtotal",
"discount_percentage": "Descuento",
"taxable_amount": "Importe base",
"total_amount": "Total"
}
} }
} }
} }

View File

@ -5,6 +5,7 @@ import {
} from "@repo/shadcn-ui/components"; } from "@repo/shadcn-ui/components";
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { useInvoiceItemSummary } from '../../../hooks'; import { useInvoiceItemSummary } from '../../../hooks';
import { useTranslation } from "../../../i18n";
import { CustomerInvoiceItemFormData } from '../../../schemas'; import { CustomerInvoiceItemFormData } from '../../../schemas';
@ -17,22 +18,23 @@ export const HoverCardTotalsSummary = ({
item, item,
children, children,
}: HoverCardTotalsSummaryProps) => { }: HoverCardTotalsSummaryProps) => {
const { format } = useMoney() const { t } = useTranslation();
const summary = useInvoiceItemSummary(item) const { format } = useMoney();
const summary = useInvoiceItemSummary(item);
const SummaryBlock = () => ( const SummaryBlock = () => (
<div className="space-y-2"> <div className="space-y-2">
<h4 className="text-sm font-semibold mb-3">Desglose del importe</h4> <h4 className="text-sm font-semibold mb-3">{t("components.hover_card_totals_summary.label")}</h4>
<div className="flex justify-between text-sm"> <div className="flex justify-between text-sm">
<span className="text-muted-foreground">Subtotal:</span> <span className="text-muted-foreground">{t("components.hover_card_totals_summary.fields.subtotal_amount")}:</span>
<span className="font-mono">{format(summary.subtotal)}</span> <span className="font-mono">{format(summary.subtotal)}</span>
</div> </div>
{Number(item.discount_percentage?.value ?? 0) > 0 && ( {Number(item.discount_percentage?.value ?? 0) > 0 && (
<div className="flex justify-between text-sm"> <div className="flex justify-between text-sm">
<span className="text-muted-foreground"> <span className="text-muted-foreground">
Descuento ({item.discount_percentage.value ?? 0}%): {t("components.hover_card_totals_summary.fields.discount_percentage")} ({item.discount_percentage.value ?? 0}%):
</span> </span>
<span className="font-mono text-destructive"> <span className="font-mono text-destructive">
-{format(summary.discountAmount)} -{format(summary.discountAmount)}
@ -41,7 +43,7 @@ export const HoverCardTotalsSummary = ({
)} )}
<div className="flex justify-between text-sm border-t pt-2"> <div className="flex justify-between text-sm border-t pt-2">
<span className="text-muted-foreground">Base imponible:</span> <span className="text-muted-foreground">{t("components.hover_card_totals_summary.fields.taxable_amount")}:</span>
<span className="font-mono font-medium"> <span className="font-mono font-medium">
{format(summary.baseAmount)} {format(summary.baseAmount)}
</span> </span>
@ -55,7 +57,7 @@ export const HoverCardTotalsSummary = ({
))} ))}
<div className="flex justify-between text-sm border-t pt-2 font-semibold"> <div className="flex justify-between text-sm border-t pt-2 font-semibold">
<span>Total:</span> <span>{t("components.hover_card_totals_summary.fields.total_amount")}:</span>
<span className="font-mono">{format(summary.total)}</span> <span className="font-mono">{format(summary.total)}</span>
</div> </div>
</div> </div>

View File

@ -105,12 +105,13 @@ export const TableView = ({ items, actions }: TableViewProps) => {
<TableHeader className="sticky top-0 z-20 bg-background shadow-sm"> <TableHeader className="sticky top-0 z-20 bg-background shadow-sm">
<TableRow className="bg-muted/30 text-xs text-muted-foreground"> <TableRow className="bg-muted/30 text-xs text-muted-foreground">
<TableHead className="w-10 text-center">#</TableHead> <TableHead className="w-10 text-center">#</TableHead>
<TableHead>Descripción</TableHead> <TableHead>{t("form_fields.item.description.label")}</TableHead>
<TableHead className="text-right w-24">Cantidad</TableHead> <TableHead className="text-right w-24">{t("form_fields.item.quantity.label")}</TableHead>
<TableHead className="text-right w-32">Precio Unit.</TableHead> <TableHead className="text-right w-32">{t("form_fields.item.unit_amount.label")}</TableHead>
<TableHead className="text-right w-24">% Desc.</TableHead> <TableHead className="text-right w-24">{t("form_fields.item.discount_percentage.label")}</TableHead>
<TableHead className="text-right w-32">Total</TableHead> <TableHead className="text-right w-32">{t("form_fields.item.tax_codes.label")}</TableHead>
<TableHead className="w-44 text-center">Acciones</TableHead> <TableHead className="text-right w-32">{t("form_fields.item.total_amount.label")}</TableHead>
<TableHead className="w-44 text-center">{t("common.actions")}</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
@ -212,9 +213,7 @@ export const TableView = ({ items, actions }: TableViewProps) => {
control={control} control={control}
name={`item.${i}.tax_codes`} name={`item.${i}.tax_codes`}
required required
label={t("form_fields.item.tax_codes.label")}
placeholder={t("form_fields.item.tax_codes.placeholder")} placeholder={t("form_fields.item.tax_codes.placeholder")}
description={t("form_fields.item.tax_codes.description")}
/> />
</TableCell> </TableCell>