feat(pos): receipt VAT breakdown — per-line rate + subtotals by type
This commit is contained in:
@@ -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">
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user