2021-03-21 15:50:50 +00:00
|
|
|
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
|
|
|
|
|
2021-03-24 06:43:05 +00:00
|
|
|
from auction.models import Inquiry
|
|
|
|
|
2021-03-21 15:50:50 +00:00
|
|
|
class BillingAddress(models.Model):
|
|
|
|
user = models.ForeignKey(get_user_model(), models.CASCADE)
|
2021-03-22 17:42:07 +00:00
|
|
|
company = models.CharField(max_length=64, null=True, blank=True)
|
|
|
|
vat_id = models.CharField(max_length=32, null=True, blank=True)
|
2021-03-21 15:50:50 +00:00
|
|
|
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)
|
2021-03-24 06:43:05 +00:00
|
|
|
inquiry = models.OneToOneField(Inquiry, null=True, blank=True, on_delete=models.SET_NULL)
|
2021-03-21 15:50:50 +00:00
|
|
|
|
|
|
|
@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
|
2021-03-24 06:43:05 +00:00
|
|
|
|
|
|
|
@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
|
2021-03-21 15:50:50 +00:00
|
|
|
|
|
|
|
def generate_invoice(self):
|
|
|
|
output = BytesIO()
|
|
|
|
|
|
|
|
pdfinfo = PDFInfo(title='Rechnung', author='Kumi Media Ltd.', subject='Rechnung')
|
|
|
|
doc = SimpleInvoice(output, pdfinfo)
|
|
|
|
|
2021-03-24 06:43:05 +00:00
|
|
|
doc.is_paid = self.is_paid
|
2021-03-21 15:50:50 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2021-03-24 06:43:05 +00:00
|
|
|
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)
|
2021-03-21 15:50:50 +00:00
|
|
|
|
|
|
|
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 }<br /><br />" if self.payment_instructions else "") + getValue("billing.bottom_tip", "")
|
|
|
|
bottom_tip += "<br /><br />" 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))
|
|
|
|
|
2021-03-22 17:42:07 +00:00
|
|
|
@classmethod
|
|
|
|
def initiate(cls, invoice):
|
|
|
|
raise NotImplementedError("%s does not implement initiate()" % cls.__name__)
|