From 12323283b31b494c6ab2093c35375674fad0f47b Mon Sep 17 00:00:00 2001
From: Dominik George <dominik.george@teckids.org>
Date: Sat, 12 Mar 2022 12:41:39 +0100
Subject: [PATCH] Re-model manual invoices to use only one Invoice object

---
 aleksis/apps/tezor/models/invoice.py | 50 +++++++++++-----------------
 1 file changed, 19 insertions(+), 31 deletions(-)

diff --git a/aleksis/apps/tezor/models/invoice.py b/aleksis/apps/tezor/models/invoice.py
index 8954842..28fc66c 100644
--- a/aleksis/apps/tezor/models/invoice.py
+++ b/aleksis/apps/tezor/models/invoice.py
@@ -2,6 +2,7 @@ from django.conf import settings
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
+from django.db.models import Q
 from django.shortcuts import reverse
 from django.utils.translation import gettext_lazy as _
 
@@ -52,6 +53,10 @@ class Invoice(BasePayment, PureDjangoModel):
     for_object_id = models.PositiveIntegerField()
     for_object = GenericForeignKey("for_content_type", "for_object_id")
 
+    # For manual invoicing
+    person = models.ForeignKey(Person, on_delete=models.SET_NULL, verbose_name=_("Invoice recipient (person)"), blank=True, null=True)
+    items = models.ManyToManyField("InvoiceItem", verbose_name=_("Invoice items"))
+
     @classmethod
     def get_variant_choices(cls):
         choices = []
@@ -66,10 +71,15 @@ class Invoice(BasePayment, PureDjangoModel):
         return self.__class__.VARIANT_DISPLAY[self.variant][1]
 
     def get_purchased_items(self):
-        return self.for_object.get_purchased_items()
+        for item in self.items.all():
+            yield item.as_purchased_item()
+        else:
+            return self.for_object.get_purchased_items()
 
     def get_person(self):
-        if hasattr(self.for_object, "person"):
+        if self.person:
+            return self.person
+        elif hasattr(self.for_object, "person"):
             return self.for_object.person
         elif hasattr(self.for_object, "get_person"):
             return self.for_object.get_person()
@@ -78,7 +88,8 @@ class Invoice(BasePayment, PureDjangoModel):
 
     class Meta:
         constraints = [
-            models.UniqueConstraint(fields=["number", "group"], name="number_uniq_per_group")
+            models.UniqueConstraint(fields=["number", "group"], name="number_uniq_per_group"),
+            models.CheckConstraint(Q(for_object_id__isnull=True) | Q(person__isnull=True)),
         ]
 
     @property
@@ -116,35 +127,12 @@ class Invoice(BasePayment, PureDjangoModel):
         return reverse("invoice_by_token", kwargs={"slug": self.token})
 
 
-class ManualInvoice(ExtensibleModel):
-    invoice_group = models.ForeignKey(InvoiceGroup, on_delete=models.CASCADE, verbose_name=_("Invoice group"))
-    person = models.ForeignKey(Person, on_delete=models.SET_NULL, verbose_name=_("Invoice recipient (person)"))
-
-    item_description = models.CharField(max_length=255, verbose_name=_("Purchased item"))
+class InvoiceItem(ExtensibleModel):
+    sku = models.CharField(max_length=255, verbose_name=_("Article no."), blank=True)
+    description = models.CharField(max_length=255, verbose_name=_("Purchased item"))
     price = models.DecimalField(verbose_name=_("Item gross price"), max_digits=9, decimal_places=2, default="0.0")
     currency = models.CharField(max_length=10, verbose_name=_("Currency"))
     tax_rate = models.DecimalField(verbose_name=_("Tax rate"), max_digits=4, decimal_places=1, default="0.0")
 
-    variant = models.CharField(verbose_name=_("Payment variant"), max_length=255, blank=True)
-
-    def get_purchased_items(self):
-        yield PurchasedItem(name=self.item_description, quantity=1, price=self.price, currency=self.currency, sku="", tax_rate=self.tax_rate)
-
-    def get_invoice(self):
-        invoice, __ = Invoice.objects.get_or_create(for_content_type=ContentType.objects.get_for_model(self), for_object_id=self.pk, defaults={
-            "group": self.invoice_group,
-            "variant": self.variant,
-            "number": f"FIXME",
-            "currency": self.currency,
-            "total": self.price,
-            "tax": self.total / (self.tax_rate + 100) * self.tax_rate,
-            "description": self.item_description,
-            "billing_first_name": self.person.first_name,
-            "billing_last_name": self.person.last_name,
-            "billing_address_1": f"{self.person.street} {self.person.housenumber}",
-            "billing_city": self.person.place,
-            "billing_postcode": self.person.postal_code,
-            "billing_email": self.person.email,
-        })
-
-        return invoice
+    def as_purchased_item(self):
+        yield PurchasedItem(name=self.description, quantity=1, price=self.price, currency=self.currency, sku=self.sku, tax_rate=self.tax_rate)
-- 
GitLab