commit creditnote stuff
This commit is contained in:
parent
da4843bb06
commit
c6d4c1edc3
|
@ -7,6 +7,7 @@ admin.site.register(models.EpayPayment)
|
||||||
admin.site.register(models.CoinifyAPIInvoice)
|
admin.site.register(models.CoinifyAPIInvoice)
|
||||||
admin.site.register(models.CoinifyAPICallback)
|
admin.site.register(models.CoinifyAPICallback)
|
||||||
admin.site.register(models.Invoice)
|
admin.site.register(models.Invoice)
|
||||||
|
admin.site.register(models.CreditNote)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.ProductCategory)
|
@admin.register(models.ProductCategory)
|
||||||
|
@ -55,12 +56,12 @@ class OrderAdmin(admin.ModelAdmin):
|
||||||
]
|
]
|
||||||
|
|
||||||
list_filter = [
|
list_filter = [
|
||||||
'user',
|
|
||||||
'camp',
|
'camp',
|
||||||
'payment_method',
|
'payment_method',
|
||||||
'open',
|
'open',
|
||||||
'paid',
|
'paid',
|
||||||
'cancelled',
|
'cancelled',
|
||||||
|
'user',
|
||||||
]
|
]
|
||||||
|
|
||||||
exclude = ['products']
|
exclude = ['products']
|
||||||
|
|
|
@ -11,6 +11,10 @@ def send_email(emailtype, recipient, formatdict, subject, sender='BornHack <info
|
||||||
text_template = 'emails/invoice_email.txt'
|
text_template = 'emails/invoice_email.txt'
|
||||||
html_template = 'emails/invoice_email.html'
|
html_template = 'emails/invoice_email.html'
|
||||||
attachment_filename = formatdict['filename']
|
attachment_filename = formatdict['filename']
|
||||||
|
if emailtype == 'creditnote':
|
||||||
|
text_template = 'emails/creditnote_email.txt'
|
||||||
|
html_template = 'emails/creditnote_email.html'
|
||||||
|
attachment_filename = formatdict['creditnote'].filename
|
||||||
elif emailtype == 'testmail':
|
elif emailtype == 'testmail':
|
||||||
text_template = 'emails/testmail.txt'
|
text_template = 'emails/testmail.txt'
|
||||||
else:
|
else:
|
||||||
|
@ -40,6 +44,24 @@ def send_email(emailtype, recipient, formatdict, subject, sender='BornHack <info
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def send_creditnote_email(creditnote):
|
||||||
|
# put formatdict together
|
||||||
|
formatdict = {
|
||||||
|
'creditnote': creditnote,
|
||||||
|
}
|
||||||
|
|
||||||
|
subject = 'BornHack creditnote %s' % creditnote.pk
|
||||||
|
|
||||||
|
# send mail
|
||||||
|
return send_email(
|
||||||
|
emailtype='creditnote',
|
||||||
|
recipient=creditnote.user.email,
|
||||||
|
formatdict=formatdict,
|
||||||
|
subject=subject,
|
||||||
|
sender='info@bornhack.dk',
|
||||||
|
attachment=creditnote.pdf.read(),
|
||||||
|
)
|
||||||
|
|
||||||
def send_invoice_email(invoice):
|
def send_invoice_email(invoice):
|
||||||
# put formatdict together
|
# put formatdict together
|
||||||
formatdict = {
|
formatdict = {
|
||||||
|
|
|
@ -3,15 +3,15 @@ from django.core.files import File
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from shop.pdf import generate_pdf_letter
|
from shop.pdf import generate_pdf_letter
|
||||||
from shop.email import send_invoice_email
|
from shop.email import send_invoice_email, send_creditnote_email
|
||||||
from shop.models import Order, Invoice
|
from shop.models import Order, Invoice, CreditNote
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
args = 'none'
|
args = 'none'
|
||||||
help = 'Send out invoices that have not been sent yet'
|
help = 'Generate invoices and credit notes, and email invoices that have not been sent yet'
|
||||||
|
|
||||||
def output(self, message):
|
def output(self, message):
|
||||||
self.stdout.write('%s: %s' % (timezone.now().strftime("%Y-%m-%d %H:%M:%S"), message))
|
self.stdout.write('%s: %s' % (timezone.now().strftime("%Y-%m-%d %H:%M:%S"), message))
|
||||||
|
@ -53,6 +53,7 @@ class Command(BaseCommand):
|
||||||
invoice.pdf.save(invoice.filename, File(pdffile))
|
invoice.pdf.save(invoice.filename, File(pdffile))
|
||||||
invoice.save()
|
invoice.save()
|
||||||
|
|
||||||
|
###############################################################
|
||||||
# check if we need to send out any invoices (only where pdf has been generated)
|
# check if we need to send out any invoices (only where pdf has been generated)
|
||||||
for invoice in Invoice.objects.filter(sent_to_customer=False).exclude(pdf=''):
|
for invoice in Invoice.objects.filter(sent_to_customer=False).exclude(pdf=''):
|
||||||
# send the email
|
# send the email
|
||||||
|
@ -63,6 +64,46 @@ class Command(BaseCommand):
|
||||||
else:
|
else:
|
||||||
self.output('ERROR: Unable to send invoice email for order %s to %s' % (invoice.order.pk, invoice.order.user.email))
|
self.output('ERROR: Unable to send invoice email for order %s to %s' % (invoice.order.pk, invoice.order.user.email))
|
||||||
|
|
||||||
|
###############################################################
|
||||||
|
# check if we need to generate any pdf creditnotes?
|
||||||
|
for creditnote in CreditNote.objects.filter(pdf=''):
|
||||||
|
# put the dict with data for the pdf together
|
||||||
|
formatdict = {
|
||||||
|
'creditnote': creditnote,
|
||||||
|
}
|
||||||
|
|
||||||
|
# generate the pdf
|
||||||
|
try:
|
||||||
|
pdffile = generate_pdf_letter(
|
||||||
|
filename=creditnote.filename,
|
||||||
|
template='pdf/creditnote.html',
|
||||||
|
formatdict=formatdict,
|
||||||
|
)
|
||||||
|
self.output('Generated pdf for creditnote %s' % creditnote)
|
||||||
|
except Exception as E:
|
||||||
|
self.output('ERROR: Unable to generate PDF file for creditnote #%s. Error: %s' % (creditnote.pk, E))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# so, do we have a pdf?
|
||||||
|
if not pdffile:
|
||||||
|
self.output('ERROR: Unable to generate PDF file for creditnote #%s' % creditnote.pk)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# update creditnote object with the file
|
||||||
|
creditnote.pdf.save(creditnote.filename, File(pdffile))
|
||||||
|
creditnote.save()
|
||||||
|
|
||||||
|
###############################################################
|
||||||
|
# check if we need to send out any creditnotes (only where pdf has been generated)
|
||||||
|
for creditnote in CreditNote.objects.filter(sent_to_customer=False).exclude(pdf=''):
|
||||||
|
# send the email
|
||||||
|
if send_creditnote_email(creditnote=creditnote):
|
||||||
|
self.output('OK: Creditnote email sent to %s' % creditnote.user.email)
|
||||||
|
creditnote.sent_to_customer=True
|
||||||
|
creditnote.save()
|
||||||
|
else:
|
||||||
|
self.output('ERROR: Unable to send creditnote email for creditnote %s to %s' % (creditnote.pk, creditnote.user.email))
|
||||||
|
|
||||||
# pause for a bit
|
# pause for a bit
|
||||||
sleep(60)
|
sleep(60)
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,6 @@ class Order(CreatedUpdatedModel):
|
||||||
through='shop.OrderProductRelation'
|
through='shop.OrderProductRelation'
|
||||||
)
|
)
|
||||||
|
|
||||||
user = models.ForeignKey(
|
|
||||||
'auth.User',
|
|
||||||
verbose_name=_('User'),
|
|
||||||
help_text=_('The user this order belongs to.'),
|
|
||||||
related_name='orders',
|
|
||||||
)
|
|
||||||
|
|
||||||
paid = models.BooleanField(
|
paid = models.BooleanField(
|
||||||
verbose_name=_('Paid?'),
|
verbose_name=_('Paid?'),
|
||||||
|
@ -264,17 +258,55 @@ class EpayPayment(CreatedUpdatedModel, UUIDModel):
|
||||||
txnid = models.IntegerField()
|
txnid = models.IntegerField()
|
||||||
|
|
||||||
|
|
||||||
|
class CreditNote(CreatedUpdatedModel):
|
||||||
|
amount = models.DecimalField()
|
||||||
|
text = models.TextField()
|
||||||
|
pdf = models.FileField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
upload_to='creditnotes/'
|
||||||
|
)
|
||||||
|
user = models.ForeignKey(
|
||||||
|
'auth.User',
|
||||||
|
verbose_name=_('User'),
|
||||||
|
help_text=_('The user this credit note belongs to.'),
|
||||||
|
related_name='creditnotes',
|
||||||
|
)
|
||||||
|
paid = models.BooleanField(
|
||||||
|
verbose_name=_('Paid?'),
|
||||||
|
help_text=_('Whether this creditnote has been paid.'),
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
sent_to_customer = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'creditnote#%s - %s DKK (sent to %s: %s)' % (
|
||||||
|
self.id,
|
||||||
|
self.amount,
|
||||||
|
self.user.email,
|
||||||
|
self.sent_to_customer,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vat(self):
|
||||||
|
return Decimal(self.total*Decimal(0.2))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def filename(self):
|
||||||
|
return 'bornhack_creditnote_%s.pdf' % self.pk
|
||||||
|
|
||||||
class Invoice(CreatedUpdatedModel):
|
class Invoice(CreatedUpdatedModel):
|
||||||
order = models.OneToOneField('shop.Order')
|
order = models.OneToOneField('shop.Order')
|
||||||
pdf = models.FileField(null=True, blank=True, upload_to='invoices/')
|
pdf = models.FileField(null=True, blank=True, upload_to='invoices/')
|
||||||
sent_to_customer = models.BooleanField(default=False)
|
sent_to_customer = models.BooleanField(default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'invoice#%s - order %s - %s - total %s DKK (sent to customer: %s)' % (
|
return 'invoice#%s - order %s - %s - total %s DKK (sent to %s: %s)' % (
|
||||||
self.id,
|
self.id,
|
||||||
self.order.id,
|
self.order.id,
|
||||||
self.order.created,
|
self.order.created,
|
||||||
self.order.total,
|
self.order.total,
|
||||||
|
self.order.user.email,
|
||||||
self.sent_to_customer,
|
self.sent_to_customer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
12
shop/templates/emails/creditnote_email.html
Normal file
12
shop/templates/emails/creditnote_email.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Hello!<br>
|
||||||
|
<br>
|
||||||
|
This email contains your creditnote from BornHack.<br>
|
||||||
|
The creditnote number is <b>{{ creditnote.pk }}</b>.<br>
|
||||||
|
<br>
|
||||||
|
The creditnote is attached in PDF format with the filename <b>{{ creditnote.filename }}</b>. The PDF contains the details about what this creditnote covers.<br>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
Best regards,<br>
|
||||||
|
<br>
|
||||||
|
The BornHack Team<br>
|
||||||
|
<br>
|
14
shop/templates/emails/creditnote_email.txt
Normal file
14
shop/templates/emails/creditnote_email.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
Hello!
|
||||||
|
|
||||||
|
This email contains your creditnote from BornHack.
|
||||||
|
The creditnote number is {{ creditnote.pk }}.
|
||||||
|
|
||||||
|
The creditnote is attached in PDF format with
|
||||||
|
the filename {{ creditnote.filename }}. The PDF
|
||||||
|
contains the details about what this creditnote
|
||||||
|
covers.
|
||||||
|
|
||||||
|
|
||||||
|
Best regards,
|
||||||
|
|
||||||
|
The BornHack Team
|
44
shop/templates/pdf/creditnote.html
Normal file
44
shop/templates/pdf/creditnote.html
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{% load static from staticfiles %}
|
||||||
|
{% load shop_tags %}
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
|
||||||
|
<table style="width:100%;">
|
||||||
|
<tr>
|
||||||
|
<td style="width: 75%;"> </td>
|
||||||
|
<td>
|
||||||
|
<h3>
|
||||||
|
{{ creditnote.created|date:"b jS, Y" }}<br>
|
||||||
|
Creditnote #{{ invoice.pk }}
|
||||||
|
</h3>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<h3>Customer: {{ creditnote.order.user.email }}</h3>
|
||||||
|
<br>
|
||||||
|
<h2>CREDITNOTE</h2>
|
||||||
|
<table style="width:90%; margin:1em;">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<b>Text
|
||||||
|
<td>
|
||||||
|
<b>Amount
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ creditnote.text }}
|
||||||
|
<td>
|
||||||
|
{{ creditnote.amount|currency }}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<td>
|
||||||
|
<strong>Danish VAT (25%)</strong>
|
||||||
|
<td>
|
||||||
|
{{ creditnote.vat|currency }}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<td>
|
||||||
|
<strong>Total</strong>
|
||||||
|
<td>
|
||||||
|
{{ creditnote.amount|currency }}
|
||||||
|
</table>
|
Loading…
Reference in a new issue