Discount system brainstorm.

Create issue
Issue #1381 new
Benoit Clennett-Sirois created an issue

Hi Satchmo Team,

I have already produced a few satchmo sites (I participated in about 4 of these projects so far), and discount system has historically always been a major headache, we usually resolved the problem by monkeypatching the whole discount system or trying to customize it using signals (which gave us limited options for customization). I think that a major refactoring of the discount system would be worth it.

So i'm opening this ticket mostly to start a discussion on the current implementation of the discount system and how it could be improved.

Here are my current complaints about the discount system: Cannot create automatic $ amount discounts. find_best_auto_discount simply compares percentages, which doesn't actually return the best discount (10% on 2 items @ 10$ ea. is better than 25% on 1 item at 4$, yet the best discount returned is the 25$ one). Cannot apply multiple discount to one order (discount code is a field and that's the discount that's applied on the order), also the system will likely break when trying to apply multiple discounts with Satchmo, in its current form. No way to "preview" the discount on the cart. *Very limited customizability

Although I am currently working on some code to resolve this for a current project, it's quite similar to things I've done before.. it's quite hacky and it's not fun to write.

I would like to define here what a discount consists of, and if I may, I'll refer to discount as a promotion. I know that a lot of retailers have different concepts when it comes to pricing, they sometimes have 3 prices for a product, the retail price, the store price and the sale price. The sale price here is not a discount, but rather it's the item's effective price, the store price / retail price are just stored to show how good of a deal the sale price is, so I won't be talking about sales here, but rather about promotions.

So in my mind, a promotion consists of: A bucket (such as a cart) containing priced items on which the discounts are applied, either individually on line items, or on other aspects of the bucket (tax discount?) A set of reward (An applicator) such as free shipping, $ amount off, % off, a free item, etc. This is the reward that will be applied on the bucket. *A set of conditions (A set of criteria) such as required product combination, required categories, minimum dollar amount, date range, discount code, customer location, etc. The conditions must all validate for the discount to be valid.

I once started working on some code to create highly adaptable generic discount system in python that could be plugged into satchmo.. or any other python-based e-commerce app.

This is part of the test cases:

