verteileRabatt function
List<KasseneckItem>
verteileRabatt(
- List<
KasseneckItem> positionen, - int rabattCents, {
- String name = 'Rabatt',
Rabatt als negative Position(en) — eine je Steuersatz, anteilig zum Bruttoumsatz dieses Satzes.
Gerundet nach dem größten Rest (Hare-Niemeyer): die Cent, die beim Abrunden übrig bleiben, gehen der Reihe nach an die Gruppen mit dem größten Bruchteil; keine Zeile ist je größer als der Umsatz ihres Satzes, und die Summe der Zeilen ist immer genau der Rabatt.
Rabatt- und Stornozeilen (negativ) zählen nicht als Umsatz: ein zweiter Rabatt rechnet nur auf die Ware.
Implementation
List<KasseneckItem> verteileRabatt(List<KasseneckItem> positionen, int rabattCents, {String name = 'Rabatt'}) {
if (rabattCents < 0) {
throw ArgumentError.value(rabattCents, 'rabattCents', 'Rabatt muss eine ganze Zahl in Cent >= 0 sein');
}
if (rabattCents == 0) return const [];
final gruppen = <VatRate, int>{};
for (final p in positionen) {
final betrag = p.totalCents;
if (betrag <= 0) continue;
gruppen[p.vat] = (gruppen[p.vat] ?? 0) + betrag;
}
final saetze = gruppen.keys.toList();
final gesamt = gruppen.values.fold(0, (s, u) => s + u);
if (rabattCents > gesamt) {
throw ArgumentError.value(rabattCents, 'rabattCents', 'Rabatt uebersteigt den Umsatz');
}
final exakt = [for (final s in saetze) rabattCents * gruppen[s]! / gesamt];
final anteile = [for (final x in exakt) x.floor()];
var rest = rabattCents - anteile.fold(0, (s, a) => s + a);
// Restcent: größter Bruchteil zuerst, bei Gleichstand größerer Umsatz.
final reihenfolge = List<int>.generate(saetze.length, (i) => i)
..sort((a, b) {
final bruch = (exakt[b] - anteile[b]).compareTo(exakt[a] - anteile[a]);
return bruch != 0 ? bruch : gruppen[saetze[b]]!.compareTo(gruppen[saetze[a]]!);
});
for (var runde = 0; rest > 0 && runde <= saetze.length; runde++) {
for (final i in reihenfolge) {
if (rest == 0) break;
if (anteile[i] < gruppen[saetze[i]]!) {
anteile[i] += 1;
rest -= 1;
}
}
}
final zeilen = <KasseneckItem>[];
for (var i = 0; i < saetze.length; i++) {
if (anteile[i] <= 0) continue;
// kind 'discount': der Bon fasst die Zeilen zu einer Summenzeile mit
// Zwischensumme zusammen, der Bericht fuehrt sie als "Rabatte"
// (Zwilling von verteileRabatt im JS-Paket seit 0.6.42).
zeilen.add(KasseneckItem(name: name, quantity: 1, priceCents: -anteile[i], vat: saetze[i], kind: 'discount'));
}
return zeilen;
}