feat(pos): receipt VAT breakdown — per-line rate + subtotals by type

This commit is contained in:
Deploy
2026-08-26 22:58:33 +02:00
parent ff0d470845
commit 3b11662229
6 changed files with 161 additions and 5 deletions

View File

@@ -13,6 +13,25 @@ interface ReceiptModalProps {
showActions?: boolean; // false when used just for printing
}
function vatRateLabel(rate: 'general' | 'reduced' | 'super-reduced'): string {
switch (rate) {
case 'general':
return '21% (general)';
case 'reduced':
return '10% (reducido)';
case 'super-reduced':
return '4% (super-reducido)';
}
}
function taxBaseCents(receipt: PosReceipt): number {
return (
receipt.taxBreakdown.generalBaseCents +
receipt.taxBreakdown.reducedBaseCents +
receipt.taxBreakdown.superReducedBaseCents
);
}
export default function ReceiptModal({
receipt,
initialEmail = '',
@@ -122,6 +141,11 @@ export default function ReceiptModal({
<span className="block text-xs text-gray-500">
{formatPrice(item.unitPriceCents)}/ud{item.freeItem ? ' · libre' : ''}
</span>
{!item.freeItem && item.vatRate && (
<span className="block text-xs text-gray-400">
IVA {vatRateLabel(item.vatRate)}
</span>
)}
{item.discountCents > 0 && (
<span className="block text-xs text-red-600">
Dto. {formatPrice(item.discountCents)}
@@ -146,8 +170,30 @@ export default function ReceiptModal({
<span>{formatPrice(receipt.discountCents)}</span>
</p>
)}
<p className="flex justify-between">
<span>IVA</span>
<p className="flex justify-between border-t border-gray-300 pt-1 font-semibold">
<span>Base imponible</span>
<span>{formatPrice(taxBaseCents(receipt))}</span>
</p>
{receipt.taxBreakdown.generalTaxCents > 0 && (
<p className="flex justify-between text-xs text-gray-600">
<span>· IVA 21% (general)</span>
<span>{formatPrice(receipt.taxBreakdown.generalTaxCents)}</span>
</p>
)}
{receipt.taxBreakdown.reducedTaxCents > 0 && (
<p className="flex justify-between text-xs text-gray-600">
<span>· IVA 10% (reducido)</span>
<span>{formatPrice(receipt.taxBreakdown.reducedTaxCents)}</span>
</p>
)}
{receipt.taxBreakdown.superReducedTaxCents > 0 && (
<p className="flex justify-between text-xs text-gray-600">
<span>· IVA 4% (super-reducido)</span>
<span>{formatPrice(receipt.taxBreakdown.superReducedTaxCents)}</span>
</p>
)}
<p className="flex justify-between font-semibold">
<span>Total IVA</span>
<span>{formatPrice(receipt.taxCents)}</span>
</p>
<p className="flex justify-between border-t border-gray-900 pt-2 text-xl font-bold">

View File

@@ -25,6 +25,17 @@ export interface ReceiptItem {
taxCents: number;
totalCents: number;
freeItem: boolean;
/** Type of VAT applied to this line. null when unknown (free items, legacy). */
vatRate: 'general' | 'reduced' | 'super-reduced' | null;
}
export interface ReceiptTaxBreakdown {
generalBaseCents: number;
generalTaxCents: number;
reducedBaseCents: number;
reducedTaxCents: number;
superReducedBaseCents: number;
superReducedTaxCents: number;
}
export interface ReceiptPayment {
@@ -57,6 +68,7 @@ export interface PosReceipt {
discountCents: number;
taxCents: number;
totalCents: number;
taxBreakdown: ReceiptTaxBreakdown;
payments: ReceiptPayment[];
changeCents: number;
header: string | null;