{{{ #!python

class TestDiscount(TestCase): def setUp(self): pass

def testDiscount(self):
    # Create a few criteria
    cr1 = ValueCriterion('Val1', AtLeast(10) & AtMost(13))
    cr2 = ValueCriterion('Val2', AtLeast(100))
    cr3 = InclusionCriterion('Inc2', IncludesOne([1, 6]))
    cr4 = InclusionCriterion('Inc2', IncludesOne([8, 6]))
    cr5 = NullCriterion('Null')

    # Create a few discounts
    d1 = Discount(
        criterion = cr1 & cr3,
        start_date =, month=1, day=1),
        end_date =, month=1, day=1),

    d2 = Discount(
        criterion = cr1 | cr2,
        start_date =, month=1, day=1),
        end_date =, month=1, day=1),

    d3 = Discount(
        criterion = cr1 & cr2,
        start_date =, month=1, day=1),
        end_date =, month=1, day=1),

    d4 = Discount(
        criterion = cr1 & cr4,
        start_date =, month=1, day=1),
        end_date =, month=1, day=1),

    d5 = Discount(
        criterion = cr1 & cr4,
        start_date =, month=1, day=1),
        end_date =, month=1, day=1),

    d6 = Discount(
        criterion = cr5,
        start_date =, month=1, day=1),
        end_date =, month=1, day=1),

    d7 = Discount(
        criterion = CodeCriterion('Code1', 'seekret') & \
            LengthCriterion('Len1', AtLeast(4)),
        start_date =, month=1, day=1),
        end_date =, month=1, day=1),

    # Create a bucket
    b = Bucket()
    b2 = Bucket()
    b.items = (
        BucketItem(1, 3, 'Pickle'),
        BucketItem(1, 3, 'Pickle'),
        BucketItem(2, 1, 'Tomato'),
        BucketItem(3, 1, 'Baozi'),
        BucketItem(4, 2, 'Piano'),

    b2.items = (
        BucketItem(1, 3, 'Pickle'),

    self.assertTrue(d7.is_valid(b, code='seekret'))
    self.assertFalse(d7.is_valid(b, code='wrong'))
    self.assertFalse(d7.is_valid(b2, code='seekret'))


I don't know if this code is still useful, but I'll ask my former employer if they don't mind releasing what we've started working on (it's since been abandoned)

If not well this is just an example of an API that could be very adaptable for discount systems in various python-based apps, and it would be nice to have somthing this adaptable for satchmo.

What do you guys think?

Comments (11)

  1. Benoit Clennett-Sirois reporter

    On another note, the testcases I am showing here don't show how applicators are specified... but ultimately when defining a discount, you'd also have to pass applicators as keyword arguments.. or something like this. I don't think I had the applicators working yet inside the discount class. Anyways, it's just sample code.

  2. Benoit Clennett-Sirois reporter

    This is just sample code, and it has nothing to do with the database so far. The idea was to create a django backend for storage, and a satchmo plugin that would make use of the discount system.

    I don't think it's possible with such business rules to resolve this with database queries only, I think much of it will involve data crunching with python, such as.. iterating through all valid discounts for an order, finding out the discounted amount, sort by highest discounted amount, return best discount...

    For example, consider the complexity of trying to do only DB queries to find out which of the following discount is the best:

    • 1 gives you 25% off and applies on all marked-down products, anywhere in the category branch "Pants"
    • 1 gives you 10$ off on all T-Shirts with a minimum purchase of 100$.

    Anyways like I said, it may not be the solution, but I think a solution is needed.

  3. Chris Moffitt repo owner

    I do agree we need to make the discount process more robust. I do think this is a decent approach but we need to think through all the use cases.

    Once we get 0.9.2 released we can certainly look at this.

  4. Andrea de la Huerta

    Hi, i'm back in this satchmo project where I had to mess-up with discounts (and in a hurry) to get what I needed done. So I've been trying for about 3 hrs to "fix" my old quick&dirty fixes into something clean and waterproof, but I'm going mad in this discount-code-jungle where everything is probably far more complicated than it should. And so many other potential-issues I've found. So I think the best approach is to begin here instead of going mad inside the code.

    The first question would be, is there still interesst in a new discount-system? Or is this issue frozen?

    If the idea of creating a new, better and easier discount-system is still alive, then I would be glad to contribute wherever I can.

    I find the very customizable approach of benoitcsirois sounds good.

    In my case we need 2 types of discounts or promotions (fits better).

    One thing I had to struggle with, was to get a list of all products (active) which currently have an promotion-price on them.

    Another thing missing, was the possibility of adding more than one discount (displaying them each on a line in the order-summaries). Like one line for Promotion A, one line for Shipping Discount, One line for a special Payment Discount, etc.

    There are so many different "standard" ways, which probably depend on the country, and so much more custom-needs. That I definitely agree with a very generic way of defining a Discount and providing a fairly easy way of customizing it, instead of offering an out-of-the-box system, which will fit many, but many others will have a hard time trying to change it. It fits more the satchmo philosophy.

    And not only discounts. I think extra-charges could fit on this Schema too.

    So, the way I see it, there are this kinds of "order-price-changes"... - Product-level price-changes ... which are (always?) attached to a product (options, variations), and can be left out of this model? That means, that is the "real" price of the Product.

    - And then, all order-relevant changes (shipping, payments, order-amount, product-combinations, time-limited promotions, user-group, etc.) which also could include extra charges on the order for shipping, payment, extra big or difficult to send items, etc.

    So, i'm thinking of a Model, approaching the one presented above, in which this order-price-change items can be attached (per ForeignKey to the Order?) per signals to the order/products. There could be an admin-mask for creating very common discounts (shipping, payment, etc.) and new ones should be easy to plugin to the order during the checkout-process per signals.

    Another thought: If we do get a new-discount-system, should it co-exist with the old one? or should it completely replace it? Maybe a co-existence would allow more freedom, as there would be no need to think about backward-compatibility issues. And allowing the users to "switch" when they are ready. Leaving the old one for a while marked as "deprecated".

    So, those are my 2 cents in this brainstorm. I hope this stone starts rolling after all :-)

  5. Benoit Clennett-Sirois reporter

    Hey Andrea,

    I think that a good approach would be to replace the existing one, I don't think that the existing one allows enough flexibility by design (1 discount per order for example). And not generic enough.

    I *have* to make several improvements to the discount system for my client. I have implemented "promotions" using the price query signals, but I am not sure it's the best approach for now. I have also started to work on a system based on the one shown above .

    Perhaps we can create a fork the project and start working on this? I basically need to make this work, but there's now rush, perhaps over the next few months.

  6. Andrea de la Huerta

    Hi Ben (?),

    sorry for taking so long to reply. I was on vacations. Ok, then we are on a similar situation. I also have some pressure to make things work quite quickly - that is I updated the shop to use the latest satchmo/django/sorl and now it's all about fixing this Discount issues. And I really want to take a better approach on this whole discount-theme instead of wasting my time on my q&d fix from the last time. So I find the idea of the fork sound really good. I already have a fork using my q&d fix for modifying the discount based on shippment/payment options (using a new signal I added to the trunk "satchmo_order_total_calc") and also to use the latest sorl... you can have a look to the diff if you want to here:

    So how should we proceed, do you want to create the new fork?

  7. Benoit Clennett-Sirois reporter

    Edit Actually scratch this, we should ensure that this kind of collaboration is public, as input from others is important. I just forked satchmo here:

    I sent you an invite to share admin rights.

    Perhaps we can continue the brainstorm in the wiki?

    Hi Andrea, yea Ben is fine :)

    How about we chat on Google about this, I'd be curious to find out what types of discounts your clients have requested, then we can maybe synthesize this information.. and try to come up with something generic enough to support *everything*.

    my email: benoitcsirois at gmail period com.

  8. Tomas Neme

    I've also filed a couple of issues on discounts, which for the most part are no more than more caveats in the discounts system. #1448 is the only one I find now, and I was about to file a new one saying

    • When a Configurable Product (variations base product) gets marked for an auto discount, the variations are not discounted, and the discount's isValid returns False for them
    • The "only one discount per order" thing is really bothering. I want to have a discount on books and another one on shoes, having both auto discounts active actually works, but the buyer can't get discount on both items
  9. Andrea de la Huerta

    Hi Tomas,

    sorry, about the repsonse-times, i've been really busy this days and I didn't realize you had posted something. I had forgotten to watch this issue.

    Anyways, yes, the "only one discount per order" was already on our list and is definitely a BIG item on the list ;-). Btw, the "list" is on the wiki-page of the satchmo-discounts-fork (mentioned above). I also "recently" found out about the different problems with the variations. I was already working on a patch for it, and found so many things that "felt" wrong on the code, and that's why I decided to join Ben's efforts to completely whipe and start something new on the whole Discount-Theme. But on the other side, we haven't had any feedback at all from other devs or users to back up this idea, so I don't know if all this is worth the effort. The site of my client has been "freezed" on it's acutal working state and the discount-revolution is not going to happen in the next future. So ... I don't know about Ben, but I'm not sure if I'll continue on this - not even really started - project. I might change my mind if more people would be interessted or even better involved in this.

  10. Log in to comment