def get_initial(self): settings = EventSettings.get_solo() attrs = { attr: getattr(settings, attr) for attr in EventSettingsForm().fields } attrs.update({'initialized': True}) return attrs
def _setup_base(self): s = EventSettings.get_solo() s.initialized = True s.receipt_address = "Foo" s.save() self.session = cashdesk_session_before_factory(create_items=False) self.troubleshooter = user_factory(troubleshooter=True, superuser=False, password='******') self.backoffice_user = user_factory(troubleshooter=True, backoffice=True, password='******') self.cashier1 = user_factory(password='******') self.cashier2 = user_factory(password='******') self.item_full = Item.objects.create(name='Wristband red', description='Full pass', initial_stock=200) self.item_d1 = Item.objects.create(name='Wristband 1', description='Day 1', initial_stock=100) self.item_d2 = Item.objects.create(name='Wristband 2', description='Day 2', initial_stock=100) self.item_transport = Item.objects.create( name='Public transport', description='Public transport ticket', initial_stock=100, is_receipt=True) self.prod_full = Product.objects.create(name='Full pass', price=100, tax_rate=19) self.prod_d1 = Product.objects.create(name='Day pass Day 1', price=35, tax_rate=19) self.prod_d2 = Product.objects.create(name='Day pass Day 2', price=35, tax_rate=19) self.prod_transport = Product.objects.create(name='Public transport', price=16.7, tax_rate=0) ProductItem.objects.create(product=self.prod_full, item=self.item_full, amount=1) ProductItem.objects.create(product=self.prod_d1, item=self.item_d1, amount=1) ProductItem.objects.create(product=self.prod_d2, item=self.item_d2, amount=1) ProductItem.objects.create(product=self.prod_transport, item=self.item_transport, amount=1) self.desk1 = Cashdesk.objects.create(name='Desk 1', ip_address='10.1.1.1') self.desk2 = Cashdesk.objects.create(name='Desk 2', ip_address='10.1.1.2')
def event_settings(): settings = EventSettings.get_solo() settings.invoice_address = 'Foo Conferences\n42 Bar St\nBaz City' settings.save() return settings
def _build_receipt(self, transaction: Transaction) -> Union[None, str]: from postix.core.models import EventSettings settings = EventSettings.get_solo() total_sum = 0 position_lines = list() tax_sums = defaultdict(int) tax_symbols = dict() positions = transaction.positions.exclude(type='redeem', value=Decimal('0.00')) if not positions.exists(): return # Caution! This code assumes that cancellations are always made on transaction level cancellations = positions.filter(type='reverse') if cancellations.exists(): cancels = cancellations.first().reverses.transaction else: cancels = None for position in transaction.positions.all(): if position.value == 0: continue total_sum += position.value tax_sums[position.tax_rate] += position.tax_value if position.tax_rate not in tax_symbols: tax_symbols[position.tax_rate] = ascii_uppercase[len(tax_sums) - 1] formatargs = { 'product_name': position.product.name, 'tax_str': tax_symbols[position.tax_rate], 'gap': ' ' * (29 - len(position.product.name)), 'price': self._format_number(position.value), 'upgrade': _('Upgrade'), 'upgradegap': ' ' * (29 - len(_('Upgrade'))), } if position.has_constraint_bypass: position_lines.append(' {product_name}'.format(**formatargs)) position_lines.append( ' {upgrade} ({tax_str}){upgradegap} {price}'.format( **formatargs)) else: pos_str = ' {product_name} ({tax_str}){gap} {price}'.format( **formatargs) position_lines.append(pos_str) total_taxes = sum(tax_sums.values()) if not position_lines: return # Only get a new receipt ID after all early outs have passed # to make sure that we'll end up with actual consecutive numbers if not transaction.receipt_id: transaction.set_receipt_id(retry=3) is_copy = False else: is_copy = True receipt = bytearray([self.ESC, 0x61, 1]).decode() # center text receipt += bytearray([self.ESC, 0x45, 1]).decode() # emphasize receipt += settings.name + '\r\n\r\n' receipt += bytearray([self.ESC, 0x45, 0]).decode() # de-emphasize if settings.receipt_address is not None: receipt += settings.receipt_address + '\r\n\r\n' if cancels: receipt += bytearray([self.ESC, 0x45, 1]).decode() # emphasize receipt += _('Cancellation') + '\r\n' receipt += bytearray([self.ESC, 0x45, 0]).decode() # de-emphasize receipt += _('for receipt {}').format( cancels.receipt_id) + '\r\n\r\n' if is_copy: receipt += bytearray([self.ESC, 0x45, 1]).decode() # emphasize receipt += _('Receipt copy') + '\r\n\r\n' receipt += bytearray([self.ESC, 0x45, 0]).decode() # de-emphasize receipt += SEPARATOR receipt += " {: <26} EUR\r\n".format(_('Ticket')) receipt += SEPARATOR receipt += '\r\n'.join(position_lines) receipt += '\r\n' receipt += SEPARATOR receipt += bytearray( [self.ESC, 0x61, 2]).decode() # right-align text (0 would be left-align) receipt += _("Net sum: {}").format( self._format_number(total_sum - total_taxes)) receipt += '\r\n' for tax in sorted(list(tax_symbols))[::-1]: receipt += _( "Tax {tax_rate}% ({tax_identifier}): {tax_amount}").format( tax_rate=tax, tax_identifier=tax_symbols[tax], tax_amount=self._format_number(tax_sums[tax]), ) receipt += '\r\n' receipt += _("Total: {}").format(self._format_number(total_sum)) receipt += '\r\n' * 3 receipt += bytearray([self.ESC, 0x61, 1]).decode() # center text receipt += settings.receipt_footer + '\r\n' tz = timezone.get_current_timezone() receipt += '{} {}\r\n'.format( transaction.datetime.astimezone(tz).strftime("%d.%m.%Y %H:%M"), transaction.session.cashdesk.name, ) receipt += _('Receipt number: {}').format(transaction.receipt_id) receipt += '\r\n\r\n\r\n' return receipt
def _build_attendance(self, arrived: List['PreorderPosition'], not_arrived: List['PreorderPosition']): def shorten(line): if len(line) <= 40: return line return line[:39] + '...' def build_lines(position): information_lines = [ ' ' + l.strip() for l in position.information.split('\n') if l.strip() ] information_lines = [ info.split('–', maxsplit=1)[-1].strip() for info in information_lines ] return information_lines from postix.core.models import EventSettings settings = EventSettings.get_solo() arrived_lines = [build_lines(position) for position in arrived] not_arrived_lines = [build_lines(position) for position in not_arrived] attendance = bytearray([self.ESC, 0x61, 1]).decode() # center text attendance += bytearray([self.ESC, 0x45, 1]).decode() # emphasize attendance += settings.name + ' ' + _('Attendance') + '\r\n' attendance += timezone.now().strftime("%Y-%m-%d %H:%M") + '\r\n' attendance += bytearray([self.ESC, 0x45, 0]).decode() # de-emphasize if arrived_lines: arrived_lines = sum(arrived_lines, []) attendance += bytearray([self.ESC, 0x61, 1]).decode() # center text attendance += '\r\n' count = '{count} '.format(count=len(arrived)) seplen = (40 - len(count + _('Arrived'))) // 2 attendance += SEPARATOR_CHAR * seplen + ' ' + count + _( 'Arrived') + ' ' + SEPARATOR_CHAR * seplen + '\r\n' attendance += bytearray( [self.ESC, 0x61, 0]).decode() # right-align text (2 would be left-align) attendance += '\r\n'.join(arrived_lines) attendance += '\r\n' if not_arrived_lines: not_arrived_lines = sum(not_arrived_lines, []) attendance += bytearray([self.ESC, 0x61, 1]).decode() # center text attendance += '\r\n' count = '{count} '.format(count=len(not_arrived)) seplen = (40 - len(count + _('Not arrived'))) // 2 attendance += SEPARATOR_CHAR * seplen + ' ' + count + _( 'Not arrived') + ' ' + SEPARATOR_CHAR * seplen + '\r\n' attendance += bytearray( [self.ESC, 0x61, 2]).decode() # right-align text (0 would be left-align) attendance += '\r\n'.join(not_arrived_lines) attendance += '\r\n' attendance += bytearray([self.ESC, 0x61, 1]).decode() # center text attendance += '\r\n' return attendance
def generate_invoice(transaction: Transaction, address: str) -> str: path = transaction.get_invoice_path() if path: return path _buffer = BytesIO() settings = EventSettings.get_solo() 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