part of '../product.dart'; /// Satu baris keranjang. /// /// Untuk produk timbangan, **satu penimbangan = satu baris**: dua kali menimbang /// Ikan Tude menghasilkan dua baris terpisah dengan [quantity] 1 dan [weight] /// masing-masing. Karena itu baris dibedakan lewat [lineId], bukan lewat /// kombinasi produk + varian. @freezed class ProductQuantity with _$ProductQuantity { const ProductQuantity._(); const factory ProductQuantity({ required Product product, ProductVariant? variant, required int quantity, required String notes, @Default('') String lineId, /// Angka timbangan dalam satuan produk. `null` untuk produk cacah. double? weight, }) = _ProductQuantity; factory ProductQuantity.empty() => ProductQuantity(product: Product.empty(), quantity: 0, notes: ''); bool get isWeighted => weight != null; double get unitPrice => product.price + (variant?.priceModifier ?? 0); /// Harga baris: `weight × unit_price` untuk timbangan, `quantity × unit_price` /// untuk produk cacah. double get lineTotal => isWeighted ? unitPrice * weight! : unitPrice * quantity; /// `4,2 ons` atau `2` — dipakai di kolom Qty keranjang. String get quantityLabel { if (!isWeighted) return quantity.toString(); final label = product.unitLabel; final value = weight!.weightFormat; return label.isEmpty ? value : '$value $label'; } } extension ProductQuantityListExt on List { /// Total keranjang: baris berbobot pakai `weight × harga`, baris cacah pakai /// `quantity × harga`. double get cartTotal => fold(0, (sum, item) => sum + item.lineTotal); /// Baris berbobot dihitung satu — satu penimbangan, satu barang. int get cartItemCount => fold(0, (sum, item) => sum + (item.isWeighted ? 1 : item.quantity)); }