From 39bc12e3519ddacc8987761d45fb391bafa1a42e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C4=93teris=20Caune?= <cuu508@gmail.com>
Date: Tue, 9 Jan 2018 16:45:38 +0200
Subject: [PATCH] Invoices to email.

---
 hc/lib/emails.py                              | 12 ++++
 hc/payments/forms.py                          |  5 --
 hc/payments/invoices.py                       |  2 -
 .../0004_subscription_send_invoices.py        | 20 ++++++
 hc/payments/models.py                         |  9 +++
 hc/payments/tests/test_billing.py             | 16 +++++
 hc/payments/tests/test_pdf_invoice.py         |  9 +--
 hc/payments/urls.py                           |  1 +
 hc/payments/views.py                          | 68 +++++++++++++++----
 templates/accounts/billing.html               | 49 +++++++++++--
 templates/accounts/profile.html               |  2 +-
 templates/emails/invoice-body-html.html       | 15 ++++
 templates/emails/invoice-body-text.html       |  8 +++
 templates/emails/invoice-subject.html         |  2 +
 14 files changed, 188 insertions(+), 30 deletions(-)
 delete mode 100644 hc/payments/forms.py
 create mode 100644 hc/payments/migrations/0004_subscription_send_invoices.py
 create mode 100644 hc/payments/tests/test_billing.py
 create mode 100644 templates/emails/invoice-body-html.html
 create mode 100644 templates/emails/invoice-body-text.html
 create mode 100644 templates/emails/invoice-subject.html

diff --git a/hc/lib/emails.py b/hc/lib/emails.py
index d33cfac3..73bcbd83 100644
--- a/hc/lib/emails.py
+++ b/hc/lib/emails.py
@@ -58,3 +58,15 @@ def verify_email(to, ctx):
 
 def report(to, ctx):
     send("report", to, ctx)
+
+
+def invoice(to, ctx, filename, pdf_data):
+    ctx["SITE_ROOT"] = settings.SITE_ROOT
+    subject = render('emails/invoice-subject.html', ctx).strip()
+    text = render('emails/invoice-body-text.html', ctx)
+    html = render('emails/invoice-body-html.html', ctx)
+
+    msg = EmailMultiAlternatives(subject, text, to=(to, ))
+    msg.attach_alternative(html, "text/html")
+    msg.attach(filename, pdf_data, "application/pdf")
+    msg.send()
diff --git a/hc/payments/forms.py b/hc/payments/forms.py
deleted file mode 100644
index 7dd571e7..00000000
--- a/hc/payments/forms.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django import forms
-
-
-class BillToForm(forms.Form):
-    bill_to = forms.CharField(max_length=500, required=False)
diff --git a/hc/payments/invoices.py b/hc/payments/invoices.py
index f85a3a75..5391cf8a 100644
--- a/hc/payments/invoices.py
+++ b/hc/payments/invoices.py
@@ -93,8 +93,6 @@ class PdfInvoice(Canvas):
             self.text(s.strip())
 
         self.linefeed()
-        self.text("If you have a credit card on file it will be "
-                  "automatically charged within 24 hours.", align="center")
 
         self.showPage()
         self.save()
diff --git a/hc/payments/migrations/0004_subscription_send_invoices.py b/hc/payments/migrations/0004_subscription_send_invoices.py
new file mode 100644
index 00000000..568a7894
--- /dev/null
+++ b/hc/payments/migrations/0004_subscription_send_invoices.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.6 on 2018-01-09 12:52
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('payments', '0003_subscription_address_id'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='subscription',
+            name='send_invoices',
+            field=models.BooleanField(default=True),
+        ),
+    ]
diff --git a/hc/payments/models.py b/hc/payments/models.py
index 35e8104c..eb953351 100644
--- a/hc/payments/models.py
+++ b/hc/payments/models.py
@@ -1,6 +1,7 @@
 from django.conf import settings
 from django.contrib.auth.models import User
 from django.db import models
