from django.db import models
from django.contrib.auth import get_user_model
from django.core.files.base import ContentFile
from django.utils import timezone
from dateutil.relativedelta import relativedelta
from pyinvoice.models import InvoiceInfo, ServiceProviderInfo, ClientInfo, Item, Transaction, PDFInfo
from pyinvoice.templates import SimpleInvoice
from polymorphic.models import PolymorphicModel
from django_countries.fields import CountryField
from io import BytesIO
import urllib.request
from dbsettings.functions import getValue
from .functions import invoice_upload_path
from auction.models import Inquiry
class BillingAddress(models.Model):
user = models.ForeignKey(get_user_model(), models.CASCADE)
company = models.CharField(max_length=64, null=True, blank=True)
vat_id = models.CharField(max_length=32, null=True, blank=True)
first_name = models.CharField(max_length=64)
last_name = models.CharField(max_length=64)
street = models.CharField(max_length=128)
city = models.CharField(max_length=64)
zip = models.CharField(max_length=16)
state = models.CharField(max_length=32, null=True, blank=True)
country = CountryField()
class Invoice(models.Model):
user = models.ForeignKey(get_user_model(), models.PROTECT)
billing_address = models.ForeignKey(BillingAddress, models.PROTECT)
currency = models.CharField(max_length=3)
tax_rate = models.DecimalField(max_digits=4, decimal_places=2)
invoice = models.FileField(null=True, blank=True, upload_to=invoice_upload_path)
inquiry = models.OneToOneField(Inquiry, null=True, blank=True, on_delete=models.SET_NULL)
@property
def price_net(self):
price = 0
for item in self.invoiceitem_set().all():
price += item.net_total
return price
@property
def tax(self):
return round(self.price_net * self.tax_rate / 100, 2)
@property
def price_gross(self):
return self.price_net + self.tax
@property
def payment_instructions(self):
return "Alle Preise in %s." % self.currency
@property
def url(self):
return False
@property
def is_paid(self):
paid_amount = 0
for payment in self.invoicepayment_set().all():
paid_amount += payment.amount
if paid_amount >= price_gross:
return True
return False
def generate_invoice(self):
output = BytesIO()
pdfinfo = PDFInfo(title='Rechnung', author='Kumi Media Ltd.', subject='Rechnung')
doc = SimpleInvoice(output, pdfinfo)
doc.is_paid = self.is_paid
doc.invoice_info = InvoiceInfo("%s%i" % (getValue("billing.prefix", ""), self.id), timezone.now().date(), timezone.now().date())
provider_data = { key: value for key, value in {
"name": getValue("billing.provider.name", False),
"street": getValue("billing.provider.street", False),
"city": getValue("billing.provider.city", False),
"state": getValue("billing.provider.state", False),
"country": getValue("billing.provider.country", False),
"post_code": getValue("billing.provider.zip", False),
"vat_tax_number": getValue("billing.provider.vat_id", False)
}.items() if value}
doc.service_provider_info = ServiceProviderInfo(**provider_data)
logo_url = getValue("billing.logo_url", False)
if logo_url:
try:
logo = BytesIO(urllib.request.urlopen(logo_url).read())
doc.logo(logo)
except:
pass
profile = self.user.clientprofile if self.inquiry else self.user.partnerprofile
client_data = { key: value for key, value in {
"name": profile.full_name,
"street": profile.street,
"city": profile.city,
"state": profile.state,
"country": profile.country,
"post_code": profile.zip,
"vat_tax_number": profile.vat_id,
"email": self.user.email,
}.items() if value}
doc.client_info = ClientInfo(**client_data)
for item in self.invoiceitem_set().all():
doc.add_item(Item(item.name, item.description, item.count, item.net_each))
doc.set_item_tax_rate(self.tax_rate)
for payment in self.invoicepayment_set().all():
doc.add_transaction(Transaction(payment.gateway, payment.gateway_id, payment.timestamp, payment.amount))
bottom_tip = (f"{ self.payment_instructions }
" if self.payment_instructions else "") + getValue("billing.bottom_tip", "")
bottom_tip += "
" if bottom_tip else ""
bottom_tip += "Rechnung erstellt: %s" % str(timezone.now())
doc.set_bottom_tip(bottom_tip)
doc.finish()
self.invoice = ContentFile(output.getvalue(), "invoice.pdf")
self.save()
class InvoiceItem(models.Model):
invoice = models.ForeignKey(Invoice, models.CASCADE)
name = models.CharField(max_length=64)
description = models.CharField(max_length=256, null=True, blank=True)
count = models.IntegerField()
net_each = models.DecimalField(max_digits=11, decimal_places=2)
class InvoicePayment(PolymorphicModel):
invoice = models.ForeignKey(Invoice, models.PROTECT)
amount = models.DecimalField(max_digits=9, decimal_places=2)
gateway_id = models.CharField(max_length=256)
timestamp = models.DateTimeField(default=timezone.now)
@property
def gateway(self):
raise NotImplementedError("%s does not implement gateway" % type(self))
@classmethod
def initiate(cls, invoice):
raise NotImplementedError("%s does not implement initiate()" % cls.__name__)