Beispiel #1
0
class DiscountCode(Discount):
    """
    A code that can be entered at the checkout process to have a
    discount applied to the total purchase amount.
    """

    code = fields.DiscountCodeField(_("Code"), unique=True)
    min_purchase = fields.MoneyField(_("Minimum total purchase"))
    free_shipping = models.BooleanField(_("Free shipping"))
    uses_remaining = models.IntegerField(_("Uses remaining"), blank=True,
        null=True, help_text=_("If you wish to limit the number of times a "
            "code may be used, set this value. It will be decremented upon "
            "each use."))

    objects = managers.DiscountCodeManager()

    def calculate(self, amount):
        """
        Calculates the discount for the given amount.
        """
        if self.discount_deduct is not None:
            # Don't apply to amounts that would be negative after
            # deduction.
            if self.discount_deduct < amount:
                return self.discount_deduct
        elif self.discount_percent is not None:
            return amount / Decimal("100") * self.discount_percent
        return 0

    class Meta:
        verbose_name = _("Discount code")
        verbose_name_plural = _("Discount codes")
Beispiel #2
0
class DiscountCode(Discount):
    """
    A code that can be entered at the checkout process to have a
    discount applied to the total purchase amount.
    """

    code = fields.DiscountCodeField(_("Code"), unique=True)
    min_purchase = fields.MoneyField(_("Minimum total purchase"))
    free_shipping = models.BooleanField(_("Free shipping"))

    objects = managers.DiscountCodeManager()

    def calculate(self, amount):
        """
        Calculates the discount for the given amount.
        """
        if self.discount_deduct is not None:
            # Don't apply to amounts that would be negative after
            # deduction.
            if self.discount_deduct < amount:
                return self.discount_deduct
        elif self.discount_percent is not None:
            return amount / Decimal("100") * self.discount_percent
        return 0
Beispiel #3
0
class Order(models.Model):

    billing_detail_first_name = CharField(_("First name"), max_length=100)
    billing_detail_last_name = CharField(_("Last name"), max_length=100)
    billing_detail_street = CharField(_("Street"), max_length=100)
    billing_detail_city = CharField(_("City/Suburb"), max_length=100)
    billing_detail_state = CharField(_("State/Region"), max_length=100)
    billing_detail_postcode = CharField(_("Zip/Postcode"), max_length=10)
    billing_detail_country = CharField(_("Country"), max_length=100)
    billing_detail_phone = CharField(_("Phone"), max_length=20)
    billing_detail_email = models.EmailField(_("Email"))
    shipping_detail_first_name = CharField(_("First name"), max_length=100)
    shipping_detail_last_name = CharField(_("Last name"), max_length=100)
    shipping_detail_street = CharField(_("Street"), max_length=100)
    shipping_detail_city = CharField(_("City/Suburb"), max_length=100)
    shipping_detail_state = CharField(_("State/Region"), max_length=100)
    shipping_detail_postcode = CharField(_("Zip/Postcode"), max_length=10)
    shipping_detail_country = CharField(_("Country"), max_length=100)
    shipping_detail_phone = CharField(_("Phone"), max_length=20)
    additional_instructions = models.TextField(_("Additional instructions"),
                                               blank=True)
    time = models.DateTimeField(_("Time"), auto_now_add=True, null=True)
    key = CharField(max_length=40)
    user_id = models.IntegerField(blank=True, null=True)
    shipping_type = CharField(_("Shipping type"), max_length=50, blank=True)
    shipping_total = fields.MoneyField(_("Shipping total"))
    tax_type = CharField(_("Tax type"), max_length=50, blank=True)
    tax_total = fields.MoneyField(_("Tax total"))
    item_total = fields.MoneyField(_("Item total"))
    discount_code = fields.DiscountCodeField(_("Discount code"), blank=True)
    discount_total = fields.MoneyField(_("Discount total"))
    total = fields.MoneyField(_("Order total"))
    transaction_id = CharField(_("Transaction ID"),
                               max_length=255,
                               null=True,
                               blank=True)

    status = models.IntegerField(
        _("Status"),
        choices=settings.SHOP_ORDER_STATUS_CHOICES,
        default=settings.SHOP_ORDER_STATUS_CHOICES[0][0])

    objects = managers.OrderManager()

    # These are fields that are stored in the session. They're copied to
    # the order in setup() and removed from the session in complete().
    session_fields = ("shipping_type", "shipping_total", "discount_total",
                      "discount_code", "tax_type", "tax_total")

    class Meta:
        verbose_name = _("Order")
        verbose_name_plural = _("Orders")
        ordering = ("-id", )

    def __unicode__(self):
        return "#%s %s %s" % (self.id, self.billing_name(), self.time)

    def billing_name(self):
        return "%s %s" % (self.billing_detail_first_name,
                          self.billing_detail_last_name)

    def setup(self, request):
        """
        Set order fields that are stored in the session, item_total
        and total based on the given cart, and copy the cart items
        to the order. Called in the final step of the checkout process
        prior to the payment handler being called.
        """
        self.key = request.session.session_key
        self.user_id = request.user.id
        for field in self.session_fields:
            if field in request.session:
                setattr(self, field, request.session[field])
        self.total = self.item_total = request.cart.total_price()
        if self.shipping_total is not None:
            self.shipping_total = Decimal(str(self.shipping_total))
            self.total += self.shipping_total
        if self.discount_total is not None:
            self.total -= self.discount_total
        if self.tax_total is not None:
            self.total += self.tax_total
        self.save()  # We need an ID before we can add related items.
        for item in request.cart:
            product_fields = [f.name for f in SelectedProduct._meta.fields]
            item = dict([(f, getattr(item, f)) for f in product_fields])
            self.items.create(**item)

    def complete(self, request):
        """
        Remove order fields that are stored in the session, reduce the
        stock level for the items in the order, decrement the uses
        remaining count for discount code (if applicable) and then
        delete the cart.
        """
        self.save()  # Save the transaction ID.
        for field in self.session_fields:
            if field in request.session:
                del request.session[field]
        try:
            del request.session["order"]
        except KeyError:
            pass
        for item in request.cart:
            try:
                variation = ProductVariation.objects.get(sku=item.sku)
            except ProductVariation.DoesNotExist:
                pass
            else:
                variation.update_stock(item.quantity * -1)
                variation.product.actions.purchased()
        code = request.session.get('discount_code')
        if code:
            DiscountCode.objects.active().filter(code=code).update(
                uses_remaining=F('uses_remaining') - 1)
        request.cart.delete()

    def details_as_dict(self):
        """
        Returns the billing_detail_* and shipping_detail_* fields
        as two name/value pairs of fields in a dict for each type.
        Used in template contexts for rendering each type as groups
        of names/values.
        """
        context = {}
        for fieldset in ("billing_detail", "shipping_detail"):
            fields = [(f.verbose_name, getattr(self, f.name))
                      for f in self._meta.fields
                      if f.name.startswith(fieldset)]
            context["order_%s_fields" % fieldset] = fields
        return context

    def invoice(self):
        """
        Returns the HTML for a link to the PDF invoice for use in the
        order listing view of the admin.
        """
        url = reverse("shop_invoice", args=(self.id, ))
        text = ugettext("Download PDF invoice")
        return "<a href='%s?format=pdf'>%s</a>" % (url, text)

    invoice.allow_tags = True
    invoice.short_description = ""
