@ -0,0 +1,16 @@ | |||
from django import forms | |||
from hc.accounts.forms import LowercaseEmailField | |||
class InvoiceEmailingForm(forms.Form): | |||
send_invoices = forms.IntegerField(min_value=0, max_value=2) | |||
invoice_email = LowercaseEmailField(required=False) | |||
def update_subscription(self, sub): | |||
sub.send_invoices = self.cleaned_data["send_invoices"] > 0 | |||
if self.cleaned_data["send_invoices"] == 2: | |||
sub.invoice_email = self.cleaned_data["invoice_email"] | |||
else: | |||
sub.invoice_email = "" | |||
sub.save() |
@ -0,0 +1,20 @@ | |||
# -*- coding: utf-8 -*- | |||
# Generated by Django 1.11.6 on 2018-04-21 15:23 | |||
from __future__ import unicode_literals | |||
from django.db import migrations, models | |||
class Migration(migrations.Migration): | |||
dependencies = [ | |||
('payments', '0005_subscription_plan_name'), | |||
] | |||
operations = [ | |||
migrations.AddField( | |||
model_name='subscription', | |||
name='invoice_email', | |||
field=models.EmailField(blank=True, max_length=254), | |||
), | |||
] |
@ -1,16 +1,33 @@ | |||
from mock import patch | |||
from hc.payments.models import Subscription | |||
from hc.test import BaseTestCase | |||
class SetPlanTestCase(BaseTestCase): | |||
class BillingCase(BaseTestCase): | |||
@patch("hc.payments.models.braintree") | |||
def test_it_saves_send_invoices_flag(self, mock): | |||
def test_it_disables_invoice_emailing(self): | |||
self.client.login(username="[email protected]", password="password") | |||
form = {"save_send_invoices": True} | |||
form = {"send_invoices": "0"} | |||
self.client.post("/accounts/profile/billing/", form) | |||
sub = Subscription.objects.get() | |||
self.assertFalse(sub.send_invoices) | |||
self.assertEqual(sub.invoice_email, "") | |||
def test_it_enables_invoice_emailing(self): | |||
self.client.login(username="[email protected]", password="password") | |||
form = {"send_invoices": "1"} | |||
self.client.post("/accounts/profile/billing/", form) | |||
sub = Subscription.objects.get() | |||
self.assertTrue(sub.send_invoices) | |||
self.assertEqual(sub.invoice_email, "") | |||
def test_it_saves_invoice_email(self): | |||
self.client.login(username="[email protected]", password="password") | |||
form = {"send_invoices": "2", "invoice_email": "[email protected]"} | |||
self.client.post("/accounts/profile/billing/", form) | |||
sub = Subscription.objects.get() | |||
self.assertTrue(sub.send_invoices) | |||
self.assertEqual(sub.invoice_email, "[email protected]") |
@ -0,0 +1,75 @@ | |||
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, ["[email protected]"]) | |||
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 = "[email protected]" | |||
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, ["[email protected]"]) |
@ -0,0 +1,8 @@ | |||
#invoice-email { | |||
margin-left: 50px; | |||
width: 300px; | |||
} | |||
#invoice-emailing-status { | |||
margin-right: 150px; | |||
} |
@ -148,27 +148,24 @@ | |||
<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> | |||
<h2>Invoices to Email</h2> | |||
<p id="invoice-emailing-status"> | |||
{% if sub.send_invoices %} | |||
Send the invoice to | |||
{{ sub.invoice_email|default:request.user.email }} | |||
each time my payment method is successfully charged. | |||
{% else %} | |||
Do not email invoices to me. | |||
{% endif %} | |||
</p> | |||
<button | |||
data-toggle="modal" | |||
data-target="#invoice-emailing-modal" | |||
class="btn btn-default pull-right"> | |||
Change Preference | |||
</button> | |||
</div> | |||
{% if send_invoices_status == "success" %} | |||
<div class="panel-footer"> | |||
@ -447,6 +444,68 @@ | |||
</form> | |||
</div> | |||
</div> | |||
<div id="invoice-emailing-modal" class="modal pm-modal"> | |||
<div class="modal-dialog"> | |||
<form method="post"> | |||
{% csrf_token %} | |||
<div class="modal-content"> | |||
<div class="modal-header"> | |||
<button type="button" class="close" data-dismiss="modal">×</button> | |||
<h4>Invoices to Email</h4> | |||
</div> | |||
<div class="modal-body"> | |||
<label class="radio-container"> | |||
<input | |||
type="radio" | |||
name="send_invoices" | |||
value="0" | |||
{% if not sub.send_invoices %} checked {% endif %}> | |||
<span class="radiomark"></span> | |||
Do not email invoices to me | |||
</label> | |||
<label class="radio-container"> | |||
<input | |||
type="radio" | |||
name="send_invoices" | |||
value="1" | |||
{% if sub.send_invoices and not sub.invoice_email %} checked {% endif %}> | |||
<span class="radiomark"></span> | |||
Send invoices to {{ profile.user.email }} | |||
</label> | |||
<label class="radio-container"> | |||
<input | |||
type="radio" | |||
name="send_invoices" | |||
value="2" | |||
{% if sub.send_invoices and sub.invoice_email %} checked {% endif %}> | |||
<span class="radiomark"></span> | |||
Send invoices to this email address: | |||
</label> | |||
<input | |||
id="invoice-email" | |||
name="invoice_email" | |||
placeholder="[email protected]" | |||
value="{{ sub.invoice_email }}" | |||
type="email" | |||
class="form-control" /> | |||
</div> | |||
<div class="modal-footer"> | |||
<button type="button" class="btn btn-default" data-dismiss="modal"> | |||
Cancel | |||
</button> | |||
<button type="submit" class="btn btn-primary"> | |||
Save Changes | |||
</button> | |||
</div> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
{% endblock %} | |||
{% block scripts %} | |||