Skip to content

Commit a306b8e

Browse files
author
Jani Giannoudis
committed
added time period with support for day hour calculations
added input attribute names function: added user type with test for self-employed and admin function: added user culture and access to payroll calendar function: new secure method ChangeValueType to change the value type case change function: added support to control the case change reason case change function: new methods for updating the start and end date fields case build function: added validation and info support case validate function: added info support report: added record extensions to add relation columns and localization columns report function: added access to raw case values report function: info support added report build function: validation support added report build function: added support for building input lists from queries report build function: added support to hide/show/read only one report parameter updated version to 0.9.0-beta.4
1 parent 5c6fbc7 commit a306b8e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2423
-371
lines changed

Client.Scripting/ClientScript.cs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,19 @@ public enum LogLevel
9090
Fatal
9191
}
9292

93+
/// <summary>The type of the user</summary>
94+
public enum UserType
95+
{
96+
/// <summary>Regular user</summary>
97+
User = 0,
98+
/// <summary>User with employee self-service</summary>
99+
Employee = 1,
100+
/// <summary>Tenant administrator</summary>
101+
TenantAdministrator = 2,
102+
/// <summary>System administrator</summary>
103+
SystemAdministrator = 3
104+
}
105+
93106
/// <summary>The case type</summary>
94107
public enum CaseType
95108
{
@@ -186,6 +199,87 @@ public enum DataMergeSchemaChange
186199

187200
#endregion
188201

202+
#region Input
203+
204+
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
205+
206+
/// <summary>Predefined input attributes</summary>
207+
public static class InputAttributes
208+
{
209+
public static readonly string Prefix = "input.";
210+
211+
// transient
212+
public static readonly string EditInfo = $"{Prefix}editInfo";
213+
public static readonly string Validity = $"{Prefix}validity";
214+
215+
// case general
216+
public static readonly string Icon = $"{Prefix}icon";
217+
public static readonly string Priority = $"{Prefix}priority";
218+
219+
// case field general
220+
public static readonly string Group = $"{Prefix}group";
221+
public static readonly string Separator = $"{Prefix}separator";
222+
public static readonly string Hidden = $"{Prefix}hidden";
223+
public static readonly string HiddenName = $"{Prefix}hiddenName";
224+
public static readonly string HiddenDates = $"{Prefix}hiddenDates";
225+
public static readonly string ShowDescription = $"{Prefix}showDescription";
226+
public static readonly string Variant = $"{Prefix}variant";
227+
228+
// case field start
229+
public static readonly string StartLabel = $"{Prefix}startLabel";
230+
public static readonly string StartHelp = $"{Prefix}startHelp";
231+
public static readonly string StartRequired = $"{Prefix}startRequired";
232+
public static readonly string StartReadOnly = $"{Prefix}startReadOny";
233+
public static readonly string StartHidden = $"{Prefix}startHidden";
234+
public static readonly string StartFormat = $"{Prefix}startFormat";
235+
public static readonly string StartPickerOpen = $"{Prefix}startPickerOpen";
236+
public static readonly string StartPickerType = $"{Prefix}startPickerType";
237+
238+
// case field end
239+
public static readonly string EndLabel = $"{Prefix}endLabel";
240+
public static readonly string EndHelp = $"{Prefix}endHelp";
241+
public static readonly string EndRequired = $"{Prefix}endRequired";
242+
public static readonly string EndReadOnly = $"{Prefix}endReadOny";
243+
public static readonly string EndHidden = $"{Prefix}endHidden";
244+
public static readonly string EndFormat = $"{Prefix}endFormat";
245+
public static readonly string EndPickerOpen = $"{Prefix}endPickerOpen";
246+
public static readonly string EndPickerType = $"{Prefix}endPickerType";
247+
248+
// case field value
249+
public static readonly string ValueLabel = $"{Prefix}valueLabel";
250+
public static readonly string ValueAdornment = $"{Prefix}valueAdornment";
251+
public static readonly string ValueHelp = $"{Prefix}valueHelp";
252+
public static readonly string ValueMask = $"{Prefix}valueMask";
253+
public static readonly string ValueRequired = $"{Prefix}valueRequired";
254+
public static readonly string ValueReadOnly = $"{Prefix}valueReadOnly";
255+
public static readonly string ValuePickerOpen = $"{Prefix}valuePickerOpen";
256+
public static readonly string ValuePickerStatic = $"{Prefix}valuePickerStatic";
257+
public static readonly string ValueTimePicker = $"{Prefix}valueTimePicker";
258+
public static readonly string Culture = $"{Prefix}culture";
259+
public static readonly string MinValue = $"{Prefix}minValue";
260+
public static readonly string MaxValue = $"{Prefix}maxValue";
261+
public static readonly string StepSize = $"{Prefix}stepSize";
262+
public static readonly string Format = $"{Prefix}format";
263+
public static readonly string LineCount = $"{Prefix}lineCount";
264+
public static readonly string MaxLength = $"{Prefix}maxLength";
265+
public static readonly string Check = $"{Prefix}check";
266+
public static readonly string ValueHistory = $"{Prefix}valueHistory";
267+
268+
// case field attachments
269+
public static readonly string Attachment = $"{Prefix}attachment";
270+
public static readonly string AttachmentExtensions = $"{Prefix}attachmentExtensions";
271+
272+
// list
273+
// no actions for the list attributes List, ListSelection and ListResult
274+
public static readonly string List = $"{Prefix}list";
275+
public static readonly string ListValues = $"{Prefix}listValues";
276+
public static readonly string ListSelection = $"{Prefix}listSelection";
277+
}
278+
279+
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
280+
281+
#endregion
282+
189283
#region Error
190284

191285
/// <summary>Payroll script exception</summary>

Client.Scripting/Date.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ public static TimeSpan Min(TimeSpan left, TimeSpan right) =>
8080
/// <summary>Get the maximum timespan</summary>
8181
public static TimeSpan Max(TimeSpan left, TimeSpan right) =>
8282
left > right ? left : right;
83-
#region Parser
83+
84+
#region Convert
8485

8586
/// <summary>Parse date time string</summary>
8687
/// <param name="dateValue">The date value</param>

Client.Scripting/DatePeriod.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,22 @@ public DatePeriod(DateTime? start, DateTime? end)
6464
HasEnd = GetHasEnd();
6565
}
6666

