Skip to content

Commit 5e08bd5

Browse files
authored
Merge pull request #7415 from mattbrailsford/uc-discount-providers-updates
Provide more realistic discount rule / reward provider examples
2 parents 0269b4e + fb29ec9 commit 5e08bd5

File tree

1 file changed

+99
-52
lines changed

1 file changed

+99
-52
lines changed

16/umbraco-commerce/key-concepts/discount-rules-and-rewards.md

Lines changed: 99 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,30 @@ There are two types of Discount Rules in Umbraco Commerce:
2323
An example of an Order Discount Rule Provider would look something like this:
2424

2525
```csharp
26-
[DiscountRuleProvider("myCustomOrderRule")]
27-
public class MyCustomOrderRuleProvider : OrderDiscountRuleProviderBase<MyCustomOrderRuleProviderSettings>
26+
[DiscountRuleProvider("customerEmailDomainRule")]
27+
public class CustomerEmailDomainRuleProvider : OrderDiscountRuleProviderBase<CustomerEmailDomainSettings>
2828
{
29-
public override async Task<DiscountRuleResult> ValidateRuleAsync(DiscountRuleContext ctx, MyCustomOrderRuleProviderSettings settings)
29+
public CustomerEmailDomainRuleProvider(UmbracoCommerceContext ctx) : base(ctx) { }
30+
31+
public override DiscountRuleResult ValidateRule(DiscountRuleContext ctx, CustomerEmailDomainSettings settings)
3032
{
31-
if (/* Some custom logic */)
32-
return Fulfilled();
33+
var customerEmail = ctx.Order.CustomerInfo.Email;
34+
35+
if (string.IsNullOrEmpty(customerEmail) || string.IsNullOrEmpty(settings.EmailDomain))
36+
return Unfulfilled();
3337

34-
return Unfulfilled();
38+
// Check if customer email ends with the specified domain
39+
return customerEmail.EndsWith($"@{settings.EmailDomain}", StringComparison.OrdinalIgnoreCase)
40+
? Fulfilled()
41+
: Unfulfilled();
3542
}
3643
}
3744

38-
public class MyCustomOrderRuleProviderSettings
45+
public class CustomerEmailDomainSettings
3946
{
40-
[DiscountRuleProviderSetting(Key = "priceType")]
41-
public OrderPriceType PriceType { get; set; }
42-
43-
...
47+
[DiscountRuleProviderSetting(Key = "emailDomain", LabelUiAlias = "My.PropertyEditorUi.MyDiscountRuleLabel")]
48+
public string EmailDomain { get; set; }
4449
}
45-
4650
```
4751