+from django.template.loader import render_to_string
 
 if settings.USE_PAYMENTS:
     import braintree
@@ -29,6 +30,7 @@ class Subscription(models.Model):
     subscription_id = models.CharField(max_length=10, blank=True)
     plan_id = models.CharField(max_length=10, blank=True)
     address_id = models.CharField(max_length=2, blank=True)
+    send_invoices = models.BooleanField(default=True)
 
     objects = SubscriptionManager()
 
@@ -176,6 +178,13 @@ class Subscription(models.Model):
 
         return self._address
 
+    def flattened_address(self):
+        if self.address_id:
+            ctx = {"a": self.address}
+            return render_to_string("payments/address_plain.html", ctx)
+        else:
+            return self.user.email
+
     @property
     def transactions(self):
         if not hasattr(self, "_tx"):
diff --git a/hc/payments/tests/test_billing.py b/hc/payments/tests/test_billing.py
new file mode 100644
index 00000000..bba7f2bf
--- /dev/null
+++ b/hc/payments/tests/test_billing.py
@@ -0,0 +1,16 @@
+from mock import patch
+
+from hc.payments.models import Subscription
+from hc.test import BaseTestCase
+
+
+class SetPlanTestCase(BaseTestCase):
+
+    @patch("hc.payments.models.braintree")
+    def test_it_saves_send_invoices_flag(self, mock):
+        self.client.login(username="alice@example.org", password="password")
+
+        form = {"save_send_invoices": True}
+        self.client.post("/accounts/profile/billing/", form)
+        sub = Subscription.objects.get()
+        self.assertFalse(sub.send_invoices)
diff --git a/hc/payments/tests/test_pdf_invoice.py b/hc/payments/tests/test_pdf_invoice.py
index 85bc13f2..35738892 100644
--- a/hc/payments/tests/test_pdf_invoice.py
+++ b/hc/payments/tests/test_pdf_invoice.py
@@ -55,11 +55,12 @@ class PdfInvoiceTestCase(BaseTestCase):
 
     @skipIf(reportlab is None, "reportlab not installed")
     @patch("hc.payments.models.braintree")
-    def test_it_shows_company_data(self, mock_braintree):
-        self.profile.bill_to = "Alice and Partners"
-        self.profile.save()
+    def test_it_shows_company_data(self, mock):
+        self.sub.address_id = "aa"
+        self.sub.save()
 
-        mock_braintree.Transaction.find.return_value = self.tx
+        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/")
diff --git a/hc/payments/urls.py b/hc/payments/urls.py
index 7256e6b2..0fdb0966 100644
--- a/hc/payments/urls.py
+++ b/hc/payments/urls.py
@@ -35,4 +35,5 @@ urlpatterns = [
         views.get_client_token,
         name="hc-get-client-token"),
 
+    url(r'^pricing/charge/$', views.charge_webhook),
 ]
diff --git a/hc/payments/views.py b/hc/payments/views.py
index fba84efb..98eb7646 100644
--- a/hc/payments/views.py
+++ b/hc/payments/views.py
@@ -4,9 +4,11 @@ from django.http import (HttpResponseBadRequest, HttpResponseForbidden,
                          JsonResponse, HttpResponse)
 from django.shortcuts import get_object_or_404, redirect, render
 from django.template.loader import render_to_string
+from django.views.decorators.csrf import csrf_exempt
 from django.views.decorators.http import require_POST
-
+import six
 from hc.api.models import Check
+from hc.lib import emails
 from hc.payments.invoices import PdfInvoice
 from hc.payments.models import Subscription
 
@@ -37,15 +39,37 @@ def billing(request):
     # subscription object is not created just by viewing a page.
     sub = Subscription.objects.filter(user_id=request.user.id).first()
 
