Esempio n. 1
0
def test_custom_rules_business(event):
    tr = TaxRule(event=event,
                 rate=Decimal('10.00'),
                 price_includes_tax=False,
                 custom_rules=json.dumps([
                     {
                         'country': 'ZZ',
                         'address_type': 'business',
                         'action': 'no'
                     },
                 ]))
    ia = InvoiceAddress(is_business=True, country=Country('AT'))
    assert not tr.is_reverse_charge(ia)
    assert not tr._tax_applicable(ia)
    assert tr.tax_rate_for(ia) == Decimal('0.00')
    assert tr.tax(Decimal('100.00'), invoice_address=ia) == TaxedPrice(
        gross=Decimal('100.00'),
        net=Decimal('100.00'),
        tax=Decimal('0.00'),
        rate=Decimal('0.00'),
        name='',
    )

    ia = InvoiceAddress(is_business=False, country=Country('DE'))
    assert not tr.is_reverse_charge(ia)
    assert tr._tax_applicable(ia)
    assert tr.tax_rate_for(ia) == Decimal('10.00')
    assert tr.tax(Decimal('100.00'), invoice_address=ia) == TaxedPrice(
        gross=Decimal('110.00'),
        net=Decimal('100.00'),
        tax=Decimal('10.00'),
        rate=Decimal('10.00'),
        name='',
    )
Esempio n. 2
0
def test_from_net_price(event):
    tr = TaxRule(event=event, rate=Decimal('10.00'), price_includes_tax=False)
    tp = tr.tax(Decimal('100.00'))
    assert tp.gross == Decimal('110.00')
    assert tp.net == Decimal('100.00')
    assert tp.tax == Decimal('10.00')
    assert tp.rate == Decimal('10.00')
Esempio n. 3
0
def test_reverse_charge_no_address(event):
    tr = TaxRule(event=event,
                 eu_reverse_charge=True,
                 rate=Decimal('10.00'),
                 price_includes_tax=False)
    assert not tr.is_reverse_charge(None)
    assert tr._tax_applicable(None)
Esempio n. 4
0
def test_from_gross_price(event):
    tr = TaxRule(event=event, rate=Decimal('10.00'), price_includes_tax=True)
    tp = tr.tax(Decimal('100.00'))
    assert tp.gross == Decimal('100')
    assert tp.net == Decimal('90.91')
    assert tp.tax == Decimal('100.00') - Decimal('90.91')
    assert tp.rate == Decimal('10.00')
Esempio n. 5
0
def test_reverse_charge_no_country(event):
    tr = TaxRule(event=event,
                 eu_reverse_charge=True,
                 rate=Decimal('10.00'),
                 price_includes_tax=False)
    ia = InvoiceAddress()
    assert not tr.is_reverse_charge(ia)
    assert tr._tax_applicable(ia)
    assert tr.tax_rate_for(ia) == Decimal('10.00')
Esempio n. 6
0
def test_reverse_charge_individual_eu(event):
    tr = TaxRule(event=event,
                 eu_reverse_charge=True,
                 home_country=Country('DE'),
                 rate=Decimal('10.00'),
                 price_includes_tax=False)
    ia = InvoiceAddress(is_business=False, country=Country('AT'))
    assert not tr.is_reverse_charge(ia)
    assert tr._tax_applicable(ia)
    assert tr.tax_rate_for(ia) == Decimal('10.00')
Esempio n. 7
0
def test_reverse_charge_disabled(event):
    tr = TaxRule(event=event,
                 eu_reverse_charge=False,
                 home_country=Country('DE'),
                 rate=Decimal('10.00'),
                 price_includes_tax=False)
    ia = InvoiceAddress(is_business=True,
                        vat_id='AT12346',
                        vat_id_validated=True,
                        country=Country('AT'))
    assert not tr.is_reverse_charge(ia)
    assert tr._tax_applicable(ia)
    assert tr.tax_rate_for(ia) == Decimal('10.00')
Esempio n. 8
0
def test_reverse_charge_valid_vat_id_business_3rdc(event):
    tr = TaxRule(
        event=event, eu_reverse_charge=True, home_country=Country('DE'),
        rate=Decimal('10.00'), price_includes_tax=False
    )
    ia = InvoiceAddress(
        is_business=True,
        country=Country('US'),
        vat_id='US12346',
        vat_id_validated=True
    )
    assert not tr.is_reverse_charge(ia)
    assert not tr.tax_applicable(ia)
