Klaus-Uwe Mitterer
f6b7fab525
Modifying inquiry model and creating offer model Creating client profile views Fixing stars and hearts template tags
163 lines
5.6 KiB
Python
163 lines
5.6 KiB
Python
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 }<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))
|
|
|
|
@classmethod
|
|
def initiate(cls, invoice):
|
|
raise NotImplementedError("%s does not implement initiate()" % cls.__name__)
|