Remove PDF invoice generation bits - these are unlikely to ever be useful in the open source version.

This commit is contained in:
Pēteris Caune 2019-09-15 18:39:32 +03:00
parent 34925f2cdf
commit accdfb637b
No known key found for this signature in database
GPG Key ID: E28D7679E9A9EDE2
9 changed files with 2 additions and 311 deletions

View File

@ -1,98 +0,0 @@
# coding: utf-8
try:
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import inch
from reportlab.pdfgen.canvas import Canvas
W, H = A4
except ImportError:
# Don't crash if reportlab is not installed.
Canvas = object
def f(dt):
return dt.strftime("%b. %-d, %Y")
class PdfInvoice(Canvas):
def __init__(self, fileobj):
Canvas.__init__(self, fileobj, pagesize=A4, pageCompression=0)
self.head_y = H - inch * 0.5
def linefeed(self):
self.head_y -= inch / 8
def text(self, s, align="left", size=10, bold=False):
self.head_y -= inch / 24
self.linefeed()
self.setFont("Helvetica-Bold" if bold else "Helvetica", size)
if align == "left":
self.drawString(inch * 0.5, self.head_y, s)
elif align == "right":
self.drawRightString(W - inch * 0.5, self.head_y, s)
elif align == "center":
self.drawCentredString(W / 2, self.head_y, s)
self.head_y -= inch / 24
def hr(self):
self.setLineWidth(inch / 72 / 8)
self.line(inch * 0.5, self.head_y, W - inch * 0.5, self.head_y)
def row(self, items, align="left", bold=False, size=10):
self.head_y -= inch / 8
self.linefeed()
self.setFont("Helvetica-Bold" if bold else "Helvetica", size)
self.drawString(inch * 0.5, self.head_y, items[0])
self.drawString(inch * 3.5, self.head_y, items[1])
self.drawString(inch * 5.5, self.head_y, items[2])
self.drawRightString(W - inch * 0.5, self.head_y, items[3])
self.head_y -= inch / 8
def render(self, tx, bill_to):
invoice_id = "MS-HC-%s" % tx.id.upper()
self.setTitle(invoice_id)
self.text("SIA Monkey See Monkey Do", size=16)
self.linefeed()
self.text("Gaujas iela 4-2")
self.text("Valmiera, LV-4201, Latvia")
self.text("VAT: LV44103100701")
self.linefeed()
created = f(tx.created_at)
self.text("Date Issued: %s" % created, align="right")
self.text("Invoice Id: %s" % invoice_id, align="right")
self.linefeed()
self.hr()
self.row(["Description", "Start", "End", tx.currency_iso_code], bold=True)
self.hr()
start = f(tx.subscription_details.billing_period_start_date)
end = f(tx.subscription_details.billing_period_end_date)
if tx.currency_iso_code == "USD":
amount = "$%s" % tx.amount
elif tx.currency_iso_code == "EUR":
amount = "%s" % tx.amount
else:
amount = "%s %s" % (tx.currency_iso_code, tx.amount)
self.row(["healthchecks.io paid plan", start, end, amount])
self.hr()
self.row(["", "", "", "Total: %s" % amount], bold=True)
self.linefeed()
self.text("Bill to:", bold=True)
for s in bill_to.split("\n"):
self.text(s.strip())
self.linefeed()
self.showPage()
self.save()

View File

@ -173,13 +173,6 @@ class Subscription(models.Model):
return self._address return self._address
def flattened_address(self):
if self.address_id:
ctx = {"a": self.address, "email": self.user.email}
return render_to_string("payments/address_plain.html", ctx)
else:
return self.user.email
@property @property
def transactions(self): def transactions(self):
if not hasattr(self, "_tx"): if not hasattr(self, "_tx"):

View File

@ -23,4 +23,4 @@ class BillingHistoryTestCase(BaseTestCase):
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.get("/accounts/profile/billing/history/") r = self.client.get("/accounts/profile/billing/history/")
self.assertContains(r, "123") self.assertContains(r, "123")
self.assertContains(r, "def456") self.assertContains(r, "456")

View File

