add some new features

This commit is contained in:
2026-10-29 15:03:48 +01:00
parent c876405a8f
commit 20012db615
39 changed files with 2908 additions and 97 deletions
+71
View File
@@ -0,0 +1,71 @@
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,
),
);
}
+73
View File
@@ -0,0 +1,73 @@
import '../models/bauteil_model.dart';
import '../models/kauf_filament_model.dart';
import 'abfall_berechnung.dart';
class BauteilPreis {
const BauteilPreis({
required this.materialKosten,
required this.abfallKosten,
required this.aufschlagProzent,
required this.gesamtpreis,
});
final double materialKosten;
final double abfallKosten;
final double aufschlagProzent;
final double gesamtpreis;
double get aufschlagKosten => double.parse(
(gesamtpreis - materialKosten - abfallKosten).toStringAsFixed(2),
);
}
class BauteilPreisBerechnung {
static double _cent(double value) => double.parse(value.toStringAsFixed(2));
static BauteilPreis? berechne(
BauteilModel entry,
Iterable<KaufFilamentModel> rollen,
double aufschlag, {
BauteilModel? previous,
}) {
if (!entry.gewichtGramm.isFinite ||
entry.gewichtGramm <= 0 ||
!entry.abfallGramm.isFinite ||
entry.abfallGramm < 0 ||
!aufschlag.isFinite ||
aufschlag < 0) {
return null;
}
double material;
double abfall;
if (previous?.materialKosten != null &&
previous?.abfallKosten != null &&
AbfallBerechnung.gleicheBasis(entry, previous!)) {
material = previous.materialKosten!;
abfall = previous.abfallKosten!;
} else {
if (entry.filamente.length > 1) {
final summe = entry.filamente.fold<double>(
0,
(sum, item) => sum + item.verbrauchGramm,
);
if (!summe.isFinite ||
(summe - entry.gesamtverbrauchGramm).abs() > .01) {
return null;
}
}
final preis = AbfallBerechnung.preisProGramm(entry, rollen);
if (preis == null) return null;
final materialRoh = entry.gewichtGramm * preis;
final abfallRoh = entry.abfallGramm * preis;
if (!materialRoh.isFinite || !abfallRoh.isFinite) return null;
material = _cent(materialRoh);
abfall = _cent(abfallRoh);
}
final gesamt = (material + abfall) * (1 + aufschlag / 100);
if (!gesamt.isFinite) return null;
return BauteilPreis(
materialKosten: material,
abfallKosten: abfall,
aufschlagProzent: aufschlag,
gesamtpreis: _cent(gesamt),
);
}
}
+21
View File
@@ -1,17 +1,38 @@
import '../controller/abfall_controller.dart';
import '../pages/abfall_view.dart';
import 'package:get/get.dart';
import '../controller/anwendung_controller.dart';
import '../controller/bauteil_controller.dart';
import '../controller/bauteil_auswertung_controller.dart';
import '../controller/export_controller.dart';
import '../pages/export_view.dart';
import '../pages/anwendung_view.dart';
import '../pages/bauteil_view.dart';
import '../pages/bauteil_auswertung_view.dart';
import '../pages/home_view.dart';
import 'sample_bindings.dart';
class SampleRouts {
static final sampleBindings = SampleBindings();
static List<GetPage<dynamic>> samplePages = [
GetPage(
name: BauteilAuswertungView.routeName,
page: () => const BauteilAuswertungView(),
binding: BindingsBuilder(
() => Get.lazyPut<BauteilAuswertungController>(
() => BauteilAuswertungController(),
),
),
),
GetPage(
name: AbfallView.routeName,
page: () => const AbfallView(),
binding: BindingsBuilder(
() => Get.lazyPut<AbfallController>(() => AbfallController()),
),
),
GetPage(
name: ExportView.routeName,
page: () => const ExportView(),
+1
View File
@@ -3,4 +3,5 @@ class StorageKeys {
static const materialien = 'filament_materialien';
static const anwendungen = 'filament_anwendungen';
static const bauteile = 'gedruckte_bauteile';
static const abfaelle = 'abfall_eintraege';
}