Esempio n. 9
0
def test_custom_rules_any_country(event):
    tr = TaxRule(
        event=event,
        rate=Decimal('10.00'), price_includes_tax=False,
        custom_rules=json.dumps([
            {'country': 'ZZ', 'address_type': '', 'action': 'no'},
        ])
    )
    ia = InvoiceAddress(
        is_business=True,
        country=Country('AT')
    )
    assert not tr.is_reverse_charge(ia)
    assert not tr.tax_applicable(ia)
Esempio n. 10
0
def test_custom_rules_override(event):
    tr = TaxRule(
        event=event, eu_reverse_charge=True, home_country=Country('DE'),
        rate=Decimal('10.00'), price_includes_tax=False,
        custom_rules=json.dumps([
            {'country': 'ZZ', 'address_type': '', 'action': 'vat'}
        ])
    )
    ia = InvoiceAddress(
        is_business=True,
        vat_id='AT12346',
        vat_id_validated=True,
        country=Country('AT')
    )
    assert not tr.is_reverse_charge(ia)
    assert tr.tax_applicable(ia)
Esempio n. 11
0
def get_fees(event, total, invoice_address, mod=''):
    fee = event.settings.get('service_fee_abs' + mod, as_type=Decimal)
    if mod and fee is None:
        fee = event.settings.get('service_fee_abs', as_type=Decimal)
    if fee and total != Decimal('0.00'):
        tax_rule = event.settings.tax_rate_default or TaxRule.zero()
        if tax_rule.tax_applicable(invoice_address):
            tax = tax_rule.tax(fee)
            return [OrderFee(
                fee_type=OrderFee.FEE_TYPE_SERVICE,
                internal_type='',
                value=fee,
                tax_rate=tax.rate,
                tax_value=tax.tax,
                tax_rule=tax_rule
            )]
        else:
            return [OrderFee(
                fee_type=OrderFee.FEE_TYPE_SERVICE,
                internal_type='',
                value=fee,
                tax_rate=Decimal('0.00'),
                tax_value=Decimal('0.00'),
                tax_rule=tax_rule
            )]
    return []
Esempio n. 12
0
def get_fees(event,
             total,
             invoice_address,
             mod='',
             request=None,
             positions=[]):
    if request is not None and not positions:
        positions = get_cart(request)
    positions = [
        pos for pos in positions
        if not pos.addon_to and pos.price != Decimal('0.00')
    ]

    fee_per_ticket = event.settings.get('service_fee_per_ticket' + mod,
                                        as_type=Decimal)
    if mod and fee_per_ticket is None:
        fee_per_ticket = event.settings.get('service_fee_per_ticket',
                                            as_type=Decimal)

    fee_abs = event.settings.get('service_fee_abs' + mod, as_type=Decimal)
    if mod and fee_abs is None:
        fee_abs = event.settings.get('service_fee_abs', as_type=Decimal)

    fee_percent = event.settings.get('service_fee_percent' + mod,
                                     as_type=Decimal)
    if mod and fee_percent is None:
        fee_percent = event.settings.get('service_fee_percent',
                                         as_type=Decimal)

    fee_per_ticket = Decimal("0") if fee_per_ticket is None else fee_per_ticket
    fee_abs = Decimal("0") if fee_abs is None else fee_abs
    fee_percent = Decimal("0") if fee_percent is None else fee_percent

    if (fee_per_ticket or fee_abs or fee_percent) and total != Decimal('0.00'):
        fee = round_decimal(
            fee_abs + total * (fee_percent / 100) +
            len(positions) * fee_per_ticket, event.currency)
        tax_rule = event.settings.tax_rate_default or TaxRule.zero()
        if tax_rule.tax_applicable(invoice_address):
            tax = tax_rule.tax(fee)
            return [
                OrderFee(fee_type=OrderFee.FEE_TYPE_SERVICE,
                         internal_type='',
                         value=fee,
                         tax_rate=tax.rate,
                         tax_value=tax.tax,
                         tax_rule=tax_rule)
            ]
        else:
            return [
                OrderFee(fee_type=OrderFee.FEE_TYPE_SERVICE,
                         internal_type='',
                         value=fee,
                         tax_rate=Decimal('0.00'),
                         tax_value=Decimal('0.00'),
                         tax_rule=tax_rule)
            ]
    return []
