Example #1
0
    def test_includes_hourly_report_with_orders(self):
        create_order(
            total=prices.Price('GBP', excl_tax=D('34.05'), tax=D('0.00')))
        create_order(
            total=prices.Price('GBP', excl_tax=D('21.90'), tax=D('0.00')))
        report = IndexView().get_hourly_report()

        self.assertEqual(len(report['order_total_hourly']), 12)
        self.assertEqual(len(report['y_range']), 11)
        self.assertEqual(report['max_revenue'], D('60'))
Example #2
0
    def calculate(self, basket):
        if (self.free_shipping_threshold is not None
                and basket.total_incl_tax >= self.free_shipping_threshold):
            return prices.Price(currency=basket.currency,
                                excl_tax=D('0.00'),
                                incl_tax=D('0.00'))

        charge = self.price_per_order
        for line in basket.lines.all():
            if line.product.is_shipping_required:
                charge += line.quantity * self.price_per_item

        # Zero tax is assumed...
        return prices.Price(currency=basket.currency,
                            excl_tax=charge,
                            incl_tax=charge)
Example #3
0
    def build_submission(self, **kwargs):
        """
        Return a dict of data that contains everything required for an order
        submission.  This includes payment details (if any).

        This can be the right place to perform tax lookups and apply them to
        the basket.
        """
        shipping_kwargs = {}
        basket = kwargs.get('basket', self.request.basket)
        shipping_address = self.get_shipping_address(basket)
        shipping_method = self.get_shipping_method(basket, shipping_address)
        billing_address = self.get_billing_address(shipping_address)
        shipping_charge = prices.Price(currency=basket.currency,
                                       excl_tax=D('0.00'),
                                       incl_tax=D('0.00'))
        if not shipping_method:
            total = None
        else:
            shipping_kwargs = self.get_shipping_kwargs()
            # add shipping charge if only method has prepaid payment type set as true
            # or has not payment_type attr (for simple methods)
            if is_prepaid_shipping(shipping_method):
                shipping_charge = shipping_method.calculate(
                    basket, shipping_kwargs or None)
            total = self.get_order_totals(basket,
                                          shipping_charge=shipping_charge)
        submission = {
            'user': self.request.user,
            'basket': basket,
            'shipping_address': shipping_address,
            'shipping_method': shipping_method,
            'shipping_charge': shipping_charge,
            'billing_address': billing_address,
            'order_total': total,
            # Details for shipping charge
            'order_kwargs': {},
            'payment_kwargs': {}
        }

        # If there is a billing address, add it to the payment kwargs as calls
        # to payment gateways generally require the billing address. Note, that
        # it normally makes sense to pass the form instance that captures the
        # billing address information. That way, if payment fails, you can
        # render bound forms in the template to make re-submission easier.
        if billing_address:
            submission['payment_kwargs']['billing_address'] = billing_address

        # Allow overrides to be passed in
        submission.update(kwargs)

        # Set guest email after overrides as we need to update the order_kwargs
        # entry.
        if (not submission['user'].is_authenticated()
                and 'guest_email' not in submission['order_kwargs']):
            email = self.checkout_session.get_guest_email()
            submission['order_kwargs']['guest_email'] = email

        return submission
Example #4
0
 def calculate(self, basket):
     base_charge = self.method.calculate(basket)
     discount = self.offer.shipping_discount(base_charge.incl_tax)
     incl_tax = base_charge.incl_tax - discount
     excl_tax = self.calculate_excl_tax(base_charge, incl_tax)
     return prices.Price(currency=base_charge.currency,
                         excl_tax=excl_tax,
                         incl_tax=incl_tax)
Example #5
0
 def calculate(self, basket, shipping_charge, **kwargs):
     excl_tax = basket.total_excl_tax + shipping_charge.excl_tax
     if basket.is_tax_known and shipping_charge.is_tax_known:
         incl_tax = basket.total_incl_tax + shipping_charge.incl_tax
     else:
         incl_tax = None
     return prices.Price(currency=basket.currency,
                         excl_tax=excl_tax,
                         incl_tax=incl_tax)
    def test_returns_correct_totals_when_tax_is_not_known(self):
        basket = mock.Mock()
        basket.total_excl_tax = D('10.00')
        basket.is_tax_known = False

        shipping_charge = prices.Price(currency=basket.currency,
                                       excl_tax=D('5.00'))

        total = self.calculator.calculate(basket, shipping_charge)

        self.assertIsInstance(total, prices.Price)
        self.assertEqual(D('10.00') + D('5.00'), total.excl_tax)
        self.assertFalse(total.is_tax_known)
Example #7
0
    def calculate(self, basket):
        # Note, when weighing the basket, we don't check whether the item
        # requires shipping or not.  It is assumed that if something has a
        # weight, then it requires shipping.
        scale = Scale(attribute_code=self.weight_attribute,
                      default_weight=self.default_weight)
        weight = scale.weigh_basket(basket)
        charge = self.get_charge(weight)

        # Zero tax is assumed...
        return prices.Price(currency=basket.currency,
                            excl_tax=charge,
                            incl_tax=charge)
