commit a bunch of invoice related stuff - generate pdf invoice, save to archive, email to customer

This commit is contained in:
Thomas Steen Rasmussen 2016-05-30 16:58:55 +02:00
parent aba0596252
commit 935af5f188
7 changed files with 162 additions and 58 deletions

View file

@ -103,3 +103,5 @@ TICKET_CATEGORY_ID = env('TICKET_CATEGORY_ID')
COINIFY_API_KEY = env('COINIFY_API_KEY') COINIFY_API_KEY = env('COINIFY_API_KEY')
COINIFY_API_SECRET = env('COINIFY_API_SECRET') COINIFY_API_SECRET = env('COINIFY_API_SECRET')
PDF_ARCHIVE_PATH='/usr/local/www/pdf_arhcive/'

View file

@ -7,6 +7,7 @@ EMAIL_HOST_USER='mymailuser'
EMAIL_HOST_PASSWORD='mymailpassword' EMAIL_HOST_PASSWORD='mymailpassword'
EMAIL_USE_TLS=True EMAIL_USE_TLS=True
EMAIL_FROM='noreply@example.com' EMAIL_FROM='noreply@example.com'
ARCHIVE_EMAIL='archive@example.com'
EPAY_MERCHANT_NUMBER=something EPAY_MERCHANT_NUMBER=something
EPAY_MD5_SECRET=something EPAY_MD5_SECRET=something
TICKET_CATEGORY_ID='' TICKET_CATEGORY_ID=''

View file

@ -20,6 +20,7 @@ EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD')
EMAIL_USE_TLS = env('EMAIL_USE_TLS') EMAIL_USE_TLS = env('EMAIL_USE_TLS')
DEFAULT_FROM_EMAIL = env('DEFAULT_FROM_EMAIL') DEFAULT_FROM_EMAIL = env('DEFAULT_FROM_EMAIL')
SERVER_EMAIL = env('DEFAULT_FROM_EMAIL') SERVER_EMAIL = env('DEFAULT_FROM_EMAIL')
ARCHIVE_EMAIL = env('ARCHIVE_EMAIL')
LOGGING = { LOGGING = {
'version': 1, 'version': 1,

68
shop/email.py Normal file
View file

@ -0,0 +1,68 @@
from django.core.mail import EmailMultiAlternatives
from django.conf import settings
from django.template.loader import render_to_string
def send_email(emailtype, recipient, formatdict, subject, sender='BornHack <noreply@bornhack.dk>', attachment=None):
### determine email type, set template and attachment vars
html_template=None
if emailtype == 'invoice':
text_template = 'emails/invoice.txt'
html_template = 'emails/invoice.html'
attachment_filename = formatdict['attachmentname']
elif emailtype == 'testmail':
text_template = 'emails/testmail.txt'
else:
print 'Unknown email type: %s' % emailtype
return False
try:
### put the basic email together
msg = EmailMultiAlternatives(subject, render_to_string(text_template, formatdict), sender, [recipient], [settings.ARCHIVE_EMAIL])
### is there a html version of this email?
if html_template:
msg.attach_alternative(render_to_string(html_template, formatdict), 'text/html')
### is there an attachment to this mail?
if attachment:
msg.attach(attachment_filename, attachment, 'application/pdf')
except Exception as E:
print 'exception while rendering email: %s' % E
return False
### send the email
msg.send()
### all good
return True
def send_invoice_email(invoice, attachment):
# put formatdict together
formatdict = {
'order': invoice.order,
'attachmentname': invoice.filename,
}
subject = 'BornHack invoice %s' % order.pk
# send mail
return send_email(
emailtype='invoice',
recipient=order.user.email,
formatdict=formatdict,
subject=subject,
sender='noreply@bornfiber.dk',
)
def send_test_email(recipient):
return send_email(
emailtype='testmail',
recipient=recipient,
subject='testmail from bornhack website',
)

View file

@ -5,7 +5,7 @@ from shop.email import send_invoice_email
class Command(BaseCommand): class Command(BaseCommand):
args = 'none' args = 'none'
help = 'Send out invoices that has not been sent yet' help = 'Send out invoices that have not been sent yet'
def handle(self, *args, **options): def handle(self, *args, **options):
self.stdout.write('Invoice worker running...') self.stdout.write('Invoice worker running...')
@ -14,25 +14,47 @@ class Command(BaseCommand):
for order in Order.objects.filter(paid=True, invoice__isnull=True): for order in Order.objects.filter(paid=True, invoice__isnull=True):
### generate invoice for this Order ### generate invoice for this Order
Invoice.objects.create(order=order) Invoice.objects.create(order=order)
self.stdout.write('Generated Invoice object for order %s' % order)
### check if we need to generate any pdf invoices
for invoice in Invoice.objects.filter(pdf_generated=False):
# put the dict with data for the pdf together
formatdict = {
'invoice': invoice,
}
# generate the pdf
try:
pdffile = generate_pdf_letter(
filename=invoice.filename,
template='invoice.html',
formatdict=formatdict,
)
self.stdout.write('Generated pdf for invoice %s' % invoice)
except Exception as E:
self.stdout.write('ERROR: Unable to generate PDF file for invoice #%s. Error: %s' % (invoice.pk, E))
continue
# so, do we have a pdf?
if not pdffile:
self.stdout.write('ERROR: Unable to generate PDF file for invoice #%s' % invoice.pk)
continue
# update invoice object
invoice.pdf_generated=True
invoice.save()
### check if we need to send out any invoices ### check if we need to send out any invoices
for invoice in Invoice.objects.filter(sent_to_customer=False): for invoice in Invoice.objects.filter(sent_to_customer=False, pdf_generated=True):
### generate PDF invoice # read the pdf invoice from the archive
try: with open(settings.INVOICE_ARCHIVE_PATH+invoice.filename, 'r') as fh:
pdffile = generate_pdf_letter('invoice.html', formatdict) pdffile = fh.read()
except Exception as E:
self.stdout.write('ERROR: Unable to generate PDF file. Error: %s' % E)
continue
if not pdffile:
self.stdout.write('ERROR: Unable to generate PDF file')
continue
if send_invoice_email(recipient=invoice.order.user.email, invoice=invoice, attachment=pdffile): # send the email
if send_invoice_email(invoice=invoice, attachment=pdffile):
self.stdout.write('OK: Invoice email sent to %s' % order.user.email) self.stdout.write('OK: Invoice email sent to %s' % order.user.email)
invoice.sent_to_customer=True invoice.sent_to_customer=True
invoice.save() invoice.save()
else: else:
self.stdout.write('ERROR: Unable to send invoice email to %s' % order.user.email) self.stdout.write('ERROR: Unable to send invoice email for order %s to %s' % (order.pk, order.user.email))
### pause for a bit ### pause for a bit
sleep(60) sleep(60)

View file

@ -252,6 +252,7 @@ class EpayPayment(CreatedUpdatedModel, UUIDModel):
class Invoice(CreatedUpdatedModel): class Invoice(CreatedUpdatedModel):
order = models.OneToOneField('shop.Order') order = models.OneToOneField('shop.Order')
pdf_generated = models.BooleanField(default=False)
sent_to_customer = models.BooleanField(default=False) sent_to_customer = models.BooleanField(default=False)
def __str__(self): def __str__(self):
@ -263,6 +264,10 @@ class Invoice(CreatedUpdatedModel):
self.sent_to_customer, self.sent_to_customer,
) )
@property
def filename(self):
return 'bornhack_invoice_%s.pdf' % self.pk
class CoinifyAPIInvoice(CreatedUpdatedModel): class CoinifyAPIInvoice(CreatedUpdatedModel):
invoicejson = JSONField() invoicejson = JSONField()

View file

@ -1,45 +1,50 @@
from wkhtmltopdf.views import PDFTemplateResponse from wkhtmltopdf.views import PDFTemplateResponse
from PyPDF2 import PdfFileWriter, PdfFileReader from PyPDF2 import PdfFileWriter, PdfFileReader
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django.conf import settings from django.conf import settings
import StringIO import StringIO
def generate_pdf_letter(template, formatdict): def generate_pdf_letter(filename, template, formatdict):
### produce text-only PDF from template ### produce text-only PDF from template
pdfgenerator = PDFTemplateResponse( pdfgenerator = PDFTemplateResponse(
request=RequestFactory().get('/'), request=RequestFactory().get('/'),
template=template, template=template,
context=formatdict, context=formatdict,
cmd_options={ cmd_options={
'margin-top': 40, 'margin-top': 40,
'margin-bottom': 50, 'margin-bottom': 50,
}, },
) )
textonlypdf = StringIO.StringIO() textonlypdf = StringIO.StringIO()
textonlypdf.write(pdfgenerator.rendered_content) textonlypdf.write(pdfgenerator.rendered_content)
### create a blank pdf to work with ### create a blank pdf to work with
finalpdf = PdfFileWriter() finalpdf = PdfFileWriter()
### open the text-only pdf ### open the text-only pdf
pdfreader = PdfFileReader(textonlypdf) pdfreader = PdfFileReader(textonlypdf)
### get watermark from watermark file ### get watermark from watermark file
watermark = PdfFileReader(open(settings.LETTERHEAD_PDF_PATH, 'rb')) watermark = PdfFileReader(open(settings.LETTERHEAD_PDF_PATH, 'rb'))
### add the watermark to all pages ### add the watermark to all pages
for pagenum in xrange(pdfreader.getNumPages()): for pagenum in xrange(pdfreader.getNumPages()):
page = pdfreader.getPage(pagenum) page = pdfreader.getPage(pagenum)
try: try:
page.mergePage(watermark.getPage(0)) page.mergePage(watermark.getPage(0))
except ValueError: except ValueError:
### watermark pdf might be broken? ### watermark pdf might be broken?
return False return False
### add page to output ### add page to output
finalpdf.addPage(page) finalpdf.addPage(page)
### return a file object with the data ### save the generated pdf to the archive
returnfile = StringIO.StringIO() with open(settings.PDF_ARCHIVE_PATH+filename, 'w') as fh:
finalpdf.write(returnfile) finalpdf.write(fh)
return returnfile self.stdout.write('Saved pdf to archive: %s' % settings.PDF_ARCHIVE_PATH+filename)
### return a file object with the data
returnfile = StringIO.StringIO()
finalpdf.write(returnfile)
return returnfile