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