322 lines
7.4 KiB
Dart
322 lines
7.4 KiB
Dart
/// Domain Entity: Order
|
|
///
|
|
/// Represents a customer order.
|
|
library;
|
|
|
|
/// Order status enum
|
|
enum OrderStatus {
|
|
/// Order has been created but not confirmed
|
|
draft,
|
|
|
|
/// Order has been confirmed
|
|
confirmed,
|
|
|
|
/// Order is being processed
|
|
processing,
|
|
|
|
/// Order is ready for shipping
|
|
ready,
|
|
|
|
/// Order has been shipped
|
|
shipped,
|
|
|
|
/// Order has been delivered
|
|
delivered,
|
|
|
|
/// Order has been completed
|
|
completed,
|
|
|
|
/// Order has been cancelled
|
|
cancelled,
|
|
|
|
/// Order has been returned
|
|
returned;
|
|
|
|
/// Get display name for status
|
|
String get displayName {
|
|
switch (this) {
|
|
case OrderStatus.draft:
|
|
return 'Draft';
|
|
case OrderStatus.confirmed:
|
|
return 'Confirmed';
|
|
case OrderStatus.processing:
|
|
return 'Processing';
|
|
case OrderStatus.ready:
|
|
return 'Ready';
|
|
case OrderStatus.shipped:
|
|
return 'Shipped';
|
|
case OrderStatus.delivered:
|
|
return 'Delivered';
|
|
case OrderStatus.completed:
|
|
return 'Completed';
|
|
case OrderStatus.cancelled:
|
|
return 'Cancelled';
|
|
case OrderStatus.returned:
|
|
return 'Returned';
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Address information
|
|
class Address {
|
|
/// Recipient name
|
|
final String? name;
|
|
|
|
/// Phone number
|
|
final String? phone;
|
|
|
|
/// Street address
|
|
final String? street;
|
|
|
|
/// Ward/commune
|
|
final String? ward;
|
|
|
|
/// District
|
|
final String? district;
|
|
|
|
/// City/province
|
|
final String? city;
|
|
|
|
/// Postal code
|
|
final String? postalCode;
|
|
|
|
const Address({
|
|
this.name,
|
|
this.phone,
|
|
this.street,
|
|
this.ward,
|
|
this.district,
|
|
this.city,
|
|
this.postalCode,
|
|
});
|
|
|
|
/// Get full address string
|
|
String get fullAddress {
|
|
final parts = [
|
|
street,
|
|
ward,
|
|
district,
|
|
city,
|
|
postalCode,
|
|
].where((part) => part != null && part.isNotEmpty).toList();
|
|
|
|
return parts.join(', ');
|
|
}
|
|
|
|
/// Create from JSON map
|
|
factory Address.fromJson(Map<String, dynamic> json) {
|
|
return Address(
|
|
name: json['name'] as String?,
|
|
phone: json['phone'] as String?,
|
|
street: json['street'] as String?,
|
|
ward: json['ward'] as String?,
|
|
district: json['district'] as String?,
|
|
city: json['city'] as String?,
|
|
postalCode: json['postal_code'] as String?,
|
|
);
|
|
}
|
|
|
|
/// Convert to JSON map
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'name': name,
|
|
'phone': phone,
|
|
'street': street,
|
|
'ward': ward,
|
|
'district': district,
|
|
'city': city,
|
|
'postal_code': postalCode,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Order Entity
|
|
///
|
|
/// Contains complete order information:
|
|
/// - Order identification
|
|
/// - Customer details
|
|
/// - Pricing and discounts
|
|
/// - Shipping information
|
|
/// - Status tracking
|
|
class Order {
|
|
/// Unique order identifier
|
|
final String orderId;
|
|
|
|
/// Human-readable order number
|
|
final String orderNumber;
|
|
|
|
/// User ID who placed the order
|
|
final String userId;
|
|
|
|
/// Current order status
|
|
final OrderStatus status;
|
|
|
|
/// Total order amount before discounts
|
|
final double totalAmount;
|
|
|
|
/// Discount amount applied
|
|
final double discountAmount;
|
|
|
|
/// Tax amount
|
|
final double taxAmount;
|
|
|
|
/// Shipping fee
|
|
final double shippingFee;
|
|
|
|
/// Final amount to pay
|
|
final double finalAmount;
|
|
|
|
/// Shipping address
|
|
final Address? shippingAddress;
|
|
|
|
/// Billing address
|
|
final Address? billingAddress;
|
|
|
|
/// Expected delivery date
|
|
final DateTime? expectedDeliveryDate;
|
|
|
|
/// Actual delivery date
|
|
final DateTime? actualDeliveryDate;
|
|
|
|
/// Order notes
|
|
final String? notes;
|
|
|
|
/// Cancellation reason
|
|
final String? cancellationReason;
|
|
|
|
/// ERPNext sales order reference
|
|
final String? erpnextSalesOrder;
|
|
|
|
/// Order creation timestamp
|
|
final DateTime createdAt;
|
|
|
|
/// Last update timestamp
|
|
final DateTime updatedAt;
|
|
|
|
const Order({
|
|
required this.orderId,
|
|
required this.orderNumber,
|
|
required this.userId,
|
|
required this.status,
|
|
required this.totalAmount,
|
|
required this.discountAmount,
|
|
required this.taxAmount,
|
|
required this.shippingFee,
|
|
required this.finalAmount,
|
|
this.shippingAddress,
|
|
this.billingAddress,
|
|
this.expectedDeliveryDate,
|
|
this.actualDeliveryDate,
|
|
this.notes,
|
|
this.cancellationReason,
|
|
this.erpnextSalesOrder,
|
|
required this.createdAt,
|
|
required this.updatedAt,
|
|
});
|
|
|
|
/// Check if order is active (not cancelled or completed)
|
|
bool get isActive =>
|
|
status != OrderStatus.cancelled &&
|
|
status != OrderStatus.completed &&
|
|
status != OrderStatus.returned;
|
|
|
|
/// Check if order can be cancelled
|
|
bool get canBeCancelled =>
|
|
status == OrderStatus.draft ||
|
|
status == OrderStatus.confirmed ||
|
|
status == OrderStatus.processing;
|
|
|
|
/// Check if order is delivered
|
|
bool get isDelivered =>
|
|
status == OrderStatus.delivered || status == OrderStatus.completed;
|
|
|
|
/// Check if order is cancelled
|
|
bool get isCancelled => status == OrderStatus.cancelled;
|
|
|
|
/// Get discount percentage
|
|
double get discountPercentage {
|
|
if (totalAmount == 0) return 0;
|
|
return (discountAmount / totalAmount) * 100;
|
|
}
|
|
|
|
/// Copy with method for immutability
|
|
Order copyWith({
|
|
String? orderId,
|
|
String? orderNumber,
|
|
String? userId,
|
|
OrderStatus? status,
|
|
double? totalAmount,
|
|
double? discountAmount,
|
|
double? taxAmount,
|
|
double? shippingFee,
|
|
double? finalAmount,
|
|
Address? shippingAddress,
|
|
Address? billingAddress,
|
|
DateTime? expectedDeliveryDate,
|
|
DateTime? actualDeliveryDate,
|
|
String? notes,
|
|
String? cancellationReason,
|
|
String? erpnextSalesOrder,
|
|
DateTime? createdAt,
|
|
DateTime? updatedAt,
|
|
}) {
|
|
return Order(
|
|
orderId: orderId ?? this.orderId,
|
|
orderNumber: orderNumber ?? this.orderNumber,
|
|
userId: userId ?? this.userId,
|
|
status: status ?? this.status,
|
|
totalAmount: totalAmount ?? this.totalAmount,
|
|
discountAmount: discountAmount ?? this.discountAmount,
|
|
taxAmount: taxAmount ?? this.taxAmount,
|
|
shippingFee: shippingFee ?? this.shippingFee,
|
|
finalAmount: finalAmount ?? this.finalAmount,
|
|
shippingAddress: shippingAddress ?? this.shippingAddress,
|
|
billingAddress: billingAddress ?? this.billingAddress,
|
|
expectedDeliveryDate: expectedDeliveryDate ?? this.expectedDeliveryDate,
|
|
actualDeliveryDate: actualDeliveryDate ?? this.actualDeliveryDate,
|
|
notes: notes ?? this.notes,
|
|
cancellationReason: cancellationReason ?? this.cancellationReason,
|
|
erpnextSalesOrder: erpnextSalesOrder ?? this.erpnextSalesOrder,
|
|
createdAt: createdAt ?? this.createdAt,
|
|
updatedAt: updatedAt ?? this.updatedAt,
|
|
);
|
|
}
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (identical(this, other)) return true;
|
|
|
|
return other is Order &&
|
|
other.orderId == orderId &&
|
|
other.orderNumber == orderNumber &&
|
|
other.userId == userId &&
|
|
other.status == status &&
|
|
other.totalAmount == totalAmount &&
|
|
other.discountAmount == discountAmount &&
|
|
other.taxAmount == taxAmount &&
|
|
other.shippingFee == shippingFee &&
|
|
other.finalAmount == finalAmount;
|
|
}
|
|
|
|
@override
|
|
int get hashCode {
|
|
return Object.hash(
|
|
orderId,
|
|
orderNumber,
|
|
userId,
|
|
status,
|
|
totalAmount,
|
|
discountAmount,
|
|
taxAmount,
|
|
shippingFee,
|
|
finalAmount,
|
|
);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return 'Order(orderId: $orderId, orderNumber: $orderNumber, status: $status, '
|
|
'finalAmount: $finalAmount, createdAt: $createdAt)';
|
|
}
|
|
}
|