Exemplo n.º 1
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, shipping_address.postcode)
     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'))
Exemplo n.º 2
0
    def test_returns_correct_totals_when_tax_is_known(self):
        basket = mock.Mock()
        basket.total_excl_tax = D('10.00')
        basket.total_incl_tax = D('12.00')
        basket.is_tax_known = True

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

        applicator = SurchargeApplicator()
        surcharges = applicator.get_applicable_surcharges(basket)

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

        self.assertIsInstance(total, prices.Price)
        self.assertEqual(D('10.00') + D('5.00') + D('20.0'), total.excl_tax)
        self.assertTrue(total.is_tax_known)
        self.assertEqual(D('12.00') + D('5.50') + D('22.00'), total.incl_tax)
        self.assertEqual(D('2.00') + D('0.50') + D('2.00'), total.tax)
Exemplo n.º 3
0
    def calculate(self, basket, postcode):
        # If the charge is free then tax must be free (musn't it?) and so we
        # immediately set the tax to zero
        package = Package(formato=CAIXA_PACOTE)
        tax = D(0)
        ret = {}
        try:
            lines = basket.lines.all()
            for line in lines:
                product = line.product
                weight = product.weight
                height = product.height
                width = product.width
                length = product.length
                if all([weight, height, width, length]):
                    package.add_item(
                        weight=weight,  # Peso
                        height=height,  # Altura
                        width=width,  # Largura
                        length=length  # Comprimento
                    )
                client = Client(cep_origem='31330-130')
                servicos = client.calc_preco_prazo(package, postcode, PAC)
                ret.update(service=servicos[0])
                count = 0
                for servico in servicos:
                    tax += D(servico.valor) * line.quantity
            print('TAX', tax)
        except Exception as e:
            tax = D(0)

        price = prices.Price(currency=basket.currency,
                             excl_tax=D('0.00'),
                             tax=tax)
        ret.update(price=price)
        return ret
 def calculate(self, basket):
     return prices.Price(currency=basket.currency,
                         excl_tax=D('0.00'),
                         incl_tax=D('0.00'))
Exemplo n.º 5
0
 def calculate(self, basket):
     return prices.Price(
         currency=basket.currency,
         excl_tax=self.charge_excl_tax,
         incl_tax=self.charge_incl_tax)
Exemplo n.º 6
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'))
Exemplo n.º 7
0
 def calculate(self, basket, **kwargs):
     return prices.Price(currency=basket.currency,
                         excl_tax=self.excl_tax,
                         incl_tax=self.incl_tax)
Exemplo n.º 8
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)
Exemplo n.º 9
0
 def calculate(self, basket):
     total = basket.num_items * self.charge_per_item
     return prices.Price(
         currency=basket.currency,
         excl_tax=total,
         incl_tax=total)
Exemplo n.º 10
0
 def calculate(self, order):
     return prices.Price(currency=order.currency,
                         excl_tax=D('0.00'),
                         tax=D('0.00'))
Exemplo n.º 11
0
 def calculate(self, order):
     return prices.Price(
         currency=order.currency,
         excl_tax=D('0.00'),
         # we insist a tax percentage on order total.
         tax=(order.total_excl_tax * self.rate).quantize(D('0.01')))
Exemplo n.º 12
0
 def calculate(self, basket):
     base_charge = self.method.calculate(basket)
     discount = self.offer.shipping_discount(base_charge.excl_tax)
     excl_tax = base_charge.excl_tax - discount
     return prices.Price(currency=base_charge.currency, excl_tax=excl_tax)
Exemplo n.º 13
0
    def calculate(self, basket):

        total = D('55.00')
        return prices.Price(currency=basket.currency,
                            excl_tax=total,
                            incl_tax=total)
Exemplo n.º 14
0
 def calculate(self):
     return prices.Price(
         currency=self.currency,
         excl_tax=self.excl_tax,
         tax=(self.excl_tax * self.rate).quantize(D('0.01'))
     )