+    send_invoices_status = "default"
+    if request.method == "POST":
+        if "save_send_invoices" in request.POST:
+            sub = Subscription.objects.for_user(request.user)
+            sub.send_invoices = "send_invoices" in request.POST
+            sub.save()
+            send_invoices_status = "success"
+
     ctx = {
         "page": "billing",
         "profile": request.profile,
         "sub": sub,
         "num_checks": Check.objects.filter(user=request.user).count(),
         "team_size": request.profile.member_set.count() + 1,
-        "team_max": request.profile.team_limit + 1
+        "team_max": request.profile.team_limit + 1,
+        "send_invoices_status": send_invoices_status,
+        "set_plan_status": "default",
+        "address_status": "default",
+        "payment_method_status": "default"
     }
 
+    if "set_plan_status" in request.session:
+        ctx["set_plan_status"] = request.session.pop("set_plan_status")
+
+    if "address_status" in request.session:
+        ctx["address_status"] = request.session.pop("address_status")
+
+    if "payment_method_status" in request.session:
+        ctx["payment_method_status"] = \
+            request.session.pop("payment_method_status")
+
     return render(request, "accounts/billing.html", ctx)
 
 
@@ -105,7 +129,7 @@ def set_plan(request):
         profile.sms_sent = 0
         profile.save()
 
-    request.session["first_charge"] = True
+    request.session["set_plan_status"] = "success"
     return redirect("hc-billing")
 
 
@@ -117,6 +141,7 @@ def address(request):
         if error:
             return log_and_bail(request, error)
 
+        request.session["address_status"] = "success"
         return redirect("hc-billing")
 
     ctx = {"a": sub.address}
@@ -136,6 +161,7 @@ def payment_method(request):
         if error:
             return log_and_bail(request, error)
 
