Skip to content

Commit 2e4d2d5

Browse files
authored
Merge pull request #19 from PandaTechAM/development
Selector overload added
2 parents fc947d7 + 2128224 commit 2e4d2d5

File tree

10 files changed

+296
-86
lines changed

10 files changed

+296
-86
lines changed

CommissionCalculator.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
1717
EndProject
1818
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{90D4D87F-69E1-4B48-89A5-F0971F20533E}"
1919
EndProject
20+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommissionCalculator.Benchmark", "test\CommissionCalculator.Benchmark\CommissionCalculator.Benchmark.csproj", "{BD6B5957-A234-420F-8058-B314B73A0A2A}"
21+
EndProject
2022
Global
2123
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2224
Debug|Any CPU = Debug|Any CPU
@@ -31,9 +33,14 @@ Global
3133
{2C1683D9-1F0C-4370-951E-A0C8376DD8A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
3234
{2C1683D9-1F0C-4370-951E-A0C8376DD8A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
3335
{2C1683D9-1F0C-4370-951E-A0C8376DD8A7}.Release|Any CPU.Build.0 = Release|Any CPU
36+
{BD6B5957-A234-420F-8058-B314B73A0A2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37+
{BD6B5957-A234-420F-8058-B314B73A0A2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
38+
{BD6B5957-A234-420F-8058-B314B73A0A2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
39+
{BD6B5957-A234-420F-8058-B314B73A0A2A}.Release|Any CPU.Build.0 = Release|Any CPU
3440
EndGlobalSection
3541
GlobalSection(NestedProjects) = preSolution
3642
{CACEA457-209D-4345-971F-F5A602CFCD27} = {ABDD04E0-836D-47E3-972E-7DE6A0D2F697}
3743
{2C1683D9-1F0C-4370-951E-A0C8376DD8A7} = {90D4D87F-69E1-4B48-89A5-F0971F20533E}
44+
{BD6B5957-A234-420F-8058-B314B73A0A2A} = {90D4D87F-69E1-4B48-89A5-F0971F20533E}
3845
EndGlobalSection
3946
EndGlobal

Readme.md

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,3 @@
1-
- [1. Pandatech.CommissionCalculator](#1-pandatechcommissioncalculator)
2-
- [1.1. Overview](#11-overview)
3-
- [1.2. Features](#12-features)
4-
- [1.3. Installation](#13-installation)
5-
- [1.4. Usage](#14-usage)
6-
- [1.4.1. Define Commission Rule](#141-define-commission-rule)
7-
- [1.4.2. Compute Commission](#142-compute-commission)
8-
- [1.4.3. Validation](#143-validation)
9-
- [1.4.4. Validate DateTime Overlap](#144-validate-datetime-overlap)
10-
- [1.5. CommissionRangeConfig Properties](#15-commissionrangeconfig-properties)
11-
- [1.6. Conventions](#16-conventions)
12-
- [1.7. Contributing](#17-contributing)
13-
- [1.8. License](#18-license)
14-
151
# 1. Pandatech.CommissionCalculator
162

173
## 1.1. Overview
@@ -102,11 +88,31 @@ var rules = new CommissionRule
10288

10389
### 1.4.2. Compute Commission
10490

91+
A) Standard (principal selects and applies)
92+
10593
```csharp
10694
decimal principalAmount = 1000m;
10795
decimal commission = Commission.ComputeCommission(principalAmount, rule);
10896
```
10997

98+
B) Selector-based (selector selects, principal applies)
99+
100+
Use when the value that determines the rule (e.g., ticket count) is not the amount you apply the commission to (e.g.,
101+
order price).
102+
103+
```csharp
104+
decimal orderPrice = 2000m;
105+
decimal ticketCount = 3m; // selector
106+
decimal commission = Commission.ComputeCommission(orderPrice, ticketCount, rule);
107+
```
108+
109+
> **Notes**
110+
> - The selector-based overload works with `CalculationType.Absolute`.
111+
> - If used with `CalculationType.Proportional`, an `InvalidOperationException` is thrown.
112+
> - `MinCommission/MaxCommission` are taken from the selected range (by selector) and enforced on the commission
113+
computed from the principal.
114+
> - `FlatRate` returns the flat amount (min/max ignored).
115+
110116
### 1.4.3. Validation
111117

112118
Validate your commission rules to ensure they are consistent and cover the required domain without overlaps or gaps:

src/CommissionCalculator/Commission.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,39 @@ private static decimal CalculateAbsoluteCommission(decimal principalAmount, Comm
3434
principalAmount);
3535
}
3636

37+
public static decimal ComputeCommission(decimal principalAmount, decimal selectorValue, CommissionRule rule)
38+
{
39+
if (rule.CalculationType == CalculationType.Proportional)
40+
{
41+
throw new InvalidOperationException(
42+
"Selector-based overload is incompatible with Proportional rules. Use Absolute.");
43+
}
44+
45+
var converted = ConvertCommissionRanges(rule);
46+
47+
var range = converted.CommissionRangeConfigs.FirstOrDefault(r =>
48+
selectorValue >= r.RangeStart && selectorValue < r.RangeEnd);
49+
50+
var commission = ComputeRangeCommission(
51+
range!.Type,
52+
range.CommissionAmount,
53+
range.MinCommission,
54+
range.MaxCommission,
55+
principalAmount
56+
);
57+
58+
return Math.Round(commission, converted.DecimalPlace);
59+
}
60+
3761
private static decimal CalculateProportionalCommission(decimal principalAmount, CommissionRule rule)
3862
{
3963
rule = ConvertCommissionRanges(rule);
4064

4165
decimal commission = 0;
4266

4367

44-
for (var index = 0; index < rule.CommissionRangeConfigs.Count; index++)
68+
foreach (var range in rule.CommissionRangeConfigs)
4569
{
46-
var range = rule.CommissionRangeConfigs[index];
4770
if (principalAmount >= range.RangeStart && principalAmount < range.RangeEnd)
4871
{
4972
var portionOfPrincipal = principalAmount - range.RangeStart;
@@ -125,8 +148,8 @@ private static void ValidateCommissionRule(CommissionRule rule)
125148
throw new ArgumentException("The ranges list cannot be null or empty.");
126149
}
127150

128-
if (rule.CommissionRangeConfigs.Any(
129-
r => r is { Type: CommissionType.Percentage, CommissionAmount: < -10 or > 10 }))
151+
if (rule.CommissionRangeConfigs.Any(r =>
152+
r is { Type: CommissionType.Percentage, CommissionAmount: < -10 or > 10 }))
130153
{
131154
throw new InvalidOperationException(
132155
"For 'Percentage' CommissionType, the CommissionAmount should be between -10 and 10. Commissions over 1000% are not allowed.");

src/CommissionCalculator/CommissionCalculator.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
<PackageIcon>pandatech.png</PackageIcon>
88
<PackageReadmeFile>Readme.md</PackageReadmeFile>
99
<Copyright>MIT</Copyright>
10-
<Version>4.0.1</Version>
10+
<Version>4.0.2</Version>
1111
<Authors>Pandatech</Authors>
1212
<PackageId>Pandatech.CommissionCalculator</PackageId>
1313
<Title>Pandatech.CommissionCalculator</Title>
1414
<PackageTags>Pandatech, library, calculator, commission, fee, bank, fintech</PackageTags>
1515
<Description>PandaTech.CommissionCalculator is a .NET library simplifying common fintech commission calculations.</Description>
1616
<RepositoryUrl>https://github.com/PandaTechAM/be-lib-commission-calculator.git</RepositoryUrl>
17-
<PackageReleaseNotes>Nuget updates</PackageReleaseNotes>
17+
<PackageReleaseNotes>Nuget updates and new overload added</PackageReleaseNotes>
1818
</PropertyGroup>
1919
<ItemGroup>
2020
<None Include="..\..\pandatech.png" Pack="true" PackagePath="\"/>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
using BenchmarkDotNet.Attributes;
2+
using CommissionCalculator.DTO;
3+
4+
namespace CommissionCalculator.Benchmark;
5+
6+
[MemoryDiagnoser]
7+
public class CommissionBench
8+
{
9+
private CommissionRule _proportionalRule = null!;
10+
private CommissionRule _absoluteRule = null!;
11+
private decimal _principal;
12+
private decimal _selector;
13+
14+
// Sources for decimal params
15+
public static IEnumerable<decimal> PrincipalValues => [755_789m, 1_000m, 25_000m];
16+
public static IEnumerable<decimal> SelectorValues => [1m, 3m, 5m, 10m];
17+
18+
[ParamsSource(nameof(PrincipalValues))]
19+
public decimal PrincipalParam { get; set; }
20+
21+
[ParamsSource(nameof(SelectorValues))]
22+
public decimal SelectorParam { get; set; }
23+
24+
[GlobalSetup]
25+
public void Setup()
26+
{
27+
_principal = PrincipalParam;
28+
_selector = SelectorParam;
29+
30+
_proportionalRule = new CommissionRule
31+
{
32+
CalculationType = CalculationType.Proportional,
33+
DecimalPlace = 0,
34+
CommissionRangeConfigs =
35+
[
36+
new CommissionRangeConfigs
37+
{
38+
RangeStart = 0,
39+
RangeEnd = 500,
40+
Type = CommissionType.FlatRate,
41+
CommissionAmount = 25,
42+
MinCommission = 0,
43+
MaxCommission = 0
44+
},
45+
new CommissionRangeConfigs
46+
{
47+
RangeStart = 500,
48+
RangeEnd = 1000,
49+
Type = CommissionType.Percentage,
50+
CommissionAmount = 0.1m,
51+
MinCommission = 0,
52+
MaxCommission = 0
53+
},
54+
new CommissionRangeConfigs
55+
{
56+
RangeStart = 1000,
57+
RangeEnd = 10000,
58+
Type = CommissionType.Percentage,
59+
CommissionAmount = 0.2m,
60+
MinCommission = 250,
61+
MaxCommission = 1500
62+
},
63+
new CommissionRangeConfigs
64+
{
65+
RangeStart = 10000,
66+
RangeEnd = 0,
67+
Type = CommissionType.FlatRate,
68+
CommissionAmount = 2000,
69+
MinCommission = 0,
70+
MaxCommission = 0
71+
}
72+
]
73+
};
74+
75+
_absoluteRule = new CommissionRule
76+
{
77+
CalculationType = CalculationType.Absolute,
78+
DecimalPlace = 0,
79+
CommissionRangeConfigs =
80+
[
81+
new CommissionRangeConfigs
82+
{
83+
RangeStart = 0,
84+
RangeEnd = 2,
85+
Type = CommissionType.FlatRate,
86+
CommissionAmount = 50,
87+
MinCommission = 0,
88+
MaxCommission = 0
89+
},
90+
new CommissionRangeConfigs
91+
{
92+
RangeStart = 2,
93+
RangeEnd = 4,
94+
Type = CommissionType.Percentage,
95+
CommissionAmount = 0.10m,
96+
MinCommission = 0,
97+
MaxCommission = 0
98+
},
99+
new CommissionRangeConfigs
100+
{
101+
RangeStart = 4,
102+
RangeEnd = 0,
103+
Type = CommissionType.FlatRate,
104+
CommissionAmount = 100,
105+
MinCommission = 0,
106+
MaxCommission = 0
107+
}
108+
]
109+
};
110+
}
111+
112+
[Benchmark(Baseline = true)]
113+
public decimal Proportional_PrincipalBased() => Commission.ComputeCommission(_principal, _proportionalRule);
114+
115+
[Benchmark]
116+
public decimal Absolute_PrincipalBased() => Commission.ComputeCommission(_principal, _absoluteRule);
117+
118+
[Benchmark]
119+
public decimal Absolute_SelectorBased() => Commission.ComputeCommission(_principal, _selector, _absoluteRule);
120+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net9.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\..\src\CommissionCalculator\CommissionCalculator.csproj" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<PackageReference Include="BenchmarkDotNet" Version="0.15.7" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using BenchmarkDotNet.Configs;
2+
using BenchmarkDotNet.Environments;
3+
using BenchmarkDotNet.Jobs;
4+
using BenchmarkDotNet.Running;
5+
using CommissionCalculator.Benchmark;
6+
7+
var job = Job.Default
8+
.WithRuntime(CoreRuntime.Core90)
9+
.WithWarmupCount(3)
10+
.WithIterationCount(10)
11+
.WithLaunchCount(1);
12+
13+
var config = DefaultConfig.Instance.AddJob(job);
14+
BenchmarkRunner.Run<CommissionBench>(config);

test/CommissionCalculator.Tests/CommissionCalculator.Tests.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
1414
<PackageReference Include="xunit" Version="2.9.3" />
15-
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
15+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1717
<PrivateAssets>all</PrivateAssets>
1818
</PackageReference>

test/CommissionCalculator.Tests/LoadTests.cs

Lines changed: 0 additions & 64 deletions
This file was deleted.

0 commit comments

Comments
 (0)