def generate_record(record: Record) -> str: """ Generates the PDF for a given record; returns the path to the record PDF. """ _buffer = BytesIO() doc = get_default_document( _buffer, footer=EventSettings.objects.get().report_footer) style = get_paragraph_style() # Header: info text and qr code title_str = '[{}] {}beleg'.format( EventSettings.objects.get().short_name, 'Einnahme' if record.type == 'inflow' else 'Ausgabe') title = Paragraph(title_str, style['Heading1']) tz = timezone.get_current_timezone() datetime = record.datetime.astimezone(tz) logo = scale_image(get_qr_image(record), 100) header = Table( data=[ [[ title, ], logo], ], colWidths=[doc.width / 2] * 2, style=TableStyle([ ('ALIGN', (0, 0), (0, 0), 'LEFT'), ('ALIGN', (1, 0), (1, 0), 'RIGHT'), ('VALIGN', (0, 0), (1, 0), 'TOP'), ]), ) info = [['Datum', datetime.strftime('%Y-%m-%d, %H:%M')], ['Von' if record.type == 'inflow' else 'Nach', str(record.entity)], ['Betrag', CURRENCY.format(record.amount)]] info = [[ Paragraph(line[0], style['Heading3']), Paragraph(line[1], style['Normal']) ] for line in info] info_table = Table( data=info, colWidths=[90, doc.width - 90], style=TableStyle([]), ) # Signatures col_width = (doc.width - 35) / 2 signature1 = Table( data=[[ 'Bearbeiter/in: {}'.format(record.backoffice_user.get_full_name()), '', '' ]], colWidths=[col_width, 35, col_width], style=TableStyle([ ('FONTSIZE', (0, 0), (0, 0), FONTSIZE), ('LINEABOVE', (0, 0), (0, 0), 1.2, colors.black), ('VALIGN', (0, 0), (0, 0), 'TOP'), ]), ) if record.carrier: signature2 = Table( data=[[ '{}: {}'.format( 'Einlieferer/in' if record.type == 'inflow' else 'Emfpänger/in', record.carrier), '', '' ]], colWidths=[col_width, 35, col_width], style=TableStyle([ ('FONTSIZE', (0, 0), (0, 0), FONTSIZE), ('LINEABOVE', (0, 0), (0, 0), 1.2, colors.black), ('VALIGN', (0, 0), (0, 0), 'TOP'), ]), ) story = [ header, Spacer(1, 15 * mm), info_table, Spacer(1, 40 * mm), signature1, ] if record.carrier: story.append(Spacer(1, 40 * mm)) story.append(signature2) doc.build(story) _buffer.seek(0) stored_name = default_storage.save(record.get_new_record_path(), ContentFile(_buffer.read())) return stored_name
def generate_report(session: CashdeskSession) -> str: """ Generates a closing report for a CashdeskSession; returns the path to the report PDF. """ _buffer = BytesIO() doc = get_default_document( _buffer, footer=EventSettings.objects.get().report_footer) style = get_paragraph_style() # Header: info text and qr code title_str = '[{}] Kassenbericht #{}'.format( EventSettings.objects.get().short_name, session.pk) title = Paragraph(title_str, style['Heading1']) tz = timezone.get_current_timezone() text = """{user} an {cashdesk}<br/>{start} – {end}""".format( user=session.user.get_full_name(), cashdesk=session.cashdesk, start=session.start.astimezone(tz).strftime('%Y-%m-%d %H:%M:%S'), end=session.end.astimezone(tz).strftime('%Y-%m-%d %H:%M:%S'), ) info = Paragraph(text, style['Normal']) logo = scale_image(get_qr_image(session), 100) header = Table( data=[ [[title, info], logo], ], colWidths=[doc.width / 2] * 2, style=TableStyle([ ('ALIGN', (0, 0), (0, 0), 'LEFT'), ('ALIGN', (1, 0), (1, 0), 'RIGHT'), ('VALIGN', (0, 0), (1, 0), 'TOP'), ]), ) # Sales table sales_heading = Paragraph('Tickets', style['Heading3']) data = [ ['Ticket', 'Einzelpreis', 'Presale', 'Verkauf', 'Stornos', 'Gesamt'], ] sales_raw_data = session.get_product_sales() sales = [[ p['product'].name, CURRENCY.format(p['value_single']), p['presales'], p['sales'], p['reversals'], CURRENCY.format(p['value_total']) ] for p in sales_raw_data] data += sales data += [[ '', '', '', '', '', CURRENCY.format(sum([p['value_total'] for p in sales_raw_data])) ]] last_row = len(data) - 1 sales = Table( data=data, colWidths=[120] + [(doc.width - 120) / 5] * 5, style=TableStyle([ ('FONTSIZE', (0, 0), (5, last_row), FONTSIZE), # TODO: register bold font and use here: ('FACE', (0,0), (3,0), 'boldfontname'), ('ALIGN', (0, 0), (0, last_row), 'LEFT'), ('ALIGN', (1, 0), (5, last_row), 'RIGHT'), ('LINEABOVE', (0, 1), (5, 1), 1.0, colors.black), ('LINEABOVE', (5, last_row), (5, last_row), 1.2, colors.black), ]), ) # Items table items_heading = Paragraph('Auszählung', style['Heading3']) data = [ ['', 'Einzählung', 'Umsatz', 'Auszählung', 'Differenz'], ] # geld immer decimal mit € und nachkommastellen cash_transactions = session.get_cash_transaction_total() cash = [ [ 'Bargeld', CURRENCY.format(session.cash_before), CURRENCY.format(cash_transactions), CURRENCY.format(session.cash_after), CURRENCY.format(session.cash_before + cash_transactions - session.cash_after) ], ] items = [[ d['item'].name, d['movements'], d['transactions'], abs(d['final_movements']), d['total'] ] for d in session.get_current_items()] last_row = len(items) + 1 items = Table( data=data + cash + items, colWidths=[120] + [(doc.width - 120) / 4] * 4, style=TableStyle([ ('FONTSIZE', (0, 0), (4, last_row), FONTSIZE), # TODO: register bold font and use here: ('FACE', (0,0), (3,0), 'boldfontname'), ('ALIGN', (0, 0), (0, last_row), 'LEFT'), ('ALIGN', (1, 0), (4, last_row), 'RIGHT'), ('LINEABOVE', (0, 1), (4, 1), 1.0, colors.black), ]), ) # Signatures col_width = (doc.width - 35) / 2 signatures = Table( data=[[ 'Kassierer/in: {}'.format(session.user.get_full_name()), '', 'Ausgezählt durch {}'.format( session.backoffice_user_after.get_full_name()) ]], colWidths=[col_width, 35, col_width], style=TableStyle([ ('FONTSIZE', (0, 0), (2, 0), FONTSIZE), ('LINEABOVE', (0, 0), (0, 0), 1.2, colors.black), ('LINEABOVE', (2, 0), (2, 0), 1.2, colors.black), ('VALIGN', (0, 0), (2, 0), 'TOP'), ]), ) story = [ header, Spacer(1, 15 * mm), sales_heading, sales, Spacer(1, 10 * mm), items_heading, items, Spacer(1, 30 * mm), signatures, ] doc.build(story) _buffer.seek(0) stored_name = default_storage.save(session.get_new_report_path(), ContentFile(_buffer.read())) return stored_name
def generate_invoice(transaction: Transaction, address: str) -> str: path = transaction.get_invoice_path() if path: return path _buffer = BytesIO() settings = EventSettings.objects.get() doc = get_default_document(_buffer, footer=settings.invoice_footer) style = get_paragraph_style() # Header our_address = settings.invoice_address.replace('\n', '<br />') our_address = Paragraph(our_address, style['Normal']) our_title = Paragraph(_('Invoice from'), style['Heading5']) their_address = address.replace('\n', '<br />') their_address = Paragraph(their_address, style['Normal']) their_title = Paragraph(_('Invoice to'), style['Heading5']) data = [[their_title, '', our_title], [their_address, '', our_address]] header = Table( data=data, colWidths=[doc.width * 0.3, doc.width * 0.3, doc.width * 0.4], style=TableStyle([ ('FONTSIZE', (0, 0), (2, 1), FONTSIZE), ('VALIGN', (0, 0), (2, 1), 'TOP'), ]), ) date = Table( data=[[now().strftime('%Y-%m-%d')]], colWidths=[doc.width], style=TableStyle([ ('ALIGN', (0, 0), (0, 0), 'RIGHT'), ]), ) invoice_title = Paragraph( _('Invoice for receipt {}').format(transaction.receipt_id), style['Heading1']) data = [ [_('Product'), _('Tax rate'), _('Net'), _('Gross')], ] total_tax = 0 for position in transaction.positions.all(): total_tax += position.tax_value data.append([ position.product.name, '{} %'.format(position.tax_rate), CURRENCY.format(position.value - position.tax_value, ), CURRENCY.format(position.value) ]) data.append([_('Included taxes'), '', '', CURRENCY.format(total_tax)]) data.append( [_('Invoice total'), '', '', CURRENCY.format(transaction.value)]) last_row = len(data) - 1 transaction_table = Table( data=data, colWidths=[doc.width * 0.5] + [doc.width * 0.5 / 3] * 3, style=TableStyle([ ('FONTSIZE', (0, 0), (3, last_row), FONTSIZE), # TODO: register bold font and use here: ('FACE', (0,0), (3,0), 'boldfontname'), ('ALIGN', (0, 0), (1, last_row), 'LEFT'), ('ALIGN', (2, 0), (3, last_row), 'RIGHT'), ('LINEABOVE', (0, 1), (3, 1), 1.0, colors.black), ('LINEABOVE', (3, last_row - 1), (3, last_row - 1), 1.0, colors.black), ('LINEABOVE', (3, last_row), (3, last_row), 1.2, colors.black), ]), ) disclaimer_text = _('This invoice is only valid with receipt #{}.').format( transaction.receipt_id) disclaimer_text += _('The invoice total has already been paid.') disclaimer = Paragraph(disclaimer_text, style['Normal']) story = [ header, Spacer(1, 15 * mm), date, invoice_title, Spacer(1, 25 * mm), transaction_table, Spacer(1, 25 * mm), disclaimer ] doc.build(story) _buffer.seek(0) stored_name = default_storage.save( transaction.get_invoice_path(allow_nonexistent=True), ContentFile(_buffer.read())) return stored_name