+        request.session["payment_method_status"] = "success"
         return redirect("hc-billing")
 
     ctx = {
@@ -167,15 +193,29 @@ def pdf_invoice(request, transaction_id):
     response = HttpResponse(content_type='application/pdf')
     filename = "MS-HC-%s.pdf" % transaction.id.upper()
     response['Content-Disposition'] = 'attachment; filename="%s"' % filename
-
-    bill_to = []
-    if sub.address_id:
-        ctx = {"a": sub.address}
-        bill_to = render_to_string("payments/address_plain.html", ctx)
-    elif request.user.profile.bill_to:
-        bill_to = request.user.profile.bill_to
-    else:
-        bill_to = request.user.email
-
-    PdfInvoice(response).render(transaction, bill_to)
+    PdfInvoice(response).render(transaction, sub.flattened_address())
     return response
+
+
+@csrf_exempt
+@require_POST
+def charge_webhook(request):
+    sig = str(request.POST["bt_signature"])
+    payload = str(request.POST["bt_payload"])
+
+    import braintree
+    doc = braintree.WebhookNotification.parse(sig, payload)
+    if doc.kind != "subscription_charged_successfully":
+        return HttpResponseBadRequest()
+
+    sub = Subscription.objects.get(subscription_id=doc.subscription.id)
+    if sub.send_invoices:
+        transaction = doc.subscription.transactions[0]
+        filename = "MS-HC-%s.pdf" % transaction.id.upper()
+
+        sink = six.BytesIO()
+        PdfInvoice(sink).render(transaction, sub.flattened_address())
+        ctx = {"tx": transaction}
+        emails.invoice(sub.user.email, ctx, filename, sink.getvalue())
+
+    return HttpResponse()
diff --git a/templates/accounts/billing.html b/templates/accounts/billing.html
index 096e0c1e..cf33b984 100644
--- a/templates/accounts/billing.html
+++ b/templates/accounts/billing.html
@@ -35,7 +35,7 @@
 
         <div class="row">
             <div class="col-sm-6">
-                <div class="panel panel-default">
+                <div class="panel panel-{{ set_plan_status }}">
                     <div class="panel-body settings-block">
                         <h2>Billing Plan</h2>
 
@@ -96,9 +96,14 @@
                             Change Billing Plan
                         </button>
                     </div>
+                    {% if set_plan_status == "success" %}
+                    <div class="panel-footer">
+                        Your billing plan has been updated!
+                    </div>
+                    {% endif %}
                 </div>
 
-                <div class="panel panel-default">
+                <div class="panel panel-{{ payment_method_status }}">
                     <div class="panel-body settings-block">
                         <h2>Payment Method</h2>
                         {% if sub.payment_method_token %}
@@ -113,10 +118,15 @@
                             class="btn btn-default pull-right">
                             Change Payment Method</button>
                     </div>
+                    {% if payment_method_status == "success" %}
+                    <div class="panel-footer">
+                        Your payment method has been updated!
+                    </div>
+                    {% endif %}
                 </div>
             </div>
             <div class="col-sm-6">
-                <div class="panel panel-default">
+                <div class="panel panel-{{ address_status }}">
                     <div class="panel-body settings-block">
                         <h2>Billing Address</h2>
 
@@ -137,7 +147,7 @@
                             Change Billing Address
                         </button>
                     </div>
-                    {% if status == "info" %}
+                    {% if address_status == "success" %}
                     <div class="panel-footer">
                         Your billing address has been updated!
                     </div>
@@ -146,6 +156,37 @@
             </div>
         </div>
 
+        <div class="panel panel-{{ send_invoices_status }}">
+            <div class="panel-body settings-block">
+                <form method="post">
+                    {% csrf_token %}
+                    <h2>Invoices to Email</h2>
+
+                    <label class="checkbox-container">
+                        <input
+                            name="send_invoices"
+                            type="checkbox"
+                            {% if sub.send_invoices %}checked{% endif %}>
+                        <span class="checkmark"></span>
+                        Send the invoice to {{ request.user.email }}
+                       each time my payment method is successfully charged.
+                    </label>
+
+                    <button
+                        type="submit"
+                        name="save_send_invoices"
+                        class="btn btn-default pull-right">
+                        Save Changes
+                    </button>
+                </form>
+            </div>
+            {% if send_invoices_status == "success" %}
+            <div class="panel-footer">
+                Your preferences have been updated!
+            </div>
+            {% endif %}
+        </div>
+
         <div class="panel panel-default">
             <div class="panel-body settings-block">
                 <h2>Billing History</h2>
diff --git a/templates/accounts/profile.html b/templates/accounts/profile.html
index 68b48867..3de48784 100644
--- a/templates/accounts/profile.html
+++ b/templates/accounts/profile.html
@@ -57,7 +57,7 @@
             </div>
         </div>
 
-        <div class="panel panel-{{ api_status }}">
+        <div class="panel panel-{{ send_invoices_status }}">
             <div class="panel-body settings-block">
                 <h2>API Access</h2>
                 {% if profile.api_key %}
diff --git a/templates/emails/invoice-body-html.html b/templates/emails/invoice-body-html.html
new file mode 100644
index 00000000..05c85a1b
--- /dev/null
+++ b/templates/emails/invoice-body-html.html
@@ -0,0 +1,15 @@
+{% extends "emails/base.html" %}
+{% load hc_extras %}
+
+{% block content %}
+Hello,<br />
+Here's your invoice from {% site_name %} for
+{{ tx.subscription_details.billing_period_start_date }} - {{ tx.subscription_details.billing_period_end_date }}
+.</p>
+
+{% endblock %}
+
+{% block content_more %}
+Thanks,<br />
+The {% escaped_site_name %} Team
+{% endblock %}
diff --git a/templates/emails/invoice-body-text.html b/templates/emails/invoice-body-text.html
new file mode 100644
index 00000000..132e3603
--- /dev/null
+++ b/templates/emails/invoice-body-text.html
@@ -0,0 +1,8 @@
+{% load hc_extras %}
+Hello,
+
+Here's your invoice from {% site_name %}.
+
+--
+Regards,
+{% site_name %}
diff --git a/templates/emails/invoice-subject.html b/templates/emails/invoice-subject.html
new file mode 100644
index 00000000..c4c2085b
--- /dev/null
+++ b/templates/emails/invoice-subject.html
@@ -0,0 +1,2 @@
+{% load hc_extras %}
+Invoice from {% site_name %}