Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions Api/Api.Controller/PayrollController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,63 @@ await caseValueTool.GetTimeCaseValuesAsync(caseValueDate, caseType, caseFieldNam
return new LookupValueDataMap().ToApi(valueData);
}

protected async Task<ActionResult<ApiObject.LookupRangeResult[]>> GetPayrollLookupRangesAsync(
DomainObject.PayrollQuery query, string[] lookupNames, decimal? rangeValue, string culture)
{
try
{
// tenant
var authResult = await TenantRequestAsync(query.TenantId);
if (authResult != null)
{
return authResult;
}

// validate lookup names
if (lookupNames == null || lookupNames.Length == 0)
{
return BadRequest("Missing lookup names");
}

// query setup
var setupQuery = await SetupQuery(query);
if (setupQuery.Item2 != null)
{
// invalid setup response
return setupQuery.Item2;
}
var querySetup = setupQuery.Item1;

query.RegulationDate ??= CurrentEvaluationDate;
query.EvaluationDate ??= CurrentEvaluationDate;

// lookup provider
var lookupProvider = this.NewRegulationLookupProvider(Runtime.DbContext, querySetup.Tenant,
querySetup.Payroll, query.RegulationDate, query.EvaluationDate);

// build range brackets for each lookup
var results = new List<DomainObject.LookupRangeResult>();
foreach (var lookupName in lookupNames)
{
var lookupSet = await lookupProvider.GetLookupAsync(Runtime.DbContext, lookupName);
if (lookupSet == null)
{
return BadRequest($"Unknown lookup {lookupName}");
}

var result = DomainObject.LookupSetExtensions.BuildRangeBrackets(lookupSet, rangeValue);
result.LookupName = lookupName;
results.Add(result);
}

return new LookupRangeResultMap().ToApi(results);
}
catch (Exception exception)
{
return InternalServerError(exception);
}
}

protected async Task<ActionResult<ApiObject.ReportSet[]>> GetPayrollReportsAsync(
DomainObject.PayrollQuery query, string[] reportNames,
OverrideType? overrideType, UserType? userType, string clusterSetName)
Expand Down
15 changes: 15 additions & 0 deletions Api/Api.Map/LookupRangeBracketMap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using DomainObject = PayrollEngine.Domain.Model;
using ApiObject = PayrollEngine.Api.Model;
using Riok.Mapperly.Abstractions;

namespace PayrollEngine.Api.Map;

/// <summary>
/// Map a domain object with an api object
/// </summary>
[Mapper(EnumMappingStrategy = EnumMappingStrategy.ByName, EnumMappingIgnoreCase = true)]
public partial class LookupRangeBracketMap : ApiMapBase<DomainObject.LookupRangeBracket, ApiObject.LookupRangeBracket>
{
public override partial ApiObject.LookupRangeBracket ToApi(DomainObject.LookupRangeBracket domainObject);
public override partial DomainObject.LookupRangeBracket ToDomain(ApiObject.LookupRangeBracket apiObject);
}
15 changes: 15 additions & 0 deletions Api/Api.Map/LookupRangeResultMap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using DomainObject = PayrollEngine.Domain.Model;
using ApiObject = PayrollEngine.Api.Model;
using Riok.Mapperly.Abstractions;

namespace PayrollEngine.Api.Map;

/// <summary>
/// Map a domain object with an api object
/// </summary>
[Mapper(EnumMappingStrategy = EnumMappingStrategy.ByName, EnumMappingIgnoreCase = true)]
public partial class LookupRangeResultMap : ApiMapBase<DomainObject.LookupRangeResult, ApiObject.LookupRangeResult>
{
public override partial ApiObject.LookupRangeResult ToApi(DomainObject.LookupRangeResult domainObject);
public override partial DomainObject.LookupRangeResult ToDomain(ApiObject.LookupRangeResult apiObject);
}
38 changes: 38 additions & 0 deletions Api/Api.Model/LookupRangeBracket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace PayrollEngine.Api.Model;

/// <summary>
/// A lookup range bracket with computed bounds
/// </summary>
// ReSharper disable UnusedAutoPropertyAccessor.Global
// ReSharper disable PropertyCanBeMadeInitOnly.Global
public class LookupRangeBracket
{
/// <summary>
/// The lookup value key
/// </summary>
public string Key { get; set; }

/// <summary>
/// The lookup value as JSON
/// </summary>
public string Value { get; set; }

/// <summary>
/// The range start (lower bound)
/// </summary>
public decimal LowerBound { get; set; }

/// <summary>
/// The range end (upper bound), null for unbounded last bracket
/// </summary>
public decimal? UpperBound { get; set; }

/// <summary>
/// The original range value from the lookup value
/// </summary>
public decimal? RangeValue { get; set; }

/// <inheritdoc/>
public override string ToString() =>
$"{Key}: {LowerBound} - {UpperBound}";
}
45 changes: 45 additions & 0 deletions Api/Api.Model/LookupRangeResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Collections.Generic;