Esempio n. 13
0
def test_custom_rules_country_rate_subtract_from_gross(event):
    tr = TaxRule(event=event,
                 rate=Decimal('10.00'),
                 price_includes_tax=False,
                 custom_rules=json.dumps([
                     {
                         'country': 'EU',
                         'address_type': 'business_vat_id',
                         'action': 'vat',
                         'rate': '100.00'
                     },
                 ]))
    ia = InvoiceAddress(is_business=True,
                        country=Country('DE'),
                        vat_id='DE1234',
                        vat_id_validated=True)
    assert tr.tax_rate_for(ia) == Decimal('100.00')
    assert not tr.is_reverse_charge(ia)
    assert tr._tax_applicable(ia)
    assert tr.tax(
        Decimal('100.00'),
        invoice_address=ia,
        subtract_from_gross=Decimal('20.00')) == TaxedPrice(
            gross=Decimal(
                '163.64'),  # ((100 * 1.1) - 20) / (1 + 10%) * (1 + 100%)
            net=Decimal('81.82'),
            tax=Decimal('81.82'),
            rate=Decimal('100.00'),
            name='',
        )
Esempio n. 14
0
def test_custom_rules_country_rate_keep_gross_if_rate_changes(event):
    tr = TaxRule(event=event,
                 rate=Decimal('10.00'),
                 price_includes_tax=False,
                 keep_gross_if_rate_changes=True,
                 custom_rules=json.dumps([
                     {
                         'country': 'EU',
                         'address_type': 'business_vat_id',
                         'action': 'vat',
                         'rate': '100.00'
                     },
                 ]))
    ia = InvoiceAddress(is_business=True, country=Country('DE'))
    assert not tr.is_reverse_charge(ia)
    assert tr._tax_applicable(ia)
    assert tr.tax_rate_for(ia) == Decimal('10.00')
    assert tr.tax(Decimal('100.00'), invoice_address=ia) == TaxedPrice(
        gross=Decimal('110.00'),
        net=Decimal('100.00'),
        tax=Decimal('10.00'),
        rate=Decimal('10.00'),
        name='',
    )
    ia = InvoiceAddress(is_business=True,
                        country=Country('DE'),
                        vat_id='DE1234',
                        vat_id_validated=True)
    assert tr.tax_rate_for(ia) == Decimal('100.00')
    assert not tr.is_reverse_charge(ia)
    assert tr._tax_applicable(ia)
    assert tr.tax(Decimal('100.00'), invoice_address=ia) == TaxedPrice(
        gross=Decimal('110.00'),
        net=Decimal('55.00'),
        tax=Decimal('55.00'),
        rate=Decimal('100.00'),
        name='',
    )
def get_fee(event, total, invoice_address):
    payment_fee = round_decimal(Decimal('0.25') + Decimal('0.029') * total)
    payment_fee_tax_rule = event.settings.tax_rate_default or TaxRule.zero()
    if payment_fee_tax_rule.tax_applicable(invoice_address):
        payment_fee_tax = payment_fee_tax_rule.tax(payment_fee,
                                                   base_price_is='gross')
        return OrderFee(fee_type=OrderFee.FEE_TYPE_PAYMENT,
                        value=payment_fee,
                        tax_rate=payment_fee_tax.rate,
                        tax_value=payment_fee_tax.tax,
                        tax_rule=payment_fee_tax_rule)
    else:
        return OrderFee(fee_type=OrderFee.FEE_TYPE_PAYMENT,
                        value=payment_fee,
                        tax_rate=Decimal('0.00'),
                        tax_value=Decimal('0.00'),
                        tax_rule=payment_fee_tax_rule)
Esempio n. 16
0
def test_custom_rules_vat_id(event):
    tr = TaxRule(event=event,
                 rate=Decimal('10.00'),
                 price_includes_tax=False,
                 custom_rules=json.dumps([
                     {
                         'country': 'EU',
                         'address_type': 'business_vat_id',
                         'action': 'reverse'
                     },
                 ]))
    ia = InvoiceAddress(is_business=True, country=Country('AT'))
    assert not tr.is_reverse_charge(ia)
    assert tr._tax_applicable(ia)
    assert tr.tax_rate_for(ia) == Decimal('10.00')
    ia = InvoiceAddress(is_business=True,
                        country=Country('DE'),
                        vat_id='DE1234',
                        vat_id_validated=True)
    assert tr.is_reverse_charge(ia)
    assert not tr._tax_applicable(ia)
    assert tr.tax_rate_for(ia) == Decimal('0.00')