@ -1,74 +0,0 @@
from mock import Mock, patch
from unittest import skipIf
from django.core import mail
from django.utils.timezone import now
from hc.payments.models import Subscription
from hc.test import BaseTestCase
try:
import reportlab
except ImportError:
reportlab = None
class ChargeWebhookTestCase(BaseTestCase):
def setUp(self):
super(ChargeWebhookTestCase, self).setUp()
self.sub = Subscription(user=self.alice)
self.sub.subscription_id = "test-id"
self.sub.customer_id = "test-customer-id"
self.sub.send_invoices = True
self.sub.save()
self.tx = Mock()
self.tx.id = "abc123"
self.tx.customer_details.id = "test-customer-id"
self.tx.created_at = now()
self.tx.currency_iso_code = "USD"
self.tx.amount = 5
self.tx.subscription_details.billing_period_start_date = now()
self.tx.subscription_details.billing_period_end_date = now()
@skipIf(reportlab is None, "reportlab not installed")
@patch("hc.payments.views.Subscription.objects.by_braintree_webhook")
def test_it_works(self, mock_getter):
mock_getter.return_value = self.sub, self.tx
r = self.client.post("/pricing/charge/")
self.assertEqual(r.status_code, 200)
# See if email was sent
self.assertEqual(len(mail.outbox), 1)
msg = mail.outbox[0]
self.assertEqual(msg.subject, "Invoice from Mychecks")
self.assertEqual(msg.to, ["alice@example.org"])
self.assertEqual(msg.attachments[0][0], "MS-HC-ABC123.pdf")
@patch("hc.payments.views.Subscription.objects.by_braintree_webhook")
def test_it_obeys_send_invoices_flag(self, mock_getter):
mock_getter.return_value = self.sub, self.tx
self.sub.send_invoices = False
self.sub.save()
r = self.client.post("/pricing/charge/")
self.assertEqual(r.status_code, 200)
# It should not send the email
self.assertEqual(len(mail.outbox), 0)
@skipIf(reportlab is None, "reportlab not installed")
@patch("hc.payments.views.Subscription.objects.by_braintree_webhook")
def test_it_uses_invoice_email(self, mock_getter):
mock_getter.return_value = self.sub, self.tx
self.sub.invoice_email = "alices_accountant@example.org"
self.sub.save()
r = self.client.post("/pricing/charge/")
self.assertEqual(r.status_code, 200)
# See if the email was sent to Alice's accountant:
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].to, ["alices_accountant@example.org"])

View File

@ -1,65 +0,0 @@
from mock import Mock, patch
from unittest import skipIf
from django.utils.timezone import now
from hc.payments.models import Subscription
from hc.test import BaseTestCase
try:
import reportlab
except ImportError:
reportlab = None
class PdfInvoiceTestCase(BaseTestCase):
def setUp(self):
super(PdfInvoiceTestCase, self).setUp()
self.sub = Subscription(user=self.alice)
self.sub.subscription_id = "test-id"
self.sub.customer_id = "test-customer-id"
self.sub.save()
self.tx = Mock()
self.tx.id = "abc123"
self.tx.customer_details.id = "test-customer-id"
self.tx.created_at = now()
self.tx.currency_iso_code = "USD"
self.tx.amount = 5
self.tx.subscription_details.billing_period_start_date = now()
self.tx.subscription_details.billing_period_end_date = now()
@skipIf(reportlab is None, "reportlab not installed")
@patch("hc.payments.models.braintree")
def test_it_works(self, mock_braintree):
mock_braintree.Transaction.find.return_value = self.tx
self.client.login(username="alice@example.org", password="password")
r = self.client.get("/invoice/pdf/abc123/")
self.assertTrue(b"ABC123" in r.content)
self.assertTrue(b"alice@example.org" in r.content)
@patch("hc.payments.models.braintree")
def test_it_checks_customer_id(self, mock_braintree):
tx = Mock()
tx.id = "abc123"
tx.customer_details.id = "test-another-customer-id"
tx.created_at = None
mock_braintree.Transaction.find.return_value = tx
self.client.login(username="alice@example.org", password="password")
r = self.client.get("/invoice/pdf/abc123/")
self.assertEqual(r.status_code, 403)
@skipIf(reportlab is None, "reportlab not installed")
@patch("hc.payments.models.braintree")
def test_it_shows_company_data(self, mock):
self.sub.address_id = "aa"
self.sub.save()
mock.Transaction.find.return_value = self.tx
mock.Address.find.return_value = {"company": "Alice and Partners"}
self.client.login(username="alice@example.org", password="password")
r = self.client.get("/invoice/pdf/abc123/")
self.assertTrue(b"Alice and Partners" in r.content)