Example #8
0
    def validate(self, attrs):
        request = self.context['request']

        if request.user.is_anonymous:
            if not settings.IZI_ALLOW_ANON_CHECKOUT:
                message = _('Anonymous checkout forbidden')
                raise serializers.ValidationError(message)

            if not attrs.get('guest_email'):
                # Always require the guest email field if the user is anonymous
                message = _('Guest email is required for anonymous checkouts')
                raise serializers.ValidationError(message)
        else:
            if 'guest_email' in attrs:
                # Don't store guest_email field if the user is authenticated
                del attrs['guest_email']

        basket = attrs.get('basket')
        basket = assign_basket_strategy(basket, request)
        if basket.num_items <= 0:
            message = _('Cannot checkout with empty basket')
            raise serializers.ValidationError(message)

        shipping_method = self._shipping_method(
            request, basket, attrs.get('shipping_method_code'),
            attrs.get('shipping_address'))
        shipping_charge = shipping_method.calculate(basket)
        posted_shipping_charge = attrs.get('shipping_charge')

        if posted_shipping_charge is not None:
            posted_shipping_charge = prices.Price(**posted_shipping_charge)
            # test submitted data.
            if not posted_shipping_charge == shipping_charge:
                message = _('Shipping price incorrect %s != %s' %
                            (posted_shipping_charge, shipping_charge))
                raise serializers.ValidationError(message)

        posted_total = attrs.get('total')
        total = OrderTotalCalculator().calculate(basket, shipping_charge)
        if posted_total is not None:
            if posted_total != total.incl_tax:
                message = _('Total incorrect %s != %s' %
                            (posted_total, total.incl_tax))
                raise serializers.ValidationError(message)

        # update attrs with validated data.
        attrs['total'] = total
        attrs['shipping_method'] = shipping_method
        attrs['shipping_charge'] = shipping_charge
        attrs['basket'] = basket
        return attrs
Example #9
0
 def get_shipping_charge(self, basket):
     shipping_charge = prices.Price(currency=basket.currency,
                                    excl_tax=D('0.00'),
                                    incl_tax=D('0.00'))
     shipping_address = self.get_shipping_address(basket)
     shipping_method = self.get_shipping_method(basket, shipping_address)
     if shipping_method:
         shipping_kwargs = self.get_shipping_kwargs()
         shipping_charge = shipping_method.calculate(
             basket, shipping_kwargs or None)
     else:
         # It's unusual to get here as a shipping method should be set by
         # the time this skip-condition is called. In the absence of any
         # other evidence, we assume the shipping charge is zero.
         #shipping_charge = D('0.00')
         pass
     return shipping_charge
Example #10
0
 def skip_unless_payment_is_required(self, request):
     # Check to see if payment is actually required for this order.
     shipping_address = self.get_shipping_address(request.basket)
     shipping_method = self.get_shipping_method(request.basket,
                                                shipping_address)
     if shipping_method:
         shipping_charge = shipping_method.calculate(request.basket)
     else:
         # It's unusual to get here as a shipping method should be set by
         # the time this skip-condition is called. In the absence of any
         # other evidence, we assume the shipping charge is zero.
         shipping_charge = prices.Price(currency=request.basket.currency,
                                        excl_tax=D('0.00'),
                                        tax=D('0.00'))
     total = self.get_order_totals(request.basket, shipping_charge)
     if total.excl_tax == D('0.00'):
         raise exceptions.PassedSkipCondition(
             url=reverse('checkout:preview'))
Example #11
0
 def calculate(self, basket):
     return prices.Price(currency=basket.currency,
                         excl_tax=self.charge_excl_tax,
                         incl_tax=self.charge_incl_tax)
Example #12
0
 def calculate(self, basket):
     # If the charge is free then tax must be free (musn't it?) and so we
     # immediately set the tax to zero
     return prices.Price(currency=basket.currency,
                         excl_tax=D('0.00'),
                         tax=D('0.00'))
