update database
This commit is contained in:
273
lib/features/orders/domain/entities/invoice.dart
Normal file
273
lib/features/orders/domain/entities/invoice.dart
Normal file
@@ -0,0 +1,273 @@
|
||||
/// Domain Entity: Invoice
|
||||
///
|
||||
/// Represents an invoice for an order.
|
||||
library;
|
||||
|
||||
/// Invoice type enum
|
||||
enum InvoiceType {
|
||||
/// Standard invoice
|
||||
standard,
|
||||
|
||||
/// Proforma invoice
|
||||
proforma,
|
||||
|
||||
/// Credit note
|
||||
creditNote,
|
||||
|
||||
/// Debit note
|
||||
debitNote;
|
||||
}
|
||||
|
||||
/// Invoice status enum
|
||||
enum InvoiceStatus {
|
||||
/// Draft invoice
|
||||
draft,
|
||||
|
||||
/// Invoice has been submitted
|
||||
submitted,
|
||||
|
||||
/// Partially paid
|
||||
partiallyPaid,
|
||||
|
||||
/// Fully paid
|
||||
paid,
|
||||
|
||||
/// Overdue invoice
|
||||
overdue,
|
||||
|
||||
/// Cancelled invoice
|
||||
cancelled;
|
||||
|
||||
/// Get display name for status
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case InvoiceStatus.draft:
|
||||
return 'Draft';
|
||||
case InvoiceStatus.submitted:
|
||||
return 'Submitted';
|
||||
case InvoiceStatus.partiallyPaid:
|
||||
return 'Partially Paid';
|
||||
case InvoiceStatus.paid:
|
||||
return 'Paid';
|
||||
case InvoiceStatus.overdue:
|
||||
return 'Overdue';
|
||||
case InvoiceStatus.cancelled:
|
||||
return 'Cancelled';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoice Entity
|
||||
///
|
||||
/// Contains complete invoice information:
|
||||
/// - Invoice identification
|
||||
/// - Associated order
|
||||
/// - Amounts and calculations
|
||||
/// - Payment tracking
|
||||
/// - Status and dates
|
||||
class Invoice {
|
||||
/// Unique invoice identifier
|
||||
final String invoiceId;
|
||||
|
||||
/// Invoice number (human-readable)
|
||||
final String invoiceNumber;
|
||||
|
||||
/// User ID
|
||||
final String userId;
|
||||
|
||||
/// Order ID
|
||||
final String? orderId;
|
||||
|
||||
/// Invoice type
|
||||
final InvoiceType invoiceType;
|
||||
|
||||
/// Issue date
|
||||
final DateTime issueDate;
|
||||
|
||||
/// Due date
|
||||
final DateTime dueDate;
|
||||
|
||||
/// Currency code (e.g., VND, USD)
|
||||
final String currency;
|
||||
|
||||
/// Subtotal amount
|
||||
final double subtotalAmount;
|
||||
|
||||
/// Tax amount
|
||||
final double taxAmount;
|
||||
|
||||
/// Discount amount
|
||||
final double discountAmount;
|
||||
|
||||
/// Shipping amount
|
||||
final double shippingAmount;
|
||||
|
||||
/// Total amount
|
||||
final double totalAmount;
|
||||
|
||||
/// Amount paid so far
|
||||
final double amountPaid;
|
||||
|
||||
/// Amount remaining to be paid
|
||||
final double amountRemaining;
|
||||
|
||||
/// Invoice status
|
||||
final InvoiceStatus status;
|
||||
|
||||
/// Payment terms
|
||||
final String? paymentTerms;
|
||||
|
||||
/// Invoice notes
|
||||
final String? notes;
|
||||
|
||||
/// ERPNext invoice reference
|
||||
final String? erpnextInvoice;
|
||||
|
||||
/// Creation timestamp
|
||||
final DateTime createdAt;
|
||||
|
||||
/// Last update timestamp
|
||||
final DateTime updatedAt;
|
||||
|
||||
/// Last reminder sent timestamp
|
||||
final DateTime? lastReminderSent;
|
||||
|
||||
const Invoice({
|
||||
required this.invoiceId,
|
||||
required this.invoiceNumber,
|
||||
required this.userId,
|
||||
this.orderId,
|
||||
required this.invoiceType,
|
||||
required this.issueDate,
|
||||
required this.dueDate,
|
||||
required this.currency,
|
||||
required this.subtotalAmount,
|
||||
required this.taxAmount,
|
||||
required this.discountAmount,
|
||||
required this.shippingAmount,
|
||||
required this.totalAmount,
|
||||
required this.amountPaid,
|
||||
required this.amountRemaining,
|
||||
required this.status,
|
||||
this.paymentTerms,
|
||||
this.notes,
|
||||
this.erpnextInvoice,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.lastReminderSent,
|
||||
});
|
||||
|
||||
/// Check if invoice is fully paid
|
||||
bool get isPaid => status == InvoiceStatus.paid || amountRemaining <= 0;
|
||||
|
||||
/// Check if invoice is overdue
|
||||
bool get isOverdue =>
|
||||
status == InvoiceStatus.overdue ||
|
||||
(!isPaid && DateTime.now().isAfter(dueDate));
|
||||
|
||||
/// Check if invoice is partially paid
|
||||
bool get isPartiallyPaid =>
|
||||
amountPaid > 0 && amountPaid < totalAmount;
|
||||
|
||||
/// Get payment percentage
|
||||
double get paymentPercentage {
|
||||
if (totalAmount == 0) return 0;
|
||||
return (amountPaid / totalAmount) * 100;
|
||||
}
|
||||
|
||||
/// Get days until due
|
||||
int get daysUntilDue => dueDate.difference(DateTime.now()).inDays;
|
||||
|
||||
/// Get days overdue
|
||||
int get daysOverdue {
|
||||
if (!isOverdue) return 0;
|
||||
return DateTime.now().difference(dueDate).inDays;
|
||||
}
|
||||
|
||||
/// Copy with method for immutability
|
||||
Invoice copyWith({
|
||||
String? invoiceId,
|
||||
String? invoiceNumber,
|
||||
String? userId,
|
||||
String? orderId,
|
||||
InvoiceType? invoiceType,
|
||||
DateTime? issueDate,
|
||||
DateTime? dueDate,
|
||||
String? currency,
|
||||
double? subtotalAmount,
|
||||
double? taxAmount,
|
||||
double? discountAmount,
|
||||
double? shippingAmount,
|
||||
double? totalAmount,
|
||||
double? amountPaid,
|
||||
double? amountRemaining,
|
||||
InvoiceStatus? status,
|
||||
String? paymentTerms,
|
||||
String? notes,
|
||||
String? erpnextInvoice,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
DateTime? lastReminderSent,
|
||||
}) {
|
||||
return Invoice(
|
||||
invoiceId: invoiceId ?? this.invoiceId,
|
||||
invoiceNumber: invoiceNumber ?? this.invoiceNumber,
|
||||
userId: userId ?? this.userId,
|
||||
orderId: orderId ?? this.orderId,
|
||||
invoiceType: invoiceType ?? this.invoiceType,
|
||||
issueDate: issueDate ?? this.issueDate,
|
||||
dueDate: dueDate ?? this.dueDate,
|
||||
currency: currency ?? this.currency,
|
||||
subtotalAmount: subtotalAmount ?? this.subtotalAmount,
|
||||
taxAmount: taxAmount ?? this.taxAmount,
|
||||
discountAmount: discountAmount ?? this.discountAmount,
|
||||
shippingAmount: shippingAmount ?? this.shippingAmount,
|
||||
totalAmount: totalAmount ?? this.totalAmount,
|
||||
amountPaid: amountPaid ?? this.amountPaid,
|
||||
amountRemaining: amountRemaining ?? this.amountRemaining,
|
||||
status: status ?? this.status,
|
||||
paymentTerms: paymentTerms ?? this.paymentTerms,
|
||||
notes: notes ?? this.notes,
|
||||
erpnextInvoice: erpnextInvoice ?? this.erpnextInvoice,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
lastReminderSent: lastReminderSent ?? this.lastReminderSent,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is Invoice &&
|
||||
other.invoiceId == invoiceId &&
|
||||
other.invoiceNumber == invoiceNumber &&
|
||||
other.userId == userId &&
|
||||
other.orderId == orderId &&
|
||||
other.invoiceType == invoiceType &&
|
||||
other.totalAmount == totalAmount &&
|
||||
other.amountPaid == amountPaid &&
|
||||
other.status == status;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
invoiceId,
|
||||
invoiceNumber,
|
||||
userId,
|
||||
orderId,
|
||||
invoiceType,
|
||||
totalAmount,
|
||||
amountPaid,
|
||||
status,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Invoice(invoiceId: $invoiceId, invoiceNumber: $invoiceNumber, '
|
||||
'status: $status, totalAmount: $totalAmount, amountPaid: $amountPaid, '
|
||||
'amountRemaining: $amountRemaining)';
|
||||
}
|
||||
}
|
||||
321
lib/features/orders/domain/entities/order.dart
Normal file
321
lib/features/orders/domain/entities/order.dart
Normal file
@@ -0,0 +1,321 @@
|
||||
/// 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)';
|
||||
}
|
||||
}
|
||||
117
lib/features/orders/domain/entities/order_item.dart
Normal file
117
lib/features/orders/domain/entities/order_item.dart
Normal file
@@ -0,0 +1,117 @@
|
||||
/// Domain Entity: Order Item
|
||||
///
|
||||
/// Represents a single line item in an order.
|
||||
library;
|
||||
|
||||
/// Order Item Entity
|
||||
///
|
||||
/// Contains item-level information in an order:
|
||||
/// - Product reference
|
||||
/// - Quantity and pricing
|
||||
/// - Discounts
|
||||
/// - Notes
|
||||
class OrderItem {
|
||||
/// Unique order item identifier
|
||||
final String orderItemId;
|
||||
|
||||
/// Order ID this item belongs to
|
||||
final String orderId;
|
||||
|
||||
/// Product ID
|
||||
final String productId;
|
||||
|
||||
/// Quantity ordered
|
||||
final double quantity;
|
||||
|
||||
/// Unit price at time of order
|
||||
final double unitPrice;
|
||||
|
||||
/// Discount percentage applied
|
||||
final double discountPercent;
|
||||
|
||||
/// Subtotal (quantity * unitPrice * (1 - discountPercent/100))
|
||||
final double subtotal;
|
||||
|
||||
/// Item notes
|
||||
final String? notes;
|
||||
|
||||
const OrderItem({
|
||||
required this.orderItemId,
|
||||
required this.orderId,
|
||||
required this.productId,
|
||||
required this.quantity,
|
||||
required this.unitPrice,
|
||||
required this.discountPercent,
|
||||
required this.subtotal,
|
||||
this.notes,
|
||||
});
|
||||
|
||||
/// Calculate subtotal before discount
|
||||
double get subtotalBeforeDiscount => quantity * unitPrice;
|
||||
|
||||
/// Calculate discount amount
|
||||
double get discountAmount =>
|
||||
subtotalBeforeDiscount * (discountPercent / 100);
|
||||
|
||||
/// Calculate subtotal after discount (for verification)
|
||||
double get calculatedSubtotal => subtotalBeforeDiscount - discountAmount;
|
||||
|
||||
/// Check if item has discount
|
||||
bool get hasDiscount => discountPercent > 0;
|
||||
|
||||
/// Copy with method for immutability
|
||||
OrderItem copyWith({
|
||||
String? orderItemId,
|
||||
String? orderId,
|
||||
String? productId,
|
||||
double? quantity,
|
||||
double? unitPrice,
|
||||
double? discountPercent,
|
||||
double? subtotal,
|
||||
String? notes,
|
||||
}) {
|
||||
return OrderItem(
|
||||
orderItemId: orderItemId ?? this.orderItemId,
|
||||
orderId: orderId ?? this.orderId,
|
||||
productId: productId ?? this.productId,
|
||||
quantity: quantity ?? this.quantity,
|
||||
unitPrice: unitPrice ?? this.unitPrice,
|
||||
discountPercent: discountPercent ?? this.discountPercent,
|
||||
subtotal: subtotal ?? this.subtotal,
|
||||
notes: notes ?? this.notes,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is OrderItem &&
|
||||
other.orderItemId == orderItemId &&
|
||||
other.orderId == orderId &&
|
||||
other.productId == productId &&
|
||||
other.quantity == quantity &&
|
||||
other.unitPrice == unitPrice &&
|
||||
other.discountPercent == discountPercent &&
|
||||
other.subtotal == subtotal;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
orderItemId,
|
||||
orderId,
|
||||
productId,
|
||||
quantity,
|
||||
unitPrice,
|
||||
discountPercent,
|
||||
subtotal,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'OrderItem(orderItemId: $orderItemId, productId: $productId, '
|
||||
'quantity: $quantity, unitPrice: $unitPrice, subtotal: $subtotal)';
|
||||
}
|
||||
}
|
||||
237
lib/features/orders/domain/entities/payment_line.dart
Normal file
237
lib/features/orders/domain/entities/payment_line.dart
Normal file
@@ -0,0 +1,237 @@
|
||||
/// Domain Entity: Payment Line
|
||||
///
|
||||
/// Represents a payment transaction for an invoice.
|
||||
library;
|
||||
|
||||
/// Payment method enum
|
||||
enum PaymentMethod {
|
||||
/// Cash payment
|
||||
cash,
|
||||
|
||||
/// Bank transfer
|
||||
bankTransfer,
|
||||
|
||||
/// Credit card
|
||||
creditCard,
|
||||
|
||||
/// E-wallet (Momo, ZaloPay, etc.)
|
||||
ewallet,
|
||||
|
||||
/// Check
|
||||
check,
|
||||
|
||||
/// Other method
|
||||
other;
|
||||
|
||||
/// Get display name for payment method
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case PaymentMethod.cash:
|
||||
return 'Cash';
|
||||
case PaymentMethod.bankTransfer:
|
||||
return 'Bank Transfer';
|
||||
case PaymentMethod.creditCard:
|
||||
return 'Credit Card';
|
||||
case PaymentMethod.ewallet:
|
||||
return 'E-Wallet';
|
||||
case PaymentMethod.check:
|
||||
return 'Check';
|
||||
case PaymentMethod.other:
|
||||
return 'Other';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Payment status enum
|
||||
enum PaymentStatus {
|
||||
/// Payment pending
|
||||
pending,
|
||||
|
||||
/// Payment is being processed
|
||||
processing,
|
||||
|
||||
/// Payment completed successfully
|
||||
completed,
|
||||
|
||||
/// Payment failed
|
||||
failed,
|
||||
|
||||
/// Payment refunded
|
||||
refunded,
|
||||
|
||||
/// Payment cancelled
|
||||
cancelled;
|
||||
|
||||
/// Get display name for status
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case PaymentStatus.pending:
|
||||
return 'Pending';
|
||||
case PaymentStatus.processing:
|
||||
return 'Processing';
|
||||
case PaymentStatus.completed:
|
||||
return 'Completed';
|
||||
case PaymentStatus.failed:
|
||||
return 'Failed';
|
||||
case PaymentStatus.refunded:
|
||||
return 'Refunded';
|
||||
case PaymentStatus.cancelled:
|
||||
return 'Cancelled';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Payment Line Entity
|
||||
///
|
||||
/// Contains payment transaction information:
|
||||
/// - Payment details
|
||||
/// - Payment method
|
||||
/// - Bank information
|
||||
/// - Status tracking
|
||||
class PaymentLine {
|
||||
/// Unique payment line identifier
|
||||
final String paymentLineId;
|
||||
|
||||
/// Invoice ID this payment is for
|
||||
final String invoiceId;
|
||||
|
||||
/// Payment number (human-readable)
|
||||
final String paymentNumber;
|
||||
|
||||
/// Payment date
|
||||
final DateTime paymentDate;
|
||||
|
||||
/// Payment amount
|
||||
final double amount;
|
||||
|
||||
/// Payment method
|
||||
final PaymentMethod paymentMethod;
|
||||
|
||||
/// Bank name (for bank transfer)
|
||||
final String? bankName;
|
||||
|
||||
/// Bank account number (for bank transfer)
|
||||
final String? bankAccount;
|
||||
|
||||
/// Reference number (transaction ID, check number, etc.)
|
||||
final String? referenceNumber;
|
||||
|
||||
/// Payment notes
|
||||
final String? notes;
|
||||
|
||||
/// Payment status
|
||||
final PaymentStatus status;
|
||||
|
||||
/// Receipt URL
|
||||
final String? receiptUrl;
|
||||
|
||||
/// ERPNext payment entry reference
|
||||
final String? erpnextPaymentEntry;
|
||||
|
||||
/// Creation timestamp
|
||||
final DateTime createdAt;
|
||||
|
||||
/// Processing timestamp
|
||||
final DateTime? processedAt;
|
||||
|
||||
const PaymentLine({
|
||||
required this.paymentLineId,
|
||||
required this.invoiceId,
|
||||
required this.paymentNumber,
|
||||
required this.paymentDate,
|
||||
required this.amount,
|
||||
required this.paymentMethod,
|
||||
this.bankName,
|
||||
this.bankAccount,
|
||||
this.referenceNumber,
|
||||
this.notes,
|
||||
required this.status,
|
||||
this.receiptUrl,
|
||||
this.erpnextPaymentEntry,
|
||||
required this.createdAt,
|
||||
this.processedAt,
|
||||
});
|
||||
|
||||
/// Check if payment is completed
|
||||
bool get isCompleted => status == PaymentStatus.completed;
|
||||
|
||||
/// Check if payment is pending
|
||||
bool get isPending => status == PaymentStatus.pending;
|
||||
|
||||
/// Check if payment is being processed
|
||||
bool get isProcessing => status == PaymentStatus.processing;
|
||||
|
||||
/// Check if payment failed
|
||||
bool get isFailed => status == PaymentStatus.failed;
|
||||
|
||||
/// Check if payment has receipt
|
||||
bool get hasReceipt => receiptUrl != null && receiptUrl!.isNotEmpty;
|
||||
|
||||
/// Copy with method for immutability
|
||||
PaymentLine copyWith({
|
||||
String? paymentLineId,
|
||||
String? invoiceId,
|
||||
String? paymentNumber,
|
||||
DateTime? paymentDate,
|
||||
double? amount,
|
||||
PaymentMethod? paymentMethod,
|
||||
String? bankName,
|
||||
String? bankAccount,
|
||||
String? referenceNumber,
|
||||
String? notes,
|
||||
PaymentStatus? status,
|
||||
String? receiptUrl,
|
||||
String? erpnextPaymentEntry,
|
||||
DateTime? createdAt,
|
||||
DateTime? processedAt,
|
||||
}) {
|
||||
return PaymentLine(
|
||||
paymentLineId: paymentLineId ?? this.paymentLineId,
|
||||
invoiceId: invoiceId ?? this.invoiceId,
|
||||
paymentNumber: paymentNumber ?? this.paymentNumber,
|
||||
paymentDate: paymentDate ?? this.paymentDate,
|
||||
amount: amount ?? this.amount,
|
||||
paymentMethod: paymentMethod ?? this.paymentMethod,
|
||||
bankName: bankName ?? this.bankName,
|
||||
bankAccount: bankAccount ?? this.bankAccount,
|
||||
referenceNumber: referenceNumber ?? this.referenceNumber,
|
||||
notes: notes ?? this.notes,
|
||||
status: status ?? this.status,
|
||||
receiptUrl: receiptUrl ?? this.receiptUrl,
|
||||
erpnextPaymentEntry: erpnextPaymentEntry ?? this.erpnextPaymentEntry,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
processedAt: processedAt ?? this.processedAt,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is PaymentLine &&
|
||||
other.paymentLineId == paymentLineId &&
|
||||
other.invoiceId == invoiceId &&
|
||||
other.paymentNumber == paymentNumber &&
|
||||
other.amount == amount &&
|
||||
other.paymentMethod == paymentMethod &&
|
||||
other.status == status;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
paymentLineId,
|
||||
invoiceId,
|
||||
paymentNumber,
|
||||
amount,
|
||||
paymentMethod,
|
||||
status,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PaymentLine(paymentLineId: $paymentLineId, paymentNumber: $paymentNumber, '
|
||||
'amount: $amount, paymentMethod: $paymentMethod, status: $status)';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user