update database

This commit is contained in:
Phuoc Nguyen
2025-10-24 11:31:48 +07:00
parent f95fa9d0a6
commit c4272f9a21
126 changed files with 23528 additions and 2234 deletions

View File

@@ -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,
);
}
}

View 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)';
}
}