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")
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
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 = ""
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>'