67+
/// <summary>
68+
/// Create period from start date and duration
69+
/// </summary>
70+
/// <param name="start">Period start</param>
71+
/// <param name="duration">Period duration</param>
72+
public static DatePeriod FromStart(DateTime start, TimeSpan duration) =>
73+
new(start, start.Add(duration));
74+
75+
/// <summary>
76+
/// Create period from end date and duration
77+
/// </summary>
78+
/// <param name="duration">Period duration</param>
79+
/// <param name="end">Period end</param>
80+
public static DatePeriod FromEnd(TimeSpan duration, DateTime end) =>
81+
new(end.Subtract(duration), end);
82+
6783
// internal
6884
private bool GetHasStart() => Start != Date.MinValue;
6985
private bool GetHasEnd() => End != Date.MaxValue;
@@ -93,6 +109,10 @@ public DatePeriod(DateTime? start, DateTime? end)
93109
[JsonIgnore]
94110
public bool IsMoment => Start.Equals(End);
95111

112+
/// <summary>Test if period is empty</summary>
113+
[JsonIgnore]
114+
public bool IsEmpty => IsMoment;
115+
96116
/// <summary>Test if start and end are UTC</summary>
97117
[JsonIgnore]
98118
public bool IsUtc => Start.IsUtc() && End.IsUtc();

Client.Scripting/Extensions.cs

Lines changed: 208 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ public static string ToPeriodStartString(this DateTime start) =>
620620
/// <param name="end">The period end date</param>
621621
/// <returns>The formatted period end date</returns>
622622
public static string ToPeriodEndString(this DateTime end) =>
623-
IsMidnight(end) || IsLastMomentOfDay(end) ? $"{end.ToShortDateString()} {end.ToShortTimeString()}" : end.ToShortDateString();
623+
IsMidnight(end) || IsLastMomentOfDay(end) ? end.ToShortDateString() : $"{end.ToShortDateString()} {end.ToShortTimeString()}";
624624

