Skip to content

Commit 77d37ea

Browse files
author
Jani Giannoudis
committed
lookup: added range mode
added lookup set extensions lookup value: disabled JSON serialization of object value updated version to 0.9.0-beta.12
1 parent 3081da3 commit 77d37ea

File tree

7 files changed

+237
-4
lines changed

7 files changed

+237
-4
lines changed

Client.Core/Model/ILookup.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public interface ILookup : IModel, IAttributeObject, IKeyEquatable<ILookup>
2121
/// <summary>The override type</summary>
2222
OverrideType OverrideType { get; set; }
2323

24+
/// <summary>Lookup range mode</summary>
25+
LookupRangeMode RangeMode { get; set; }
26+
2427
/// <summary>The lookup range size</summary>
2528
decimal? RangeSize { get; set; }
2629
}

Client.Core/Model/Lookup.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,18 @@ public class Lookup : ModelBase, ILookup, INameObject
2828

2929
/// <inheritdoc/>
3030
[JsonPropertyOrder(103)]
31-
public decimal? RangeSize { get; set; }
31+
public LookupRangeMode RangeMode { get; set; }
3232

3333
/// <inheritdoc/>
3434
[JsonPropertyOrder(104)]
35-
public OverrideType OverrideType { get; set; }
35+
public decimal? RangeSize { get; set; }
3636

3737
/// <inheritdoc/>
3838
[JsonPropertyOrder(105)]
39+
public OverrideType OverrideType { get; set; }
40+
41+
/// <inheritdoc/>
42+
[JsonPropertyOrder(106)]
3943
public Dictionary<string, object> Attributes { get; set; }
4044

