dinbrief 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dinbrief/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = '0.4.0'
File without changes
@@ -0,0 +1,112 @@
1
+
2
+ from decimal import Decimal
3
+ from xml.sax.saxutils import escape
4
+
5
+ from reportlab.lib import colors
6
+ from reportlab.lib.units import mm, cm
7
+ from reportlab.platypus import Paragraph
8
+ from reportlab.platypus import Spacer
9
+ from reportlab.platypus.tables import Table
10
+ from reportlab.platypus.tables import TableStyle
11
+
12
+ from ..optional_django import gettext as _
13
+ from ..styles import styles
14
+
15
+
16
+ class FlowableProxy(object):
17
+
18
+ def __getattr__(self, name):
19
+ return getattr(self.flowable, name)
20
+
21
+
22
+ class Field(FlowableProxy):
23
+ field_height = 5*mm
24
+
25
+ def __init__(self, label, field_height=None, value=None):
26
+ if field_height is not None:
27
+ self.field_height = field_height
28
+ self.flowable = Table(
29
+ data=[
30
+ [value or ''], # empty row for the text
31
+ [Paragraph(label, styles['FieldLabel'])],
32
+ ],
33
+ colWidths=[80*mm],
34
+ rowHeights=[self.field_height, None],
35
+ style=TableStyle([
36
+ ('NOSPLIT', (0, 0), (-1, -1)),
37
+ ('TOPPADDING', (0, 0), (-1, -1), 0),
38
+ ('RIGHTPADDING', (0, 0), (-1, -1), 0),
39
+ ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
40
+ ('LEFTPADDING', (0, 0), (-1, -1), 0),
41
+ ('LINEBELOW', (0, 0), (0, 0), 0.2*mm, colors.black),
42
+ #('TOPPADDING', (0, 0), (0, 0), 5*mm),
43
+ ]),
44
+ hAlign='LEFT'
45
+ )
46
+
47
+
48
+ class SignatureField(Field):
49
+ field_height = 15*mm
50
+
51
+ def __init__(self, label=None, field_height=None, value=None):
52
+ if label is None:
53
+ label = _('Place, date and signature')
54
+ super(SignatureField, self).__init__(label, field_height, value)
55
+
56
+
57
+ class TwoSignaturesField(FlowableProxy):
58
+ field_height = 15*mm
59
+
60
+ def __init__(self, label_left, label_right, field_height=None,
61
+ value_left=None, value_right=None):
62
+ if field_height is not None:
63
+ self.field_height = field_height
64
+ self.flowable = Table(
65
+ data=[
66
+ [value_left or '', '', value_right or ''], # empty row for the text
67
+ [
68
+ Paragraph(label_left, styles['FieldLabel']),
69
+ '',
70
+ Paragraph(label_right, styles['FieldLabel']),
71
+ ],
72
+ ],
73
+ colWidths=[80*mm, 10*mm, 80*mm],
74
+ rowHeights=[self.field_height, None],
75
+ style=TableStyle([
76
+ ('NOSPLIT', (0, 0), (-1, -1)),
77
+ ('TOPPADDING', (0, 0), (-1, -1), 0),
78
+ ('RIGHTPADDING', (0, 0), (-1, -1), 0),
79
+ ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
80
+ ('LEFTPADDING', (0, 0), (-1, -1), 0),
81
+ ('LINEBELOW', (0, 0), (0, 0), 0.2*mm, colors.black),
82
+ ('LINEBELOW', (2, 0), (2, 0), 0.2*mm, colors.black),
83
+ ]),
84
+ hAlign='LEFT'
85
+ )
86
+
87
+
88
+ class PostalCodeAndCityField(FlowableProxy):
89
+ field_height = 5*mm
90
+ def __init__(self):
91
+ self.flowable = Table(
92
+ data=[
93
+ ['', '', ''], # empty row for the text
94
+ [
95
+ Paragraph(_('Postal code'), styles['FieldLabel']),
96
+ '', # Space between postal code and city
97
+ Paragraph(_('City'), styles['FieldLabel']),
98
+ ],
99
+ ],
100
+ colWidths=[20*mm, 5*mm, 55*mm],
101
+ rowHeights=[self.field_height, None],
102
+ style=TableStyle([
103
+ ('NOSPLIT', (0, 0), (-1, -1)),
104
+ ('TOPPADDING', (0, 0), (-1, -1), 0),
105
+ ('RIGHTPADDING', (0, 0), (-1, -1), 0),
106
+ ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
107
+ ('LEFTPADDING', (0, 0), (-1, -1), 0),
108
+ ('LINEBELOW', (0, 0), (0, 0), 0.2*mm, colors.black),
109
+ ('LINEBELOW', (2, 0), (2, 0), 0.2*mm, colors.black),
110
+ ]),
111
+ hAlign='LEFT'
112
+ )
@@ -0,0 +1,72 @@
1
+ from __future__ import absolute_import
2
+
3
+ try:
4
+ from io import BytesIO
5
+ except ImportError:
6
+ from StringIO import StringIO as BytesIO
7
+
8
+ import qrcode
9
+ from reportlab.platypus.flowables import Image
10
+
11
+ from ..styles import styles
12
+
13
+
14
+ def qrcode_image(data):
15
+ # create QRCode object
16
+ code = qrcode.QRCode(
17
+ box_size=1,
18
+ border=0,
19
+ error_correction=qrcode.constants.ERROR_CORRECT_M)
20
+ code.add_data(data)
21
+ code.make()
22
+ # render QRCode as PNG into memory
23
+ img = code.make_image()
24
+ img_data = BytesIO()
25
+ img.save(img_data, 'PNG')
26
+ img_data.seek(0)
27
+ img_data.__repr__ = lambda: 'qrcode.png'
28
+ # create image floatable
29
+ return Image(img_data)
30
+
31
+
32
+ def sepa_credit_transfer(account_holder, iban, bic, amount, reference,
33
+ purpose='', currency='EUR'):
34
+ '''
35
+ Create QRCode object according to EPC069-12:
36
+ http://www.europeanpaymentscouncil.eu/knowledge_bank_detail.cfm?documents_id=607
37
+ A list of purpose options can be found online:
38
+ http://www.hettwer-beratung.de/sepa-spezialwissen/sepa-technische-anforderungen/sepa-purpose-codes-vs-dta-textschl%C3%BCssel/
39
+ '''
40
+ assert 1 <= len(account_holder) < 70
41
+ assert len(currency) == 3
42
+ assert len(bic) in (8, 11)
43
+ assert ' ' not in bic
44
+ assert 1 <= len(iban) <= 34
45
+ assert ' ' not in iban
46
+ assert len(purpose) <= 4
47
+ assert len(reference) <= 35
48
+ return qrcode_image('\n'.join((
49
+ # Service Tag
50
+ 'BCD',
51
+ # Version
52
+ '001',
53
+ # Character set (1=utf-8)
54
+ '1',
55
+ # identification code
56
+ 'SCT',
57
+ # BIC
58
+ bic,
59
+ # name
60
+ account_holder,
61
+ # IBAN
62
+ iban,
63
+ # Amount
64
+ '%s%.2f' % (currency, amount),
65
+ # Purpose
66
+ purpose,
67
+ # Reference
68
+ reference,
69
+ # Unstructured Remittance Information
70
+ '',
71
+ # Beneficiary to originator information
72
+ '')))
@@ -0,0 +1,15 @@
1
+ from functools import partial
2
+
3
+ from reportlab.lib.units import mm
4
+ from reportlab.platypus import Paragraph
5
+ from reportlab.platypus import Spacer
6
+
7
+ from ..styles import styles
8
+
9
+
10
+ P = partial(Paragraph, style=styles['Text'])
11
+ H2 = partial(Paragraph, style=styles['H2'])
12
+ Subject = partial(Paragraph, style=styles['Subject'])
13
+ SmallSpacer = partial(Spacer, width=0, height=5*mm)
14
+ MediumSpacer = partial(Spacer, width=0, height=10*mm)
15
+ LargeSpacer = partial(Spacer, width=0, height=20*mm)
dinbrief/document.py ADDED
@@ -0,0 +1,26 @@
1
+ class Document(object):
2
+
3
+ title = ''
4
+ subject = ''
5
+ author = ''
6
+ keywords = None
7
+ creator = 'http://pypi.python.org/pypi/dinbrief'
8
+
9
+ sender = None
10
+ recipient = None
11
+ content = None
12
+ date = ''
13
+
14
+ def __init__(self, **kwargs):
15
+ # Metadata
16
+ self.title = kwargs.pop('title', self.title)
17
+ self.subject = kwargs.pop('subject', self.subject)
18
+ self.author = kwargs.pop('author', self.author)
19
+ self.keywords = kwargs.pop('keywords', self.keywords)
20
+ self.creator = kwargs.pop('creator', self.creator)
21
+ # Content
22
+ self.sender = kwargs.pop('sender', self.sender or [])
23
+ self.recipient = kwargs.pop('recipient', self.recipient or [])
24
+ self.infobox = kwargs.pop('infobox', [])
25
+ self.date = kwargs.pop('date', self.date)
26
+ self.content = kwargs.pop('content', self.content or [])
@@ -0,0 +1,5 @@
1
+ from .invoice import Invoice
2
+ from .item import Item
3
+ from .item_table import ItemTable
4
+ from .total_table import TotalTable
5
+ from .bank_transfer_form import BankTransferForm
@@ -0,0 +1,90 @@
1
+ from decimal import Decimal
2
+ from xml.sax.saxutils import escape
3
+
4
+ from reportlab.lib import colors
5
+ from reportlab.lib.units import mm, cm
6
+ from reportlab.platypus import Paragraph
7
+ from reportlab.platypus import Spacer
8
+ from reportlab.platypus.tables import Table
9
+ from reportlab.platypus.tables import TableStyle
10
+
11
+ from ..optional_django import gettext as _
12
+ from ..optional_django import number_format
13
+ from ..styles import styles
14
+
15
+
16
+ def BankTransferForm(account_holder, iban, bic, reference, amount,
17
+ currency=u'EUR', currency_label=u'€', show_qrcode=False):
18
+
19
+ col_widths = [40*mm, 60*mm]
20
+ row_heights = [7*mm, 6*mm, 6*mm, 6*mm, 7*mm]
21
+
22
+ table_style = []
23
+
24
+ if show_qrcode:
25
+ from ..contrib import qrcode
26
+ table_style += [
27
+ ('SPAN', (2, 0), (2, 4)),
28
+ ]
29
+ qrcode_size = sum(row_heights) - 4*mm
30
+ col_widths += [qrcode_size + 4*mm]
31
+ qrcode_image = qrcode.sepa_credit_transfer(
32
+ account_holder=account_holder,
33
+ iban=''.join(iban.split()),
34
+ bic=''.join(bic.split()),
35
+ reference=reference,
36
+ amount=amount,
37
+ currency=currency)
38
+ qrcode_image.drawWidth = qrcode_size
39
+ qrcode_image.drawHeight = qrcode_size
40
+
41
+ table_style += [
42
+ ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
43
+ ('VALIGN', (0, 0), (-1, -1), 'TOP'),
44
+ ('TOPPADDING', (0, 0), (-1, -1), 1*mm),
45
+ ('RIGHTPADDING', (0, 0), (-1, -1), 2*mm),
46
+ ('BOTTOMPADDING', (0, 0), (-1, -1), 1*mm),
47
+ ('LEFTPADDING', (0, 0), (-1, -1), 2*mm),
48
+ ('NOSPLIT', (0, 0), (-1, -1)),
49
+ # right
50
+ ('RIGHTPADDING', (-1, 0), (-1, -1), 2*mm),
51
+ ('LINEAFTER', (-1, 0), (-1, -1), 0.2*mm, colors.black),
52
+ # left
53
+ ('LEFTPADDING', (0, 0), (0, -1), 2*mm),
54
+ ('LINEBEFORE', (0, 0), (0, -1), 0.2*mm, colors.black),
55
+ # top
56
+ ('TOPPADDING', (0, 0), (-1, 0), 2*mm),
57
+ ('LINEABOVE', (0, 0), (-1, 0), 0.2*mm, colors.black),
58
+ # bottom
59
+ ('BOTTOMPADDING', (0, -1), (-1, -1), 3*mm),
60
+ ('LINEBELOW', (0, -1), (-1, -1), 0.2*mm, colors.black),
61
+ ]
62
+
63
+ def data_generator():
64
+ yield (
65
+ Paragraph(_(u'Account holder') + ':', styles['TableCell']),
66
+ Paragraph(account_holder, styles['TableCell'])
67
+ ) + ((qrcode_image,) if show_qrcode else ())
68
+ yield (
69
+ Paragraph(_(u'IBAN') + ':', styles['TableCell']),
70
+ Paragraph(iban, styles['TableCell']),
71
+ )
72
+ yield (
73
+ Paragraph(_(u'BIC') + ':', styles['TableCell']),
74
+ Paragraph(bic, styles['TableCell']),
75
+ )
76
+ yield (
77
+ Paragraph(_(u'Amount') + ':', styles['TableCell']),
78
+ Paragraph(u'%s %s' % (number_format(amount, 2), currency_label), styles['TableCell']),
79
+ )
80
+ yield (
81
+ Paragraph(_(u'Reference') + ':', styles['TableCell']),
82
+ Paragraph(reference, styles['TableCell']),
83
+ )
84
+
85
+ return Table(
86
+ data=list(data_generator()),
87
+ colWidths=col_widths,
88
+ rowHeights=row_heights,
89
+ style=TableStyle(table_style),
90
+ hAlign='LEFT')
@@ -0,0 +1,34 @@
1
+ from decimal import Decimal
2
+
3
+ from .vat_item import VatItem
4
+
5
+
6
+ class Invoice(object):
7
+
8
+ def __init__(self, items=None, currency=u'€'):
9
+ self.items = items or []
10
+ self.currency = currency
11
+ self.vat_items = []
12
+ self.recalculate()
13
+
14
+ def recalculate(self):
15
+ self.vat_items = []
16
+ d = {}
17
+ for item in self.items:
18
+ if not item.vat_rate:
19
+ continue
20
+ try:
21
+ vat_item = d[item.vat_rate]
22
+ except KeyError:
23
+ vat_item = VatItem(rate=item.vat_rate)
24
+ d[item.vat_rate] = vat_item
25
+ vat_item.amount += item.vat_rate * item.subtotal
26
+ self.vat_items = sorted(d.values(), key=lambda item: item.rate)
27
+
28
+ @property
29
+ def gross(self):
30
+ return self.net + sum(vat_item.amount for vat_item in self.vat_items)
31
+
32
+ @property
33
+ def net(self):
34
+ return sum(item.total for item in self.items)
@@ -0,0 +1,35 @@
1
+ from decimal import Decimal
2
+
3
+
4
+ class Item(object):
5
+
6
+ def __init__(self, position=0, text='', period='', date=None,
7
+ price=Decimal(0), unit='', quantity=Decimal(1),
8
+ discount=Decimal(0), vat_rate=Decimal(0),
9
+ type='item'):
10
+ self.position = position
11
+ self.text = text
12
+ self.period = period
13
+ self.date = date
14
+ self.price = price
15
+ self.unit = unit
16
+ self.quantity = quantity
17
+ self.discount = discount
18
+ self.vat_rate = vat_rate
19
+ self.type = type
20
+
21
+ @property
22
+ def subtotal(self):
23
+ return self.price * self.quantity
24
+
25
+ @property
26
+ def discount_percentage(self):
27
+ return self.discount * 100
28
+
29
+ @property
30
+ def discount_amount(self):
31
+ return self.discount * self.subtotal
32
+
33
+ @property
34
+ def total(self):
35
+ return self.subtotal - self.discount_amount
@@ -0,0 +1,124 @@
1
+ from decimal import Decimal
2
+ from functools import partial
3
+ from xml.sax.saxutils import escape
4
+
5
+ from reportlab.lib import colors
6
+ from reportlab.lib.units import mm, cm
7
+ from reportlab.platypus import Paragraph
8
+ from reportlab.platypus.tables import Table
9
+ from reportlab.platypus.tables import TableStyle
10
+
11
+ from ..optional_django import gettext as _
12
+ from ..optional_django import number_format
13
+ from ..optional_django import date_format
14
+ from ..styles import styles
15
+
16
+
17
+ Head = partial(Paragraph, style=styles['TableHead'])
18
+ HeadRight = partial(Paragraph, style=styles['TableHeadRight'])
19
+ Cell = partial(Paragraph, style=styles['TableCell'])
20
+ Number = partial(Paragraph, style=styles['TableNumber'])
21
+ Title = partial(Paragraph, style=styles['TableTitle'])
22
+
23
+ title_bgcolor = colors.CMYKColor(black=0.1)
24
+
25
+
26
+ def ItemTable(brief_template, invoice):
27
+
28
+ style = [
29
+ ('VALIGN', (0, 0), (-1, 0), 'BOTTOM'),
30
+ ('VALIGN', (0, 1), (-1, -1), 'TOP'),
31
+ ('LINEBELOW', (0, 0), (-1, 0), 0.3*mm, colors.black),
32
+ #('LINEBELOW', (0, 1), (-1, -1), 0.1*mm, colors.black),
33
+ ('TOPPADDING', (0, 0), (-1, -1), 2*mm),
34
+ ('RIGHTPADDING', (0, 0), (-1, -1), 2*mm),
35
+ ('BOTTOMPADDING', (0, 0), (-1, -1), 3*mm),
36
+ ('LEFTPADDING', (0, 0), (-1, -1), 2*mm),
37
+ # no padding on the far left and far right
38
+ ('LEFTPADDING', (0, 0), (0, -1), 0),
39
+ ('RIGHTPADDING', (-1, 0), (-1, -1), 0),
40
+ # no padding between net price and unit
41
+ ('RIGHTPADDING', (3, 0), (3, -1), 0),
42
+ ('LEFTPADDING', (4, 0), (4, -1), 0),
43
+ ]
44
+
45
+ show_period_column = any(item.period for item in invoice.items)
46
+ show_date_column = any(item.date for item in invoice.items) and \
47
+ not show_period_column
48
+
49
+ col_widths = [
50
+ 8*mm, # position
51
+ 0, # description
52
+ 46*mm if show_period_column else # period
53
+ 24*mm if show_date_column else # date
54
+ 0, # neither period nor date
55
+ 21*mm, # unit price
56
+ 13*mm, # unit
57
+ 20*mm, # quantity
58
+ 26*mm # sum price
59
+ ]
60
+ col_widths[1] = brief_template.CONTENT_WIDTH - sum(col_widths)
61
+
62
+ def data_generator():
63
+ # header
64
+ yield (
65
+ HeadRight(u'#'),
66
+ Head(_('Description')),
67
+ Head(_('Period') if show_period_column else
68
+ _('Date') if show_date_column else ''),
69
+ HeadRight(_('Unit Price')),
70
+ Head(u''),
71
+ HeadRight(_('Quantity')),
72
+ HeadRight(_('Line Total')),
73
+ )
74
+ # items
75
+ row = 0
76
+ for item in invoice.items:
77
+ row += 1
78
+ item_type = getattr(item, 'type', 'item')
79
+ if item_type == 'item':
80
+ if not (item.period or item.date):
81
+ style.append(('SPAN', (1, row), (2, row)))
82
+ yield (
83
+ Number(u'%s' % item.position),
84
+ Cell(escape(item.text)),
85
+ Cell(escape(item.period) if item.period else
86
+ escape(date_format(item.date, 'SHORT_DATE_FORMAT')) if item.date else u''),
87
+ Number(u'%s €' % number_format(item.price, 2)),
88
+ Cell((u'/%s' % escape(item.unit))
89
+ if item.unit else u''),
90
+ Number(number_format(item.quantity, 2)),
91
+ Number(u'%s €' % number_format(item.subtotal, 2)),
92
+ )
93
+ if item.discount:
94
+ row += 1
95
+ percentage = number_format(item.discount_percentage)
96
+ yield (
97
+ Cell(u''),
98
+ Cell((u'%s%% ' % percentage) + _('discount')),
99
+ Cell(u''),
100
+ Cell(u''),
101
+ Cell(u''),
102
+ Cell(u''),
103
+ Number(u'–%s €' % number_format(item.discount_amount, 2)),
104
+ )
105
+ style.append(('TOPPADDING', (0, row), (-1, row), 0))
106
+ # draw line below item
107
+ style.append(('LINEBELOW', (0, row), (-1, row),
108
+ 0.1*mm, colors.black))
109
+ elif item_type == 'title':
110
+ style.append(('SPAN', (1, row), (6, row)))
111
+ style.append(('BACKGROUND', (0, row), (-1, row),
112
+ title_bgcolor))
113
+ style.append(('LINEBELOW', (0, row), (-1, row),
114
+ 0.1*mm, colors.black))
115
+ yield (
116
+ Number(u'%s' % item.position),
117
+ Title(escape(item.text)),
118
+ )
119
+
120
+ return Table(
121
+ data=list(data_generator()),
122
+ colWidths=col_widths,
123
+ style=TableStyle(style),
124
+ repeatRows=1)
@@ -0,0 +1,90 @@
1
+ from decimal import Decimal
2
+ from xml.sax.saxutils import escape
3
+
4
+ from reportlab.lib import colors
5
+ from reportlab.lib.units import mm, cm
6
+ from reportlab.platypus import Paragraph
7
+ from reportlab.platypus import Spacer
8
+ from reportlab.platypus.tables import Table
9
+ from reportlab.platypus.tables import TableStyle
10
+
11
+ from ..optional_django import gettext as _
12
+ from ..optional_django import number_format
13
+ from ..styles import styles
14
+
15
+
16
+ def TotalTable(brief_template, invoice):
17
+
18
+ table_style = [
19
+ ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
20
+ ('TOPPADDING', (0, 0), (-1, -1), 1*mm),
21
+ ('RIGHTPADDING', (0, 0), (-1, -1), 2*mm),
22
+ ('BOTTOMPADDING', (0, 0), (-1, -1), 1*mm),
23
+ ('LEFTPADDING', (0, 0), (-1, -1), 2*mm),
24
+ # no padding on the far left and far right
25
+ ('LEFTPADDING', (0, 0), (0, -1), 0),
26
+ ('RIGHTPADDING', (-1, 0), (-1, -1), 0),
27
+ ]
28
+
29
+ col_widths = [0, 32*mm, 24*mm]
30
+ col_widths[0] = brief_template.CONTENT_WIDTH - sum(col_widths)
31
+
32
+ if invoice.vat_items:
33
+ net_row = 0
34
+ vat_row = net_row+1
35
+ gross_row = vat_row + len(invoice.vat_items)
36
+ table_style += [
37
+ ('BOTTOMPADDING', (0, gross_row-1), (-1, gross_row-1), 2*mm),
38
+ #('TOPPADDING', (0, vat_row), (-1, gross_row), 1*mm),
39
+ #('BOTTOMPADDING', (0, vat_row), (-1, gross_row), 1*mm),
40
+ ('LINEABOVE', (0, net_row), (-1, net_row), 0.3*mm, colors.black),
41
+ ('LINEABOVE', (1, gross_row), (-1, gross_row), 0.3*mm, colors.black),
42
+ ]
43
+ def data_generator():
44
+ yield (
45
+ Paragraph(u'', styles['TableCell']),
46
+ Paragraph(_(u'Sum (net)'), styles['TableCell']),
47
+ Paragraph(u'%s €' % number_format(invoice.net, 2),
48
+ styles['TableNumber']),
49
+ )
50
+ for vat_item in invoice.vat_items:
51
+ yield (
52
+ Paragraph(u'', styles['TableCell']),
53
+ Paragraph((u'+%s%% ' % number_format(vat_item.rate * 100)) +
54
+ _('VAT'), styles['TableCell']),
55
+ Paragraph(u'%s €' % number_format(vat_item.amount, 2),
56
+ styles['TableNumber']),
57
+ )
58
+ yield (
59
+ Paragraph(u'', styles['TableCell']),
60
+ Paragraph(_(u'Sum (gross)'), styles['GrossTableCell']),
61
+ Paragraph(u'%s €' % number_format(invoice.gross, 2),
62
+ styles['GrossValueTableCell']),
63
+ )
64
+ else:
65
+ net_row = 0
66
+ gross_row = 1
67
+ table_style += [
68
+ ('TOPPADDING', (0, net_row), (-1, net_row), 2*mm),
69
+ ('BOTTOMPADDING', (0, net_row), (-1, net_row), 0),
70
+ ('LINEABOVE', (0, net_row), (-1, net_row), 0.3*mm, colors.black),
71
+ ('TOPPADDING', (0, gross_row), (-1, gross_row), 2*mm),
72
+ ('LINEABOVE', (1, gross_row), (-1, gross_row), 0.3*mm, colors.black),
73
+ ]
74
+ def data_generator():
75
+ yield (
76
+ Spacer(0, 0),
77
+ Spacer(0, 0),
78
+ Spacer(0, 0),
79
+ )
80
+ yield (
81
+ Paragraph(u'', styles['TableCell']),
82
+ Paragraph(_(u'Sum (net)'), styles['GrossTableCell']),
83
+ Paragraph(u'%s €' % number_format(invoice.gross, 2),
84
+ styles['GrossValueTableCell']),
85
+ )
86
+
87
+ return Table(
88
+ data=list(data_generator()),
89
+ colWidths=col_widths,
90
+ style=TableStyle(table_style))
@@ -0,0 +1,7 @@
1
+ from decimal import Decimal
2
+
3
+
4
+ class VatItem(object):
5
+ def __init__(self, rate, amount=Decimal(0)):
6
+ self.rate = rate
7
+ self.amount = amount
Binary file