625625
/// <summary>Test if the date is in UTC</summary>
626626
/// <param name="dateTime">The source date time</param>
@@ -1388,6 +1388,20 @@ public static List<DatePeriod> Split(this DatePeriod period, List<DateTime> spli
13881388
return splitPeriods;
13891389
}
13901390

1391+
/// <summary>Get period days</summary>
1392+
/// <param name="period">The period</param>
1393+
public static List<DateTime> Days(this DatePeriod period)
1394+
{
1395+
var days = new List<DateTime>();
1396+
var current = period.Start.Date;
1397+
while (current <= period.End.Date)
1398+
{
1399+
days.Add(current);
1400+
current = current.AddDays(1);
1401+
}
1402+
return days;
1403+
}
1404+
13911405
/// <summary>Test if a specific time moment is the first day of a period</summary>
13921406
/// <param name="test">The moment to test</param>
13931407
/// <param name="period">The period </param>
@@ -1618,6 +1632,181 @@ public static List<DatePeriod> Intersections(this IEnumerable<DatePeriod> datePe
16181632
}
16191633
}
16201634

1635+
/// <summary><see cref="TimePeriod">TimePeriod</see> extension methods</summary>
1636+
public static class TimePeriodExtensions
1637+
{
1638+
/// <summary>Test if a specific time moment is before this period</summary>
1639+
/// <param name="period">The period</param>
1640+
/// <param name="test">The moment to test</param>
1641+
/// <returns>True, if the moment is before this period</returns>
1642+
public static bool IsBefore(this TimePeriod period, decimal test) =>
1643+
test < period.Start;
1644+
1645+
/// <summary>Test if a specific time period is before this period</summary>
1646+
/// <param name="period">The period</param>
1647+
/// <param name="testPeriod">The period to test</param>
1648+
/// <returns>True, if the period is before this period</returns>
1649+
public static bool IsBefore(this TimePeriod period, TimePeriod testPeriod) =>
1650+
testPeriod.End < period.Start;
1651+
1652+
/// <summary>Test if a specific time moment is after this period</summary>
1653+
/// <param name="period">The period</param>
1654+
/// <param name="test">The moment to test</param>
1655+
/// <returns>True, if the moment is after this period</returns>
1656+
public static bool IsAfter(this TimePeriod period, decimal test) =>
1657+
test > period.End;
1658+
1659+
/// <summary>Test if a specific time period is after this period</summary>
1660+
/// <param name="period">The period</param>
1661+
/// <param name="testPeriod">The period to test</param>
1662+
/// <returns>True, if the period is after this period</returns>
1663+
public static bool IsAfter(this TimePeriod period, TimePeriod testPeriod) =>
1664+
testPeriod.Start > period.End;
1665+
1666+
/// <summary>Test if a specific time moment is within the period, including open periods</summary>
1667+
/// <param name="period">The period</param>
1668+
/// <param name="test">The moment to test</param>
1669+
/// <returns>True, if the moment is within this period</returns>
1670+
public static bool IsWithin(this TimePeriod period, decimal test) =>
1671+
test.IsWithin(period.Start, period.End);
1672+
1673+
/// <summary>Test if a specific time period is within the period, including open periods</summary>
1674+
/// <param name="period">The period</param>
1675+
/// <param name="testPeriod">The period to test</param>
1676+
/// <returns>True, if the test period is within this period</returns>
1677+
public static bool IsWithin(this TimePeriod period, TimePeriod testPeriod) =>
1678+
IsWithin(period, testPeriod.Start) && IsWithin(period, testPeriod.End);
1679+
1680+
/// <summary>Test if a specific time moment is within or before the period, including open periods</summary>
1681+
/// <param name="period">The period</param>
1682+
/// <param name="test">The moment to test</param>
1683+
/// <returns>True, if the moment is within or before this period</returns>
1684+
public static bool IsWithinOrBefore(this TimePeriod period, decimal test) =>
1685+
test <= period.End;
1686+
1687+
/// <summary>Test if a specific time moment is within or after the period, including open periods</summary>
1688+
/// <param name="period">The period</param>
1689+
/// <param name="test">The moment to test</param>
1690+
/// <returns>True, if the moment is within or after this period</returns>
1691+
public static bool IsWithinOrAfter(this TimePeriod period, decimal test) =>
1692+
test >= period.Start;
1693+
1694+
/// <summary>Test if period is overlapping this period</summary>
1695+
/// <param name="period">The period</param>
1696+
/// <param name="testPeriod">The period to test</param>
1697+
/// <returns>True, if the period is overlapping this period</returns>
1698+
public static bool IsOverlapping(this TimePeriod period, TimePeriod testPeriod) =>
1699+
testPeriod.Start < period.End && period.Start < testPeriod.End;
1700+
1701+
/// <summary>Get the intersection of a time period with this period</summary>
1702+
/// <param name="period">The period</param>
1703+
/// <param name="intersectPeriod">The period to intersect</param>
1704+
/// <returns>The intersecting time period, null if no intersection is present</returns>
1705+
public static TimePeriod Intersect(this TimePeriod period, TimePeriod intersectPeriod)
1706+
{
1707+
if (!IsOverlapping(period, intersectPeriod))
1708+
{
1709+
return null;
1710+
}
1711+
return new(
1712+
Math.Max(period.Start, intersectPeriod.Start),
1713+
Math.Min(period.End, intersectPeriod.End));
1714+
}
1715+
1716+
/// <summary>Get the hours of intersection</summary>
1717+
/// <param name="period">The period</param>
1718+
/// <param name="intersectPeriod">The period to intersect</param>
1719+
/// <returns>The intersecting duration in hours, 0 if no intersection is present</returns>
1720+
public static decimal IntersectHours(this TimePeriod period, TimePeriod intersectPeriod)
1721+
{
1722+
var intersect = Intersect(period, intersectPeriod);
1723+
return intersect == null ? 0 : (decimal)intersect.Duration.TotalHours;
1724+
}
1725+
1726+
/// <summary>Total duration of all time periods</summary>
1727+
/// <param name="timePeriods">The time periods</param>
1728+
/// <returns>Accumulated total duration</returns>
1729+
public static TimeSpan TotalDuration(this IEnumerable<TimePeriod> timePeriods)
1730+
{
1731+
var duration = TimeSpan.Zero;
1732+
return timePeriods.Aggregate(duration, (current, period) => current.Add(period.Duration));
1733+
}
1734+
1735+
/// <summary>Test if any period is overlapping another period</summary>
1736+
/// <param name="timePeriods">The time periods to test</param>
1737+
/// <returns>True, if the period is overlapping this period</returns>
1738+
public static bool HasOverlapping(this IEnumerable<TimePeriod> timePeriods)
1739+
{
1740+
var periodList = timePeriods.ToList();
1741+
for (var current = 1; current < periodList.Count; current++)
1742+
{
1743+
for (var remain = current + 1; remain < periodList.Count; remain++)
1744+
{
1745+
if (periodList[remain].IsOverlapping(periodList[current]))
1746+
{
1747+
return true;
1748+
}
1749+
}
1750+
}
1751+
return false;
1752+
}
1753+
1754+
/// <summary>Test if a specific time moment is within any time period</summary>
1755+
/// <param name="timePeriods">The time periods to test</param>
1756+
/// <param name="test">The moment to test</param>
1757+
/// <returns>True, if the moment is within this period</returns>
1758+
public static bool IsWithinAny(this IEnumerable<TimePeriod> timePeriods, decimal test) =>
1759+
timePeriods.Any(periodValue => periodValue.IsWithin(test));
1760+
1761+
/// <summary>Test if a specific time period is within any time period</summary>
1762+
/// <param name="timePeriods">The time periods to test</param>
1763+
/// <param name="testPeriod">The period to test</param>
1764+
/// <returns>True, if the test period is within this period</returns>
1765+
public static bool IsWithinAny(this IEnumerable<TimePeriod> timePeriods, TimePeriod testPeriod) =>
1766+
timePeriods.Any(periodValue => periodValue.IsWithin(testPeriod));
1767+
1768+
/// <summary>Get limits period, from the earliest start to the latest end</summary>
1769+
/// <param name="timePeriods">The time periods to evaluate</param>
1770+
/// <returns>Time period including all time periods, an anytime period for empty collections</returns>
1771+
public static TimePeriod Limits(this IEnumerable<TimePeriod> timePeriods)
1772+
{
1773+
decimal? start = null;
1774+
decimal? end = null;
1775+
foreach (var timePeriod in timePeriods)
1776+
{
1777+
// start
1778+
if (!start.HasValue || timePeriod.Start < start.Value)
1779+
{
1780+
start = timePeriod.Start;
1781+
}
1782+
// end
1783+
if (!end.HasValue || timePeriod.End > end.Value)
1784+
{
1785+
end = timePeriod.End;
1786+
}
1787+
}
1788+
return new(start, end);
1789+
}
1790+
1791+
/// <summary>Get all intersections of a time period with any time period</summary>
1792+
/// <param name="timePeriods">The time periods to test</param>
1793+
/// <param name="intersectPeriod">The period to intersect</param>
1794+
/// <returns>List of intersecting time periods</returns>
1795+
public static List<TimePeriod> Intersections(this IEnumerable<TimePeriod> timePeriods, TimePeriod intersectPeriod)
1796+
{
1797+
var intersections = new List<TimePeriod>();
1798+
foreach (var timePeriod in timePeriods)
1799+
{
1800+
var intersection = timePeriod.Intersect(intersectPeriod);
1801+
if (intersection != null)
1802+
{
1803+
intersections.Add(intersection);
1804+
}
1805+
}
1806+
return intersections;
1807+
}
1808+
}
1809+
16211810
/// <summary><see cref="CaseValue">CaseValue</see> extension methods</summary>
16221811
public static class CaseValueExtensions
16231812
{
@@ -1693,6 +1882,23 @@ public static IEnumerable<DatePeriod> Periods(this IEnumerable<CaseValue> period
16931882
}
16941883
}
16951884