Beispiel #4
0
class Order(SiteRelated):

    billing_detail_first_name = CharField(_("First name"), max_length=100)
    billing_detail_last_name = CharField(_("Last name"), max_length=100)
    billing_detail_street = CharField(_("Street"), max_length=100, blank=True)
    billing_detail_city = CharField(_("City/Suburb"),
                                    max_length=100,
                                    blank=True)
    billing_detail_state = CharField(_("State/Region"),
                                     max_length=100,
                                     blank=True)
    billing_detail_postcode = CharField(_("Zip/Postcode"),
                                        max_length=10,
                                        blank=True)
    billing_detail_country = CharField(_("Country"),
                                       max_length=100,
                                       blank=True)
    billing_detail_phone = CharField(_("Phone"), max_length=20, blank=True)
    billing_detail_email = models.EmailField(_("Email"))
    shipping_detail_first_name = CharField(_("First name"), max_length=100)
    shipping_detail_last_name = CharField(_("Last name"), max_length=100)
    shipping_detail_street = CharField(_("Street"), max_length=100, blank=True)
    shipping_detail_city = CharField(_("City/Suburb"),
                                     max_length=100,
                                     blank=True)
    shipping_detail_state = CharField(_("State/Region"),
                                      max_length=100,
                                      blank=True)
    shipping_detail_postcode = CharField(_("Zip/Postcode"),
                                         max_length=10,
                                         blank=True)
    shipping_detail_country = CharField(_("Country"),
                                        max_length=100,
                                        blank=True)
    shipping_detail_phone = CharField(_("Phone"), max_length=20, blank=True)
    additional_instructions = models.TextField(_("Additional instructions"),
                                               blank=True)
    time = models.DateTimeField(_("Time"), auto_now_add=True, null=True)
    key = CharField(max_length=40)
    user_id = models.IntegerField(blank=True, null=True)
    shipping_type = CharField(_("Shipping type"), max_length=50, blank=True)
    shipping_total = fields.MoneyField(_("Shipping total"))
    tax_type = CharField(_("Tax type"), max_length=50, blank=True)
    tax_total = fields.MoneyField(_("Tax total"))
    item_total = fields.MoneyField(_("Item total"))
    discount_code = fields.DiscountCodeField(_("Discount code"), blank=True)
    discount_total = fields.MoneyField(_("Discount total"))
    total = fields.MoneyField(_("Order total"))
    transaction_id = CharField(_("Transaction ID"),
                               max_length=255,
                               null=True,
                               blank=True)

    status = models.IntegerField(
        _("Status"),
        choices=settings.SHOP_ORDER_STATUS_CHOICES,
        default=settings.SHOP_ORDER_STATUS_CHOICES[0][0])

    persons_adults = models.IntegerField(_("Number of adults"),
                                         default=0,
                                         blank=True,
                                         null=True)
    persons_childs = models.IntegerField(_("Number of children"),
                                         default=0,
                                         blank=True,
                                         null=True)

    objects = managers.OrderManager()

    # These are fields that are stored in the session. They're copied to
    # the order in setup() and removed from the session in complete().
    session_fields = ("shipping_type", "shipping_total", "discount_total",
                      "discount_code", "tax_type", "tax_total")

    class Meta:
        verbose_name = __("commercial meaning", "Order")
        verbose_name_plural = __("commercial meaning", "Orders")
        ordering = ("-id", )

    def __unicode__(self):
        return "#%s %s %s" % (self.id, self.billing_name(), self.time)

    def billing_name(self):
        return "%s %s" % (self.billing_detail_first_name,
                          self.billing_detail_last_name)

    def setup(self, request):
        """
        Set order fields that are stored in the session, item_total
        and total based on the given cart, and copy the cart items
        to the order. Called in the final step of the checkout process
        prior to the payment handler being called.
        """
        self.key = request.session.session_key
        self.user_id = request.user.id
        for field in self.session_fields:
            if field in request.session:
                setattr(self, field, request.session[field])
        self.total = self.item_total = request.cart.total_price()
        if self.shipping_total is not None:
            self.shipping_total = Decimal(str(self.shipping_total))
            self.total += self.shipping_total
        if self.discount_total is not None:
            self.total -= Decimal(self.discount_total)
        if self.tax_total is not None:
            self.total += Decimal(self.tax_total)
        self.save()  # We need an ID before we can add related items.
        for item in request.cart:
            product_fields = [f.name for f in SelectedProduct._meta.fields]
            item = dict([(f, getattr(item, f)) for f in product_fields])
            self.items.create(**item)

    def complete(self, request):
        """
        Remove order fields that are stored in the session, reduce the
        stock level for the items in the order, decrement the uses
        remaining count for discount code (if applicable) and then
        delete the cart.
        """
        self.save()  # Save the transaction ID.
        discount_code = request.session.get('discount_code')
        clear_session(request, "order", *self.session_fields)
        for item in request.cart:
            try:
                variation = ProductVariation.objects.get(sku=item.sku)
            except ProductVariation.DoesNotExist:
                pass
            else:
                if variation.product.content_model == 'reservableproduct':
                    reservableproduct = ReservableProduct.objects.get(
                        product_ptr=variation.product.id)
                    # we also create a reservable order reservation
                    for date in utils.daterange(item.from_date, item.to_date):
                        reservation = ReservableProductReservation.objects.get(
                            date=date, product=reservableproduct)
                        if reservation:
                            reservation_order = ReservableProductOrderReservation(
                                order=self, reservation=reservation)
                            reservation_order.save()
                else:
                    variation.update_stock(item.quantity * -1)
                variation.product.actions.purchased()
        # create reservations in external hook for reservable products (if any)
        if self.has_reservables:
            for item in self.items.all():
                try:
                    variation = ProductVariation.objects.get(sku=item.sku)
                except ProductVariation.DoesNotExist:
                    pass
                else:
                    if variation.product.content_model == 'reservableproduct':
                        # create reservation via hook (if any)
                        # external_order_id = reservableproduct.reserve_via_hook(item.from_date, item.to_date, self.id)
                        external_order_id = -1
                        if external_order_id == -1 or not external_order_id:
                            # External hook reported an error
                            # TODO: send extra notice to shop admins to process this manually
                            pass
                        item.external_order_id = external_order_id
                        item.save()
        if discount_code:
            DiscountCode.objects.active().filter(code=discount_code).update(
                uses_remaining=models.F('uses_remaining') - 1)
        request.cart.delete()

    def details_as_dict(self):
        """
        Returns the billing_detail_* and shipping_detail_* fields
        as two name/value pairs of fields in a dict for each type.
        Used in template contexts for rendering each type as groups
        of names/values.
        """
        context = {}
        for fieldset in ("billing_detail", "shipping_detail"):
            fields = [(f.verbose_name, getattr(self, f.name))
                      for f in self._meta.fields
                      if f.name.startswith(fieldset)]
            context["order_%s_fields" % fieldset] = fields
        return context

    def invoice(self):
        """
        Returns the HTML for a link to the PDF invoice for use in the
        order listing view of the admin.
        """
        url = reverse("shop_invoice", args=(self.id, ))
        text = ugettext("Download PDF invoice")
        return "<a href='%s?format=pdf'>%s</a>" % (url, text)

    invoice.allow_tags = True
    invoice.short_description = ""

    def has_reservables(self):
        for item in self.items.all():
            if item.from_date and item.to_date:
                return True
        return False

    def change_url(self):
        return '<a href="' + reverse("admin:shop_order_change", args=[
            self.id
        ]) + '">' + str(self) + '</a>'