Exemplo n.º 15
0
def place_order(sender, **kwargs):
    """ collect basket, user, shipping_method and address, order_number, total
    and pass them to handle_order_placement, but first add payment events and
    sources """
    request = kwargs.get('request', None) or HttpRequest()
    basket = sender
    user = basket.owner if basket.owner else AnonymousUser()
    guest_email = None

    strategy = selector.strategy(user=user)
    session_data = shipping_address = shipping_method = None
    log.debug("initialising: \n basket = %s \n usr = %s \n strategy = %s",
              basket, user, strategy)
    basket.strategy = strategy
    amount_allocated = kwargs['OutSum']
    session_key = kwargs['session_key']
    order_num = kwargs['order_num']
    if session_key is not None:
        session = SessionStore(session_key=session_key)
        if len(session.items()):
            log.debug("Session %s successfully restored", session)
            request.session = session
            request.user = user
            session_data = CheckoutSessionData(request)
            if isinstance(user, AnonymousUser):
                guest_email = session_data.get_guest_email()

    order_placement = RobokassaOrderPlacement()
    order_placement.request = request
    if session_data is not None:
        order_placement.checkout_session = session_data
        shipping_address = order_placement.get_shipping_address(basket)
        shipping_method = order_placement.get_shipping_method(
            basket, shipping_address)
        total = order_placement.get_order_totals(basket, shipping_method)
    else:  # session not found, lets try to place order anyway
        log.error(("Session was not restored, trying default order for "
                   "basket #%s"), basket.id)
        basket.is_shipping_required = False
        total = prices.Price(currency=basket.currency,
                             excl_tax=basket.total_excl_tax,
                             incl_tax=basket.total_incl_tax)

    # now create payment source and events
    source_type, is_created = SourceType.objects.get_or_create(
        name=u'Робокасса', code='robokassa')
    source = Source(source_type=source_type,
                    amount_allocated=amount_allocated,
                    amount_debited=amount_allocated)
    order_placement.add_payment_source(source)
    order_placement.add_payment_event('allocated', amount_allocated)
    order_placement.add_payment_event('debited', amount_allocated)
    post_payment.send(sender=order_placement, user=user, source=source)

    # all done lets place an order
    order_placement.handle_order_placement(order_num,
                                           user,
                                           basket,
                                           shipping_address,
                                           shipping_method,
                                           total,
                                           guest_email=guest_email)
