This commit is contained in:
root 2013-11-18 19:58:50 +00:00
commit a3f40f2a90
5 changed files with 58 additions and 12 deletions

View file

@ -1,15 +1,16 @@
import logging import logging
import requests import requests
import stripe
from flask import (abort, redirect, request, url_for, render_template, from flask import (abort, redirect, request, url_for, render_template,
make_response) make_response, Response)
from flask.ext.login import login_user, UserMixin, login_required from flask.ext.login import login_user, UserMixin, login_required
from flask.ext.principal import identity_changed, Identity, AnonymousIdentity from flask.ext.principal import identity_changed, Identity, AnonymousIdentity
from data import model from data import model
from app import app, login_manager, mixpanel from app import app, login_manager, mixpanel
from auth.permissions import QuayDeferredPermissionUser from auth.permissions import QuayDeferredPermissionUser, AdministerOrganizationPermission
from util.invoice import renderInvoiceToPdf
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -102,6 +103,23 @@ def privacy():
return render_template('privacy.html') return render_template('privacy.html')
@app.route('/receipt', methods=['GET'])
def receipt():
id = request.args.get('id')
if id:
invoice = stripe.Invoice.retrieve(id)
if invoice:
org = model.get_user_or_org_by_customer_id(invoice.customer)
if org and org.organization:
admin_org = AdministerOrganizationPermission(org.username)
if admin_org.can():
file_data = renderInvoiceToPdf(invoice, org)
return Response(file_data,
mimetype="application/pdf",
headers={"Content-Disposition":
"attachment;filename=receipt.pdf"})
abort(404)
def common_login(db_user): def common_login(db_user):
if login_user(_LoginWrappedDBUser(db_user.username, db_user)): if login_user(_LoginWrappedDBUser(db_user.username, db_user)):
logger.debug('Successfully signed in as: %s' % db_user.username) logger.debug('Successfully signed in as: %s' % db_user.username)

View file

@ -17,3 +17,4 @@ apscheduler
python-daemon python-daemon
paramiko paramiko
python-digitalocean python-digitalocean
xhtml2pdf

View file

@ -5,29 +5,34 @@ Flask-Mail==0.9.0
Flask-Principal==0.4.0 Flask-Principal==0.4.0
Jinja2==2.7.1 Jinja2==2.7.1
MarkupSafe==0.18 MarkupSafe==0.18
Pillow==2.2.1
PyMySQL==0.6.1 PyMySQL==0.6.1
Werkzeug==0.9.4 Werkzeug==0.9.4
argparse==1.2.1 argparse==1.2.1
beautifulsoup4==4.3.2 beautifulsoup4==4.3.2
blinker==1.3 blinker==1.3
boto==2.15.0 boto==2.17.0
distribute==0.6.34 distribute==0.6.34
ecdsa==0.10 ecdsa==0.10
eventlet==0.14.0 eventlet==0.14.0
greenlet==0.4.1 greenlet==0.4.1
gunicorn==18.0 gunicorn==18.0
html5lib==1.0b3
itsdangerous==0.23 itsdangerous==0.23
lockfile==0.9.1 lockfile==0.9.1
marisa-trie==0.5.1 marisa-trie==0.5.1
mixpanel-py==3.0.0 mixpanel-py==3.0.0
paramiko==1.12.0 paramiko==1.12.0
peewee==2.1.4 peewee==2.1.5
py-bcrypt==0.4 py-bcrypt==0.4
pyPdf==1.13
pycrypto==2.6.1 pycrypto==2.6.1
python-daemon==1.6 python-daemon==1.6
python-dateutil==2.1 python-dateutil==2.2
python-digitalocean==0.5 python-digitalocean==0.5.1
requests==2.0.0 reportlab==2.7
requests==2.0.1
six==1.4.1 six==1.4.1
stripe==1.9.8 stripe==1.9.8
wsgiref==0.1.2 wsgiref==0.1.2
xhtml2pdf==0.0.5

View file

@ -49,12 +49,13 @@
<th>Billing Date/Time</th> <th>Billing Date/Time</th>
<th>Amount Due</th> <th>Amount Due</th>
<th>Status</th> <th>Status</th>
<th></th>
</thead> </thead>
<tbody class="invoice" ng-repeat="invoice in invoices"> <tbody class="invoice" ng-repeat="invoice in invoices">
<tr class="invoice-title" ng-click="toggleInvoice(invoice.id)"> <tr class="invoice-title">
<td><span class="invoice-datetime">{{ invoice.date * 1000 | date:'medium' }}</span></td> <td ng-click="toggleInvoice(invoice.id)"><span class="invoice-datetime">{{ invoice.date * 1000 | date:'medium' }}</span></td>
<td><span class="invoice-amount">{{ invoice.amount_due / 100 }}</span></td> <td ng-click="toggleInvoice(invoice.id)"><span class="invoice-amount">{{ invoice.amount_due / 100 }}</span></td>
<td> <td>
<span class="invoice-status"> <span class="invoice-status">
<span class="success" ng-show="invoice.paid">Paid - Thank you!</span> <span class="success" ng-show="invoice.paid">Paid - Thank you!</span>
@ -63,6 +64,11 @@
<span class="pending" ng-show="!invoice.paid && !invoice.attempted">Payment pending</span> <span class="pending" ng-show="!invoice.paid && !invoice.attempted">Payment pending</span>
</span> </span>
</td> </td>
<td>
<a ng-show="invoice.paid" href="/receipt?id={{ invoice.id }}" download="receipt.pdf" target="_new">
<i class="fa fa-download" title="Download Receipt" bs-tooltip="tooltip.title"></i>
</a>
</td>
</tr> </tr>
<tr ng-class="invoiceExpanded[invoice.id] ? 'in' : 'out'" class="invoice-details panel-collapse collapse"> <tr ng-class="invoiceExpanded[invoice.id] ? 'in' : 'out'" class="invoice-details panel-collapse collapse">

View file

@ -1,5 +1,7 @@
from datetime import datetime from datetime import datetime
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
from xhtml2pdf import pisa
import StringIO
jinja_options = { jinja_options = {
"loader": FileSystemLoader('util'), "loader": FileSystemLoader('util'),
@ -7,6 +9,20 @@ jinja_options = {
env = Environment(**jinja_options) env = Environment(**jinja_options)
def renderInvoiceToPdf(invoice, user):
""" Renders a nice PDF display for the given invoice. """
sourceHtml = renderInvoiceToHtml(invoice, user)
output = StringIO.StringIO()
pisaStatus = pisa.CreatePDF(sourceHtml, dest=output)
if pisaStatus.err:
return None
value = output.getvalue()
output.close()
return value
def renderInvoiceToHtml(invoice, user): def renderInvoiceToHtml(invoice, user):
""" Renders a nice HTML display for the given invoice. """ """ Renders a nice HTML display for the given invoice. """
def get_price(price): def get_price(price):