Gutschrift: Sachwert ohne Betrag = keine Rechnung (kein ZUGFeRD)
Eine Sachwert-Gutschrift darf betragslos sein (Betrag leer/0): dann findet keine Rechnungsstellung statt - der Kunde hat den Gegenstand einfach als Subvention erhalten. - Backend: leeres amount -> 0; Sachwert erlaubt 0, Geld verlangt > 0 (400 sonst). vatRelevant bei 0 erzwungen-false. - PDF: betragsloser Sachwert -> Titel 'Sachwert-Uebergabe', kein Betrags-/USt-Block (Hinweis keine Rechnungsstellung), KEIN ZUGFeRD- Embedding. Mit Betrag -> unveraendert ZUGFeRD. - Frontend: Wert-Feld bei Sachwert optional; USt-Block ausgeblendet ohne Betrag; Liste zeigt 'Sachwert ohne Betrag (keine Rechnung)'. Verifiziert: Sachwert 0 -> kein factur-x.xml; Geld 0 -> 400; Sachwert 200 -> ZUGFeRD. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -142,14 +142,20 @@ function CreditNoteFormModal({
|
||||
onError: (err: Error) => toast.error(err.message || 'Speichern fehlgeschlagen'),
|
||||
});
|
||||
|
||||
const amountLabel = form.vatRelevant
|
||||
? `Betrag (${form.amountBasis === 'NETTO' ? 'netto' : 'brutto'})`
|
||||
: 'Betrag';
|
||||
const amountNum = Number(form.amount) || 0;
|
||||
// Sachwert darf betragslos sein (reine Übergabe → keine Rechnung).
|
||||
const isSachwert = form.type === 'SACHWERT';
|
||||
const nonMonetary = isSachwert && amountNum <= 0;
|
||||
|
||||
const canSave =
|
||||
!!form.amount &&
|
||||
Number(form.amount) >= 0 &&
|
||||
(form.type !== 'SACHWERT' || form.sachwertDescription.trim().length > 0);
|
||||
const amountLabel = isSachwert
|
||||
? 'Wert (optional)'
|
||||
: form.vatRelevant
|
||||
? `Betrag (${form.amountBasis === 'NETTO' ? 'netto' : 'brutto'})`
|
||||
: 'Betrag';
|
||||
|
||||
const canSave = isSachwert
|
||||
? form.sachwertDescription.trim().length > 0 // Betrag optional
|
||||
: amountNum > 0; // Geld: Betrag > 0 Pflicht
|
||||
|
||||
return (
|
||||
<Modal isOpen onClose={onClose} title={editing ? `Gutschrift ${editing.number} bearbeiten` : 'Gutschrift anlegen'} size="lg">
|
||||
@@ -184,18 +190,8 @@ function CreditNoteFormModal({
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* USt-Block */}
|
||||
{/* Betrag + USt-Block */}
|
||||
<div className="rounded-lg border border-gray-200 p-3 space-y-3 bg-gray-50">
|
||||
<label className="flex items-center gap-2 text-sm font-medium">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={form.vatRelevant}
|
||||
onChange={(e) => set('vatRelevant', e.target.checked)}
|
||||
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
USt-relevant (mit Umsatzsteuer ausweisen)
|
||||
</label>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||
<Input
|
||||
label={amountLabel}
|
||||
@@ -205,7 +201,7 @@ function CreditNoteFormModal({
|
||||
onChange={(e) => set('amount', e.target.value)}
|
||||
placeholder="0,00"
|
||||
/>
|
||||
{form.vatRelevant && (
|
||||
{amountNum > 0 && form.vatRelevant && (
|
||||
<>
|
||||
<Select
|
||||
label="Basis"
|
||||
@@ -227,11 +223,28 @@ function CreditNoteFormModal({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-x-6 gap-y-1 text-sm">
|
||||
<span>Netto: <strong>{formatEuro(preview.net)}</strong></span>
|
||||
<span>USt: <strong>{formatEuro(preview.vat)}</strong></span>
|
||||
<span>Brutto: <strong>{formatEuro(preview.gross)}</strong></span>
|
||||
</div>
|
||||
{nonMonetary ? (
|
||||
<p className="text-sm text-gray-600">
|
||||
Ohne Betrag: reine Sachwert-Übergabe – <strong>keine Rechnung, keine USt</strong>. Es wird nur der Empfang des Gegenstands als Subvention dokumentiert.
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<label className="flex items-center gap-2 text-sm font-medium">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={form.vatRelevant}
|
||||
onChange={(e) => set('vatRelevant', e.target.checked)}
|
||||
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
USt-relevant (mit Umsatzsteuer ausweisen)
|
||||
</label>
|
||||
<div className="flex flex-wrap gap-x-6 gap-y-1 text-sm">
|
||||
<span>Netto: <strong>{formatEuro(preview.net)}</strong></span>
|
||||
<span>USt: <strong>{formatEuro(preview.vat)}</strong></span>
|
||||
<span>Brutto: <strong>{formatEuro(preview.gross)}</strong></span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{form.type === 'GELD' && (
|
||||
@@ -439,10 +452,12 @@ export default function CreditNotesSection({ contractId, canEdit }: { contractId
|
||||
<Badge variant={cn.type === 'GELD' ? 'info' : 'default'}>
|
||||
{cn.type === 'GELD' ? 'Geld' : 'Sachwert'}
|
||||
</Badge>
|
||||
{cn.vatRelevant ? (
|
||||
<Badge variant="default">USt {cn.vatRate}%</Badge>
|
||||
) : (
|
||||
<Badge variant="default">ohne USt</Badge>
|
||||
{!(cn.type === 'SACHWERT' && cn.amountGross === 0) && (
|
||||
cn.vatRelevant ? (
|
||||
<Badge variant="default">USt {cn.vatRate}%</Badge>
|
||||
) : (
|
||||
<Badge variant="default">ohne USt</Badge>
|
||||
)
|
||||
)}
|
||||
<Badge variant="default">{cn.customerType === 'FIRMA' ? 'Firma' : 'Privat'}</Badge>
|
||||
{cn.type === 'SACHWERT' && cn.goodsReceived && (
|
||||
@@ -452,9 +467,15 @@ export default function CreditNotesSection({ contractId, canEdit }: { contractId
|
||||
)}
|
||||
</div>
|
||||
<div className="text-sm text-gray-700 mt-1">
|
||||
<strong>{formatEuro(cn.amountGross, cn.currency)}</strong>
|
||||
{cn.vatRelevant && (
|
||||
<span className="text-gray-500"> (netto {formatEuro(cn.amountNet, cn.currency)} + USt {formatEuro(cn.amountVat, cn.currency)})</span>
|
||||
{cn.type === 'SACHWERT' && cn.amountGross === 0 ? (
|
||||
<span className="italic text-gray-600">Sachwert ohne Betrag (keine Rechnung)</span>
|
||||
) : (
|
||||
<>
|
||||
<strong>{formatEuro(cn.amountGross, cn.currency)}</strong>
|
||||
{cn.vatRelevant && (
|
||||
<span className="text-gray-500"> (netto {formatEuro(cn.amountNet, cn.currency)} + USt {formatEuro(cn.amountVat, cn.currency)})</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<span className="text-gray-500"> · {formatDate(cn.creditDate)}</span>
|
||||
{cn.place && <span className="text-gray-500"> · {cn.place}</span>}
|
||||
|
||||
Reference in New Issue
Block a user