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