Title: Pragmatically adding give-aways/freebies to an online store
Tags: python;django;e-commerce;logic
Question: Our business currently has an online store and recently we've been offering free specials to our customers. Right now, we simply display the special and give the buyer a notice stating we will add the extra free items to their order after they checkout. Of course, it'd be nice to automate this entire process.

I've been mulling over a few ideas, mainly creating a Discount model (I'm using Django in this case, but this is more of a logic question) and having that model have a variety of flags and product lists so I could create an instance like so:

``` Discount(
       description="Get one free pair of bands when you buy two pairs of shoes.",
       valid_products=[BigProductA, BigProductB],
       received_products=[FreebieProductA, FreebieProductB],
       special_in_intervals=2, # Whenever the user buys 2, give one for free
       )
```

This logic kind of works. I can then take a look at what is in their cart and test against the existing Discounts in the model and see if they apply for anything. The biggest problem with this is it can get very messy especially if you have multiple specials going on and I just don't see it working out too well.

Unfortunately, that's really my best idea for this right now. So, I come to ask you guys: What do you think is the best approach for this? I'm not looking for code, just some ideas of logic and ways to do this. :)

Thanks in advance!


Here is the accepted answer: Welcome to hell. Stay a while. ;) Ahem.

Discounts are a mess, so it's not surprising that you feel tainted by having to work with them. From a design point of view, the testing should be part of the ```Discount``` instance, i.e. there should be an ```appliesTo(cart)``` method and an ```apply(cart)``` method. The first tells you whether a discount applies, the second one actually applies the discount. I suggest that the ```apply()``` method doesn't change the "user part" of the cart but instead modifies extra fields, so you can easily reset the cart (drop all discounts) and run the process again.

This way, you can cleanly implement the two types of discounts that appear most often: "Get X for free, when buying Y" and "get a rebate of X% if you buy for Y $$$". Since you don't change the original figures, you can easily apply multiple discounts and rebates.

I also suggest to back this up with a whole lot of unit tests to make sure the whole thing behaves as you expect. Otherwise the next discount might be your last. :)

Comment for this answer: I would store it in special/additional fields in the cart. You must be able to identify the additional items which the discounts added, etc. That way, the total calculator can ignore them while the renderer can still display them. Don't hesitate to add transient fields to the items in the cart if you need them; just don't modify the data in other fields.
Comment for this answer: Thanks for the answer. So far, I'm keeping all the logic within the `Discount` model and I plan to keep it that way. My biggest issue is the process of giving the user their free item to select from a list and then knowing not to do it again. Where would you store that data? I have a few ideas but they all start off good and then go into a messy hell of logic and too much steps. Still thinking though ...
Comment for this answer: Thanks, that seems to be the best route for this. Will of course have to write many unit tests to ensure it works correctly, oh god .. :)

Here is another answer: I don't quiet get the question - but if you select DISTINCT (I'm writing "pseudo logic" in SQL) all free items that match the items in the car , and then if you wish to give only one or n of them - 
SELECT TOP(n) DISTINCT from tblFREE where freebeid in (select freebdid from tbl itemsfreebe where items in (Select Items from CART where **** Freebe givaway LOGIC***))

freebe giveaway logic is the generic placeholder that should always evaluate for true or false:

like where (select count(*) from cart >2)
so if the logic works - you'll get items in the list, and if not - you'll get nothing.

you can move this logic to your code and run only the first part of the "query" in the DB...

logic can be used with AND or OR with other logics....

once the user accept the offer - you add the list to the cart, and should rais a flag that the discount/freebee was applied - so it won't happen twice...

I wonder what does it means that it easier to SQL it than to say it :-)

I hope that targets your question...


{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 2, 'CommentCount': 0, 'ContentLicense': 'CC BY-SA 2.5', 'CreationDate': '2009:00:11/09/09', 'FavoriteCount': None, 'PostTypeId': 1, 'Tags': '<python><django><e-commerce><logic>', 'Title': 'Pragmatically adding give-aways/freebies to an online store', 'ViewCount': 128, 'id': 1701956, 'user_AboutMe': '<p>computers!</p>\n', 'user_AccountId': 33972.0, 'user_DisplayName': 'Bartek', 'user_Reputation': 14879.0, 'user_Views': 1103.0, 'user_id': 95509.0}}