1885+
/// <summary>Get first matching period containing the test date</summary>
1886+
/// <param name="caseValues">The case period values</param>
1887+
/// <param name="date">The date of the case value</param>
1888+
/// <returns>Accumulated total duration</returns>
1889+
public static CaseValue CaseValueWithin(this IEnumerable<CaseValue> caseValues, DateTime date)
1890+
{
1891+
foreach (var caseValue in caseValues)
1892+
{
1893+
var period = caseValue.Period();
1894+
if (period.IsWithin(date))
1895+
{
1896+
return caseValue;
1897+
}
1898+
}
1899+
return null;
1900+
}
1901+
16961902
/// <summary>Get case period values grouped by value</summary>
16971903
/// <param name="periodValues">The case period values</param>
16981904
/// <returns>Case period values grouped by value</returns>
@@ -1710,7 +1916,7 @@ public static TimeSpan TotalDuration(this IEnumerable<CaseValue> periodValues) =
17101916
/// <param name="intersectPeriod">The period to intersect</param>
17111917
/// <returns>List of intersecting date periods</returns>
17121918
public static List<CaseValue> Intersections(this IEnumerable<CaseValue> periodValues, DatePeriod intersectPeriod) =>
1713-
[..periodValues.Where(periodValue => periodValue.Period().IsOverlapping(intersectPeriod))];
1919+
[.. periodValues.Where(periodValue => periodValue.Period().IsOverlapping(intersectPeriod))];
17141920

17151921
/// <summary>Get case period values matching a period predicate</summary>
17161922
/// <param name="periodValues">The time periods to test</param>

0 commit comments

Comments
 (0)