Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add product discounts (OCC-31) #136

Closed
sarahelsaig opened this issue Jul 31, 2022 · 5 comments · Fixed by #227
Closed

Add product discounts (OCC-31) #136

sarahelsaig opened this issue Jul 31, 2022 · 5 comments · Fixed by #227
Assignees
Milestone

Comments

@sarahelsaig
Copy link
Contributor

sarahelsaig commented Jul 31, 2022

Note: see #9 for general discussions about various types of promotions. This issue is a subset of that.

Concept

Let's start with simple discounts associated with a specific product. The discount would be described with a separate DiscountPart. Abstraction should be similar to how prices are done in OCC. So there should be an IPromotionProvider that describes the service that interacts with the ShoppingCartItem (e.g. applies the discount from its content item's DiscountPart) and an IPromotionService that resolves and applies the promotion providers.

DiscountPart Fields or Properties

  • Percentage (int/NumericField) a percentage discount to be applied to the product price.
    How should rounding be handled? (for reference here is what Nwazet did)
  • Amount (Amount) a flat number to be subtracted from the price (ofc result can't be negative)
  • BeginningUtc (DateTime/DateTimeField) if the current time is lower, the discount is ignored.
  • ExpirationUtc (DateTime/DateTimeField) if the current time is higher, the discount is ignored.
  • Minimum (int/NumericField) if the quantity is lower, the discount is ignored.
  • Maximum (int/NumericField) if the quantity is higher, the discount is ignored (0 should be treated as infinite).

You shouldn't be able to have both Percentage and Amount (use handler).

Services

IPromotionService should have Task<IList<ShoppingCartItem>> AddPromotionsAsync(IList<ShoppingCartItem> items); (like price). This should sort and select the applicable IPromotionProviders, call the ApplyingPromotion event (see below), then apply the resulting promo(s) on the shopping cart item.

IPromotionProvider should also have similar members as its price counterpart, just for promotions. There should be one implementation that uses DiscountPart, e.g. DiscountProvider.

Event

  • ApplyingPromotion: extension point for changing the promo providers' order, or removing some or all of them before they are applied to the shopping cart item.

Others

  • You'll probably have to include a new property in PrioritizedPrice to store the old price before the promo was applied. That can be used as a crossed-out price on the product page and cart.
  • Update the cart and product views accordingly.
  • When is the IPromotionProvider applied? I guess it has to be used in PricePartDisplayDriver and PriceVariantsPartDisplayDriver for display; and in ShoppingCartController.AddItem to finalize the offer. In the future we may have other IPromotionProvider that works on already added cart items (e.g. coupon codes), but we don't have to prepare for that just yet.

This is just speculation, if you can find a better approach, go for it.

Tests

Include unit tests with dummy service implementations. Refer to OrchardCore.Commerce.Tests.PriceTests for inspiration.

Jira issue

@sarahelsaig sarahelsaig changed the title Add product discounts Add product discounts (OCC-31) Aug 16, 2022
@DemeSzabolcs DemeSzabolcs self-assigned this Oct 16, 2022
@DemeSzabolcs
Copy link
Contributor

What do you think, should this be in a separate project? Like OrchardCore.Commerce.Promotion / OrchardCore.Commerce.Discount

@0liver
Copy link
Contributor

0liver commented Nov 10, 2022

A separate feature, yes. A separate module, not necessarily.

The one thing that would speak for a separate module: Orchard.Commerce.Tax is also one.

In the end, you'll always be able to move the necessary files to a new project before you finish 😉

@sarahelsaig
Copy link
Contributor Author

sarahelsaig commented Nov 10, 2022

If it's practical to have a separate module, go for it. OrchardCore.Commerce.Promotion sounds good for the whole issue. No need to divvy up any further than that. However if it's too interdependent with the other stuff in OrchardCore.Commerce then just make it a separate feature within OrchardCore.Commerce. Eventually we should Separate OrchardCore.Commerce into more projects (OCC-81 ; #175), but don't be blocked by that.

@bleroy
Copy link
Member

bleroy commented Nov 10, 2022

So far, I've separated into different modules only if the code is independent and has a (re)use case outside of the module. The currency stuff for instance, is generic and useful outside of the commerce module. For feature areas, we have Orchard features that are designed exactly for this. The project could also be reorganized into feature area folders if it becomes too untidy with the current organization. Separate modules don't seem to address any user scenario.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants