Merge branch 'master' of https://bitbucket.org/yackob03/quay
This commit is contained in:
commit
a3f40f2a90
5 changed files with 58 additions and 12 deletions
|
@ -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)
|
||||||
|
|
|
@ -17,3 +17,4 @@ apscheduler
|
||||||
python-daemon
|
python-daemon
|
||||||
paramiko
|
paramiko
|
||||||
python-digitalocean
|
python-digitalocean
|
||||||
|
xhtml2pdf
|
|
@ -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
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Reference in a new issue