Generate bill PDFs from /my/bill

This commit is contained in:
fnux 2020-05-07 15:38:49 +02:00
parent 3874165189
commit ae2bad5754
6 changed files with 96 additions and 115 deletions

View file

@ -81,7 +81,6 @@ urlpatterns = [
path('', include(router.urls)),
# web/ = stuff to view in the browser
path('web/pdf/', payviews.MyPDFView.as_view(), name='pdf'),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), # for login to REST API
path('openapi', get_schema_view(
title="uncloud",

View file

@ -0,0 +1,19 @@
# Generated by Django 3.0.6 on 2020-05-07 13:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0010_order_description'),
]
operations = [
migrations.AddField(
model_name='billingaddress',
name='organization',
field=models.CharField(default='', max_length=100),
preserve_default=False,
),
]

View file

@ -444,6 +444,7 @@ class BillingAddress(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
organization = models.CharField(max_length=100)
name = models.CharField(max_length=100)
street = models.CharField(max_length=100)
city = models.CharField(max_length=50)

View file

@ -95,7 +95,7 @@ class BillRecordSerializer(serializers.Serializer):
class BillingAddressSerializer(serializers.ModelSerializer):
class Meta:
model = BillingAddress
fields = ['uuid', 'name', 'street', 'city', 'postal_code', 'country', 'vat_number']
fields = ['uuid', 'organization', 'name', 'street', 'city', 'postal_code', 'country', 'vat_number']
class BillSerializer(serializers.ModelSerializer):
billing_address = BillingAddressSerializer(read_only=True)
@ -103,7 +103,7 @@ class BillSerializer(serializers.ModelSerializer):
class Meta:
model = Bill
fields = ['reference', 'owner', 'amount', 'vat_amount', 'total',
fields = ['uuid', 'reference', 'owner', 'amount', 'vat_amount', 'total',
'due_date', 'creation_date', 'starting_date', 'ending_date',
'records', 'final', 'billing_address']

View file

@ -26,7 +26,7 @@
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Bill name</title>
<title>{{ bill.reference }} | {{ bill.uuid }}</title>
<style>
body {
@ -49,6 +49,10 @@
-webkit-margin-start: 0px;
-webkit-margin-end: 0px;
}
table {
width: 100%;
text-align: left;
}
.bold {
font-weight: bold;
}
@ -668,11 +672,15 @@ oAsAAAAAAACGQNAFAAAAAAAAQyDoAgAAAAAAgCEQdAEAAAAAAMAQCLoAAAAAAABgCP83AL6WQ1Y7
<br>
</div>
<div class="d1">
<b>Faeh+Faeh GmbH </b>
<br> Pascal Faeh
<span><</span>pascal@faehundfaeh.ch>
<br> Via Nova
<br> 7017 Flims
{% if bill.billing_address.organization != "" %}
<b>{{ bill.billing_address.organization }}</b>
<br>{{ bill.billing_address.name }} <bill.owner.email>
{% else %}
<b>{{ bill.billing_address.name }} <bill.owner.email></b>
{% endif %}
<br>{{ bill.billing_address.street }}
<br>{{ bill.billing_address.postal_code }} {{ bill.billing_address.city }}
<br>{{ bill.billing_address.country }}
<br>
</div>
<div class="d4">
@ -683,118 +691,62 @@ oAsAAAAAAACGQNAFAAAAAAAAQyDoAgAAAAAAgCEQdAEAAAAAAMAQCLoAAAAAAABgCP83AL6WQ1Y7
</div>
<div class="b2">
2018-04-21<br>
20180421FAEH1<br>
2018-05-20
{{ bill.creation_date.date }}<br>
{% if bill.billing_address.vat_number != "" %}
{{ bill.billing_address.vat_number %}<br>
{% else %}
None<br>
{% endif %}
{{ bill.billing_address.vat_number }}<br>
{{ bill.due_date }}
</div>
</div>
<div style="clear: both;"></div>
<div class="d5">
<h1>RECHNUNG</h1>
</div>
<div class="wf th">
<p class="bold">
<span class="tl">Beschreibung</span>
<span class="tr">Netto CHF</span>
</p>
</div>
<div class="wf">
<p class="ts">
<span class="tl">NAS Synology DS1817+</span>
<span class="tr">1234.56</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s card Synology E10G17-F2</span>
<span class="tr">345.67</span>
</p>
<p class="ts">
<span class="tl">1OGbit/s switch HP</span>
<span class="tr">567.89</span>
</p>
<p class="ts">
<span class="tl">Festplatten 10 TB NAS RED Pro</span>
<span class="tr">3456.78</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s Transceiver Synology</span>
<span class="tr">123.45</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s Transceiver Switch kompatibel</span>
<span class="tr">123.45</span>
</p>
<p class="ts">
<span class="tl">NAS Synology DS1817+</span>
<span class="tr">1234.56</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s card Synology E10G17-F2</span>
<span class="tr">345.67</span>
</p>
<p class="ts">
<span class="tl">1OGbit/s switch HP</span>
<span class="tr">567.89</span>
</p>
<p class="ts">
<span class="tl">Festplatten 10 TB NAS RED Pro</span>
<span class="tr">3456.78</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s Transceiver Synology</span>
<span class="tr">123.45</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s Transceiver Switch kompatibel</span>
<span class="tr">123.45</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s Transceiver Switch kompatibel</span>
<span class="tr">123.45</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s Transceiver Switch kompatibel</span>
<span class="tr">123.45</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s Transceiver Switch kompatibel</span>
<span class="tr">123.45</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s Transceiver Switch kompatibel</span>
<span class="tr">123.45</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s Transceiver Switch kompatibel</span>
<span class="tr">123.45</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s Transceiver Switch kompatibel</span>
<span class="tr">123.45</span>
</p>
<p class="ts">
<span class="tl">10Gbit/s Transceiver Switch kompatibel</span>
<span class="tr">123.45</span>
</p>
</div>
<table>
<thead>
<tr>
<th>Beschreibung</th>
<th>Detail</th>
<th>Amount</th>
<th>VAT</th>
<th class="tr">Total</tH>
</tr>
</thead>
<tbody>
{% for record in bill.records %}
<tr class="table-list">
<td>{{ record.description }}</td>
<td>
{{ record.recurring_price }} * {{ record.recurring_count }}
{{ record.recurring_period }}
{% if record.one_time_price != 0 %}
+ one time {{ record.one_time_price }}
{% endif %}
</td>
<td>{{ record.amount }}</td>
<td>{{ record.vat_amount }} ({{ record.vat_rate }})</td>
<td class="tr">{{ record.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="wf th">
<p class="ts">
<span class="tl">Total</span>
<span class="tr">12345.67</span>
<span class="tr">{{ bill.amount }}</span>
</p>
<p class="ts">
<span class="tl">7.70% Mehrwertsteuer</span>
<span class="tr">891.00</span>
<span class="tl">VAT</span>
<span class="tr">{{ bill.vat_amount }}</span>
</p>
</div>
<div class="wf pc">
<p class="bold">
<span class="tl">Gesamtbetrag</span>
<span class="tr">23456.78</span>
<span class="tr">{{ bill.total }}</span>
</p>
</div>
<div class="wf footer">

View file

@ -9,6 +9,10 @@ from rest_framework.reverse import reverse
from rest_framework.decorators import renderer_classes
from vat_validator import validate_vat, vies
from vat_validator.countries import EU_COUNTRY_CODES
from hardcopy import bytestring_to_pdf
from django.core.files.temp import NamedTemporaryFile
from django.http import FileResponse
from django.template.loader import render_to_string
import json
import logging
@ -190,6 +194,20 @@ class BillViewSet(viewsets.ReadOnlyModelViewSet):
many=True)
return Response(serializer.data)
@action(detail=True, methods=['get'])
def download(self, *args, **kwargs):
bill = self.get_object()
output_file = NamedTemporaryFile()
bill_html = render_to_string("bill.html.j2", {'bill': bill})
bytestring_to_pdf(bill_html.encode('utf-8'), output_file)
response = FileResponse(output_file, content_type="application/pdf")
response['Content-Disposition'] = 'filename="{}_{}.pdf"'.format(
bill.reference, bill.uuid
)
return response
class OrderViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = OrderSerializer
@ -287,19 +305,11 @@ class AdminOrderViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.CreateModelMixin,
viewsets.GenericViewSet):
serializer_class = OrderSerializer
permission_classes = [permissions.IsAdminUser]
def get_serializer(self, *args, **kwargs):
return OrderSerializer(*args, **kwargs, admin=True)
return self.serializer_class(*args, **kwargs, admin=True)
def get_queryset(self):
return Order.objects.all()
# PDF tests
from django.views.generic import TemplateView
from hardcopy.views import PDFViewMixin, PNGViewMixin
class MyPDFView(PDFViewMixin, TemplateView):
template_name = "bill.html"
# def get_filename(self):
# return "my_file_{}.pdf".format(now().strftime('Y-m-d'))