0001-lingo-send-emails-when-notifying-new-invoices-13122.patch
combo/apps/lingo/models.py | ||
---|---|---|
33 | 33 |
from django.forms import models as model_forms, Select |
34 | 34 |
from django.utils.translation import ugettext_lazy as _ |
35 | 35 |
from django.utils import timezone, dateparse |
36 |
from django.core.mail import EmailMultiAlternatives |
|
37 |
from django.core.urlresolvers import reverse |
|
36 | 38 |
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied |
37 | 39 |
from django.utils.http import urlencode |
38 | 40 | |
39 | 41 |
from django.contrib.auth.models import User |
42 |
from django.template.loader import render_to_string |
|
40 | 43 | |
41 | 44 |
from combo.data.fields import RichTextField |
42 | 45 |
from combo.data.models import CellBase |
... | ... | |
239 | 242 |
notification = Notification.objects.filter_by_id(invoice_id).filter(user=user).last() |
240 | 243 |
if notification.end_timestamp < timezone.now(): |
241 | 244 |
remind_id = 'remind-%s' % invoice_id |
242 |
Notification.notify(user, _('Reminder: invoice %s to pay') % invoice['label'], |
|
245 |
notification, created = Notification.notify(user, _('Reminder: invoice %s to pay') % invoice['label'],
|
|
243 | 246 |
id=remind_id, end_timestamp=notification_end_timestamp) |
247 |
if created: |
|
248 |
self.notify_remote_invoice_by_email(user, invoice) |
|
249 | ||
250 |
def notify_remote_invoice_by_email(self, user, invoice): |
|
251 | ||
252 |
subject_template = 'lingo/combo/invoice_email_notification_subject.txt' |
|
253 |
text_body_template = 'lingo/combo/invoice_email_notification_body.txt' |
|
254 |
html_body_template = 'lingo/combo/invoice_email_notification_body.html' |
|
255 | ||
256 |
remote_item = build_remote_item(invoice, self) |
|
257 |
payment_url = reverse('view-item', kwargs={'regie_id': self.id, |
|
258 |
'item_crypto_id': remote_item.crypto_id}) |
|
259 |
ctx = {'item': remote_item} |
|
260 |
ctx.update({'payment_url': urlparse.urljoin(settings.SITE_BASE_URL, payment_url)}) |
|
261 |
subject = render_to_string([subject_template], ctx).strip() |
|
262 |
text_body = render_to_string([text_body_template], ctx) |
|
263 |
html_body = render_to_string([html_body_template], ctx) |
|
264 |
message = EmailMultiAlternatives(subject, text_body, to=[user.email]) |
|
265 |
message.attach_alternative(html_body, 'text/html') |
|
266 |
if invoice['has_pdf']: |
|
267 |
invoice_pdf = self.get_invoice_pdf(user, invoice['id']) |
|
268 |
message.attach('%s.pdf' % invoice['id'], invoice_pdf.content, 'application/pdf') |
|
269 |
message.send() |
|
244 | 270 | |
245 | 271 | |
246 | 272 |
class BasketItem(models.Model): |
combo/apps/lingo/templates/lingo/combo/invoice_email_notification_body.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 | ||
3 |
<html> |
|
4 |
<body style="max-width: 60em"> |
|
5 |
<p>{% blocktrans with id=item.id creation_date=item.creation_date|date:"DATE_FORMAT" amount=item.amount %} |
|
6 |
We inform you that your invoice nr. {{ id }} issued on {{ creation_date }} of amount of {{ amount }}€ is available on {{ site_title }}. |
|
7 |
{% endblocktrans %}</p> |
|
8 |
{% if item.online_payment %} |
|
9 |
<p>{% blocktrans %}You can <a href="{{ payment_url }}">view and pay it online</a>.{% endblocktrans %}</p> |
|
10 |
{% else %} |
|
11 |
<p>{% blocktrans %}You can view it by going on your <a href="{{ portal_url }}">{{ site_title }}</a>.{% endblocktrans %}</p> |
|
12 |
{% if item.no_online_payment_reason == 'autobilling' %} |
|
13 |
<p>{% blocktrans with debit_date=item.payment_limit_date|date:"DATE_FORMAT" %} |
|
14 |
The amount of this invoice will be debited from your account at {{ debit_date }}. |
|
15 |
{% endblocktrans %} |
|
16 |
</p> |
|
17 |
{% endif %} |
|
18 |
{% endif %} |
|
19 |
</body> |
|
20 |
</html> |
combo/apps/lingo/templates/lingo/combo/invoice_email_notification_body.txt | ||
---|---|---|
1 |
{% load i18n %} |
|
2 |
{% blocktrans with id=item.id creation_date=item.creation_date|date:"DATE_FORMAT" amount=item.amount %} |
|
3 |
We inform you that your invoice nr. {{ id }} issued on {{ creation_date }} of amount |
|
4 |
of {{ amount }}€ is available on {{ site_title }}.{% endblocktrans %} |
|
5 | ||
6 |
{% if item.online_payment %}{% blocktrans %}You can view and pay it online on {{ payment_url }}.{% endblocktrans %} |
|
7 |
{% else %}{% blocktrans %}You can view it by going on {{ portal_url }}.{% endblocktrans %} |
|
8 | ||
9 |
{% if item.no_online_payment_reason == 'autobilling' %} |
|
10 |
{% blocktrans with debit_date=item.payment_limit_date|date:"DATE_FORMAT" %}The amount of this invoice will be debited from your account at {{ debit_date }}.{% endblocktrans %} |
|
11 |
{% endif %} |
|
12 |
{% endif %} |
|
13 |
combo/apps/lingo/templates/lingo/combo/invoice_email_notification_subject.txt | ||
---|---|---|
1 |
{% load i18n %} |
|
2 |
{% if site_title %} |
|
3 |
{% blocktrans with invoice_id=item.id %} |
|
4 |
{{ site_title }}: new invoice nr. {{ invoice_id }} is available |
|
5 |
{% endblocktrans %} |
|
6 |
{% else %} |
|
7 |
{% blocktrans with invoice_id=item.id %} |
|
8 |
New invoice nr. {{ invoice_id }} is available |
|
9 |
{% endblocktrans %} |
|
10 |
{% endif %} |
combo/settings.py | ||
---|---|---|
288 | 288 |
# default delay for invoice payment notifications in days |
289 | 289 |
LINGO_NEW_INVOICES_NOTIFICATION_DELAY = 10 |
290 | 290 | |
291 |
# default site |
|
292 |
SITE_BASE_URL = 'http://localhost' |
|
293 | ||
291 | 294 |
# timeout used in python-requests call, in seconds |
292 | 295 |
# we use 28s by default: timeout just before web server, which is usually 30s |
293 | 296 |
REQUESTS_TIMEOUT = 28 |
tests/test_lingo_remote_regie.py | ||
---|---|---|
9 | 9 |
from django.core.urlresolvers import reverse |
10 | 10 |
from django.conf import settings |
11 | 11 |
from django.core.management import call_command |
12 |
from django.utils.timezone import timedelta, now |
|
13 |
from django.contrib.auth.models import User |
|
12 | 14 | |
13 | 15 |
from combo.utils import check_query, aes_hex_encrypt |
14 | 16 |
from combo.data.models import Page |
... | ... | |
36 | 38 |
}, |
37 | 39 |
] |
38 | 40 | |
41 |
@pytest.fixture |
|
42 |
def user(): |
|
43 |
try: |
|
44 |
admin = User.objects.get(username='foo') |
|
45 |
except User.DoesNotExist: |
|
46 |
admin = User.objects.create_user('foo', email=None, password='bar') |
|
47 |
admin.email = 'foo@example.net' |
|
48 |
admin.save() |
|
49 |
return admin |
|
50 | ||
39 | 51 |
@pytest.fixture |
40 | 52 |
def remote_regie(): |
41 | 53 |
try: |
... | ... | |
336 | 348 |
assert Transaction.objects.all()[0].to_be_paid_remote_items is None |
337 | 349 | |
338 | 350 |
call_command('update_transactions') |
351 | ||
352 |
@mock.patch('combo.apps.lingo.models.requests.get') |
|
353 |
def test_send_new_remote_invoices_by_email(mock_get, user, app, remote_regie, mailoutbox): |
|
354 |
datetime_format = '%Y-%m-%dT%H:%M:%S' |
|
355 |
invoice_now = now() |
|
356 |
creation_date = (invoice_now - timedelta(days=1)).strftime(datetime_format) |
|
357 |
pay_limit_date = (invoice_now + timedelta(days=30)).strftime(datetime_format) |
|
358 |
FAKE_PENDING_INVOICES = { |
|
359 |
'data' : {'foo': {'invoices': [{'id': '01', 'label': '010101', 'paid': False, |
|
360 |
'amount': '37.26', 'total_amount': '37.26', 'online_payment': True, |
|
361 |
'has_pdf': True, 'created': creation_date, |
|
362 |
'pay_limit_date': pay_limit_date}]}, |
|
363 |
} |
|
364 |
} |
|
365 |
mock_response = mock.Mock(status_code=200, content=json.dumps(FAKE_PENDING_INVOICES)) |
|
366 |
mock_response.json.return_value = FAKE_PENDING_INVOICES |
|
367 |
mock_get.return_value = mock_response |
|
368 |
remote_regie.notify_new_remote_invoices() |
|
369 |
assert len(mailoutbox) == 1 |
|
370 |
assert mailoutbox[0].recipients() == ['foo@example.net'] |
|
371 |
assert mailoutbox[0].from_email == settings.DEFAULT_FROM_EMAIL |
|
372 |
assert mailoutbox[0].subject == 'New invoice nr. 01 is available' |
|
373 |
assert mailoutbox[0].attachments[0][0] == '01.pdf' |
|
374 |
assert mailoutbox[0].attachments[0][2] == 'application/pdf' |
|
339 |
- |