Added components to article

This commit is contained in:
Dennis Nemec
2026-05-11 17:12:05 +02:00
parent 2470299a10
commit ac6b03227d
37 changed files with 1189 additions and 513 deletions

View File

@ -1,5 +1,7 @@
import 'package:hl_lieferservice/dto/article.dart';
import 'component.dart';
class Article {
Article({
required this.name,
@ -11,13 +13,21 @@ class Article {
required this.scannable,
required this.scannedAmount,
required this.scannedRemovedAmount,
required this.isParent,
this.components = const [],
this.scannedDate,
this.removeNoteId
this.removeNoteId,
this.warehouseNr,
this.warehouseName,
});
final String name;
final String articleNumber;
final int internalId;
final bool isParent;
final List<Component> components;
final String? warehouseNr;
final String? warehouseName;
int amount;
double price;
@ -36,7 +46,35 @@ class Article {
return price * scannedAmount * ((100 + tax) / 100);
}
/// Whether this article is fully scanned.
///
/// For parent articles (Stückliste): delegates to components — all must be
/// individually scanned. For regular articles: the classic amount check.
bool get isFullyScanned {
if (isParent && components.isNotEmpty) {
return components.every((c) => c.isFullyScanned);
}
return scannedAmount + scannedRemovedAmount >= amount;
}
/// Find a component by its article number, or `null` if none matches.
Component? findComponent(String articleNumber) {
for (final c in components) {
if (c.articleNumber == articleNumber) return c;
}
return null;
}
/// Whether this article *or* any of its components carries [articleNumber].
bool hasArticleNumber(String articleNumber) {
if (this.articleNumber == articleNumber) return true;
return components.any((c) => c.articleNumber == articleNumber);
}
bool unscanned() {
if (isParent && components.isNotEmpty) {
return components.every((c) => c.scannedAmount == 0);
}
return scannedAmount == 0;
}
@ -58,6 +96,11 @@ class Article {
price: double.parse(dto.price == "" ? "0.0" : dto.price),
scannable: dto.scannable,
tax: double.parse(dto.taxRate == "" ? "19" : dto.taxRate),
isParent: dto.isParent,
components: dto.components?.map(Component.fromDTO).toList() ?? [],
warehouseNr: dto.warehouseNr?.isEmpty ?? true ? null : dto.warehouseNr,
warehouseName:
dto.warehouseName?.isEmpty ?? true ? null : dto.warehouseName,
);
}
}

34
lib/model/component.dart Normal file
View File

@ -0,0 +1,34 @@
import 'package:hl_lieferservice/dto/component.dart';
class Component {
Component({
required this.articleNumber,
required this.name,
required this.quantity,
required this.position,
this.scannedAmount = 0,
});
final String articleNumber;
final String name;
final double quantity;
final double position;
int scannedAmount;
/// Required scan count derived from BOM quantity (e.g. 7.0 → 7).
int get requiredAmount => quantity.ceil();
bool get isFullyScanned => scannedAmount >= requiredAmount;
bool get needsScanning => scannedAmount < requiredAmount;
factory Component.fromDTO(ComponentDTO dto) {
return Component(
articleNumber: dto.articleNr,
name: dto.name,
quantity: double.tryParse(dto.quantity) ?? 0.0,
position: double.tryParse(dto.pos) ?? 0.0,
);
}
}

View File

@ -295,29 +295,42 @@ class Delivery implements Comparable<Delivery> {
List<Article> getDeliveredArticles() {
return articles
.where(
(article) => article.scannedAmount > 0 || !article.scannable,
)
.where((article) {
if (!article.scannable) return true;
if (article.isParent && article.components.isNotEmpty) {
return article.isFullyScanned;
}
return article.scannedAmount > 0;
})
.toList();
}
bool containsArticle(String articleNr) {
return articles.any((article) => article.articleNumber == articleNr);
return articles.any((article) => article.hasArticleNumber(articleNr));
}
Article getArticle(String nr) {
return articles.firstWhere((article) => article.articleNumber == nr);
}
/// Find the parent article whose BOM contains [componentArticleNr].
Article? findParentOfComponent(String componentArticleNr) {
for (final article in articles) {
if (article.isParent &&
article.findComponent(componentArticleNr) != null) {
return article;
}
}
return null;
}
List<Article> getScannableArticles() {
return articles.where((article) => article.scannable).toList();
}
bool allArticlesScanned() {
return getScannableArticles().every(
(article) =>
article.amount ==
article.scannedAmount + article.scannedRemovedAmount,
(article) => article.isFullyScanned,
);
}