Esempio n. 17
0
def test_reverse_charge_no_country(event):
    tr = TaxRule(event=event,
                 eu_reverse_charge=True,
                 rate=Decimal('10.00'),
                 price_includes_tax=False)
    ia = InvoiceAddress()
    assert not tr.is_reverse_charge(ia)
    assert tr._tax_applicable(ia)
    assert tr.tax_rate_for(ia) == Decimal('10.00')
    assert tr.tax(Decimal('100.00'), invoice_address=ia) == TaxedPrice(
        gross=Decimal('110.00'),
        net=Decimal('100.00'),
        tax=Decimal('10.00'),
        rate=Decimal('10.00'),
        name='',
    )
Esempio n. 18
0
def test_reverse_charge_individual_3rdc(event):
    tr = TaxRule(event=event,
                 eu_reverse_charge=True,
                 home_country=Country('DE'),
                 rate=Decimal('10.00'),
                 price_includes_tax=False)
    ia = InvoiceAddress(is_business=False, country=Country('US'))
    assert not tr.is_reverse_charge(ia)
    assert not tr._tax_applicable(ia)
    assert tr.tax_rate_for(ia) == Decimal('0.00')
    assert tr.tax(Decimal('100.00'), invoice_address=ia) == TaxedPrice(
        gross=Decimal('100.00'),
        net=Decimal('100.00'),
        tax=Decimal('0.00'),
        rate=Decimal('0.00'),
        name='',
    )
Esempio n. 19
0
def test_reverse_charge_valid_vat_id_business_eu(event):
    tr = TaxRule(event=event,
                 eu_reverse_charge=True,
                 home_country=Country('DE'),
                 rate=Decimal('10.00'),
                 price_includes_tax=False)
    ia = InvoiceAddress(is_business=True,
                        vat_id='AT12346',
                        vat_id_validated=True,
                        country=Country('AT'))
    assert tr.is_reverse_charge(ia)
    assert not tr._tax_applicable(ia)
    assert tr.tax_rate_for(ia) == Decimal('0.00')
    assert tr.tax(Decimal('100.00'), invoice_address=ia) == TaxedPrice(
        gross=Decimal('100.00'),
        net=Decimal('100.00'),
        tax=Decimal('0.00'),
        rate=Decimal('0.00'),
        name='',
    )
