72 lines
2.4 KiB
Dart
72 lines
2.4 KiB
Dart
import '../models/bauteil_model.dart';
|
|
import '../models/kauf_filament_model.dart';
|
|
|
|
class AbfallBerechnung {
|
|
/// Roll price is divided by its original weight, never by remaining stock.
|
|
/// For multi-filament prints, waste follows each filament's usage share.
|
|
static double? kosten(
|
|
BauteilModel bauteil,
|
|
Iterable<KaufFilamentModel> filamente,
|
|
) {
|
|
if (bauteil.abfallGramm == 0) return 0;
|
|
if (!bauteil.abfallGramm.isFinite ||
|
|
bauteil.abfallGramm < 0 ||
|
|
bauteil.filamente.isEmpty) {
|
|
return null;
|
|
}
|
|
final preis = preisProGramm(bauteil, filamente);
|
|
if (preis == null) return null;
|
|
final kosten = bauteil.abfallGramm * preis;
|
|
return kosten.isFinite ? double.parse(kosten.toStringAsFixed(2)) : null;
|
|
}
|
|
|
|
static double? preisProGramm(
|
|
BauteilModel bauteil,
|
|
Iterable<KaufFilamentModel> filamente,
|
|
) {
|
|
if (bauteil.filamente.isEmpty) return null;
|
|
final rollen = {for (final rolle in filamente) rolle.filamentId: rolle};
|
|
final gesamtverbrauch = bauteil.filamente.fold<double>(
|
|
0,
|
|
(sum, item) => sum + item.verbrauchGramm,
|
|
);
|
|
if (bauteil.filamente.length > 1 &&
|
|
(!gesamtverbrauch.isFinite || gesamtverbrauch <= 0)) {
|
|
return null;
|
|
}
|
|
var preis = 0.0;
|
|
for (final verbrauch in bauteil.filamente) {
|
|
final rolle = rollen[verbrauch.filamentId];
|
|
final startgewicht = rolle?.gewichtGramm;
|
|
if (rolle == null ||
|
|
startgewicht == null ||
|
|
startgewicht <= 0 ||
|
|
!rolle.preis.isFinite ||
|
|
rolle.preis < 0 ||
|
|
!verbrauch.verbrauchGramm.isFinite ||
|
|
verbrauch.verbrauchGramm < 0 ||
|
|
(bauteil.filamente.length > 1 && verbrauch.verbrauchGramm == 0)) {
|
|
return null;
|
|
}
|
|
final anteil = bauteil.filamente.length == 1
|
|
? 1.0
|
|
: verbrauch.verbrauchGramm / gesamtverbrauch;
|
|
preis += anteil * (rolle.preis / startgewicht);
|
|
}
|
|
return preis.isFinite ? preis : null;
|
|
}
|
|
|
|
/// Changing metadata alone must not change an already recorded material cost.
|
|
static bool gleicheBasis(BauteilModel a, BauteilModel b) =>
|
|
a.gewichtGramm == b.gewichtGramm &&
|
|
a.abfallGramm == b.abfallGramm &&
|
|
a.filamente.length == b.filamente.length &&
|
|
a.filamente.every(
|
|
(item) => b.filamente.any(
|
|
(other) =>
|
|
item.filamentId == other.filamentId &&
|
|
item.verbrauchGramm == other.verbrauchGramm,
|
|
),
|
|
);
|
|
}
|