namespace PayrollEngine.Api.Model;

/// <summary>
/// Result of a lookup range bracket computation
/// </summary>
// ReSharper disable UnusedAutoPropertyAccessor.Global
// ReSharper disable PropertyCanBeMadeInitOnly.Global
public class LookupRangeResult
{
/// <summary>
/// The lookup name
/// </summary>
public string LookupName { get; set; }

/// <summary>
/// The lookup range mode
/// </summary>
public LookupRangeMode RangeMode { get; set; }

/// <summary>
/// The lookup range size
/// </summary>
public decimal? RangeSize { get; set; }

/// <summary>
/// The matching bracket for threshold mode, null for progressive
/// </summary>
public LookupRangeBracket MatchingBracket { get; set; }

/// <summary>
/// All matching brackets for progressive mode, null for threshold
/// </summary>
public List<LookupRangeBracket> MatchingBrackets { get; set; }

/// <summary>
/// All range brackets
/// </summary>
public List<LookupRangeBracket> AllBrackets { get; set; }

/// <inheritdoc/>
public override string ToString() =>
LookupName;
}
71 changes: 71 additions & 0 deletions Api/Api.Model/PayrollEngine.Api.Model.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions Backend.Controller/PayrollController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,42 @@ public override async Task<ActionResult> QueryPayrollCaseChangeValuesAsync(int t
lookupName, lookupKey, rangeValue, culture);
}

/// <summary>
/// Get payroll lookup range brackets
/// </summary>
/// <param name="tenantId">The tenant id</param>
/// <param name="payrollId">The payroll id</param>
/// <param name="lookupNames">The lookup names (case-insensitive)</param>
/// <param name="rangeValue">Optional value to find matching bracket(s)</param>
/// <param name="regulationDate">The regulation date (default: UTC now)</param>
/// <param name="evaluationDate">The evaluation date (default: UTC now)</param>
/// <param name="culture">The content culture</param>
/// <returns>The lookup range results</returns>
[HttpGet("{payrollId}/lookups/ranges")]
[OkResponse]
[NotFoundResponse]
[ApiOperationId("GetPayrollLookupRanges")]
public async Task<ActionResult<ApiObject.LookupRangeResult[]>> GetPayrollLookupRangesAsync(int tenantId, int payrollId,
[FromQuery][Required] string[] lookupNames, [FromQuery] decimal? rangeValue,
[FromQuery] DateTime? regulationDate, [FromQuery] DateTime? evaluationDate, [FromQuery] string culture)
{
// authorization
var authResult = await TenantRequestAsync(tenantId);
if (authResult != null)
{
return authResult;
}
return await base.GetPayrollLookupRangesAsync(
new()
{
TenantId = tenantId,
PayrollId = payrollId,
RegulationDate = regulationDate,
EvaluationDate = evaluationDate
},
lookupNames, rangeValue, culture);
}

/// <summary>
/// Get payroll report sets
/// </summary>
Expand Down
70 changes: 70 additions & 0 deletions Domain/Domain.Model/LookupRangeBracket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;

namespace PayrollEngine.Domain.Model;

/// <summary>
/// A lookup range bracket with computed bounds
/// </summary>
// ReSharper disable PropertyCanBeMadeInitOnly.Global
public class LookupRangeBracket : IEquatable<LookupRangeBracket>
{
/// <summary>
/// The lookup value key
/// </summary>
public string Key { get; set; }

/// <summary>
/// The lookup value as JSON
/// </summary>
public string Value { get; set; }

/// <summary>
/// The range start (lower bound)
/// </summary>
public decimal LowerBound { get; set; }

/// <summary>
/// The range end (upper bound), null for unbounded last bracket
/// </summary>
public decimal? UpperBound { get; set; }

/// <summary>
/// The original range value from the lookup value
/// </summary>
public decimal? RangeValue { get; set; }

/// <summary>
/// Default constructor
/// </summary>
public LookupRangeBracket()
{
}

/// <summary>
/// Copy constructor
/// </summary>
/// <param name="source">The source to copy</param>
public LookupRangeBracket(LookupRangeBracket source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}

Key = source.Key;
Value = source.Value;
LowerBound = source.LowerBound;
UpperBound = source.UpperBound;
RangeValue = source.RangeValue;
}

/// <summary>Compare two objects</summary>
/// <param name="compare">The object to compare with this</param>
/// <returns>True for objects with the same data</returns>
public bool Equals(LookupRangeBracket compare) =>
CompareTool.EqualProperties(this, compare);

/// <inheritdoc/>
public override string ToString() =>
$"{Key}: {LowerBound} - {UpperBound}";
}
Loading