""" Invoice models for tracking customer billing. """ import uuid from django.db import models from django.utils import timezone class Invoice(models.Model): """Invoice records""" STATUS_CHOICES = ( ('draft', 'Draft'), ('sent', 'Sent'), ('paid', 'Paid'), ('overdue', 'Overdue'), ('cancelled', 'Cancelled'), ) PAYMENT_CHOICES = ( ('check', 'Check'), ('credit_card', 'Credit Card'), ('bank_transfer', 'Bank Transfer'), ('cash', 'Cash'), ) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) customer = models.ForeignKey('Customer', on_delete=models.CASCADE, related_name='invoices') date = models.DateField() # Related items accounts = models.ManyToManyField('Account', related_name='invoices', blank=True) projects = models.ManyToManyField('Project', related_name='invoices', blank=True) # Status and payment status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft') date_paid = models.DateField(blank=True, null=True) payment_type = models.CharField(max_length=20, choices=PAYMENT_CHOICES, blank=True, null=True) # Financial total_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0) # Timestamps created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) sent_at = models.DateTimeField(blank=True, null=True) class Meta: app_label = 'core' verbose_name = 'Invoice' verbose_name_plural = 'Invoices' ordering = ['-date'] indexes = [ models.Index(fields=['customer']), models.Index(fields=['date']), models.Index(fields=['status']), ] def __str__(self): return f"Invoice for {self.customer.name} on {self.date}" def save(self, *args, **kwargs): """Override save to update timestamps based on status""" if self.status == 'sent' and not self.sent_at: self.sent_at = timezone.now() super().save(*args, **kwargs) @property def is_paid(self): """Check if invoice is paid""" return self.status == 'paid' @property def is_overdue(self): """Check if invoice is overdue""" # Normally you'd check against a due date field, but we'll use a simple 30 day rule if self.status not in ['paid', 'cancelled'] and self.sent_at: return (timezone.now().date() - self.date).days > 30 return False @property def days_outstanding(self): """Calculate days since invoice was sent""" if not self.sent_at: return 0 return (timezone.now().date() - self.sent_at.date()).days