274 lines
6.3 KiB
Dart
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)';
|
|
}
|
|
}
|