Esempio n. 20
0
    def create(self, validated_data):
        fees_data = validated_data.pop(
            'fees') if 'fees' in validated_data else []
        positions_data = validated_data.pop(
            'positions') if 'positions' in validated_data else []
        payment_provider = validated_data.pop('payment_provider', None)
        payment_info = validated_data.pop('payment_info', '{}')
        payment_date = validated_data.pop('payment_date', now())
        force = validated_data.pop('force', False)
        self._send_mail = validated_data.pop('send_mail', False)

        if 'invoice_address' in validated_data:
            iadata = validated_data.pop('invoice_address')
            name = iadata.pop('name', '')
            if name and not iadata.get('name_parts'):
                iadata['name_parts'] = {'_legacy': name}
            ia = InvoiceAddress(**iadata)
        else:
            ia = None

        with self.context['event'].lock() as now_dt:
            free_seats = set()
            seats_seen = set()
            consume_carts = validated_data.pop('consume_carts', [])
            delete_cps = []
            quota_avail_cache = {}
            v_budget = {}
            voucher_usage = Counter()
            if consume_carts:
                for cp in CartPosition.objects.filter(
                        event=self.context['event'],
                        cart_id__in=consume_carts,
                        expires__gt=now()):
                    quotas = (cp.variation.quotas.filter(subevent=cp.subevent)
                              if cp.variation else cp.item.quotas.filter(
                                  subevent=cp.subevent))
                    for quota in quotas:
                        if quota not in quota_avail_cache:
                            quota_avail_cache[quota] = list(
                                quota.availability())
                        if quota_avail_cache[quota][1] is not None:
                            quota_avail_cache[quota][1] += 1
                    if cp.voucher:
                        voucher_usage[cp.voucher] -= 1
                    if cp.expires > now_dt:
                        if cp.seat:
                            free_seats.add(cp.seat)
                    delete_cps.append(cp)

            errs = [{} for p in positions_data]

            for i, pos_data in enumerate(positions_data):

                if pos_data.get('voucher'):
                    v = pos_data['voucher']

                    if pos_data.get('addon_to'):
                        errs[i]['voucher'] = [
                            'Vouchers are currently not supported for add-on products.'
                        ]
                        continue

                    if not v.applies_to(pos_data['item'],
                                        pos_data.get('variation')):
                        errs[i]['voucher'] = [
                            error_messages['voucher_invalid_item']
                        ]
                        continue

                    if v.subevent_id and pos_data.get(
                            'subevent').pk != v.subevent_id:
                        errs[i]['voucher'] = [
                            error_messages['voucher_invalid_subevent']
                        ]
                        continue

                    if v.valid_until is not None and v.valid_until < now_dt:
                        errs[i]['voucher'] = [
                            error_messages['voucher_expired']
                        ]
                        continue

                    voucher_usage[v] += 1
                    if voucher_usage[v] > 0:
                        redeemed_in_carts = CartPosition.objects.filter(
                            Q(voucher=pos_data['voucher'])
                            & Q(event=self.context['event'])
                            & Q(expires__gte=now_dt)).exclude(
                                pk__in=[cp.pk for cp in delete_cps])
                        v_avail = v.max_usages - v.redeemed - redeemed_in_carts.count(
                        )
                        if v_avail < voucher_usage[v]:
                            errs[i]['voucher'] = [
                                'The voucher has already been used the maximum number of times.'
                            ]

                    if v.budget is not None:
                        price = pos_data.get('price')
                        if price is None:
                            price = get_price(
                                item=pos_data.get('item'),
                                variation=pos_data.get('variation'),
                                voucher=v,
                                custom_price=None,
                                subevent=pos_data.get('subevent'),
                                addon_to=pos_data.get('addon_to'),
                                invoice_address=ia,
                            ).gross
                        pbv = get_price(
                            item=pos_data['item'],
                            variation=pos_data.get('variation'),
                            voucher=None,
                            custom_price=None,
                            subevent=pos_data.get('subevent'),
                            addon_to=pos_data.get('addon_to'),
                            invoice_address=ia,
                        )

                        if v not in v_budget:
                            v_budget[v] = v.budget - v.budget_used()
                        disc = pbv.gross - price
                        if disc > v_budget[v]:
                            new_disc = v_budget[v]
                            v_budget[v] -= new_disc
                            if new_disc == Decimal('0.00') or pos_data.get(
                                    'price') is not None:
                                errs[i]['voucher'] = [
                                    'The voucher has a remaining budget of {}, therefore a discount of {} can not be '
                                    'given.'.format(v_budget[v] + new_disc,
                                                    disc)
                                ]
                                continue
                            pos_data['price'] = price + (disc - new_disc)
                        else:
                            v_budget[v] -= disc

                seated = pos_data.get('item').seat_category_mappings.filter(
                    subevent=pos_data.get('subevent')).exists()
                if pos_data.get('seat'):
                    if not seated:
                        errs[i]['seat'] = [
                            'The specified product does not allow to choose a seat.'
                        ]
                    try:
                        seat = self.context['event'].seats.get(
                            seat_guid=pos_data['seat'],
                            subevent=pos_data.get('subevent'))
                    except Seat.DoesNotExist:
                        errs[i]['seat'] = [
                            'The specified seat does not exist.'
                        ]
                    else:
                        pos_data['seat'] = seat
                        if (seat not in free_seats and not seat.is_available(
                                sales_channel=validated_data.get(
                                    'sales_channel', 'web'))
                            ) or seat in seats_seen:
                            errs[i]['seat'] = [
                                ugettext_lazy(
                                    'The selected seat "{seat}" is not available.'
                                ).format(seat=seat.name)
                            ]
                        seats_seen.add(seat)
                elif seated:
                    errs[i]['seat'] = [
                        'The specified product requires to choose a seat.'
                    ]

            if not force:
                for i, pos_data in enumerate(positions_data):
                    if pos_data.get('voucher'):
                        if pos_data['voucher'].allow_ignore_quota or pos_data[
                                'voucher'].block_quota:
                            continue

                    new_quotas = (pos_data.get('variation').quotas.filter(
                        subevent=pos_data.get('subevent'))
                                  if pos_data.get('variation') else
                                  pos_data.get('item').quotas.filter(
                                      subevent=pos_data.get('subevent')))
                    if len(new_quotas) == 0:
                        errs[i]['item'] = [
                            ugettext_lazy(
                                'The product "{}" is not assigned to a quota.'
                            ).format(str(pos_data.get('item')))
                        ]
                    else:
                        for quota in new_quotas:
                            if quota not in quota_avail_cache:
                                quota_avail_cache[quota] = list(
                                    quota.availability())

                            if quota_avail_cache[quota][1] is not None:
                                quota_avail_cache[quota][1] -= 1
                                if quota_avail_cache[quota][1] < 0:
                                    errs[i]['item'] = [
                                        ugettext_lazy(
                                            'There is not enough quota available on quota "{}" to perform the operation.'
                                        ).format(quota.name)
                                    ]

            if any(errs):
                raise ValidationError({'positions': errs})

            if validated_data.get('locale', None) is None:
                validated_data['locale'] = self.context[
                    'event'].settings.locale
            order = Order(event=self.context['event'], **validated_data)
            order.set_expires(
                subevents=[p.get('subevent') for p in positions_data])
            order.meta_info = "{}"
            order.total = Decimal('0.00')
            order.save()

            if ia:
                ia.order = order
                ia.save()

            pos_map = {}
            for pos_data in positions_data:
                answers_data = pos_data.pop('answers', [])
                addon_to = pos_data.pop('addon_to', None)
                attendee_name = pos_data.pop('attendee_name', '')
                if attendee_name and not pos_data.get('attendee_name_parts'):
                    pos_data['attendee_name_parts'] = {
                        '_legacy': attendee_name
                    }
                pos = OrderPosition(**pos_data)
                pos.order = order
                if addon_to:
                    pos.addon_to = pos_map[addon_to]

                if pos.price is None:
                    price = get_price(
                        item=pos.item,
                        variation=pos.variation,
                        voucher=pos.voucher,
                        custom_price=None,
                        subevent=pos.subevent,
                        addon_to=pos.addon_to,
                        invoice_address=ia,
                    )
                    pos.price = price.gross
                    pos.tax_rate = price.rate
                    pos.tax_value = price.tax
                    pos.tax_rule = pos.item.tax_rule
                else:
                    pos._calculate_tax()

                pos.price_before_voucher = get_price(
                    item=pos.item,
                    variation=pos.variation,
                    voucher=None,
                    custom_price=None,
                    subevent=pos.subevent,
                    addon_to=pos.addon_to,
                    invoice_address=ia,
                ).gross

                if pos.voucher:
                    Voucher.objects.filter(pk=pos.voucher.pk).update(
                        redeemed=F('redeemed') + 1)
                pos.save()
                pos_map[pos.positionid] = pos
                for answ_data in answers_data:
                    options = answ_data.pop('options', [])
                    answ = pos.answers.create(**answ_data)
                    answ.options.add(*options)

            for cp in delete_cps:
                cp.delete()

        order.total = sum([p.price for p in order.positions.all()])
        for fee_data in fees_data:
            is_percentage = fee_data.pop('_treat_value_as_percentage', False)
            if is_percentage:
                fee_data['value'] = round_decimal(
                    order.total * (fee_data['value'] / Decimal('100.00')),
                    self.context['event'].currency)
            is_split_taxes = fee_data.pop('_split_taxes_like_products', False)

            if is_split_taxes:
                d = defaultdict(lambda: Decimal('0.00'))
                trz = TaxRule.zero()
                for p in pos_map.values():
                    tr = p.tax_rule
                    d[tr] += p.price - p.tax_value

                base_values = sorted([tuple(t) for t in d.items()],
                                     key=lambda t: (t[0] or trz).rate)
                sum_base = sum(t[1] for t in base_values)
                fee_values = [
                    (t[0],
                     round_decimal(fee_data['value'] * t[1] / sum_base,
                                   self.context['event'].currency))
                    for t in base_values
                ]
                sum_fee = sum(t[1] for t in fee_values)

                # If there are rounding differences, we fix them up, but always leaning to the benefit of the tax
                # authorities
                if sum_fee > fee_data['value']:
                    fee_values[0] = (fee_values[0][0], fee_values[0][1] +
                                     (fee_data['value'] - sum_fee))
                elif sum_fee < fee_data['value']:
                    fee_values[-1] = (fee_values[-1][0], fee_values[-1][1] +
                                      (fee_data['value'] - sum_fee))

                for tr, val in fee_values:
                    fee_data['tax_rule'] = tr
                    fee_data['value'] = val
                    f = OrderFee(**fee_data)
                    f.order = order
                    f._calculate_tax()
                    f.save()
            else:
                f = OrderFee(**fee_data)
                f.order = order
                f._calculate_tax()
                f.save()

        order.total += sum([f.value for f in order.fees.all()])
        order.save(update_fields=['total'])

        if order.total == Decimal('0.00') and validated_data.get(
                'status') == Order.STATUS_PAID and not payment_provider:
            payment_provider = 'free'

        if order.total == Decimal(
                '0.00') and validated_data.get('status') != Order.STATUS_PAID:
            order.status = Order.STATUS_PAID
            order.save()
            order.payments.create(amount=order.total,
                                  provider='free',
                                  state=OrderPayment.PAYMENT_STATE_CONFIRMED,
                                  payment_date=now())
        elif payment_provider == "free" and order.total != Decimal('0.00'):
            raise ValidationError(
                'You cannot use the "free" payment provider for non-free orders.'
            )
        elif validated_data.get('status') == Order.STATUS_PAID:
            if not payment_provider:
                raise ValidationError(
                    'You cannot create a paid order without a payment provider.'
                )
            order.payments.create(amount=order.total,
                                  provider=payment_provider,
                                  info=payment_info,
                                  payment_date=payment_date,
                                  state=OrderPayment.PAYMENT_STATE_CONFIRMED)
        elif payment_provider:
            order.payments.create(amount=order.total,
                                  provider=payment_provider,
                                  info=payment_info,
                                  state=OrderPayment.PAYMENT_STATE_CREATED)

        return order