View File

@ -16,10 +16,6 @@ urlpatterns = [
views.payment_method, views.payment_method,
name="hc-payment-method", name="hc-payment-method",
), ),
path(
"invoice/pdf/<slug:transaction_id>/", views.pdf_invoice, name="hc-invoice-pdf"
),
path("pricing/update/", views.update, name="hc-update-subscription"), path("pricing/update/", views.update, name="hc-update-subscription"),
path("pricing/token/", views.token, name="hc-get-client-token"), path("pricing/token/", views.token, name="hc-get-client-token"),
path("pricing/charge/", views.charge_webhook),
] ]

View File

@ -1,21 +1,11 @@
from io import BytesIO
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import ( from django.http import HttpResponseBadRequest, JsonResponse
HttpResponseBadRequest,
HttpResponseForbidden,
JsonResponse,
HttpResponse,
)
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from hc.api.models import Check from hc.api.models import Check
from hc.lib import emails
from hc.payments.forms import InvoiceEmailingForm from hc.payments.forms import InvoiceEmailingForm
from hc.payments.invoices import PdfInvoice
from hc.payments.models import Subscription from hc.payments.models import Subscription
@ -184,39 +174,3 @@ def billing_history(request):
ctx = {"transactions": transactions} ctx = {"transactions": transactions}
return render(request, "payments/billing_history.html", ctx) return render(request, "payments/billing_history.html", ctx)
@login_required
def pdf_invoice(request, transaction_id):
sub, tx = Subscription.objects.by_transaction(transaction_id)
# Does this transaction belong to a customer we know about?
if sub is None or tx is None:
return HttpResponseForbidden()
# Does the transaction's customer match the currently logged in user?
if sub.user != request.user and not request.user.is_superuser:
return HttpResponseForbidden()
response = HttpResponse(content_type="application/pdf")
filename = "MS-HC-%s.pdf" % tx.id.upper()
response["Content-Disposition"] = 'attachment; filename="%s"' % filename
PdfInvoice(response).render(tx, sub.flattened_address())
return response
@csrf_exempt
@require_POST
def charge_webhook(request):
sub, tx = Subscription.objects.by_braintree_webhook(request)
if sub.send_invoices:
filename = "MS-HC-%s.pdf" % tx.id.upper()
sink = BytesIO()
PdfInvoice(sink).render(tx, sub.flattened_address())
ctx = {"tx": tx}
recipient = sub.invoice_email or sub.user.email
emails.invoice(recipient, ctx, filename, sink.getvalue())
return HttpResponse()

View File

@ -1,8 +0,0 @@
{% if a.company %}{{ a.company }}
{% else %}{{ email }}
{% endif %}{% if a.extended_address %}VAT: {{ a.extended_address }}
{% endif %}{% if a.street_address %}{{ a.street_address }}
{% endif %}{% if a.locality %}{{ a.locality }}
{% endif %}{% if a.region %}{{ a.region }}
{% endif %}{% if a.country_name %}{{ a.country_name }}
{% endif %}{% if a.postal_code %}{{ a.postal_code }}{% endif %}

View File

@ -6,7 +6,6 @@
<th>Amount</th> <th>Amount</th>
<th>Type</th> <th>Type</th>
<th>Status</th> <th>Status</th>
<th></th>
</tr> </tr>
{% for tx in transactions %} {% for tx in transactions %}
<tr {% if tx.type == "credit" %}class="text-muted"{% endif %}> <tr {% if tx.type == "credit" %}class="text-muted"{% endif %}>
@ -29,12 +28,6 @@
</td> </td>
<td>{{ tx.type|capfirst }}</td> <td>{{ tx.type|capfirst }}</td>
<td><code>{{ tx.status }}</code></td> <td><code>{{ tx.status }}</code></td>
<td>
{% if tx.type == "credit" %}
{% else %}
<a href="{% url 'hc-invoice-pdf' tx.id %}">PDF Invoice</a>
{% endif %}
</td>
</tr> </tr>
{% endfor%} {% endfor%}
</table> </table>