# # This file is part of pretix (Community Edition). # # Copyright (C) 2014-2020 Raphael Michel and contributors # Copyright (C) 2020-2021 rami.io GmbH and contributors # # This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General # Public License as published by the Free Software Foundation in version 3 of the License. # # ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are # applicable granting you additional permissions and placing additional restrictions on your usage of this software. # Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive # this file, see . # # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more # details. # # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # from django.db.models import Prefetch from django.dispatch import receiver from django.utils.formats import date_format from django.utils.translation import gettext_lazy as _ from openpyxl.styles import Alignment from openpyxl.utils import get_column_letter from ..channels import get_all_sales_channels from ..exporter import ListExporter from ..models import ItemMetaValue from ..signals import register_data_exporters from ...helpers.safe_openpyxl import SafeCell def _max(a1, a2): if a1 and a2: return max(a1, a2) return a1 or a2 def _min(a1, a2): if a1 and a2: return min(a1, a2) return a1 or a2 class ItemDataExporter(ListExporter): identifier = 'itemdata' verbose_name = _('Product data') def iterate_list(self, form_data): locales = self.event.settings.locales scs = get_all_sales_channels() header = [ _("Product ID"), _("Variation ID"), _("Product category"), _("Internal name"), ] for l in locales: header.append( _("Item name") + f" ({l})" ) for l in locales: header.append( _("Variation") + f" ({l})" ) header += [ _("Active"), _("Sales channels"), _("Default price"), _("Free price input"), _("Sales tax"), _("Is an admission ticket"), _("Generate tickets"), _("Waiting list"), _("Available from"), _("Available until"), _("This product can only be bought using a voucher."), _("This product will only be shown if a voucher matching the product is redeemed."), _("Buying this product requires approval"), _("Only sell this product as part of a bundle"), _("Allow product to be canceled or changed"), _("Minimum amount per order"), _("Maximum amount per order"), _("Requires special attention"), _("Original price"), _("This product is a gift card"), _("Require a valid membership"), _("Hide without a valid membership"), ] props = list(self.event.item_meta_properties.all()) for p in props: header.append(p.name) if form_data["_format"] == "xlsx": row = [] for h in header: c = SafeCell(self.__ws, value=h) c.alignment = Alignment(wrap_text=True, vertical='top') row.append(c) else: row = header yield row for i in self.event.items.prefetch_related( 'variations', Prefetch( 'meta_values', ItemMetaValue.objects.select_related('property'), to_attr='meta_values_cached' ) ).select_related('category', 'tax_rule'): m = i.meta_data vars = list(i.variations.all()) if vars: for v in vars: row = [ i.pk, v.pk, str(i.category) if i.category else "", i.internal_name or "", ] for l in locales: row.append(i.name.localize(l)) for l in locales: row.append(v.value.localize(l)) row += [ _("Yes") if i.active and v.active else "", ", ".join([str(sn.verbose_name) for s, sn in scs.items() if s in i.sales_channels and s in v.sales_channels]), v.default_price or i.default_price, _("Yes") if i.free_price else "", str(i.tax_rule) if i.tax_rule else "", _("Yes") if i.admission else "", _("Yes") if i.generate_tickets else "", _("Yes") if i.allow_waitinglist else "", date_format(_max(i.available_from, v.available_from).astimezone(self.timezone), "SHORT_DATETIME_FORMAT") if i.available_from or v.available_from else "", date_format(_min(i.available_until, v.available_until).astimezone(self.timezone), "SHORT_DATETIME_FORMAT") if i.available_until or v.available_until else "", _("Yes") if i.require_voucher else "", _("Yes") if i.hide_without_voucher or v.hide_without_voucher else "", _("Yes") if i.require_approval or v.require_approval else "", _("Yes") if i.require_bundling else "", _("Yes") if i.allow_cancel else "", i.min_per_order if i.min_per_order is not None else "", i.max_per_order if i.max_per_order is not None else "", _("Yes") if i.checkin_attention else "", v.original_price or i.original_price or "", _("Yes") if i.issue_giftcard else "", _("Yes") if i.require_membership or v.require_membership else "", _("Yes") if i.require_membership_hidden or v.require_membership_hidden else "", ] else: row = [ i.pk, "", str(i.category) if i.category else "", i.internal_name or "", ] for l in locales: row.append(i.name.localize(l)) for l in locales: row.append("") row += [ _("Yes") if i.active else "", ", ".join([str(sn.verbose_name) for s, sn in scs.items() if s in i.sales_channels]), i.default_price, _("Yes") if i.free_price else "", str(i.tax_rule) if i.tax_rule else "", _("Yes") if i.admission else "", _("Yes") if i.generate_tickets else "", _("Yes") if i.allow_waitinglist else "", date_format(i.available_from.astimezone(self.timezone), "SHORT_DATETIME_FORMAT") if i.available_from else "", date_format(i.available_until.astimezone(self.timezone), "SHORT_DATETIME_FORMAT") if i.available_until else "", _("Yes") if i.require_voucher else "", _("Yes") if i.hide_without_voucher else "", _("Yes") if i.require_approval else "", _("Yes") if i.require_bundling else "", _("Yes") if i.allow_cancel else "", i.min_per_order if i.min_per_order is not None else "", i.max_per_order if i.max_per_order is not None else "", _("Yes") if i.checkin_attention else "", i.original_price or "", _("Yes") if i.issue_giftcard else "", _("Yes") if i.require_membership else "", _("Yes") if i.require_membership_hidden else "", ] row += [ m.get(p.name, '') for p in props ] yield row def get_filename(self): return '{}_products'.format(self.events.first().organizer.slug) def prepare_xlsx_sheet(self, ws): self.__ws = ws ws.freeze_panes = 'A1' ws.column_dimensions['C'].width = 25 ws.column_dimensions['D'].width = 25 for i in range(len(self.event.settings.locales)): ws.column_dimensions[get_column_letter(5 + 2 * i + 0)].width = 25 ws.column_dimensions[get_column_letter(5 + 2 * i + 1)].width = 25 ws.column_dimensions[get_column_letter(5 + 2 * len(self.event.settings.locales) + 1)].width = 25 ws.row_dimensions[1].height = 40 @receiver(register_data_exporters, dispatch_uid="exporter_itemdata") def register_itemdata_exporter(sender, **kwargs): return ItemDataExporter