Example #13
0
    def calculate(self, basket, options=None):
        # TODO: move code to smth like ShippingCalculator class
        results = []
        charge = D('0.0')
        self.messages = []
        self.errors = []
        # Note, when weighing the basket, we don't check whether the item
        # requires shipping or not.  It is assumed that if something has a
        # weight, then it requires shipping.
        scale = Scale(attribute_code=self.weight_attribute,
                      default_weight=self.default_weight)
        packer = Packer(self.containers,
                        attribute_codes=self.size_attributes,
                        weight_code=self.weight_attribute,
                        default_weight=self.default_weight)
        weight = scale.weigh_basket(basket).quantize(weight_precision)
        # Should be a list of dicts { 'weight': weight, 'container' : container }
        packs = packer.pack_basket(basket)
        facade = self.facade
        if not self.destination:
            self.errors.append(
                _("ERROR! There is no shipping address for charge calculation!\n"
                  ))
        else:
            self.messages.append(
                _(u"""Approximated shipping price
                                for {weight} kg from {origin} 
                                to {destination}\n""").format(
                    weight=weight,
                    origin=self.origin,
                    destination=self.destination.city))

            # Assuming cases like http protocol suggests:
            # e=200  - OK. Result contains charge value and extra info such as Branch code, etc
            # e=404  - Result is empty, no destination found via API, redirect
            #          to address form or prompt to API city-codes selector
            # e=503  - API is offline. Skip this method.
            # e=300  - Too many choices found, Result contains list of charges-codes.
            #          Prompt to found dest-codes selector

            # an URL for AJAXed city-to-city charge lookup
            details_url = reverse_lazy('shipping:charge-details',
                                       kwargs={'slug': self.code})
            # an URL for AJAXed code by city lookup using Select2 widget
            lookup_url = reverse_lazy('shipping:city-lookup',
                                      kwargs={'slug': self.code})

            # if options set make a short call to API for final calculation
            if options:
                errors = None
                try:
                    results, errors = facade.get_charge(
                        options['senderCityId'], options['receiverCityId'],
                        packs)
                except CalculationError as e:
                    self.errors.append("Post-calculation error: %s" % e.errors)
                    self.messages.append(e.title)
                except:
                    raise
                if not errors:
                    (charge, msg, err,
                     self.extra_form) = facade.parse_results(results,
                                                             options=options)
                    if msg:
                        self.messages.append(msg)
                    if err:
                        self.errors.append(err)
                else:
                    raise CalculationError(
                        "%s -> %s" %
                        (options['senderCityId'], options['receiverCityId']),
                        errors)
            else:
                try:
                    results = facade.get_charges(weight, packs, self.origin,
                                                 self.destination)
                except ApiOfflineError:
                    self.errors.append(
                        _(u"""%s API is offline. Can't
                                         calculate anything. Sorry!""") %
                        self.name)
                    self.messages.append(
                        _(u"Please, choose another shipping method!"))
                except OriginCityNotFoundError as e:
                    # Paranoid mode as ImproperlyConfigured should be raised by facade
                    self.errors.append(
                        _(u"""City of origin '%s' not found
                                      in the shipping company 
                                      postcodes to calculate charge.""") %
                        e.title)
                    self.messages.append(
                        _(u"""It seems like we couldn't find code
                                        for the city of origin (%s).
                                        Please, select it manually, choose another 
                                        address or another shipping method.
                                    """) % e.title)
                except ImproperlyConfigured as e:  # upraised error handling
                    self.errors.append("ImproperlyConfigured error (%s)" %
                                       e.message)
                    self.messages.append(
                        "Please, select another shipping method or call site administrator!"
                    )
                except CityNotFoundError as e:
                    self.errors.append(
                        _(u"""Can't find destination city '{title}'
                                      to calculate charge. 
                                      Errors: {errors}""").format(
                            title=e.title, errors=e.errors))
                    self.messages.append(
                        _(u"""It seems like we can't find code
                                        for the city of destination (%s).
                                        Please, choose
                                        another address or another shipping method.
                                    """) % e.title)
                    if CHANGE_DESTINATION:
                        self.messages.append(
                            _("Also, you can choose city of destination manually"
                              ))
                        self.extra_form = facade.get_extra_form(
                            origin=self.origin,
                            lookup_url=lookup_url,
                            details_url=details_url)
                except TooManyFoundError as e:
                    self.errors.append(
                        _(u"Found too many destinations for given city (%s)") %
                        e.title)
                    if CHANGE_DESTINATION:
                        self.messages.append(
                            _("Please refine your shipping address"))
                        self.extra_form = facade.get_extra_form(
                            origin=self.origin,
                            choices=e.results,
                            details_url=details_url)
                except CalculationError as e:
                    self.errors.append(
                        _(u"""Error occurred during charge
                                        calculation for given city (%s)""") %
                        e.title)
                    self.messages.append(_(u"API error was: %s") % e.errors)
                    if CHANGE_DESTINATION:
                        self.extra_form = facade.get_extra_form(
                            origin=self.origin,
                            details_url=details_url,
                            lookup_url=lookup_url)
                except:
                    raise
                else:
                    (charge, msg, err, self.extra_form) = facade.parse_results(
                        results,
                        origin=self.origin,
                        dest=self.destination,
                        weight=weight,
                        packs=packs)
                    if msg:
                        self.messages.append(msg)
                    if err:
                        self.errors.append(err)

        # Zero tax is assumed...
        return prices.Price(currency=basket.currency,
                            excl_tax=charge,
                            incl_tax=charge)