Files
worker/lib/features/orders/domain/entities/invoice.dart
2025-10-24 11:31:48 +07:00

274 lines
6.3 KiB
Dart

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