4145
/// <summary>Initializes a new instance</summary>
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
using System;
2+
using System.Linq;
3+
using System.Text.Json;
4+
using System.Collections.Generic;
5+
6+
namespace PayrollEngine.Client.Model;
7+
8+
/// <summary>Extension methods for lookup sets</summary>
9+
/// <remarks>Code duplicated in PayrollEngine.Domain.Model</remarks>
10+
public static class LookupSetExtensions
11+
{
12+
/// <summary>Apply range value</summary>
13+
/// <param name="lookup">The lookup</param>
14+
/// <param name="rangeValue">The range value</param>
15+
/// <param name="valueFieldName">Value field name</param>
16+
/// <returns>Lookup range value</returns>
17+
public static decimal ApplyRangeValue(this ILookupSet lookup, decimal rangeValue, string valueFieldName = null)
18+
{
19+
if (rangeValue == 0)
20+
{
21+
return 0;
22+
}
23+
switch (lookup.RangeMode)
24+
{
25+
case LookupRangeMode.Threshold:
26+
return ApplyThresholdRangeValue(lookup, rangeValue, valueFieldName);
27+
case LookupRangeMode.Progressive:
28+
return ApplyProgressiveRangeValue(lookup, rangeValue, valueFieldName);
29+
}
30+
return 0;
31+
}
32+
33+
#region Threshold
34+
35+
/// <summary>Apply progressive factor value</summary>
36+
/// <param name="lookup">The lookup</param>
37+
/// <param name="rangeValue">The range value</param>
38+
/// <param name="valueFieldName">Value field name</param>
39+
/// <remarks>The first lookup range value must be zero.</remarks>
40+
/// <returns>Summary of all lookup ranges</returns>
41+
public static decimal ApplyThresholdRangeValue(this ILookupSet lookup, decimal rangeValue, string valueFieldName = null)
42+
{
43+
// ranges
44+
var ranges = GetLookupRanges(lookup, valueFieldName);
45+
46+
// select threshold range
47+
var result = ranges?.FirstOrDefault(x => x.IsThreshold(rangeValue));
48+
var factor = result?.Factor ?? 0;
49+
return rangeValue * factor;
50+
}
51+
52+
#endregion
53+
54+
#region Progressive
55+
56+
/// <summary>Apply progressive factor value</summary>
57+
/// <param name="lookup">The lookup</param>
58+
/// <param name="rangeValue">The range value</param>
59+
/// <param name="valueFieldName">Value field name</param>
60+
/// <remarks>The first lookup range value must be zero.</remarks>
61+
/// <returns>Summary of all lookup ranges</returns>
62+
public static decimal ApplyProgressiveRangeValue(this ILookupSet lookup, decimal rangeValue, string valueFieldName = null)
63+
{
64+
// ranges
65+
var ranges = GetLookupRanges(lookup, valueFieldName);
66+
67+
// calculate factor for each lookup range
68+
var result = ranges.Select(x => x.GetRangeFactorValue(rangeValue)).Sum();
69+
return result;
70+
}
71+
72+
#endregion
73+
74+
#region Lookup Range
75+
76+
private sealed class LookupRange
77+
{
78+
internal decimal Factor { get; }
79+
private decimal Start { get; }
80+
private decimal? End { get; set; }
81+
82+
internal LookupRange(decimal factor, decimal start)
83+
{
84+
Factor = factor;
85+
Start = start;
86+
}
87+
88+
internal void SetEnd(decimal end) => End = end;
89+
internal void SetEndByOffset(decimal offset) =>
90+
End = Start + offset;
91+
92+
internal bool IsThreshold(decimal rangeValue) =>
93+
rangeValue >= Start &&
94+
(!End.HasValue || rangeValue < End.Value);
95+
96+
internal decimal GetRangeFactorValue(decimal rangeValue)
97+
{
98+
// undefined
99+
if ((End.HasValue && End <= Start) || Factor == 0 || rangeValue == 0)
100+
{
101+
return 0;
102+
}
103+
// outside (ignore start is equals rangeValue)
104+
if (Start >= rangeValue || (End.HasValue && End < 0))
105+
{
106+
return 0;
107+
}
108+
109+
// lookup range intersection with the range value (0...range value)
110+
var rangeStart = Math.Max(Start, 0);
111+
var rangeEnd = End.HasValue ? Math.Min(End.Value, rangeValue) : rangeValue;
112+
var rangeSize = rangeEnd - rangeStart;
113+
return rangeSize > 0 ? rangeSize * Factor : 0;
114+
}
115+
116+
public override string ToString() =>
117+
$"{Start} - {End} ({Factor})";
118+
}
119+
120+
private static List<LookupRange> GetLookupRanges(ILookupSet lookup, string valueFieldName = null)
121+
{
122+
if (!lookup.Values.Any())
123+
{
124+
return new();
125+
}
126+
127+
// ranges
128+
var ranges = new List<LookupRange>();
129+
for (var i = 0; i < lookup.Values.Count; i++)
130+
{
131+
var lookupValue = lookup.Values[i];
132+
133+
// ignore lookup values without range and lookup value
134+
if (lookupValue.RangeValue == null || string.IsNullOrWhiteSpace(lookupValue.Value))
135+
{
136+
continue;
137+
}
138+
139+
// first value need to be zero
140+
if (i == 0 && lookupValue.RangeValue.Value != 0)
141+
{
142+
throw new PayrollException(
143+
$"Get range factor requires a start range value of zero ({lookupValue.RangeValue.Value}).");
144+
}
145+
146+
// factor
147+
decimal? factor = null;
148+
if (string.IsNullOrWhiteSpace(valueFieldName))
149+
{
150+
// decimal factor from lookup value
151+
factor = JsonSerializer.Deserialize<decimal?>(lookupValue.Value);
152+
}
153+
else
154+
{
155+
// decimal factor from JSON object filed
156+
var values = JsonSerializer.Deserialize<Dictionary<string, object>>(lookupValue.Value);
157+
if (values != null && values[valueFieldName] is JsonElement jsonElement)
158+
{
159+
factor = jsonElement.GetDecimal();
160+
}
161+
}
162+
163+
if (!factor.HasValue)
164+
{
165+
continue;
166+
}
167+
168+
// update previous end
169+
if (i > 0)
170+
{
171+
ranges[i - 1].SetEnd(lookupValue.RangeValue.Value);
172+
}
173+
174+
// add new range
175+
ranges.Add(new LookupRange(
176+
factor: factor.Value,
177+
start: lookupValue.RangeValue.Value));
178+
}
179+
180+
// last range
181+
if (lookup.RangeSize.HasValue)
182+
{
183+
var last = ranges.Last();
184+
last.SetEndByOffset(lookup.RangeSize.Value);
185+
}
186+
187+
return ranges;
188+
}
189+
190+
#endregion
191+
192+
}

Client.Core/Model/LookupValue.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public object[] KeyValues
3535
public string Value { get; set; }
3636

