Files
apskel-pos-flutter-v2/lib/domain/product/entities/product_quantity_entity.dart
T
efrilmandClaude Opus 5 1505a83dd3 feat: dukung penjualan produk per timbangan
Produk kini punya cara jual `unit` (cacah) atau `weight` (timbangan),
mengikuti kontrak backend di docs/integration-weight-based-products.md.

- tipe SellBy, domain/infrastructure unit baru, dan endpoint /api/v1/units
- dialog input berat saat produk timbangan ditambahkan ke keranjang
- tiap penimbangan jadi baris keranjang sendiri; total baris dihitung
  berat x harga satuan, sedangkan produk cacah tetap digabung
- request ke backend mengirim quantity 1 dan membawa berat
- migrasi DB v3: kolom sell_by, unit_id, unit_name, unit_abbreviation
  di tabel products supaya satuan tetap tampil saat offline
- tampilan keranjang, struk, split bill, void, dan refund memakai berat
- test/weight_based_products_test.dart menutup perilaku di atas (9 test)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 23:03:31 +07:00

54 lines
1.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<ProductQuantity> {
/// Total keranjang: baris berbobot pakai `weight × harga`, baris cacah pakai
/// `quantity × harga`.
double get cartTotal => fold<double>(0, (sum, item) => sum + item.lineTotal);
/// Baris berbobot dihitung satu — satu penimbangan, satu barang.
int get cartItemCount =>
fold<int>(0, (sum, item) => sum + (item.isWeighted ? 1 : item.quantity));
}