4852
All Order Discount Rule Providers inherit from a base class `OrderDiscountRuleProviderBase<TSettings>`. `TSettings` is the type of a Plain Old Class Object (POCO) model class representing the Discount Rule Providers settings.
@@ -54,7 +58,7 @@ See the [Settings Objects](discount-rules-and-rewards.md#settings-objects) secti
5458
The class must be decorated with `DiscountRuleProviderAttribute` which defines the Discount Rule Providers `alias` and `name`, and can also specify a `description` or `icon` to be displayed in the backoffice. The `DiscountRuleProviderAttribute` is also responsible for defining a `labelView` for the Provider.
5559

5660
{% hint style="info" %}
57-
See the [Label views](discount-rules-and-rewards.md#label-views) section below for more information on Label Views.
61+
See the [Labels](discount-rules-and-rewards.md#labels) section below for more information on Label Views.
5862
{% endhint %}
5963

6064
Rule Providers have a `ValidateRule` method that accepts a `DiscountRuleContext` as well as an instance of the Providers `TSettings` settings model. Inside this you can perform your custom logic, returning a `DiscountRuleResult` to notify Umbraco Commerce of the Rule outcome.
@@ -66,24 +70,26 @@ If the passed-in context (which contains a reference to the Order) meets the Rul
6670
An example of an Order Line Discount Rule Provider would look something like this:
6771

6872
```csharp
69-
[DiscountRuleProvider("myCustomOrderLineRule")]
70-
public class MyCustomOrderLineRuleProvider : OrderLineDiscountRuleProviderBase<MyCustomOrderLineRuleProviderSettings>
73+
[DiscountRuleProvider("minimumQuantityRule")]
74+
public class MinimumQuantityRuleProvider : OrderLineDiscountRuleProviderBase<MinimumQuantitySettings>
7175
{
72-
public override async Task<DiscountRuleResult> ValidateRuleAsync(DiscountRuleContext ctx, MyCustomOrderLineRuleProviderSettings settings)
76+
public MinimumQuantityRuleProvider(UmbracoCommerceContext ctx) : base(ctx) { }
77+
78+
public override DiscountRuleResult ValidateRule(DiscountRuleContext ctx, MinimumQuantitySettings settings)
7379
{
74-
if (/* Some custom logic */)
75-
return Fulfilled(fulfilledOrderLines);
76-
77-
return Unfulfilled();
80+
// Check if any line meets minimum quantity
81+
var qualifyingLines = ctx.ApplicableOrderLines
82+
.Where(line => line.Quantity >= settings.MinimumQuantity)
83+
.ToList();
84+
85+
return qualifyingLines.Any() ? Fulfilled(qualifyingLines) : Unfulfilled();
7886
}
7987
}
8088

81-
public class MyCustomOrderLineRuleProviderSettings
89+
public class MinimumQuantitySettings
8290
{
83-
[DiscountRuleProviderSetting(Key = "priceType")]
84-
public OrderPriceType PriceType { get; set; }
85-
86-
...
91+
[DiscountRuleProviderSetting(Key = "minimumQuantity")]
92+
public decimal MinimumQuantity { get; set; }
8793
}
8894

8995
```
@@ -97,27 +103,49 @@ All Order Line Discount Rule Providers inherit from a base class `OrderLineDisco
97103
An example of a Discount Reward Provider would look something like this:
98104

99105
```csharp
100-
[DiscountRewardProvider("myDiscountReward")]
101-
public class MyDiscountRewardProvider : DiscountRewardProviderBase<MyDiscountRewardProviderSettings>
106+
[DiscountRewardProvider("tieredPercentageReward")]
107+
public class TieredPercentageRewardProvider : DiscountRewardProviderBase<TieredPercentageSettings>
102108
{
103-
public override async Task<DiscountRewardCalculation> CalculateRewardAsync(DiscountRewardContext ctx, MyDiscountRewardProviderSettings settings)
109+
public TieredPercentageRewardProvider(UmbracoCommerceContext ctx) : base(ctx) { }
110+
111+
public override DiscountRewardCalculation CalculateReward(DiscountRewardContext ctx, TieredPercentageSettings settings)
104112
{
105113
var result = new DiscountRewardCalculation();
114+
var orderTotal = ctx.OrderCalculation.SubtotalPrice.Value.WithoutTax;
115+
116+
// Determine discount percentage based on order value
117+
var discountPercentage = orderTotal >= settings.HighTierThreshold ? settings.HighTierPercentage :
118+
orderTotal >= settings.MidTierThreshold ? settings.MidTierPercentage :
119+
settings.BaseTierPercentage;
120+
121+
var discountAmount = orderTotal * (discountPercentage / 100m);
106122

107-
// Some custom calculation logic goes here
123+
// Adjustment price must be negative for discounts or positive for fees
124+
var price = new Price(discountAmount * -1, 0, ctx.Order.CurrencyId);
125+
126+
result.SubtotalPriceAdjustments.Add(new DiscountAdjustment(ctx.Discount, price));
108127

109128
return result;
110129
}
111130
}
112131

113-
public class MyDiscountRewardProviderSettings
132+
public class TieredPercentageSettings
114133
{
115-
[DiscountRewardProviderSetting(Key = "priceType")]
116-
public OrderPriceType PriceType { get; set; }
134+
[DiscountRuleProviderSetting(Key = "baseTierPercentage")]
135+
public decimal BaseTierPercentage { get; set; }
117136

118-
...
119-
}
137+
[DiscountRuleProviderSetting(Key = "midTierThreshold")]
138+
public decimal MidTierThreshold { get; set; }
120139

140+
[DiscountRuleProviderSetting(Key = "midTierPercentage")]
141+
public decimal MidTierPercentage { get; set; }
142+
143+
[DiscountRuleProviderSetting(Key = "highTierThreshold")]
144+
public decimal HighTierThreshold { get; set; }
145+
146+
[DiscountRuleProviderSetting(Key = "highTierPercentage")]
147+
public decimal HighTierPercentage { get; set; }
148+
}
121149
```
122150

123151
All Discount Reward Providers inherit from a base class `DiscountRewardProviderBase<TSettings>`. `TSettings` is the Type of a POCO model class representing the Discount Reward Providers settings.
@@ -129,7 +157,7 @@ See the [Settings Objects](settings-objects.md) documentation for more informati
129157
The class must be decorated with `DiscountRewardProviderAttribute` which defines the Discount Reward Providers `alias` and `name`. It can also specify a `description` or `icon` to be displayed in the Umbraco Commerce backoffice. The `DiscountRewardProviderAttribute` is responsible for defining a `labelView` for the Provider.
130158

131159
{% hint style="info" %}
132-
See the [Label views](discount-rules-and-rewards.md#label-views) section below for more information on Label Views.
160+
See the [Labels](discount-rules-and-rewards.md#labels) section below for more information on Label Views.
133161
{% endhint %}
134162

135163
Reward Providers have a `CalculateReward` method that accepts a `DiscountRewardContext` as well as an instance of the Providers `TSettings` settings model. Inside this, you can perform your custom calculation logic, returning a `DiscountRewardCalculation` instance that defines any Reward values to apply to the Order.
@@ -152,48 +180,67 @@ See the [Settings Objects](settings-objects.md) documentation for more informati
152180

153181
### Labels
154182

155-
Both the `DiscountRuleProviderAttribute` and the `DiscountRewardProviderAttribute` allow you to define a `LabelUiAlias` for the Provider. This should be the alias of a UI component registered as a Property Editor UI implementation.
183+
Both the `DiscountRuleProviderAttribute` and the `DiscountRewardProviderAttribute` allow you to define a `ViewUiAlias` to use as a label for the Provider. This should be the alias of a UI component registered as a Property Editor UI implementation.
184+
185+
```csharp
186+
public class CustomerEmailDomainSettings
187+
{
188+
[DiscountRuleProviderSetting(Key = "emailDomain", ViewUiAlias = "My.PropertyEditorUi.CustomerEmailDomainDiscountRuleLabel")]
189+
public string EmailDomain { get; set; }
190+
}
191+
```
156192

157193
A basic label component is defined as follows:
158194

159195
```typescript
160-
import { customElement, html, property } from "@umbraco-cms/backoffice/external/lit";
196+
import { css, customElement, html, property, when } from "@umbraco-cms/backoffice/external/lit";
161197
import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element";
198+
import { UmbTextStyles } from "@umbraco-cms/backoffice/style";
162199

163-
@customElement('my-discount-rule-label')
164-
export class MyDiscountRuleLabelElement extends UmbLitElement {
200+
@customElement('uc-customer-email-domain-discount-rule-label')
201+
export class UcCustomerEmailDomainDiscountRuleLabelElement extends UmbLitElement {
165202

166203
@property()
167-
value?:Record<string, unknown>;
204+
value?: Record<string, unknown>;
168205

169206
render() {
170-
return html`-- CREATE YOUR LABEL HERE --`
207+
return when(this.value, () => html`
208+
Customer email ends with '@${this.value!.emailDomain}'
209+
`)
171210
}
211+
212+
static styles = [
213+
UmbTextStyles,
214+
css`
215+
:host {
216+
display: block;
217+
}
218+
`,
219+
];
172220
}
173221

174-
export default MyDiscountRuleLabelElement;
222+
export default UcCustomerEmailDomainDiscountRuleLabelElement;
175223

176224
declare global {
177225
interface HTMLElementTagNameMap {
178-
'my-discount-rule-label': MyDiscountRuleLabelElement;
226+
'uc-customer-email-domain-discount-rule-label': UcCustomerEmailDomainDiscountRuleLabelElement;
179227
}
180228
}
181-
182229
```
183230

184-
The component will pass a `Record<string, unknown>` value representing the rule/rewards configured values. Use this value to create your label.
231+
The component will be passed a `Record<string, unknown>` value representing the rule/rewards configured values. Use this value to create your label.
185232

186233
Once defined, your component can be registered as a Property Editor UI via a manifest entry.
187234

188235
```javascript
189-
const myDiscountRuleLabelManifest = {
236+
const customerEmailDomainDiscountRuleLabelManifest = {
190237
type: "propertyEditorUi",
191-
alias: "My.PropertyEditorUi.MyDiscountRuleLabel",
192-
name: "My Discount Rule Label",
193-
element: () => import('./my-discount-rule-label.element.js')
194-
};
238+
alias: "My.PropertyEditorUi.CustomerEmailDomainDiscountRuleLabel",
239+
name: "Customer Email Domain Discount Rule Label",
240+
element: () => import('./customer-email-domain-discount-rule-label.element.js')
241+
};
195242

196-
export const manifests = [ myDiscountRuleLabelManifest ];
243+
export const manifests = [ customerEmailDomainDiscountRuleLabelManifest ];
197244
```
198245

199246
{% hint style="info" %}
@@ -217,4 +264,4 @@ Umbraco Commerce will automatically look for the following entries:
217264
| `ucDiscount{type}Providers_{providerAlias}Settings{settingAlias}Label` | A label for a rule/reward provider setting |
218265
| `ucDiscount{type}Providers_{providerAlias}Settings{settingAlias}Description` | A description for a rule/reward provider setting |
219266

220-
Here `{type}` can be either `Rule` or `Reward`. `{providerAlias}` is the alias of the rule/reward provider, and `{settingAlias}` is the alias of a setting.
267+
Here `{type}` can be either `Rule` or `Reward`. `{providerAlias}` is the alias of the rule/reward provider, and `{settingAlias}` is the alias of a setting.

0 commit comments

Comments
 (0)