commit a bunch of invoice related stuff - generate pdf invoice, save to archive, email to customer
This commit is contained in:
parent
aba0596252
commit
935af5f188
|
@ -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/'
|
|
@ -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=''
|
||||||
|
|
|
@ -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
68
shop/email.py
Normal 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',
|
||||||
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
95
shop/pdf.py
95
shop/pdf.py
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue