def get_form_kwargs(self): kwargs = super().get_form_kwargs() if self.copy_from: i = modelcopy(self.copy_from) i.pk = None kwargs['instance'] = i else: kwargs['instance'] = ItemCategory(event=self.request.event) return kwargs
def order_overview( event: Event, subevent: SubEvent=None, date_filter='', date_from=None, date_until=None, fees=False, admission_only=False ) -> Tuple[List[Tuple[ItemCategory, List[Item]]], Dict[str, Tuple[Decimal, Decimal]]]: items = event.items.all().select_related( 'category', # for re-grouping ).prefetch_related( 'variations' ).order_by('category__position', 'category_id', 'position', 'name') qs = OrderPosition.all if subevent: qs = qs.filter(subevent=subevent) if admission_only: qs = qs.filter(item__admission=True) items = items.filter(admission=True) if date_from and isinstance(date_from, date): date_from = make_aware(datetime.combine( date_from, time(hour=0, minute=0, second=0, microsecond=0) ), event.timezone) if date_until and isinstance(date_until, date): date_until = make_aware(datetime.combine( date_until + timedelta(days=1), time(hour=0, minute=0, second=0, microsecond=0) ), event.timezone) if date_filter == 'order_date': if date_from: qs = qs.filter(order__datetime__gte=date_from) if date_until: qs = qs.filter(order__datetime__lt=date_until) elif date_filter == 'last_payment_date': p_date = OrderPayment.objects.filter( order=OuterRef('order'), state__in=[OrderPayment.PAYMENT_STATE_CONFIRMED, OrderPayment.PAYMENT_STATE_REFUNDED], payment_date__isnull=False ).values('order').annotate( m=Max('payment_date') ).values('m').order_by() qs = qs.annotate(payment_date=Subquery(p_date, output_field=DateTimeField())) if date_from: qs = qs.filter(payment_date__gte=date_from) if date_until: qs = qs.filter(payment_date__lt=date_until) counters = qs.filter( order__event=event ).annotate( status=Case( When(canceled=True, then=Value('c')), default=F('order__status') ) ).values( 'item', 'variation', 'status' ).annotate(cnt=Count('id'), price=Sum('price'), tax_value=Sum('tax_value')).order_by() states = { 'canceled': Order.STATUS_CANCELED, 'paid': Order.STATUS_PAID, 'pending': Order.STATUS_PENDING, 'expired': Order.STATUS_EXPIRED, } num = {} for l, s in states.items(): num[l] = { (p['item'], p['variation']): (p['cnt'], p['price'], p['price'] - p['tax_value']) for p in counters if p['status'] == s } num['total'] = dictsum(num['pending'], num['paid']) for item in items: item.all_variations = list(item.variations.all()) item.has_variations = (len(item.all_variations) > 0) item.num = {} if item.has_variations: for var in item.all_variations: variid = var.id var.num = {} for l in states.keys(): var.num[l] = num[l].get((item.id, variid), (0, 0, 0)) var.num['total'] = num['total'].get((item.id, variid), (0, 0, 0)) for l in states.keys(): item.num[l] = tuplesum(var.num[l] for var in item.all_variations) item.num['total'] = tuplesum(var.num['total'] for var in item.all_variations) else: for l in states.keys(): item.num[l] = num[l].get((item.id, None), (0, 0, 0)) item.num['total'] = num['total'].get((item.id, None), (0, 0, 0)) nonecat = ItemCategory(name=_('Uncategorized')) # Regroup those by category items_by_category = sorted( [ # a group is a tuple of a category and a list of items (cat if cat is not None else nonecat, [i for i in items if i.category == cat]) for cat in set([i.category for i in items]) # insert categories into a set for uniqueness # a set is unsorted, so sort again by category ], key=lambda group: (group[0].position, group[0].id) if ( group[0] is not None and group[0].id is not None) else (0, 0) ) for c in items_by_category: c[0].num = {} for l in states.keys(): c[0].num[l] = tuplesum(item.num[l] for item in c[1]) c[0].num['total'] = tuplesum(item.num['total'] for item in c[1]) # Payment fees payment_cat_obj = DummyObject() payment_cat_obj.name = _('Fees') payment_items = [] if not subevent and fees: qs = OrderFee.all.filter( order__event=event ).annotate( status=Case( When(canceled=True, then=Value('c')), default=F('order__status') ) ) if date_filter == 'order_date': if date_from: qs = qs.filter(order__datetime__gte=date_from) if date_until: qs = qs.filter(order__datetime__lt=date_until) elif date_filter == 'last_payment_date': qs = qs.annotate(payment_date=Subquery(p_date, output_field=DateTimeField())) if date_from: qs = qs.filter(payment_date__gte=date_from) if date_until: qs = qs.filter(payment_date__lt=date_until) counters = qs.values( 'fee_type', 'internal_type', 'status' ).annotate(cnt=Count('id'), value=Sum('value'), tax_value=Sum('tax_value')).order_by() for l, s in states.items(): num[l] = { (o['fee_type'], o['internal_type']): (o['cnt'], o['value'], o['value'] - o['tax_value']) for o in counters if o['status'] == s } num['total'] = dictsum(num['pending'], num['paid']) provider_names = { k: v.verbose_name for k, v in event.get_payment_providers().items() } names = dict(OrderFee.FEE_TYPES) for pprov, total in sorted(num['total'].items(), key=lambda i: i[0]): ppobj = DummyObject() if pprov[0] == OrderFee.FEE_TYPE_PAYMENT: ppobj.name = '{} - {}'.format(names[pprov[0]], provider_names.get(pprov[1], pprov[1])) else: name = pprov[1] for r, resp in order_fee_type_name.send(sender=event, fee_type=pprov[0], internal_type=pprov[1]): if resp: name = resp break ppobj.name = '{} - {}'.format(names[pprov[0]], name) ppobj.provider = pprov[1] ppobj.has_variations = False ppobj.num = {} for l in states.keys(): ppobj.num[l] = num[l].get(pprov, (0, 0, 0)) ppobj.num['total'] = total payment_items.append(ppobj) payment_cat_obj.num = {} for l in states.keys(): payment_cat_obj.num[l] = ( Dontsum(''), sum(i.num[l][1] for i in payment_items), sum(i.num[l][2] for i in payment_items) ) payment_cat_obj.num['total'] = ( Dontsum(''), sum(i.num['total'][1] for i in payment_items), sum(i.num['total'][2] for i in payment_items) ) payment_cat = (payment_cat_obj, payment_items) any_payment = any(payment_cat_obj.num[s][1] for s in states.keys()) if any_payment: items_by_category.append(payment_cat) total = { 'num': {'total': tuplesum(c.num['total'] for c, i in items_by_category)} } for l in states.keys(): total['num'][l] = tuplesum(c.num[l] for c, i in items_by_category) return items_by_category, total
def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]], Dict[str, Tuple[Decimal, Decimal]]]: items = event.items.all().select_related( 'category', # for re-grouping ).order_by('category__position', 'category_id', 'name') num_total = { (p['item'], p['variation']): (p['cnt'], p['price']) for p in (OrderPosition.objects .filter(order__event=event, order__status__in=[Order.STATUS_PENDING, Order.STATUS_EXPIRED, Order.STATUS_PAID]) .values('item', 'variation') .annotate(cnt=Count('id'), price=Sum('price')).order_by()) } num_canceled = { (p['item'], p['variation']): (p['cnt'], p['price']) for p in (OrderPosition.objects .filter(order__event=event, order__status=Order.STATUS_CANCELED) .values('item', 'variation') .annotate(cnt=Count('id'), price=Sum('price')).order_by()) } num_refunded = { (p['item'], p['variation']): (p['cnt'], p['price']) for p in (OrderPosition.objects .filter(order__event=event, order__status=Order.STATUS_REFUNDED) .values('item', 'variation') .annotate(cnt=Count('id'), price=Sum('price')).order_by()) } num_pending = { (p['item'], p['variation']): (p['cnt'], p['price']) for p in (OrderPosition.objects .filter(order__event=event, order__status__in=(Order.STATUS_PENDING, Order.STATUS_EXPIRED)) .values('item', 'variation') .annotate(cnt=Count('id'), price=Sum('price')).order_by()) } num_paid = { (p['item'], p['variation']): (p['cnt'], p['price']) for p in (OrderPosition.objects .filter(order__event=event, order__status=Order.STATUS_PAID) .values('item', 'variation') .annotate(cnt=Count('id'), price=Sum('price')).order_by()) } for item in items: item.all_variations = list(item.variations.all()) item.has_variations = (len(item.all_variations) > 0) if item.has_variations: for var in item.all_variations: variid = var.id var.num_total = num_total.get((item.id, variid), (0, 0)) var.num_pending = num_pending.get((item.id, variid), (0, 0)) var.num_canceled = num_canceled.get((item.id, variid), (0, 0)) var.num_refunded = num_refunded.get((item.id, variid), (0, 0)) var.num_paid = num_paid.get((item.id, variid), (0, 0)) item.num_total = tuplesum(var.num_total for var in item.all_variations) item.num_pending = tuplesum(var.num_pending for var in item.all_variations) item.num_canceled = tuplesum(var.num_canceled for var in item.all_variations) item.num_refunded = tuplesum(var.num_refunded for var in item.all_variations) item.num_paid = tuplesum(var.num_paid for var in item.all_variations) else: item.num_total = num_total.get((item.id, None), (0, 0)) item.num_pending = num_pending.get((item.id, None), (0, 0)) item.num_canceled = num_canceled.get((item.id, None), (0, 0)) item.num_refunded = num_refunded.get((item.id, None), (0, 0)) item.num_paid = num_paid.get((item.id, None), (0, 0)) nonecat = ItemCategory(name=_('Uncategorized')) # Regroup those by category items_by_category = sorted( [ # a group is a tuple of a category and a list of items (cat if cat is not None else nonecat, [i for i in items if i.category == cat]) for cat in set([i.category for i in items]) # insert categories into a set for uniqueness # a set is unsorted, so sort again by category ], key=lambda group: (group[0].position, group[0].id) if ( group[0] is not None and group[0].id is not None) else (0, 0) ) for c in items_by_category: c[0].num_total = tuplesum(item.num_total for item in c[1]) print(c[1], c[0].num_total, [item.num_total for item in c[1]]) c[0].num_pending = tuplesum(item.num_pending for item in c[1]) c[0].num_canceled = tuplesum(item.num_canceled for item in c[1]) c[0].num_refunded = tuplesum(item.num_refunded for item in c[1]) c[0].num_paid = tuplesum(item.num_paid for item in c[1]) # Payment fees payment_cat_obj = DummyObject() payment_cat_obj.name = _('Payment method fees') payment_items = [] num_total = { o['payment_provider']: (o['cnt'], o['payment_fee']) for o in (Order.objects .filter(event=event) .values('payment_provider') .annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by()) } num_canceled = { o['payment_provider']: (o['cnt'], o['payment_fee']) for o in (Order.objects .filter(event=event, status=Order.STATUS_CANCELED) .values('payment_provider') .annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by()) } num_refunded = { o['payment_provider']: (o['cnt'], o['payment_fee']) for o in (Order.objects .filter(event=event, status=Order.STATUS_REFUNDED) .values('payment_provider') .annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by()) } num_pending = { o['payment_provider']: (o['cnt'], o['payment_fee']) for o in (Order.objects .filter(event=event, status__in=(Order.STATUS_PENDING, Order.STATUS_EXPIRED)) .values('payment_provider') .annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by()) } num_paid = { o['payment_provider']: (o['cnt'], o['payment_fee']) for o in (Order.objects .filter(event=event, status=Order.STATUS_PAID) .values('payment_provider') .annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by()) } provider_names = {} responses = register_payment_providers.send(event) for receiver, response in responses: provider = response(event) provider_names[provider.identifier] = provider.verbose_name for pprov, total in num_total.items(): ppobj = DummyObject() ppobj.name = provider_names.get(pprov, pprov) ppobj.provider = pprov ppobj.has_variations = False ppobj.num_total = total ppobj.num_canceled = num_canceled.get(pprov, (0, 0)) ppobj.num_refunded = num_refunded.get(pprov, (0, 0)) ppobj.num_pending = num_pending.get(pprov, (0, 0)) ppobj.num_paid = num_paid.get(pprov, (0, 0)) payment_items.append(ppobj) payment_cat_obj.num_total = (Dontsum(''), sum(i.num_total[1] for i in payment_items)) payment_cat_obj.num_canceled = (Dontsum(''), sum(i.num_canceled[1] for i in payment_items)) payment_cat_obj.num_refunded = (Dontsum(''), sum(i.num_refunded[1] for i in payment_items)) payment_cat_obj.num_pending = (Dontsum(''), sum(i.num_pending[1] for i in payment_items)) payment_cat_obj.num_paid = (Dontsum(''), sum(i.num_paid[1] for i in payment_items)) payment_cat = (payment_cat_obj, payment_items) items_by_category.append(payment_cat) total = { 'num_total': tuplesum(c.num_total for c, i in items_by_category), 'num_pending': tuplesum(c.num_pending for c, i in items_by_category), 'num_canceled': tuplesum(c.num_canceled for c, i in items_by_category), 'num_refunded': tuplesum(c.num_refunded for c, i in items_by_category), 'num_paid': tuplesum(c.num_paid for c, i in items_by_category) } return items_by_category, total
def order_overview( event: Event, subevent: SubEvent = None ) -> Tuple[List[Tuple[ItemCategory, List[Item]]], Dict[str, Tuple[Decimal, Decimal]]]: items = event.items.all().select_related( 'category', # for re-grouping ).prefetch_related('variations').order_by('category__position', 'category_id', 'position', 'name') qs = OrderPosition.objects if subevent: qs = qs.filter(subevent=subevent) counters = qs.filter(order__event=event).values( 'item', 'variation', 'order__status').annotate(cnt=Count('id'), price=Sum('price'), tax_value=Sum('tax_value')).order_by() num_canceled = {(p['item'], p['variation']): (p['cnt'], p['price'], p['price'] - p['tax_value']) for p in counters if p['order__status'] == Order.STATUS_CANCELED} num_refunded = {(p['item'], p['variation']): (p['cnt'], p['price'], p['price'] - p['tax_value']) for p in counters if p['order__status'] == Order.STATUS_REFUNDED} num_paid = {(p['item'], p['variation']): (p['cnt'], p['price'], p['price'] - p['tax_value']) for p in counters if p['order__status'] == Order.STATUS_PAID} num_pending = {(p['item'], p['variation']): (p['cnt'], p['price'], p['price'] - p['tax_value']) for p in counters if p['order__status'] == Order.STATUS_PENDING} num_expired = {(p['item'], p['variation']): (p['cnt'], p['price'], p['price'] - p['tax_value']) for p in counters if p['order__status'] == Order.STATUS_EXPIRED} num_total = dictsum(num_pending, num_paid) for item in items: item.all_variations = list(item.variations.all()) item.has_variations = (len(item.all_variations) > 0) if item.has_variations: for var in item.all_variations: variid = var.id var.num_total = num_total.get((item.id, variid), (0, 0, 0)) var.num_pending = num_pending.get((item.id, variid), (0, 0, 0)) var.num_expired = num_expired.get((item.id, variid), (0, 0, 0)) var.num_canceled = num_canceled.get((item.id, variid), (0, 0, 0)) var.num_refunded = num_refunded.get((item.id, variid), (0, 0, 0)) var.num_paid = num_paid.get((item.id, variid), (0, 0, 0)) item.num_total = tuplesum(var.num_total for var in item.all_variations) item.num_pending = tuplesum(var.num_pending for var in item.all_variations) item.num_expired = tuplesum(var.num_expired for var in item.all_variations) item.num_canceled = tuplesum(var.num_canceled for var in item.all_variations) item.num_refunded = tuplesum(var.num_refunded for var in item.all_variations) item.num_paid = tuplesum(var.num_paid for var in item.all_variations) else: item.num_total = num_total.get((item.id, None), (0, 0, 0)) item.num_pending = num_pending.get((item.id, None), (0, 0, 0)) item.num_expired = num_expired.get((item.id, None), (0, 0, 0)) item.num_canceled = num_canceled.get((item.id, None), (0, 0, 0)) item.num_refunded = num_refunded.get((item.id, None), (0, 0, 0)) item.num_paid = num_paid.get((item.id, None), (0, 0, 0)) nonecat = ItemCategory(name=_('Uncategorized')) # Regroup those by category items_by_category = sorted( [ # a group is a tuple of a category and a list of items (cat if cat is not None else nonecat, [i for i in items if i.category == cat]) for cat in set([i.category for i in items]) # insert categories into a set for uniqueness # a set is unsorted, so sort again by category ], key=lambda group: (group[0].position, group[0].id) if (group[0] is not None and group[0].id is not None) else (0, 0)) for c in items_by_category: c[0].num_total = tuplesum(item.num_total for item in c[1]) c[0].num_pending = tuplesum(item.num_pending for item in c[1]) c[0].num_expired = tuplesum(item.num_expired for item in c[1]) c[0].num_canceled = tuplesum(item.num_canceled for item in c[1]) c[0].num_refunded = tuplesum(item.num_refunded for item in c[1]) c[0].num_paid = tuplesum(item.num_paid for item in c[1]) # Payment fees payment_cat_obj = DummyObject() payment_cat_obj.name = _('Fees') payment_items = [] if not subevent: counters = OrderFee.objects.filter(order__event=event).values( 'fee_type', 'internal_type', 'order__status').annotate(cnt=Count('id'), value=Sum('value'), tax_value=Sum('tax_value')).order_by() num_canceled = {(o['fee_type'], o['internal_type']): (o['cnt'], o['value'], o['value'] - o['tax_value']) for o in counters if o['order__status'] == Order.STATUS_CANCELED} num_refunded = {(o['fee_type'], o['internal_type']): (o['cnt'], o['value'], o['value'] - o['tax_value']) for o in counters if o['order__status'] == Order.STATUS_REFUNDED} num_pending = {(o['fee_type'], o['internal_type']): (o['cnt'], o['value'], o['value'] - o['tax_value']) for o in counters if o['order__status'] == Order.STATUS_PENDING} num_expired = {(o['fee_type'], o['internal_type']): (o['cnt'], o['value'], o['value'] - o['tax_value']) for o in counters if o['order__status'] == Order.STATUS_EXPIRED} num_paid = {(o['fee_type'], o['internal_type']): (o['cnt'], o['value'], o['value'] - o['tax_value']) for o in counters if o['order__status'] == Order.STATUS_PAID} num_total = dictsum(num_pending, num_paid) provider_names = { k: v.verbose_name for k, v in event.get_payment_providers().items() } names = dict(OrderFee.FEE_TYPES) for pprov, total in sorted(num_total.items(), key=lambda i: i[0]): ppobj = DummyObject() if pprov[0] == OrderFee.FEE_TYPE_PAYMENT: ppobj.name = '{} - {}'.format( names[OrderFee.FEE_TYPE_PAYMENT], provider_names.get(pprov[1], pprov[1])) else: ppobj.name = '{} - {}'.format(names[OrderFee.FEE_TYPE_PAYMENT], pprov[1]) ppobj.provider = pprov[1] ppobj.has_variations = False ppobj.num_total = total ppobj.num_canceled = num_canceled.get(pprov, (0, 0, 0)) ppobj.num_refunded = num_refunded.get(pprov, (0, 0, 0)) ppobj.num_expired = num_expired.get(pprov, (0, 0, 0)) ppobj.num_pending = num_pending.get(pprov, (0, 0, 0)) ppobj.num_paid = num_paid.get(pprov, (0, 0, 0)) payment_items.append(ppobj) payment_cat_obj.num_total = (Dontsum(''), sum(i.num_total[1] for i in payment_items), sum(i.num_total[2] for i in payment_items)) payment_cat_obj.num_canceled = (Dontsum(''), sum(i.num_canceled[1] for i in payment_items), sum(i.num_canceled[2] for i in payment_items)) payment_cat_obj.num_refunded = (Dontsum(''), sum(i.num_refunded[1] for i in payment_items), sum(i.num_refunded[2] for i in payment_items)) payment_cat_obj.num_expired = (Dontsum(''), sum(i.num_expired[1] for i in payment_items), sum(i.num_expired[2] for i in payment_items)) payment_cat_obj.num_pending = (Dontsum(''), sum(i.num_pending[1] for i in payment_items), sum(i.num_pending[2] for i in payment_items)) payment_cat_obj.num_paid = (Dontsum(''), sum(i.num_paid[1] for i in payment_items), sum(i.num_paid[2] for i in payment_items)) payment_cat = (payment_cat_obj, payment_items) items_by_category.append(payment_cat) total = { 'num_total': tuplesum(c.num_total for c, i in items_by_category), 'num_pending': tuplesum(c.num_pending for c, i in items_by_category), 'num_expired': tuplesum(c.num_expired for c, i in items_by_category), 'num_canceled': tuplesum(c.num_canceled for c, i in items_by_category), 'num_refunded': tuplesum(c.num_refunded for c, i in items_by_category), 'num_paid': tuplesum(c.num_paid for c, i in items_by_category) } return items_by_category, total