update database
This commit is contained in:
@@ -10,111 +10,155 @@ library;
|
||||
/// Used across all layers but originates in the domain layer.
|
||||
class Product {
|
||||
/// Unique identifier
|
||||
final String id;
|
||||
final String productId;
|
||||
|
||||
/// Product name (Vietnamese)
|
||||
final String name;
|
||||
|
||||
/// Product SKU (Stock Keeping Unit)
|
||||
final String sku;
|
||||
|
||||
/// Product description
|
||||
final String description;
|
||||
final String? description;
|
||||
|
||||
/// Price per unit (VND)
|
||||
final double price;
|
||||
/// Base price per unit (VND)
|
||||
final double basePrice;
|
||||
|
||||
/// Product images (URLs)
|
||||
final List<String> images;
|
||||
|
||||
/// Image captions
|
||||
final Map<String, String> imageCaptions;
|
||||
|
||||
/// 360-degree view link
|
||||
final String? link360;
|
||||
|
||||
/// Product specifications
|
||||
final Map<String, dynamic> specifications;
|
||||
|
||||
/// Category name
|
||||
final String? category;
|
||||
|
||||
/// Brand name
|
||||
final String? brand;
|
||||
|
||||
/// Unit of measurement (e.g., "m²", "viên", "hộp")
|
||||
final String unit;
|
||||
final String? unit;
|
||||
|
||||
/// Product image URL
|
||||
final String imageUrl;
|
||||
/// Product is active
|
||||
final bool isActive;
|
||||
|
||||
/// Category ID
|
||||
final String categoryId;
|
||||
/// Product is featured
|
||||
final bool isFeatured;
|
||||
|
||||
/// Stock availability
|
||||
final bool inStock;
|
||||
|
||||
/// Stock quantity
|
||||
final int stockQuantity;
|
||||
/// ERPNext item code
|
||||
final String? erpnextItemCode;
|
||||
|
||||
/// Created date
|
||||
final DateTime createdAt;
|
||||
|
||||
/// Optional sale price
|
||||
final double? salePrice;
|
||||
|
||||
/// Optional brand name
|
||||
final String? brand;
|
||||
/// Last updated date
|
||||
final DateTime updatedAt;
|
||||
|
||||
const Product({
|
||||
required this.id,
|
||||
required this.productId,
|
||||
required this.name,
|
||||
required this.sku,
|
||||
required this.description,
|
||||
required this.price,
|
||||
required this.unit,
|
||||
required this.imageUrl,
|
||||
required this.categoryId,
|
||||
required this.inStock,
|
||||
required this.stockQuantity,
|
||||
required this.createdAt,
|
||||
this.salePrice,
|
||||
this.description,
|
||||
required this.basePrice,
|
||||
required this.images,
|
||||
required this.imageCaptions,
|
||||
this.link360,
|
||||
required this.specifications,
|
||||
this.category,
|
||||
this.brand,
|
||||
this.unit,
|
||||
required this.isActive,
|
||||
required this.isFeatured,
|
||||
this.erpnextItemCode,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
});
|
||||
|
||||
/// Get effective price (sale price if available, otherwise regular price)
|
||||
double get effectivePrice => salePrice ?? price;
|
||||
/// Get primary image URL
|
||||
String? get primaryImage => images.isNotEmpty ? images.first : null;
|
||||
|
||||
/// Alias for primaryImage (used by UI widgets)
|
||||
String get imageUrl => primaryImage ?? '';
|
||||
|
||||
/// Category ID (alias for category field)
|
||||
String? get categoryId => category;
|
||||
|
||||
/// Check if product has 360 view
|
||||
bool get has360View => link360 != null && link360!.isNotEmpty;
|
||||
|
||||
/// Check if product has multiple images
|
||||
bool get hasMultipleImages => images.length > 1;
|
||||
|
||||
/// Check if product is on sale
|
||||
bool get isOnSale => salePrice != null && salePrice! < price;
|
||||
/// TODO: Implement sale price logic when backend supports it
|
||||
bool get isOnSale => false;
|
||||
|
||||
/// Get discount percentage
|
||||
int get discountPercentage {
|
||||
if (!isOnSale) return 0;
|
||||
return (((price - salePrice!) / price) * 100).round();
|
||||
/// Discount percentage
|
||||
/// TODO: Calculate from salePrice when backend supports it
|
||||
int get discountPercentage => 0;
|
||||
|
||||
/// Effective price (considering sales)
|
||||
/// TODO: Use salePrice when backend supports it
|
||||
double get effectivePrice => basePrice;
|
||||
|
||||
/// Check if product is low stock
|
||||
/// TODO: Implement stock tracking when backend supports it
|
||||
bool get isLowStock => false;
|
||||
|
||||
/// Check if product is in stock
|
||||
/// Currently using isActive as proxy
|
||||
bool get inStock => isActive;
|
||||
|
||||
/// Get specification value by key
|
||||
String? getSpecification(String key) {
|
||||
return specifications[key]?.toString();
|
||||
}
|
||||
|
||||
/// Check if stock is low (less than 10 items)
|
||||
bool get isLowStock => inStock && stockQuantity < 10;
|
||||
|
||||
/// Copy with method for creating modified copies
|
||||
Product copyWith({
|
||||
String? id,
|
||||
String? productId,
|
||||
String? name,
|
||||
String? sku,
|
||||
String? description,
|
||||
double? price,
|
||||
String? unit,
|
||||
String? imageUrl,
|
||||
String? categoryId,
|
||||
bool? inStock,
|
||||
int? stockQuantity,
|
||||
DateTime? createdAt,
|
||||
double? salePrice,
|
||||
double? basePrice,
|
||||
List<String>? images,
|
||||
Map<String, String>? imageCaptions,
|
||||
String? link360,
|
||||
Map<String, dynamic>? specifications,
|
||||
String? category,
|
||||
String? brand,
|
||||
String? unit,
|
||||
bool? isActive,
|
||||
bool? isFeatured,
|
||||
String? erpnextItemCode,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
}) {
|
||||
return Product(
|
||||
id: id ?? this.id,
|
||||
productId: productId ?? this.productId,
|
||||
name: name ?? this.name,
|
||||
sku: sku ?? this.sku,
|
||||
description: description ?? this.description,
|
||||
price: price ?? this.price,
|
||||
unit: unit ?? this.unit,
|
||||
imageUrl: imageUrl ?? this.imageUrl,
|
||||
categoryId: categoryId ?? this.categoryId,
|
||||
inStock: inStock ?? this.inStock,
|
||||
stockQuantity: stockQuantity ?? this.stockQuantity,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
salePrice: salePrice ?? this.salePrice,
|
||||
basePrice: basePrice ?? this.basePrice,
|
||||
images: images ?? this.images,
|
||||
imageCaptions: imageCaptions ?? this.imageCaptions,
|
||||
link360: link360 ?? this.link360,
|
||||
specifications: specifications ?? this.specifications,
|
||||
category: category ?? this.category,
|
||||
brand: brand ?? this.brand,
|
||||
unit: unit ?? this.unit,
|
||||
isActive: isActive ?? this.isActive,
|
||||
isFeatured: isFeatured ?? this.isFeatured,
|
||||
erpnextItemCode: erpnextItemCode ?? this.erpnextItemCode,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Product(id: $id, name: $name, sku: $sku, price: $price, unit: $unit, inStock: $inStock)';
|
||||
return 'Product(productId: $productId, name: $name, basePrice: $basePrice, '
|
||||
'category: $category, isActive: $isActive, isFeatured: $isFeatured)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -122,35 +166,31 @@ class Product {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is Product &&
|
||||
other.id == id &&
|
||||
other.productId == productId &&
|
||||
other.name == name &&
|
||||
other.sku == sku &&
|
||||
other.description == description &&
|
||||
other.price == price &&
|
||||
other.basePrice == basePrice &&
|
||||
other.category == category &&
|
||||
other.brand == brand &&
|
||||
other.unit == unit &&
|
||||
other.imageUrl == imageUrl &&
|
||||
other.categoryId == categoryId &&
|
||||
other.inStock == inStock &&
|
||||
other.stockQuantity == stockQuantity &&
|
||||
other.salePrice == salePrice &&
|
||||
other.brand == brand;
|
||||
other.isActive == isActive &&
|
||||
other.isFeatured == isFeatured &&
|
||||
other.erpnextItemCode == erpnextItemCode;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
id,
|
||||
productId,
|
||||
name,
|
||||
sku,
|
||||
description,
|
||||
price,
|
||||
unit,
|
||||
imageUrl,
|
||||
categoryId,
|
||||
inStock,
|
||||
stockQuantity,
|
||||
salePrice,
|
||||
basePrice,
|
||||
category,
|
||||
brand,
|
||||
unit,
|
||||
isActive,
|
||||
isFeatured,
|
||||
erpnextItemCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
106
lib/features/products/domain/entities/stock_level.dart
Normal file
106
lib/features/products/domain/entities/stock_level.dart
Normal file
@@ -0,0 +1,106 @@
|
||||
/// Domain Entity: Stock Level
|
||||
///
|
||||
/// Represents inventory stock level for a product in a warehouse.
|
||||
library;
|
||||
|
||||
/// Stock Level Entity
|
||||
///
|
||||
/// Contains inventory information for a product:
|
||||
/// - Available quantity
|
||||
/// - Reserved quantity (for pending orders)
|
||||
/// - Ordered quantity (incoming stock)
|
||||
/// - Warehouse location
|
||||
class StockLevel {
|
||||
/// Product ID
|
||||
final String productId;
|
||||
|
||||
/// Available quantity for sale
|
||||
final double availableQty;
|
||||
|
||||
/// Quantity reserved for orders
|
||||
final double reservedQty;
|
||||
|
||||
/// Quantity on order (incoming)
|
||||
final double orderedQty;
|
||||
|
||||
/// Warehouse code
|
||||
final String warehouseCode;
|
||||
|
||||
/// Last update timestamp
|
||||
final DateTime lastUpdated;
|
||||
|
||||
const StockLevel({
|
||||
required this.productId,
|
||||
required this.availableQty,
|
||||
required this.reservedQty,
|
||||
required this.orderedQty,
|
||||
required this.warehouseCode,
|
||||
required this.lastUpdated,
|
||||
});
|
||||
|
||||
/// Get total quantity (available + reserved + ordered)
|
||||
double get totalQty => availableQty + reservedQty + orderedQty;
|
||||
|
||||
/// Check if product is in stock
|
||||
bool get isInStock => availableQty > 0;
|
||||
|
||||
/// Check if stock is low (less than 10 units)
|
||||
bool get isLowStock => availableQty > 0 && availableQty < 10;
|
||||
|
||||
/// Check if out of stock
|
||||
bool get isOutOfStock => availableQty <= 0;
|
||||
|
||||
/// Get available percentage
|
||||
double get availablePercentage {
|
||||
if (totalQty == 0) return 0;
|
||||
return (availableQty / totalQty) * 100;
|
||||
}
|
||||
|
||||
/// Copy with method for immutability
|
||||
StockLevel copyWith({
|
||||
String? productId,
|
||||
double? availableQty,
|
||||
double? reservedQty,
|
||||
double? orderedQty,
|
||||
String? warehouseCode,
|
||||
DateTime? lastUpdated,
|
||||
}) {
|
||||
return StockLevel(
|
||||
productId: productId ?? this.productId,
|
||||
availableQty: availableQty ?? this.availableQty,
|
||||
reservedQty: reservedQty ?? this.reservedQty,
|
||||
orderedQty: orderedQty ?? this.orderedQty,
|
||||
warehouseCode: warehouseCode ?? this.warehouseCode,
|
||||
lastUpdated: lastUpdated ?? this.lastUpdated,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is StockLevel &&
|
||||
other.productId == productId &&
|
||||
other.availableQty == availableQty &&
|
||||
other.reservedQty == reservedQty &&
|
||||
other.orderedQty == orderedQty &&
|
||||
other.warehouseCode == warehouseCode;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
productId,
|
||||
availableQty,
|
||||
reservedQty,
|
||||
orderedQty,
|
||||
warehouseCode,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'StockLevel(productId: $productId, availableQty: $availableQty, '
|
||||
'reservedQty: $reservedQty, orderedQty: $orderedQty, warehouseCode: $warehouseCode)';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user