Esempio n. 21
0
def get_fees(event,
             total,
             invoice_address,
             mod='',
             request=None,
             positions=[],
             gift_cards=None):
    if request is not None and not positions:
        positions = get_cart(request)

    skip_free = event.settings.get('service_fee_skip_free', as_type=bool)
    if skip_free:
        positions = [pos for pos in positions if pos.price != Decimal('0.00')]

    skip_addons = event.settings.get('service_fee_skip_addons', as_type=bool)
    if skip_addons:
        positions = [pos for pos in positions if not pos.addon_to_id]

    skip_non_admission = event.settings.get('service_fee_skip_non_admission',
                                            as_type=bool)
    if skip_non_admission:
        positions = [pos for pos in positions if pos.item.admission]

    fee_per_ticket = event.settings.get('service_fee_per_ticket' + mod,
                                        as_type=Decimal)
    if mod and fee_per_ticket is None:
        fee_per_ticket = event.settings.get('service_fee_per_ticket',
                                            as_type=Decimal)

    fee_abs = event.settings.get('service_fee_abs' + mod, as_type=Decimal)
    if mod and fee_abs is None:
        fee_abs = event.settings.get('service_fee_abs', as_type=Decimal)

    fee_percent = event.settings.get('service_fee_percent' + mod,
                                     as_type=Decimal)
    if mod and fee_percent is None:
        fee_percent = event.settings.get('service_fee_percent',
                                         as_type=Decimal)

    fee_per_ticket = Decimal("0") if fee_per_ticket is None else fee_per_ticket
    fee_abs = Decimal("0") if fee_abs is None else fee_abs
    fee_percent = Decimal("0") if fee_percent is None else fee_percent

    if event.settings.get('service_fee_skip_if_gift_card', as_type=bool):
        gift_cards = gift_cards or []
        if request:
            cs = cart_session(request)
            if cs.get('gift_cards'):
                gift_cards = event.organizer.accepted_gift_cards.filter(
                    pk__in=cs.get('gift_cards'), currency=event.currency)
        summed = 0
        for gc in gift_cards:
            fval = Decimal(gc.value)  # TODO: don't require an extra query
            fval = min(fval, total - summed)
            if fval > 0:
                total -= fval
                summed += fval

    if (fee_per_ticket or fee_abs or fee_percent) and total != Decimal('0.00'):
        fee = round_decimal(
            fee_abs + total * (fee_percent / 100) +
            len(positions) * fee_per_ticket, event.currency)
        tax_rule = event.settings.tax_rate_default or TaxRule.zero()
        tax = tax_rule.tax(fee,
                           invoice_address=invoice_address,
                           base_price_is='gross')
        return [
            OrderFee(fee_type=OrderFee.FEE_TYPE_SERVICE,
                     internal_type='',
                     value=fee,
                     tax_rate=tax.rate,
                     tax_value=tax.tax,
                     tax_rule=tax_rule)
        ]
    return []