Commit ff5a05a0 authored by Michal Čihař's avatar Michal Čihař

Add support for invoice downloads

Signed-off-by: default avatarMichal Čihař <michal@cihar.com>
parent e86cb9a7
...@@ -24,6 +24,7 @@ include docs/_ext/*.py ...@@ -24,6 +24,7 @@ include docs/_ext/*.py
include docs/_static/*.png include docs/_static/*.png
include docs/_templates/*.html include docs/_templates/*.html
recursive-include examples * recursive-include examples *
include weblate/billing/test-data/*
include weblate/trans/tests/data/* include weblate/trans/tests/data/*
include weblate/trans/widget-images/*.png include weblate/trans/widget-images/*.png
include weblate/lang/plurals.txt include weblate/lang/plurals.txt
......
...@@ -234,6 +234,11 @@ DEFAULT_TRANSLATION_PROPAGATION = getvalue( ...@@ -234,6 +234,11 @@ DEFAULT_TRANSLATION_PROPAGATION = getvalue(
'DEFAULT_TRANSLATION_PROPAGATION', True 'DEFAULT_TRANSLATION_PROPAGATION', True
) )
# Billing
INVOICE_PATH = getvalue(
'INVOICE_PATH', ''
)
# Obsolete configs, needed for data migration # Obsolete configs, needed for data migration
GIT_ROOT = getvalue('GIT_ROOT', os.path.join(BASE_DIR, 'repos')) GIT_ROOT = getvalue('GIT_ROOT', os.path.join(BASE_DIR, 'repos'))
WHOOSH_INDEX = getvalue('WHOOSH_INDEX', os.path.join(BASE_DIR, 'whoosh-index')) WHOOSH_INDEX = getvalue('WHOOSH_INDEX', os.path.join(BASE_DIR, 'whoosh-index'))
...@@ -170,6 +170,12 @@ class Invoice(models.Model): ...@@ -170,6 +170,12 @@ class Invoice(models.Model):
def __str__(self): def __str__(self):
return '{0} - {1}: {2}'.format(self.start, self.end, self.billing) return '{0} - {1}: {2}'.format(self.start, self.end, self.billing)
@property
def filename(self):
if self.ref:
return '{0}.pdf'.format(self.ref)
return None
def clean(self): def clean(self):
if self.end is None or self.start is None: if self.end is None or self.start is None:
return return
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
# #
from datetime import timedelta from datetime import timedelta
import os.path
from six import StringIO from six import StringIO
...@@ -26,15 +27,27 @@ from django.test import TestCase ...@@ -26,15 +27,27 @@ from django.test import TestCase
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.management import call_command from django.core.management import call_command
from django.core.urlresolvers import reverse
from django.utils import timezone from django.utils import timezone
from weblate.billing.models import Plan, Billing, Invoice from weblate.billing.models import Plan, Billing, Invoice
from weblate.trans.models import Project from weblate.trans.models import Project
from weblate.trans.tests import OverrideSettings
TEST_DATA = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'test-data'
)
class BillingTest(TestCase): class BillingTest(TestCase):
def setUp(self): def setUp(self):
self.user = User.objects.create(username='bill') self.user = User.objects.create_user(
username='bill',
password='kill',
email='noreply@example.net'
)
self.plan = Plan.objects.create(name='test', limit_projects=1, price=0) self.plan = Plan.objects.create(name='test', limit_projects=1, price=0)
self.billing = Billing.objects.create(user=self.user, plan=self.plan) self.billing = Billing.objects.create(user=self.user, plan=self.plan)
self.invoice = Invoice.objects.create( self.invoice = Invoice.objects.create(
...@@ -42,6 +55,7 @@ class BillingTest(TestCase): ...@@ -42,6 +55,7 @@ class BillingTest(TestCase):
start=timezone.now().date() - timedelta(days=2), start=timezone.now().date() - timedelta(days=2),
end=timezone.now().date() + timedelta(days=2), end=timezone.now().date() + timedelta(days=2),
payment=10, payment=10,
ref='00000',
) )
self.projectnum = 0 self.projectnum = 0
...@@ -128,3 +142,42 @@ class BillingTest(TestCase): ...@@ -128,3 +142,42 @@ class BillingTest(TestCase):
# Validation of existing # Validation of existing
self.invoice.clean() self.invoice.clean()
@OverrideSettings(INVOICE_PATH=TEST_DATA)
def test_download(self):
# Unauthenticated
response = self.client.get(
reverse('invoice-download', kwargs={'pk': self.invoice.pk})
)
self.assertEquals(302, response.status_code)
# Not owner
User.objects.create_user(username='foo', password='bar')
self.client.login(username='foo', password='bar')
response = self.client.get(
reverse('invoice-download', kwargs={'pk': self.invoice.pk})
)
self.assertEquals(403, response.status_code)
# Owner
self.client.login(username='bill', password='kill')
response = self.client.get(
reverse('invoice-download', kwargs={'pk': self.invoice.pk})
)
self.assertContains(response, 'PDF-INVOICE')
# Invoice without file
invoice = Invoice.objects.create(
billing=self.billing,
start=timezone.now().date() - timedelta(days=2),
end=timezone.now().date() + timedelta(days=2),
payment=10,
)
response = self.client.get(
reverse('invoice-download', kwargs={'pk': invoice.pk})
)
self.assertEquals(404, response.status_code)
# Invoice with non existing file
invoice.ref = 'NON'
invoice.save()
response = self.client.get(
reverse('invoice-download', kwargs={'pk': invoice.pk})
)
self.assertEquals(404, response.status_code)
...@@ -17,3 +17,45 @@ ...@@ -17,3 +17,45 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import os.path
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from django.http import HttpResponse, Http404
from weblate import appsettings
from weblate.billing.models import Invoice
@login_required
def download_invoice(request, pk):
"""Download invoice PDF"""
invoice = get_object_or_404(Invoice, pk=pk)
if not invoice.ref:
raise Http404('No reference!')
if invoice.billing.user != request.user:
raise PermissionDenied('Not an owner!')
filename = invoice.filename
path = os.path.join(appsettings.INVOICE_PATH, filename)
if not os.path.exists(path):
raise Http404('File {0} does not exist!'.format(filename))
with open(path, 'rb') as handle:
data = handle.read()
response = HttpResponse(
data,
content_type='application/pdf'
)
response['Content-Disposition'] = 'attachment; filename={0}'.format(
filename
)
response['Content-Length'] = len(data)
return response
...@@ -379,7 +379,13 @@ ...@@ -379,7 +379,13 @@
<tr> <tr>
<td>{{ invoice.start|date:"SHORT_DATE_FORMAT" }} - {{ invoice.end|date:"SHORT_DATE_FORMAT" }}</td> <td>{{ invoice.start|date:"SHORT_DATE_FORMAT" }} - {{ invoice.end|date:"SHORT_DATE_FORMAT" }}</td>
<td>{{ invoice.payment }} {{ invoice.get_currency_display }}</td> <td>{{ invoice.payment }} {{ invoice.get_currency_display }}</td>
<td>{% trans "Not available" %}</td> <td>
{% if invoice.ref %}
<a href="{% url 'invoice-download' pk=invoice.pk %}">{{ invoice.filename }}</a>
{% else %}
{% trans "Not available" %}
{% endif %}
</td>
</tr> </tr>
{% empty %} {% empty %}
<tr><td colspan="3">{% trans "No invoices found!" %}</td/></tr> <tr><td colspan="3">{% trans "No invoices found!" %}</td/></tr>
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
# #
from django.conf.urls import include, url from django.conf.urls import include, url
from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.views.generic import RedirectView from django.views.generic import RedirectView
import django.contrib.sitemaps.views import django.contrib.sitemaps.views
...@@ -30,6 +31,7 @@ from weblate.trans.feeds import ( ...@@ -30,6 +31,7 @@ from weblate.trans.feeds import (
) )
from weblate.trans.views.changes import ChangesView, ChangesCSVView from weblate.trans.views.changes import ChangesView, ChangesCSVView
import weblate.accounts.views import weblate.accounts.views
import weblate.billing.views
import weblate.lang.views import weblate.lang.views
import weblate.trans.admin_views import weblate.trans.admin_views
import weblate.trans.views.acl import weblate.trans.views.acl
...@@ -837,3 +839,12 @@ urlpatterns = [ ...@@ -837,3 +839,12 @@ urlpatterns = [
name="search" name="search"
), ),
] ]
if 'weblate.billing' in settings.INSTALLED_APPS:
urlpatterns += [
url(
r'^invoice/(?P<pk>[0-9]+)/download/$',
weblate.billing.views.download_invoice,
name='invoice-download',
),
]
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment