USt-Behandlung: Netto-Preise, VatResolver und Steuer-Ausweis im MAN-Kreis
Einwand/Entscheidung 12.06.2026: Legacy fakturierte brutto (Steuer inkludiert, z. B. 199 Euro; steuerbefreite Kunden mit Netto-Ausweis 167,23). Alle neuen Preise sind netto; die Steuer wird zur Rechnungsstellung sauber validiert und ausgewiesen. - VatResolver + VatTreatment: DE grundsaetzlich immer mit Steuer, EU nur mit (formal plausibler) USt-ID befreit (Reverse Charge inkl. Pflichthinweis), Drittlaender grundsaetzlich befreit; EU-Laenderliste + vat_rate in config/billing.php - Schema: billing_addresses.vat_id + invoice_billing_addresses.vat_id (Snapshot pro Rechnung), invoices.tax_note; Profil-Formular schreibt die vorhandene USt-ID jetzt auch an die Rechnungsadresse - ManualInvoiceService: rechnet auf Netto-Vertragsbasis (legacy_conditions.net_cents bzw. Netto-Katalogpreis) und bestimmt Steuer/is_netto/tax_note pro Rechnung ueber den VatResolver - legacy:grandfather-subscriptions: leitet net_cents aus der letzten Legacy-Rechnung ab (brutto / 1,19 bzw. is_netto-Betrag direkt); fuer DE-Bestandskunden bleibt der Bruttobetrag unveraendert (199 brutto -> 167,23 netto + 31,77 USt = 199,00) - Doku: Decision-Update 2.1 (Netto-Klarstellung), Phase-9-Plan, Checkliste, 05-DATABASE-MERGE 5.6; offen: VIES-Validierung der USt-ID Tests: VatResolverTest (Datasets fuer alle Faelle), Reverse-Charge/ EU-/Drittland-Rechnungen, Netto-Ableitung; Suite 490 passed, 4 skipped. Pint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
1cd4d8e33a
commit
894a9436b0
19 changed files with 497 additions and 46 deletions
|
|
@ -214,16 +214,35 @@ class GrandfatherLegacySubscriptions extends Command
|
|||
'article_number' => $option['article_number'] ?? null,
|
||||
'name' => $payload['payment_option_translation']['name'] ?? null,
|
||||
'interval' => 'yearly',
|
||||
'amount_cents' => $invoice->amount_cents,
|
||||
'tax_cents' => $invoice->tax_cents,
|
||||
'total_cents' => $invoice->total_cents,
|
||||
'is_netto' => (bool) ($snapshot['is_netto'] ?? false),
|
||||
'net_cents' => $this->deriveNetCents($invoice),
|
||||
'last_total_cents' => $invoice->total_cents,
|
||||
'last_is_netto' => (bool) ($snapshot['is_netto'] ?? false),
|
||||
'source_invoice_number' => $invoice->number,
|
||||
'source_invoice_date' => $invoice->invoice_date->toDateString(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Netto-Vertragsbasis aus der letzten Legacy-Rechnung. Legacy fakturierte
|
||||
* brutto (Steuer inkludiert, z. B. 199,00 €); steuerbefreite Kunden
|
||||
* erhielten den Netto-Ausweis (`is_netto`, z. B. 167,23 €). Die neue
|
||||
* Rechnungsstellung arbeitet immer auf Netto-Basis — die Steuer wird
|
||||
* pro Rechnung über den VatResolver bestimmt.
|
||||
*/
|
||||
private function deriveNetCents(LegacyInvoice $invoice): int
|
||||
{
|
||||
$isNetto = (bool) (($invoice->raw_snapshot ?? [])['is_netto'] ?? false);
|
||||
|
||||
if ($isNetto) {
|
||||
return $invoice->total_cents;
|
||||
}
|
||||
|
||||
$vatRate = (float) config('billing.vat_rate', 0.19);
|
||||
|
||||
return (int) round($invoice->total_cents / (1 + $vatRate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Versteckter Katalog-Eintrag pro (Portal, Legacy-Artikel) — die
|
||||
* verbindlichen Beträge pro Vereinbarung liegen in legacy_conditions.
|
||||
|
|
@ -237,7 +256,8 @@ class GrandfatherLegacySubscriptions extends Command
|
|||
['article_number' => $articleNumber],
|
||||
[
|
||||
'type' => 'recurring',
|
||||
'price_cents' => $candidate['legacy_conditions']['amount_cents'],
|
||||
// Katalogpreise sind netto (Entscheidung 12.06.2026).
|
||||
'price_cents' => $candidate['legacy_conditions']['net_cents'],
|
||||
'currency' => 'EUR',
|
||||
'interval' => 'yearly',
|
||||
'is_hidden' => true,
|
||||
|
|
@ -261,12 +281,12 @@ class GrandfatherLegacySubscriptions extends Command
|
|||
private function describe(array $candidate): string
|
||||
{
|
||||
return sprintf(
|
||||
'User #%d · %s · Legacy-UPO #%d · fällig %s · %s €',
|
||||
'User #%d · %s · Legacy-UPO #%d · fällig %s · netto %s €',
|
||||
$candidate['user_id'],
|
||||
$candidate['legacy_portal'],
|
||||
$candidate['legacy_upo_id'],
|
||||
$candidate['next_due_date']->toDateString(),
|
||||
number_format($candidate['legacy_conditions']['total_cents'] / 100, 2, ',', '.'),
|
||||
number_format($candidate['legacy_conditions']['net_cents'] / 100, 2, ',', '.'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue