# -*- coding: utf-8 -*- from django.db import models from django.conf import settings from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from django.dispatch import receiver from django.db.models.signals import post_save from servo.models import (User, Customer, Order, Location, AbstractOrderItem, ServiceOrderItem,) class Invoice(models.Model): created_at = models.DateTimeField(editable=False, auto_now_add=True) created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, editable=False, on_delete=models.SET_NULL) PAYMENT_METHODS = ( (0, _("No Charge")), (1, _("Cash")), (2, _("Invoice")), (3, _("Credit Card")), (4, _("Mail payment")), (5, _("Online payment")) ) payment_method = models.IntegerField( editable=False, choices=PAYMENT_METHODS, default=PAYMENT_METHODS[0][0], verbose_name=_("Payment Method") ) is_paid = models.BooleanField(default=False, verbose_name=_("Paid")) paid_at = models.DateTimeField(null=True, editable=False) order = models.ForeignKey(Order, editable=False, on_delete=models.PROTECT) location = models.ForeignKey( Location, null=True, blank=True, editable=False, on_delete=models.SET_NULL ) customer = models.ForeignKey( Customer, null=True, editable=False, on_delete=models.SET_NULL ) # We remember the following so that the customer info on the invoice # doesn't change if the customer is modified or deleted customer_name = models.CharField( max_length=255, default=_("Walk-in"), verbose_name=_("Name") ) customer_phone = models.CharField( null=True, blank=True, max_length=128, verbose_name=_("Phone") ) customer_email = models.CharField( null=True, blank=True, max_length=128, verbose_name=_("Email") ) customer_address = models.CharField( null=True, blank=True, max_length=255, verbose_name=_("Address") ) reference = models.CharField( null=True, blank=True, max_length=255, verbose_name=_("Reference") ) total_net = models.DecimalField(max_digits=8, decimal_places=2) # total w/o taxes total_tax = models.DecimalField(max_digits=8, decimal_places=2) # total taxes total_gross = models.DecimalField(max_digits=8, decimal_places=2) # total with taxes total_margin = models.DecimalField( max_digits=8, decimal_places=2, editable=False ) def get_payment_total(self): from django.db.models import Sum result = self.payment_set.all().aggregate(Sum('amount')) return result['amount__sum'] def get_payment_methods(self): """ Returns the different payment methods used in this invoice """ payments = self.payment_set.all() return [x.get_method_display() for x in payments] def dispatch(self, products): """ Dispatches the products in this invoice from the inventory """ for p in products: soi = ServiceOrderItem.objects.get(pk=p) InvoiceItem.from_soi(soi, self) soi.product.sell(soi.amount, self.location) soi.dispatched = True soi.save() def get_absolute_url(self): from django.urls import reverse return reverse("invoices-view_invoice", args=[self.pk]) def save(self, *args, **kwargs): if self.location is None: self.location = self.order.location if self.pk is None: description = _(u'Order %s dispatched') % self.order.code self.order.notify('dispatched', description, self.created_by) return super(Invoice, self).save(*args, **kwargs) class Meta: ordering = ('-id',) app_label = 'servo' get_latest_by = "id" class InvoiceItem(AbstractOrderItem): """A line item on an invoice.""" invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE) price = models.DecimalField( max_digits=8, decimal_places=2, verbose_name=_("Sales Price") ) @classmethod def from_soi(cls, soi, invoice, invoice_item=None): """ Copies SalesOrderItem into an InvoiceItem """ if invoice_item: i = invoice_item else: i = cls(invoice=invoice) i.sn = soi.sn i.code = soi.code i.title = soi.title i.price = soi.price i.amount = soi.amount i.product = soi.product i.description = soi.description i.created_by = invoice.created_by i.save() return i class Meta: app_label = "servo" class Payment(models.Model): invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE) METHODS = ( (0, _("No Charge")), (1, _("Cash")), (2, _("Invoice")), (3, _("Credit Card")), (4, _("Mail payment")), (5, _("Online payment")) ) method = models.IntegerField( choices=METHODS, default=METHODS[0][0], verbose_name=_("Payment Method") ) created_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL) created_at = models.DateTimeField(auto_now_add=True) amount = models.DecimalField(max_digits=8, decimal_places=2) def save(self, *args, **kwargs): invoice = self.invoice if self.method > 0: description = _(u'Payment for %0.2f received') % self.amount invoice.order.notify('paid', description, self.created_by) if invoice.paid_at is None: if invoice.get_payment_total() == invoice.total_gross: invoice.paid_at = timezone.now() invoice.save() class Meta: app_label = "servo"