Exemplo n.º 16
0
    def submit(
            self,
            user,
            basket,
            shipping_address,
            shipping_method,  # noqa (too complex (10))
            shipping_charge,
            billing_address,
            order_total,
            payment_kwargs=None,
            order_kwargs=None):
        """
        Submit a basket for order placement.

        The process runs as follows:

         * Generate an order number
         * Freeze the basket so it cannot be modified any more (important when
           redirecting the user to another site for payment as it prevents the
           basket being manipulated during the payment process).
         * Attempt to take payment for the order
           - If payment is successful, place the order
           - If a redirect is required (eg PayPal, 3DSecure), redirect
           - If payment is unsuccessful, show an appropriate error message

        :basket: The basket to submit.
        :payment_kwargs: Additional kwargs to pass to the handle_payment method
        :order_kwargs: Additional kwargs to pass to the place_order method
        """
        if payment_kwargs is None:
            payment_kwargs = {}
        if order_kwargs is None:
            order_kwargs = {}

        # Taxes must be known at this point
        assert basket.is_tax_known, (
            "Basket tax must be set before a user can place an order")
        assert shipping_charge.is_tax_known, (
            "Shipping method tax must be set before a user can place an order")

        # Freeze the basket so it cannot be manipulated while the customer is
        # completing payment on a 3rd party site.  Also, store a reference to
        # the basket in the session so that we know which basket to thaw if we
        # get an unsuccessful payment response when redirecting to a 3rd party
        # site.
        self.freeze_basket(basket)
        self.checkout_session.set_submitted_basket(basket)

        # We define a general error message for when an unanticipated payment
        # error occurs.
        error_msg = _("A problem occurred while processing payment for this "
                      "order - no payment has been taken.  Please "
                      "contact customer services if this problem persists")

        signals.pre_payment.send_robust(sender=self, view=self)

        # Generate an order number for the top level that includes orders from all the shops
        top_level_order_number = self.generate_order_number(basket)
        self.checkout_session.set_order_number(top_level_order_number)

        logger.info("Order #%s: beginning submission process for basket #%d",
                    top_level_order_number, basket.id)

        items_by_shop = {}

        try:
            self.handle_payment(top_level_order_number, order_total,
                                **payment_kwargs)
        except RedirectRequired as e:
            # Redirect required (eg PayPal, 3DS)
            logger.info("Order #%s: redirecting to %s", top_level_order_number,
                        e.url)
            return HttpResponseRedirect(e.url)
        except UnableToTakePayment as e:
            # Something went wrong with payment but in an anticipated way.  Eg
            # their bankcard has expired, wrong card number - that kind of
            # thing. This type of exception is supposed to set a friendly error
            # message that makes sense to the customer.
            msg = six.text_type(e)
            logger.warning(
                "Order #%s: unable to take payment (%s) - restoring basket",
                top_level_order_number, msg)
            self.restore_frozen_basket()

            # We assume that the details submitted on the payment details view
            # were invalid (eg expired bankcard).
            return self.render_payment_details(self.request,
                                               error=msg,
                                               **payment_kwargs)
        except PaymentError as e:
            # A general payment error - Something went wrong which wasn't
            # anticipated.  Eg, the payment gateway is down (it happens), your
            # credentials are wrong - that king of thing.
            # It makes sense to configure the checkout logger to
            # mail admins on an error as this issue warrants some further
            # investigation.
            msg = six.text_type(e)
            logger.error("Order #%s: payment error (%s)",
                         top_level_order_number,
                         msg,
                         exc_info=True)
            self.restore_frozen_basket()
            messages.error(self.request, e.message)
            return self.render_payment_details(self.request,
                                               error=msg,
                                               **payment_kwargs)
            # return self.render_preview(
            #     self.request, error=error_msg, **payment_kwargs)
        except Exception as e:
            # Unhandled exception - hopefully, you will only ever see this in
            # development...
            logger.error(
                "Order #%s: unhandled exception while taking payment (%s)",
                top_level_order_number,
                e,
                exc_info=True)
            self.restore_frozen_basket()
            messages.error(self.request, e.message)
            return self.render_payment_details(self.request,
                                               error=msg,
                                               **payment_kwargs)
            # return self.render_preview(
            #     self.request, error=error_msg, **payment_kwargs)

        top_level_order = None
        try:
            top_level_order = self.handle_order_placement(
                top_level_order_number, user, basket, shipping_address,
                shipping_method, shipping_charge, order_total, billing_address,
                **order_kwargs)
        except UnableToPlaceOrder as e:
            # It's possible that something will go wrong while trying to
            # actually place an order.  Not a good situation to be in as a
            # payment transaction may already have taken place, but needs
            # to be handled gracefully.
            msg = six.text_type(e)
            logger.error("Order #%s: unable to place order - %s",
                         top_level_order_number,
                         msg,
                         exc_info=True)
            self.restore_frozen_basket()
            return self.render_preview(self.request,
                                       error=msg,
                                       **payment_kwargs)

        # Collect information to split into an order for each shop
        for line in basket.all_lines():
            if (line.product.shop not in items_by_shop):
                items_by_shop[line.product.shop] = {
                    "products": [],
                    "order_total": 0
                }
            items_by_shop[line.product.shop]["products"].append(line.product),
            items_by_shop[
                line.product.shop]["order_total"] += line.line_price_excl_tax

        for shop in items_by_shop.keys():

            # We generate the order number first as this will be used
            # in payment requests (ie before the order model has been
            # created).  We also save it in the session for multi-stage
            # checkouts (eg where we redirect to a 3rd party site and place
            # the order on a different request).
            order_number = self.generate_order_number(basket, shop.id)
            order = None
            # This is needed to clear the payment events/sources as to not create the payment event/sources on the sub orders
            self._payment_events = []
            self._payment_sources = []
            try:
                order_kwargs['shop'] = shop
                order = self.handle_order_placement(
                    order_number, user, basket, shipping_address,
                    shipping_method, shipping_charge,
                    prices.Price(currency=basket.currency,
                                 excl_tax=items_by_shop[shop]["order_total"],
                                 incl_tax=items_by_shop[shop]["order_total"]),
                    billing_address, **order_kwargs)
            except UnableToPlaceOrder as e:
                # It's possible that something will go wrong while trying to
                # actually place an order.  Not a good situation to be in as a
                # payment transaction may already have taken place, but needs
                # to be handled gracefully.
                msg = six.text_type(e)
                logger.error("Order #%s: unable to place order - %s",
                             order_number,
                             msg,
                             exc_info=True)
                self.restore_frozen_basket()
                return self.render_preview(self.request,
                                           error=msg,
                                           **payment_kwargs)

        signals.post_payment.send_robust(sender=self, view=self)

        # If all is ok with payment, try and place order
        logger.info("Order #%s: payment successful, placing order",
                    top_level_order_number)

        basket.submit()

        return self.handle_successful_order(top_level_order)