feat(rechnungen): Rabatt je Position, Reverse Charge, Ablauf-Regel (#037)
Eine echte Proxmox-Rechnung zeigt je Subscription-Key eine Zeile mit Listenpreis und eine mit "Client Discount". Unser Modell hing den Rabatt an die Rechnung - bei einer Sammelrechnung ueber zwei Hosts liesse er sich damit keinem Host zuordnen, und die Marge je Host waere falsch. Schlimmer: Er wurde nirgends abgezogen. Jetzt haengt der Rabatt an der Position, der Rechnungsbetrag ist die Summe nach Abzug, und Marge wie Summen rechnen mit dem tatsaechlich gezahlten Betrag. Die Migration verteilt vorhandene Rabatte anteilig auf die Positionen und rechnet die Summen neu - nichts geht verloren. Dazu ein eigener Steuersatz fuer den Einkauf mit Vorgabe 0: Proxmox rechnet gegenueber Unternehmen mit USt-IdNr. ueber Reverse Charge ab. Neue zwoelfte Warnregel aus Florians Ablauf: Der Kunde hat bezahlt, die Proxmox-Rechnung ist noch offen - der Moment, an dem man selbst zahlt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
-- Rabatt gehört an die Position, nicht an die Rechnung (#037).
|
||||
--
|
||||
-- Proxmox weist den Rabatt je Subscription-Key als eigene Zeile aus. Bei einer Sammelrechnung
|
||||
-- über mehrere Hosts ließe sich ein Rabatt an der Rechnung keinem Host zuordnen – und die Marge
|
||||
-- je Host wäre falsch.
|
||||
|
||||
ALTER TABLE "ProxmoxRechnungPosition" ADD COLUMN "rabatt" DECIMAL(12,2);
|
||||
|
||||
-- Vorhandene Rabatte von der Rechnung auf ihre Positionen verteilen, anteilig zum Nettobetrag.
|
||||
-- Bei nur einer Position entspricht das genau dem bisherigen Wert.
|
||||
UPDATE "ProxmoxRechnungPosition" AS p
|
||||
SET "rabatt" = ROUND(r."rabatt" * p."nettoBetrag" / NULLIF(s."summe", 0), 2)
|
||||
FROM "ProxmoxRechnung" AS r,
|
||||
(SELECT "rechnungId", SUM("nettoBetrag") AS "summe"
|
||||
FROM "ProxmoxRechnungPosition"
|
||||
GROUP BY "rechnungId") AS s
|
||||
WHERE p."rechnungId" = r."id"
|
||||
AND s."rechnungId" = r."id"
|
||||
AND r."rabatt" IS NOT NULL;
|
||||
|
||||
-- Der Rechnungsbetrag ist ab jetzt die Summe der Positionen nach Abzug ihrer Rabatte.
|
||||
UPDATE "ProxmoxRechnung" AS r
|
||||
SET "nettoBetrag" = COALESCE((
|
||||
SELECT SUM(p."nettoBetrag" - COALESCE(p."rabatt", 0))
|
||||
FROM "ProxmoxRechnungPosition" AS p
|
||||
WHERE p."rechnungId" = r."id"
|
||||
), r."nettoBetrag");
|
||||
|
||||
ALTER TABLE "ProxmoxRechnung" DROP COLUMN "rabatt";
|
||||
|
||||
-- Einkauf bei Proxmox läuft über Reverse Charge; 19 % war als Vorgabe irreführend.
|
||||
ALTER TABLE "ProxmoxRechnung" ALTER COLUMN "ustSatz" SET DEFAULT 0.00;
|
||||
@@ -261,9 +261,10 @@ model ProxmoxRechnung {
|
||||
nummer String @unique
|
||||
datum DateTime @db.Date
|
||||
faelligAm DateTime? @db.Date
|
||||
/// Summe der Positionen nach Abzug ihrer Rabatte – wird beim Speichern nachgeführt.
|
||||
nettoBetrag Decimal @db.Decimal(12, 2)
|
||||
ustSatz Decimal @default(19.00) @db.Decimal(5, 2)
|
||||
rabatt Decimal? @db.Decimal(12, 2)
|
||||
/// Bei Proxmox in aller Regel 0: Reverse Charge nach Art. 196 (#037).
|
||||
ustSatz Decimal @default(0.00) @db.Decimal(5, 2)
|
||||
zahlungsart String?
|
||||
bezahlt Boolean @default(false)
|
||||
bezahltAm DateTime? @db.Date
|
||||
@@ -284,7 +285,10 @@ model ProxmoxRechnungPosition {
|
||||
rechnungId String
|
||||
bezeichnung String
|
||||
menge Int @default(1)
|
||||
/// Listenpreis dieser Position, wie er auf der Rechnung steht.
|
||||
nettoBetrag Decimal @db.Decimal(12, 2)
|
||||
/// Rabatt dieser Position. Proxmox weist ihn je Key als eigene Zeile aus (#037).
|
||||
rabatt Decimal? @db.Decimal(12, 2)
|
||||
|
||||
rechnung ProxmoxRechnung @relation(fields: [rechnungId], references: [id], onDelete: Cascade)
|
||||
laufzeit Laufzeit?
|
||||
|
||||
+2
-1
@@ -166,7 +166,6 @@ async function seedDemo() {
|
||||
faelligAm: datum(vorlage.faelligAm),
|
||||
nettoBetrag: zugehoerig.reduce((summe, l) => summe + (l.einkauf?.nettoBetrag ?? 0), 0),
|
||||
ustSatz: vorlage.ustSatz,
|
||||
rabatt: vorlage.rabatt ?? null,
|
||||
zahlungsart: vorlage.zahlungsart ?? null,
|
||||
bezahlt: vorlage.bezahlt,
|
||||
bezahltAm: datum(vorlage.bezahltAm),
|
||||
@@ -175,6 +174,8 @@ async function seedDemo() {
|
||||
id: `prp-${l.id}`,
|
||||
bezeichnung: `${l.key} · ${l.start} bis ${l.ende}`,
|
||||
nettoBetrag: l.einkauf?.nettoBetrag ?? 0,
|
||||
// Der Rabatt hängt seit #037 an der Position – wie auf der echten Proxmox-Rechnung.
|
||||
rabatt: vorlage.rabatt ?? null,
|
||||
})),
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user