3737
/// <summary>The lookup value object</summary>
38+
[JsonReadOnly]
3839
[JsonPropertyOrder(103)]
3940
public object ValueObject
4041
{

Client.Core/PayrollEngine.Client.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
</None>
9090
</ItemGroup>
9191
<ItemGroup>
92-
<PackageReference Include="PayrollEngine.Core" Version="0.9.0-beta.11" />
92+
<PackageReference Include="PayrollEngine.Core" Version="0.9.0-beta.12" />
9393
</ItemGroup>
9494

9595
<!-- build json schema -->

Client.Core/PayrollEngine.Client.Core.xml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4998,6 +4998,9 @@
49984998
<member name="P:PayrollEngine.Client.Model.ILookup.OverrideType">
49994999
<summary>The override type</summary>
50005000
</member>
5001+
<member name="P:PayrollEngine.Client.Model.ILookup.RangeMode">
5002+
<summary>Lookup range mode</summary>
5003+
</member>
50015004
<member name="P:PayrollEngine.Client.Model.ILookup.RangeSize">
50025005
<summary>The lookup range size</summary>
50035006
</member>
@@ -6164,6 +6167,9 @@
61646167
<member name="P:PayrollEngine.Client.Model.Lookup.DescriptionLocalizations">
61656168
<inheritdoc/>
61666169
</member>
6170+
<member name="P:PayrollEngine.Client.Model.Lookup.RangeMode">
6171+
<inheritdoc/>
6172+
</member>
61676173
<member name="P:PayrollEngine.Client.Model.Lookup.RangeSize">
61686174
<inheritdoc/>
61696175
</member>
@@ -6234,6 +6240,33 @@
62346240
<member name="M:PayrollEngine.Client.Model.LookupSet.Equals(PayrollEngine.Client.Model.ILookupSet)">
62356241
<inheritdoc/>
62366242
</member>
6243+
<member name="T:PayrollEngine.Client.Model.LookupSetExtensions">
6244+
<summary>Extension methods for lookup sets</summary>
6245+
<remarks>Code duplicated in PayrollEngine.Domain.Model</remarks>
6246+
</member>
6247+
<member name="M:PayrollEngine.Client.Model.LookupSetExtensions.ApplyRangeValue(PayrollEngine.Client.Model.ILookupSet,System.Decimal,System.String)">
6248+
<summary>Apply range value</summary>
6249+
<param name="lookup">The lookup</param>
6250+
<param name="rangeValue">The range value</param>
6251+
<param name="valueFieldName">Value field name</param>
6252+
<returns>Lookup range value</returns>
6253+
</member>
6254+
<member name="M:PayrollEngine.Client.Model.LookupSetExtensions.ApplyThresholdRangeValue(PayrollEngine.Client.Model.ILookupSet,System.Decimal,System.String)">
6255+
<summary>Apply progressive factor value</summary>
6256+
<param name="lookup">The lookup</param>
6257+
<param name="rangeValue">The range value</param>
6258+
<param name="valueFieldName">Value field name</param>
6259+
<remarks>The first lookup range value must be zero.</remarks>
6260+
<returns>Summary of all lookup ranges</returns>
6261+
</member>
6262+
<member name="M:PayrollEngine.Client.Model.LookupSetExtensions.ApplyProgressiveRangeValue(PayrollEngine.Client.Model.ILookupSet,System.Decimal,System.String)">
6263+
<summary>Apply progressive factor value</summary>
6264+
<param name="lookup">The lookup</param>
6265+
<param name="rangeValue">The range value</param>
6266+
<param name="valueFieldName">Value field name</param>
6267+
<remarks>The first lookup range value must be zero.</remarks>
6268+
<returns>Summary of all lookup ranges</returns>
6269+
</member>
62376270
<member name="T:PayrollEngine.Client.Model.LookupSettings">
62386271
<summary>The lookup settings</summary>
62396272
</member>

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFramework>net9.0</TargetFramework>
5-
<Version>0.9.0-beta.11</Version>
5+
<Version>0.9.0-beta.12</Version>
66
<FileVersion>0.9.0</FileVersion>
77
<InformationalVersion></InformationalVersion>
88
<Authors>Jani Giannoudis</Authors>

0 commit comments

Comments
 (0)