Template
chore: deploy initial code base
This commit is contained in:
+114
@@ -0,0 +1,114 @@
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.Shared;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.DistributionProcess.ChargeCode;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Enums;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VerifyNUnit;
|
||||
using VerifyTests;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.StrategicOpportunities.DistributionProcess
|
||||
{
|
||||
public class DistributionProcessChargeCodeSqlBuilderTests : StrategicOpportunityTestsBase
|
||||
{
|
||||
private readonly long _testOpportunityId = 0;
|
||||
private readonly Period<DateOnly> _baselinePeriod;
|
||||
private readonly Period<DateOnly> _goalPeriod;
|
||||
private readonly Period<DateOnly> _trackingPeriod;
|
||||
private readonly double[] _rampUps;
|
||||
|
||||
public DistributionProcessChargeCodeSqlBuilderTests()
|
||||
{
|
||||
_baselinePeriod = new Period<DateOnly>(new DateOnly(2024, 1, 1), new DateOnly(2024, 12, 31));
|
||||
_goalPeriod = new Period<DateOnly>(new DateOnly(2025, 1, 1), new DateOnly(2025, 12, 31));
|
||||
_trackingPeriod = new Period<DateOnly>(new DateOnly(2026, 1, 1), new DateOnly(2026, 12, 31));
|
||||
_rampUps = [0.1, 0.2, 0.3, 0.4];
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(BuildInsertDistributedSqlTestCases))]
|
||||
public async Task BuildInsertDistributedSqlTest(DistributionMethod methodType, DistributionStage stage)
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DistributionProcessChargeCodeSqlBuilder(
|
||||
_testOpportunityId,
|
||||
_baselinePeriod,
|
||||
_goalPeriod,
|
||||
_trackingPeriod,
|
||||
_rampUps);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildInsertDistributedSql(methodType, stage);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result.Sql, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public async Task BuildDistributionSummarySqlTest(bool isInitiative)
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DistributionProcessChargeCodeSqlBuilder(
|
||||
_testOpportunityId,
|
||||
_baselinePeriod,
|
||||
_goalPeriod,
|
||||
_trackingPeriod,
|
||||
_rampUps);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildDistributionSummarySql(isInitiative);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result.Sql, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BuildUpdateDetailCommittedByRampUpSqlTest()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DistributionProcessChargeCodeSqlBuilder(
|
||||
_testOpportunityId,
|
||||
_baselinePeriod,
|
||||
_goalPeriod,
|
||||
_trackingPeriod,
|
||||
_rampUps);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildUpdateDetailCommittedByRampUpSql(_rampUps.Reverse().ToArray());
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result.Sql, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> BuildInsertDistributedSqlTestCases()
|
||||
{
|
||||
var distributionMethods = new[]
|
||||
{
|
||||
DistributionMethod.Average,
|
||||
DistributionMethod.Monthly
|
||||
};
|
||||
var distributionStages = new[]
|
||||
{
|
||||
DistributionStage.FromSourceToOpportunity,
|
||||
DistributionStage.OpportunityChangeGoalPeriod,
|
||||
DistributionStage.FromInitiativeToOpportunity,
|
||||
DistributionStage.FromOpportunityToInitiative,
|
||||
DistributionStage.InitiativeChangeTrackingPeriod
|
||||
};
|
||||
foreach (var method in distributionMethods)
|
||||
{
|
||||
foreach (var stage in distributionStages)
|
||||
{
|
||||
yield return new TestCaseData(method, stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.Shared;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.DistributionProcess.GL;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Enums;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using VerifyNUnit;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.StrategicOpportunities.DistributionProcess
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Unit"), Category("CCI")]
|
||||
public class DistributionProcessGLSqlBuilderTests : StrategicOpportunityTestsBase
|
||||
{
|
||||
private readonly long _testOpportunityId = 0;
|
||||
private readonly Period<DateOnly> _baselinePeriod;
|
||||
private readonly Period<DateOnly> _goalPeriod;
|
||||
private readonly Period<DateOnly> _trackingPeriod;
|
||||
private readonly double[] _rampUps;
|
||||
|
||||
public DistributionProcessGLSqlBuilderTests()
|
||||
{
|
||||
_baselinePeriod = new Period<DateOnly>(new DateOnly(2024, 1, 1), new DateOnly(2024, 12, 31));
|
||||
_goalPeriod = new Period<DateOnly>(new DateOnly(2025, 1, 1), new DateOnly(2025, 12, 31));
|
||||
_trackingPeriod = new Period<DateOnly>(new DateOnly(2026, 1, 1), new DateOnly(2026, 12, 31));
|
||||
_rampUps = [0.1, 0.2, 0.3, 0.4];
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(BuildInsertDistributedSqlTestCases))]
|
||||
public async Task BuildInsertDistributedSqlTest(DistributionMethod methodType, DistributionStage stage)
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DistributionProcessGLSqlBuilder(
|
||||
_testOpportunityId,
|
||||
_baselinePeriod,
|
||||
_goalPeriod,
|
||||
_trackingPeriod,
|
||||
_rampUps);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildInsertDistributedSql(methodType, stage);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result.Sql, VerifySettings)
|
||||
.UseFileName("GL " + TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public async Task BuildDistributionSummarySqlTest(bool isInitiative)
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DistributionProcessGLSqlBuilder(
|
||||
_testOpportunityId,
|
||||
_baselinePeriod,
|
||||
_goalPeriod,
|
||||
_trackingPeriod,
|
||||
_rampUps);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildDistributionSummarySql(isInitiative);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result.Sql, VerifySettings)
|
||||
.UseFileName("GL " + TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BuildUpdateDetailCommittedByRampUpSqlTest()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DistributionProcessGLSqlBuilder(
|
||||
_testOpportunityId,
|
||||
_baselinePeriod,
|
||||
_goalPeriod,
|
||||
_trackingPeriod,
|
||||
_rampUps);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildUpdateDetailCommittedByRampUpSql(_rampUps.Reverse().ToArray());
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result.Sql, VerifySettings)
|
||||
.UseFileName("GL " + TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> BuildInsertDistributedSqlTestCases()
|
||||
{
|
||||
var distributionMethods = new[]
|
||||
{
|
||||
DistributionMethod.Average,
|
||||
DistributionMethod.Monthly
|
||||
};
|
||||
var distributionStages = new[]
|
||||
{
|
||||
DistributionStage.FromSourceToOpportunity,
|
||||
DistributionStage.OpportunityChangeGoalPeriod,
|
||||
DistributionStage.FromInitiativeToOpportunity,
|
||||
DistributionStage.FromOpportunityToInitiative,
|
||||
DistributionStage.InitiativeChangeTrackingPeriod
|
||||
};
|
||||
foreach (var method in distributionMethods)
|
||||
{
|
||||
foreach (var stage in distributionStages)
|
||||
{
|
||||
yield return new TestCaseData(method, stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Dtos;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Enums;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Queries;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.DataSchema.Models.Query;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using VerifyNUnit;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.StrategicOpportunities.Queries
|
||||
{
|
||||
[TestFixture]
|
||||
public class StrategicOpportunityDetailQueryBuilderTests : StrategicOpportunityTestsBase
|
||||
{
|
||||
private IStrategicOpportunityDetailQueryBuilder _builder;
|
||||
private const long TestOpportunityId = 123;
|
||||
private const long TestMeasureId = 456;
|
||||
private const string TestStartDate = "2024-01-01";
|
||||
private const int TestTrackingMonths = 12;
|
||||
private const string SelectQuery = "SELECT * FROM TestTable";
|
||||
private const string InvalidSelectQuery = "UPDATE TestTable SET Column1 = 'Value'";
|
||||
private const string ComplexSelectQuery = @"
|
||||
SELECT
|
||||
*
|
||||
FROM dss.FactPatientCostDetail AS COSTDETAIL
|
||||
RIGHT JOIN dss.FactPatientBillingLineItemDetail AS PBLID ON PBLID.rowid = COSTDETAIL.pblidrowid
|
||||
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
|
||||
LEFT JOIN CCI.FACTEXPLORATIONPOPULATIONENCOUNTER AS EXPLORATIONPOPULATIONENCOUNTER ON EXPLORATIONPOPULATIONENCOUNTER.encounterid = PBLID.encounterid
|
||||
LEFT JOIN CCI.VIEWENCOUNTERPATIENTTYPEROLLUP AS ENCOUNTERPATIENTTYPEROLLUP ON ENCOUNTERPATIENTTYPEROLLUP.encounterid = PBLID.encounterid
|
||||
INNER JOIN FW.DIMCHARGECODE AS CC ON CC.chargecodeid = PBLID.chargecodeid
|
||||
INNER JOIN DSS.DIMPHYSICIAN AS PHYSPRIMARY ON PHYSPRIMARY.physicianid = DSSPES.primaryphysicianid
|
||||
";
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_builder = new StrategicOpportunityDetailQueryBuilder();
|
||||
}
|
||||
|
||||
#region BuildInsertQuery Tests
|
||||
|
||||
[TestCase(true, TestName = "BuildInsertQuery with APRDRG")]
|
||||
[TestCase(false, TestName = "BuildInsertQuery without APRDRG")]
|
||||
public async Task BuildInsertQuery_ShouldGenerateValidSql(bool isUsingAprdrg)
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = new StrategicOpportunityChargeCode { OpportunityId = TestOpportunityId };
|
||||
|
||||
// Act
|
||||
var result = _builder.BuildInsertQuery(opportunity, SelectQuery, isUsingAprdrg);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildInsertQuery_WithInvalidSelectQuery_ShouldThrowException()
|
||||
{
|
||||
// Arrange
|
||||
var opportunity = new StrategicOpportunityChargeCode { OpportunityId = TestOpportunityId };
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||
_builder.BuildInsertQuery(opportunity, InvalidSelectQuery, true));
|
||||
Assert.That(ex.Message, Does.Contain("Unexpected SQL syntax. SELECT should be first word."));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region BuildDeleteQuery Tests
|
||||
|
||||
[Test]
|
||||
public async Task BuildDeleteQuery_WithOpportunityId_ShouldGenerateValidSql()
|
||||
{
|
||||
// Act
|
||||
var result = _builder.BuildDeleteQuery(TestOpportunityId);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BuildDeleteQuery_WithoutOpportunityId_ShouldGenerateParameterizedSql()
|
||||
{
|
||||
// Act
|
||||
var result = _builder.BuildDeleteQuery();
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region BuildMergeQuery Tests
|
||||
|
||||
[TestCase(true, TestName = "BuildMergeQuery with APRDRG")]
|
||||
[TestCase(false, TestName = "BuildMergeQuery without APRDRG")]
|
||||
public async Task BuildMergeQuery_ShouldGenerateValidSql(bool isUsingAprdrg)
|
||||
{
|
||||
// Act
|
||||
var result = _builder.BuildMergeQuery(TestOpportunityId, SelectQuery, isUsingAprdrg);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildMergeQuery_WithInvalidSelectQuery_ShouldThrowException()
|
||||
{
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||
_builder.BuildMergeQuery(TestOpportunityId, InvalidSelectQuery, true));
|
||||
Assert.That(ex.Message, Does.Contain("Unexpected SQL syntax. SELECT should be first word."));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region BuildGetDistinctEncounterCountQuery Tests
|
||||
|
||||
[TestCase(true, TestName = "BuildGetDistinctEncounterCountQuery with APRDRG")]
|
||||
[TestCase(false, TestName = "BuildGetDistinctEncounterCountQuery without APRDRG")]
|
||||
public async Task BuildGetDistinctEncounterCountQuery_ShouldGenerateValidSql(bool isUsingAprdrg)
|
||||
{
|
||||
// Arrange
|
||||
var getData = new StrategicOpportunityGetDetailData
|
||||
{
|
||||
Dimensions = new[] { "entity", "chargeCode", "costDriver" }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = _builder.BuildGetDistinctEncounterCountQuery(TestOpportunityId, SelectQuery, isUsingAprdrg, getData);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildGetDistinctEncounterCountQuery_ShouldThrowException()
|
||||
{
|
||||
// Arrange
|
||||
var getData = new StrategicOpportunityGetDetailData
|
||||
{
|
||||
Dimensions = new[] { "entity", "chargeCode", "costDriver" }
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||
_builder.BuildGetDistinctEncounterCountQuery(TestOpportunityId, InvalidSelectQuery, true, getData));
|
||||
Assert.That(ex.Message, Does.Contain("Unexpected SQL syntax. SELECT should be first word."));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region BuildMergeSystemMeasureMergeQuery Tests
|
||||
|
||||
[TestCaseSource(nameof(GetMeasureTypeTestCases))]
|
||||
public async Task BuildMergeSystemMeasureMergeQuery_WithDifferentMeasureTypes_ShouldGenerateValidSql(MeasureType measureType)
|
||||
{
|
||||
// Act
|
||||
var result = _builder.BuildMergeSystemMeasureMergeQuery(TestOpportunityId, TestMeasureId, measureType, TestStartDate, TestTrackingMonths);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.UseFileName($"{TestContext.CurrentContext.Test.Name}_{measureType}");
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> GetMeasureTypeTestCases()
|
||||
{
|
||||
yield return new TestCaseData(MeasureType.CommittedCost).SetName("BuildMergeSystemMeasureMergeQuery with CommittedCost");
|
||||
yield return new TestCaseData(MeasureType.ActualCost).SetName("BuildMergeSystemMeasureMergeQuery with ActualCost");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetSelectMeasureSql Tests
|
||||
|
||||
[TestCaseSource(nameof(GetSelectMeasureSql_TestCases))]
|
||||
public async Task GetSelectMeasureSql_ShouldGenerateValidSql(MeasureType measureType)
|
||||
{
|
||||
// Arrange
|
||||
var marginFilters = new Dictionary<string, FilterCondition>
|
||||
{
|
||||
{ "DepartmentId", new FilterCondition { Operator = FilterOperator.In, Values = new List<string> { "1", "2", "3", "4", "5" } } },
|
||||
{ "ChargeCodeId", new FilterCondition { Operator = FilterOperator.NotIn, Values = new List<string> { "100", "200", "300" } } },
|
||||
{ "EntityId", new FilterCondition { Operator = FilterOperator.In, Values = new List<string> { "999" } } }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = _builder.GetSelectMeasureSql(TestOpportunityId, measureType, ComplexSelectQuery, marginFilters);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetSelectMeasureSql_EncountersCases()
|
||||
{
|
||||
// Arrange
|
||||
var marginFilters = new Dictionary<string, FilterCondition>();
|
||||
var complexSelectQuery = ComplexSelectQuery.Replace(
|
||||
"RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid",
|
||||
"INNER JOIN (SELECT * FROM clientdss.FactPatientEncounterSummary AS DSSPES) AS DSSPES ON DSSPES.encounterid = PBLID.encounterid"
|
||||
);
|
||||
|
||||
// Act
|
||||
var result = _builder.GetSelectMeasureSql(TestOpportunityId, MeasureType.EncountersCases, complexSelectQuery, marginFilters);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> GetSelectMeasureSql_TestCases()
|
||||
{
|
||||
foreach (var x in Enum.GetValues<MeasureType>().Where(x => x < MeasureType.CommittedCost))
|
||||
{
|
||||
yield return new TestCaseData(x)
|
||||
.SetName($"GetSelectMeasureSql with {x}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region WrapIntoMeasureMergeQuery Tests
|
||||
|
||||
[Test]
|
||||
public async Task WrapIntoMeasureMergeQuery_ShouldWrapSelectQueryCorrectly()
|
||||
{
|
||||
// Act
|
||||
var result = _builder.WrapIntoMeasureMergeQuery(SelectQuery);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region WrapIntoMeasureAverageQuery Tests
|
||||
|
||||
[Test]
|
||||
public async Task WrapIntoMeasureAverageQuery_ShouldWrapSelectQueryCorrectly()
|
||||
{
|
||||
// Act
|
||||
var result = _builder.WrapIntoMeasureAverageQuery(SelectQuery);
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetDeleteAllMeasuresSql Tests
|
||||
|
||||
[TestCaseSource(nameof(GetDeleteAllMeasuresSql_TestCases))]
|
||||
public async Task GetDeleteAllMeasuresSql_ShouldGenerateValidSql(long[] measureIds)
|
||||
{
|
||||
// Act
|
||||
var result = _builder.GetDeleteAllMeasuresSql(measureIds);
|
||||
|
||||
// Assert
|
||||
if (measureIds == null || measureIds.Length == 0)
|
||||
{
|
||||
Assert.That(result, Is.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Verifier.Verify(result, VerifySettings)
|
||||
.UseFileName(TestContext.CurrentContext.TestName());
|
||||
}
|
||||
|
||||
}
|
||||
private static IEnumerable<TestCaseData> GetDeleteAllMeasuresSql_TestCases()
|
||||
{
|
||||
yield return new TestCaseData(new long[] { 1, 2, 3, 4, 5 })
|
||||
.SetName("GetDeleteAllMeasuresSql with MeasureIds");
|
||||
yield return new TestCaseData(new long[] { })
|
||||
.SetName("GetDeleteAllMeasuresSql with empty MeasureIds");
|
||||
yield return new TestCaseData(null)
|
||||
.SetName("GetDeleteAllMeasuresSql with null MeasureIds");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+703
@@ -0,0 +1,703 @@
|
||||
using Hangfire;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.DbContexts.Snowflake;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration;
|
||||
using Strata.ContinuousImprovement.Biz.Exploration.Filters;
|
||||
using Strata.ContinuousImprovement.Biz.Notification;
|
||||
using Strata.ContinuousImprovement.Biz.Population;
|
||||
using Strata.ContinuousImprovement.Biz.Shared;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.DistributionProcess;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Enums;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Queries;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.DbContexts;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using Strata.DataSchema.Client;
|
||||
using Strata.DataSchema.Models.Query;
|
||||
using Strata.DataSchema.Models.Schema;
|
||||
using Strata.Schema.Client;
|
||||
using Strata.Schema.Client.Dtos;
|
||||
using Strata.Schema.Client.Dtos.Info;
|
||||
using Strata.SqlTools.Configuration.Common.AsyncFactory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VerifyNUnit;
|
||||
using static Strata.ContinuousImprovement.Biz.Exploration.ExplorationService;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.StrategicOpportunities
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Unit"), Category("CCI")]
|
||||
public class StrategicChargeCodeOpportunityDetailServiceTests : StrategicOpportunityTestsBase
|
||||
{
|
||||
private IStrategicChargeCodeOpportunityDetailService _strategicOpportunityDetailService;
|
||||
private Mock<IDataSchemaService> _mockDataSchemaService;
|
||||
private Mock<ISchemaServiceClient> _mockSchemaServiceClient;
|
||||
private Mock<ISnowflakeDatabaseContext> _mockSnowflakeContext;
|
||||
private Mock<IAsyncDbContextFactory<JazzDbContext>> _mockJazzDbContext;
|
||||
private Mock<IExplorationFilterService> _mockExplorationService;
|
||||
private Mock<IDistributionProcessFactory> _mockDistributionProcessFactory;
|
||||
private Mock<IStrategicOpportunityDetailQueryBuilder> _mockStrategicOpportunityDetailQueryBuilder;
|
||||
private Mock<IHubContext<NotificationHub, INotificationHub>> _mockNotificationHubClient;
|
||||
private Mock<ILogger<StrategicChargeCodeOpportunityDetailService>> _mockLogger;
|
||||
private Mock<IStrategicItemDimensionSyncService> _mockDimensionSyncService;
|
||||
private CentralDbContext _centralDbContext;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
// Create in-memory database
|
||||
var options = new DbContextOptionsBuilder<CentralDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: "TestCentralDb_" + Guid.NewGuid())
|
||||
.Options;
|
||||
|
||||
_centralDbContext = new CentralDbContext(options);
|
||||
|
||||
// Arrange mocks
|
||||
_mockSchemaServiceClient = new Mock<ISchemaServiceClient>();
|
||||
_mockJazzDbContext = new Mock<IAsyncDbContextFactory<JazzDbContext>>();
|
||||
_mockSnowflakeContext = new Mock<ISnowflakeDatabaseContext>();
|
||||
_mockDataSchemaService = new Mock<IDataSchemaService>();
|
||||
_mockExplorationService = new Mock<IExplorationFilterService>();
|
||||
_mockNotificationHubClient = new Mock<IHubContext<NotificationHub, INotificationHub>>();
|
||||
_mockLogger = new Mock<ILogger<StrategicChargeCodeOpportunityDetailService>>();
|
||||
_mockDistributionProcessFactory = new Mock<IDistributionProcessFactory>();
|
||||
_mockStrategicOpportunityDetailQueryBuilder = new Mock<IStrategicOpportunityDetailQueryBuilder>();
|
||||
_mockDimensionSyncService = new Mock<IStrategicItemDimensionSyncService>();
|
||||
_mockDimensionSyncService.Setup(x => x.UpsertAsync(It.IsAny<StrategicOpportunity>(), It.IsAny<CancellationToken>())).Returns(Task.CompletedTask);
|
||||
_mockDimensionSyncService.Setup(x => x.DeleteAsync(It.IsAny<long>())).Returns(Task.CompletedTask);
|
||||
|
||||
// Seed ExplorationPopulation data for exploration test cases
|
||||
_centralDbContext.ExplorationPopulations.Add(new ExplorationPopulation
|
||||
{
|
||||
ExplorationPopulationId = 100,
|
||||
Name = "Test Exploration Population",
|
||||
CaseTypeFamilyId = 123,
|
||||
StrataId = 1,
|
||||
FiltersJSON = @"[{""ChipType"":4,""Key"":1,""DateRange"":[""2024-01-01T00:00:00"",""2024-12-31T00:00:00""]}]"
|
||||
});
|
||||
_centralDbContext.SaveChanges();
|
||||
|
||||
// Create in-memory JazzDbContext options that will be reused
|
||||
var jazzOptions = new DbContextOptionsBuilder<JazzDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: "TestJazzDb_" + Guid.NewGuid())
|
||||
.Options;
|
||||
|
||||
// Seed the JazzDb with initial data
|
||||
using (var jazzDbContext = new JazzDbContext(jazzOptions))
|
||||
{
|
||||
jazzDbContext.SystemSettings.Add(new SystemSetting
|
||||
{
|
||||
SystemSettingId = 1,
|
||||
Name = "Flexible Exploration Inpatient Case Type Family",
|
||||
Value = "0"
|
||||
});
|
||||
jazzDbContext.SaveChanges();
|
||||
}
|
||||
|
||||
// Mock JazzDbContext factory to return a NEW context instance each time
|
||||
_mockJazzDbContext.Setup(x => x.CreateDbContextAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(() => new JazzDbContext(jazzOptions));
|
||||
|
||||
// Mock ExplorationFilterService
|
||||
_mockExplorationService.Setup(x =>
|
||||
x.GetFilterStringsAsync(It.IsAny<IEnumerable<FilterChipItem>>(), It.IsAny<IQueryParamBase>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(("WHERE DSSPES.dischargedatetime >= :DischargeDateStart AND DSSPES.dischargedatetime < :DischargeDateEnd\r\n",
|
||||
"AND fpes.EntityId = 1\r\n"));
|
||||
|
||||
// Mock DataSchemaService
|
||||
var dataTableId = 12345;
|
||||
var dataColumnId = 67890;
|
||||
var testDataSourceId = 999;
|
||||
|
||||
_mockDataSchemaService.Setup(x =>
|
||||
x.GetDataSourcesAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new List<Strata.DataSchema.Models.DataSourceManagement.DataSource>
|
||||
{
|
||||
new Strata.DataSchema.Models.DataSourceManagement.DataSource
|
||||
{
|
||||
DataSourceId = testDataSourceId,
|
||||
Name = "DS Strata Encounter Cost Detail"
|
||||
}
|
||||
});
|
||||
|
||||
_mockDataSchemaService.Setup(x =>
|
||||
x.GetDataTablesByDataSourceId(It.Is<int>(dataSourceId => dataSourceId == testDataSourceId), It.IsAny<bool>(), It.IsAny<DataTableType?>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((int dsId, bool includeChildren, DataTableType? tableType, CancellationToken ct) =>
|
||||
{
|
||||
var tableAliases = new[] { "dsspes", "pblid", "costdetail", "cc", "PATIENTTYPE", "physPrimary", "dsspeci", "explorationPopulationEncounter", "msdrg" };
|
||||
return tableAliases.Select(alias => new DataTable
|
||||
{
|
||||
DataTableId = dataTableId,
|
||||
SqlAlias = alias
|
||||
}).ToList();
|
||||
});
|
||||
|
||||
_mockDataSchemaService.Setup(x =>
|
||||
x.GetDataColumnsBySqlColumnNameAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync((string columnName, CancellationToken ct) => new List<DataColumn>
|
||||
{
|
||||
new DataColumn
|
||||
{
|
||||
DataColumnId = dataColumnId,
|
||||
DataTableId = dataTableId
|
||||
}
|
||||
});
|
||||
|
||||
_mockDataSchemaService.Setup(x =>
|
||||
x.BuildSqlQuery(It.IsAny<QueryConfig>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new SqlResponse
|
||||
{
|
||||
SqlQuery = _unitsOfServicePatPopQuery,
|
||||
Parameters = new List<KeyValuePair<string, object>>
|
||||
{
|
||||
new(":dischargedatetime_Start_0", new DateTime(2024, 1, 1)),
|
||||
new(":dischargedatetime_End_1", new DateTime(2024, 12, 31)),
|
||||
new(":chargecodeid_2", 3541)
|
||||
}
|
||||
});
|
||||
|
||||
// Mock SchemaServiceClient
|
||||
_mockSchemaServiceClient.Setup(x =>
|
||||
x.GetDimensionByGlobalIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new ScoreDimensionInfoDto { DimensionGuid = Guid.NewGuid() });
|
||||
|
||||
_mockSchemaServiceClient.Setup(x =>
|
||||
x.GetDimensionMembersFromHierarchyPathsAsync(It.IsAny<Guid>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new List<MemberDto>
|
||||
{
|
||||
new MemberDto
|
||||
{
|
||||
Id = "777",
|
||||
Name = "Charge Code Stub"
|
||||
}
|
||||
});
|
||||
|
||||
_mockSchemaServiceClient.Setup(x =>
|
||||
x.GetHierarchyNodesFromHierarchyPathsAsync(It.IsAny<Guid>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new List<HierarchyNodeDto>());
|
||||
|
||||
// Mock Snowflake context
|
||||
_mockSnowflakeContext.Setup(x =>
|
||||
x.QueryAsync<StrategicOpportunityDetail>(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<object>(), It.IsAny<TimeSpan>()))
|
||||
.ReturnsAsync(new List<StrategicOpportunityDetail>());
|
||||
|
||||
_mockSnowflakeContext.Setup(x =>
|
||||
x.ExecuteScalarAsync<double>(It.IsAny<string>(), It.IsAny<object>()))
|
||||
.ReturnsAsync(123.45);
|
||||
|
||||
_mockSnowflakeContext.Setup(x =>
|
||||
x.ExecuteCommandAsync(It.IsAny<string>(), It.IsAny<object>(), It.IsAny<TimeSpan>()))
|
||||
.ReturnsAsync(1);
|
||||
|
||||
var claimsPrincipalAccessor = TestUtilities.GetClaimsPrincipalAccessor();
|
||||
|
||||
_strategicOpportunityDetailService = new StrategicChargeCodeOpportunityDetailService(
|
||||
_mockJazzDbContext.Object,
|
||||
_centralDbContext,
|
||||
claimsPrincipalAccessor,
|
||||
_mockDataSchemaService.Object,
|
||||
_mockSchemaServiceClient.Object,
|
||||
_mockSnowflakeContext.Object,
|
||||
_mockExplorationService.Object,
|
||||
_mockDistributionProcessFactory.Object,
|
||||
_mockStrategicOpportunityDetailQueryBuilder.Object,
|
||||
_mockNotificationHubClient.Object,
|
||||
_mockDimensionSyncService.Object,
|
||||
_mockLogger.Object);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void TestSetup()
|
||||
{
|
||||
_centralDbContext.StrategicOpportunityMeasures.RemoveRange(_centralDbContext.StrategicOpportunityMeasures);
|
||||
_centralDbContext.StrategicOpportunityChargeCodes.RemoveRange(_centralDbContext.StrategicOpportunityChargeCodes);
|
||||
_centralDbContext.SaveChanges();
|
||||
}
|
||||
|
||||
private const string _unitsOfServicePatPopQuery = @"SELECT
|
||||
""DischargeDateTime"",
|
||||
ZEROIFNULL(""Value"") AS ""Value""
|
||||
|
||||
FROM (
|
||||
SELECT
|
||||
""DischargeDateTime"",
|
||||
SUM(""Value"") AS ""Value""
|
||||
|
||||
FROM (
|
||||
SELECT
|
||||
DSSPES.dischargedatetime AS ""DischargeDateTime"",
|
||||
SUM(PBLID.unitsofservice) AS ""Value""
|
||||
|
||||
FROM dss.FactPatientBillingLineItemDetail AS PBLID
|
||||
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
|
||||
|
||||
WHERE
|
||||
(((DSSPES.dischargedatetime >= :dischargedatetime_Start_0 AND DSSPES.dischargedatetime <= :dischargedatetime_End_1) AND PBLID.departmentid = :departmentid_2) AND DSSPES.primaryphysicianid = :primaryphysicianid_3)
|
||||
|
||||
GROUP BY
|
||||
""DischargeDateTime""
|
||||
|
||||
|
||||
)
|
||||
|
||||
GROUP BY
|
||||
""DischargeDateTime""
|
||||
|
||||
|
||||
)
|
||||
|
||||
ORDER BY
|
||||
""DischargeDateTime"" ASC NULLS FIRST";
|
||||
|
||||
private const string _unitsOfServiceFlexPopQuery = @"SELECT
|
||||
""DischargeDateTime"",
|
||||
ZEROIFNULL(""Value"") AS ""Value""
|
||||
|
||||
FROM (
|
||||
SELECT
|
||||
""DischargeDateTime"",
|
||||
SUM(""Value"") AS ""Value""
|
||||
|
||||
FROM (
|
||||
SELECT
|
||||
DSSPES.dischargedatetime AS ""DischargeDateTime"",
|
||||
SUM(PBLID.unitsofservice) AS ""Value""
|
||||
|
||||
FROM dss.FactPatientBillingLineItemDetail AS PBLID
|
||||
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
|
||||
LEFT JOIN CCI.FACTEXPLORATIONPOPULATIONENCOUNTER AS EXPLORATIONPOPULATIONENCOUNTER ON EXPLORATIONPOPULATIONENCOUNTER.encounterid = PBLID.encounterid
|
||||
|
||||
WHERE
|
||||
((DSSPES.dischargedatetime >= :dischargedatetime_Start_0 AND DSSPES.dischargedatetime <= :dischargedatetime_End_1) AND EXPLORATIONPOPULATIONENCOUNTER.explorationpopulationid = :explorationpopulationid_2)
|
||||
|
||||
GROUP BY
|
||||
""DischargeDateTime""
|
||||
|
||||
|
||||
)
|
||||
|
||||
GROUP BY
|
||||
""DischargeDateTime""
|
||||
|
||||
|
||||
)
|
||||
|
||||
ORDER BY
|
||||
""DischargeDateTime"" ASC NULLS FIRST";
|
||||
|
||||
private const string _contributionMarginPatPopQuery = @"SELECT
|
||||
""DischargeDateTime"",
|
||||
""dsspesNetRevenue"",
|
||||
ZEROIFNULL(""Value"") AS ""Value""
|
||||
|
||||
FROM (
|
||||
SELECT
|
||||
""DischargeDateTime"",
|
||||
""dsspesNetRevenue"",
|
||||
SUM(""Value"") AS ""Value""
|
||||
|
||||
FROM (
|
||||
SELECT
|
||||
DSSPES.dischargedatetime AS ""DischargeDateTime"",
|
||||
DSSPES.netrevenue AS ""dsspesNetRevenue"",
|
||||
SUM(COSTDETAIL.variabledirectcost) AS ""Value""
|
||||
|
||||
FROM dss.FactPatientCostDetail AS COSTDETAIL
|
||||
RIGHT JOIN dss.FactPatientBillingLineItemDetail AS PBLID ON PBLID.rowid = COSTDETAIL.pblidrowid
|
||||
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
|
||||
|
||||
WHERE
|
||||
((DSSPES.dischargedatetime >= :dischargedatetime_Start_0 AND DSSPES.dischargedatetime <= :dischargedatetime_End_1))
|
||||
|
||||
GROUP BY
|
||||
""DischargeDateTime"",
|
||||
""dsspesNetRevenue""
|
||||
|
||||
|
||||
)
|
||||
|
||||
GROUP BY
|
||||
""DischargeDateTime"",
|
||||
""dsspesNetRevenue""
|
||||
|
||||
|
||||
)
|
||||
|
||||
ORDER BY
|
||||
""DischargeDateTime"" ASC NULLS FIRST,
|
||||
""dsspesNetRevenue"" ASC NULLS FIRST";
|
||||
|
||||
private const string _contributionMarginFlexPopQuery = @"SELECT
|
||||
""DischargeDateTime"",
|
||||
""dsspesNetRevenue"",
|
||||
ZEROIFNULL(""Value"") AS ""Value""
|
||||
|
||||
FROM (
|
||||
SELECT
|
||||
""DischargeDateTime"",
|
||||
""dsspesNetRevenue"",
|
||||
SUM(""Value"") AS ""Value""
|
||||
|
||||
FROM (
|
||||
SELECT
|
||||
DSSPES.dischargedatetime AS ""DischargeDateTime"",
|
||||
DSSPES.netrevenue AS ""dsspesNetRevenue"",
|
||||
SUM(COSTDETAIL.variabledirectcost) AS ""Value""
|
||||
|
||||
FROM dss.FactPatientCostDetail AS COSTDETAIL
|
||||
RIGHT JOIN dss.FactPatientBillingLineItemDetail AS PBLID ON PBLID.rowid = COSTDETAIL.pblidrowid
|
||||
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
|
||||
LEFT JOIN CCI.FACTEXPLORATIONPOPULATIONENCOUNTER AS EXPLORATIONPOPULATIONENCOUNTER ON EXPLORATIONPOPULATIONENCOUNTER.encounterid = PBLID.encounterid
|
||||
|
||||
WHERE
|
||||
((DSSPES.dischargedatetime >= :dischargedatetime_Start_0 AND DSSPES.dischargedatetime <= :dischargedatetime_End_1) AND EXPLORATIONPOPULATIONENCOUNTER.explorationpopulationid = :explorationpopulationid_2)
|
||||
|
||||
GROUP BY
|
||||
""DischargeDateTime"",
|
||||
""dsspesNetRevenue""
|
||||
|
||||
|
||||
)
|
||||
|
||||
GROUP BY
|
||||
""DischargeDateTime"",
|
||||
""dsspesNetRevenue""
|
||||
|
||||
|
||||
)
|
||||
|
||||
ORDER BY
|
||||
""DischargeDateTime"" ASC NULLS FIRST,
|
||||
""dsspesNetRevenue"" ASC NULLS FIRST";
|
||||
|
||||
// SQL returned by BuildSqlQuery for the tracking merge path with clinical indicator population
|
||||
private const string _trackingPatPopQuery = @"SELECT
|
||||
""dsspesEncounterId"",
|
||||
""dsspesDischargeDateTime"",
|
||||
""dsspesEntityID"",
|
||||
""dsspesPhysicianId"",
|
||||
""dsspesPrimaryServiceLineId"",
|
||||
""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
""dsspesMSDRGCaseTypeFamilyId"",
|
||||
""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
""pblidDepartmentId"",
|
||||
""physPrimaryPhysicianSpecialtyId"",
|
||||
""ccChargeCodeID"",
|
||||
""ccCostDriver"",
|
||||
""encounterPatientTypeRollupPatientTypeRollup"",
|
||||
ZEROIFNULL(""Units"") AS ""Units"",
|
||||
ZEROIFNULL(""Cost"") AS ""Cost""
|
||||
|
||||
FROM (
|
||||
SELECT
|
||||
""dsspesEncounterId"",
|
||||
""dsspesDischargeDateTime"",
|
||||
""dsspesEntityID"",
|
||||
""dsspesPhysicianId"",
|
||||
""dsspesPrimaryServiceLineId"",
|
||||
""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
""dsspesMSDRGCaseTypeFamilyId"",
|
||||
""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
""pblidDepartmentId"",
|
||||
""physPrimaryPhysicianSpecialtyId"",
|
||||
""ccChargeCodeID"",
|
||||
""ccCostDriver"",
|
||||
""encounterPatientTypeRollupPatientTypeRollup"",
|
||||
SUM(""Units"") AS ""Units"",
|
||||
SUM(""Cost"") AS ""Cost""
|
||||
|
||||
FROM (
|
||||
SELECT
|
||||
DSSPES.encounterid AS ""dsspesEncounterId"",
|
||||
DSSPES.dischargedatetime AS ""dsspesDischargeDateTime"",
|
||||
DSSPES.entityid AS ""dsspesEntityID"",
|
||||
DSSPES.primaryphysicianid AS ""dsspesPhysicianId"",
|
||||
DSSPES.servicelineid AS ""dsspesPrimaryServiceLineId"",
|
||||
DSSPES.cptcodecasetypefamilyid AS ""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
DSSPES.msdrgcasetypefamilyid AS ""dsspesMSDRGCaseTypeFamilyId"",
|
||||
DSSPES.aprdrgcasetypefamilyid AS ""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
PBLID.departmentid AS ""pblidDepartmentId"",
|
||||
PHYSPRIMARY.physicianspecialtyid AS ""physPrimaryPhysicianSpecialtyId"",
|
||||
CC.chargecodeid AS ""ccChargeCodeID"",
|
||||
IFNULL(CC.costdriver::TEXT, '') AS ""ccCostDriver"",
|
||||
IFNULL(ENCOUNTERPATIENTTYPEROLLUP.patienttyperollup::TEXT, '') AS ""encounterPatientTypeRollupPatientTypeRollup"",
|
||||
NULL AS ""Cost"",
|
||||
SUM(PBLID.unitsofservice) AS ""Units""
|
||||
|
||||
FROM dss.FactPatientBillingLineItemDetail AS PBLID
|
||||
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
|
||||
LEFT JOIN dss.FactPatientEncounterClinicalIndicator AS DSSPECI ON DSSPECI.encounterid = PBLID.encounterid
|
||||
LEFT JOIN CCI.VIEWENCOUNTERPATIENTTYPEROLLUP AS ENCOUNTERPATIENTTYPEROLLUP ON ENCOUNTERPATIENTTYPEROLLUP.encounterid = PBLID.encounterid
|
||||
INNER JOIN FW.DIMCHARGECODE AS CC ON CC.chargecodeid = PBLID.chargecodeid
|
||||
INNER JOIN DSS.DIMPHYSICIAN AS PHYSPRIMARY ON PHYSPRIMARY.physicianid = DSSPES.primaryphysicianid
|
||||
|
||||
WHERE
|
||||
((DSSPES.dischargedatetime >= :dischargedatetime_Start_0 AND DSSPES.dischargedatetime <= :dischargedatetime_End_1) AND DSSPECI.clinicalindicatorid = :clinicalindicatorid_2)
|
||||
|
||||
GROUP BY
|
||||
""dsspesEncounterId"",
|
||||
""dsspesDischargeDateTime"",
|
||||
""dsspesEntityID"",
|
||||
""dsspesPhysicianId"",
|
||||
""dsspesPrimaryServiceLineId"",
|
||||
""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
""dsspesMSDRGCaseTypeFamilyId"",
|
||||
""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
""pblidDepartmentId"",
|
||||
""physPrimaryPhysicianSpecialtyId"",
|
||||
""ccChargeCodeID"",
|
||||
""ccCostDriver"",
|
||||
""encounterPatientTypeRollupPatientTypeRollup""
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
DSSPES.encounterid AS ""dsspesEncounterId"",
|
||||
DSSPES.dischargedatetime AS ""dsspesDischargeDateTime"",
|
||||
DSSPES.entityid AS ""dsspesEntityID"",
|
||||
DSSPES.primaryphysicianid AS ""dsspesPhysicianId"",
|
||||
DSSPES.servicelineid AS ""dsspesPrimaryServiceLineId"",
|
||||
DSSPES.cptcodecasetypefamilyid AS ""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
DSSPES.msdrgcasetypefamilyid AS ""dsspesMSDRGCaseTypeFamilyId"",
|
||||
DSSPES.aprdrgcasetypefamilyid AS ""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
PBLID.departmentid AS ""pblidDepartmentId"",
|
||||
PHYSPRIMARY.physicianspecialtyid AS ""physPrimaryPhysicianSpecialtyId"",
|
||||
CC.chargecodeid AS ""ccChargeCodeID"",
|
||||
IFNULL(CC.costdriver::TEXT, '') AS ""ccCostDriver"",
|
||||
IFNULL(ENCOUNTERPATIENTTYPEROLLUP.patienttyperollup::TEXT, '') AS ""encounterPatientTypeRollupPatientTypeRollup"",
|
||||
SUM(COSTDETAIL.variabledirectcost) AS ""Cost"",
|
||||
NULL AS ""Units""
|
||||
|
||||
FROM dss.FactPatientCostDetail AS COSTDETAIL
|
||||
RIGHT JOIN dss.FactPatientBillingLineItemDetail AS PBLID ON PBLID.rowid = COSTDETAIL.pblidrowid
|
||||
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
|
||||
LEFT JOIN dss.FactPatientEncounterClinicalIndicator AS DSSPECI ON DSSPECI.encounterid = PBLID.encounterid
|
||||
LEFT JOIN CCI.VIEWENCOUNTERPATIENTTYPEROLLUP AS ENCOUNTERPATIENTTYPEROLLUP ON ENCOUNTERPATIENTTYPEROLLUP.encounterid = PBLID.encounterid
|
||||
INNER JOIN FW.DIMCHARGECODE AS CC ON CC.chargecodeid = PBLID.chargecodeid
|
||||
INNER JOIN DSS.DIMPHYSICIAN AS PHYSPRIMARY ON PHYSPRIMARY.physicianid = DSSPES.primaryphysicianid
|
||||
|
||||
WHERE
|
||||
((DSSPES.dischargedatetime >= :dischargedatetime_Start_3 AND DSSPES.dischargedatetime <= :dischargedatetime_End_4) AND DSSPECI.clinicalindicatorid = :clinicalindicatorid_5)
|
||||
|
||||
GROUP BY
|
||||
""dsspesEncounterId"",
|
||||
""dsspesDischargeDateTime"",
|
||||
""dsspesEntityID"",
|
||||
""dsspesPhysicianId"",
|
||||
""dsspesPrimaryServiceLineId"",
|
||||
""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
""dsspesMSDRGCaseTypeFamilyId"",
|
||||
""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
""pblidDepartmentId"",
|
||||
""physPrimaryPhysicianSpecialtyId"",
|
||||
""ccChargeCodeID"",
|
||||
""ccCostDriver"",
|
||||
""encounterPatientTypeRollupPatientTypeRollup""
|
||||
|
||||
|
||||
)
|
||||
|
||||
GROUP BY
|
||||
""dsspesEncounterId"",
|
||||
""dsspesDischargeDateTime"",
|
||||
""dsspesEntityID"",
|
||||
""dsspesPhysicianId"",
|
||||
""dsspesPrimaryServiceLineId"",
|
||||
""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
""dsspesMSDRGCaseTypeFamilyId"",
|
||||
""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
""pblidDepartmentId"",
|
||||
""physPrimaryPhysicianSpecialtyId"",
|
||||
""ccChargeCodeID"",
|
||||
""ccCostDriver"",
|
||||
""encounterPatientTypeRollupPatientTypeRollup""
|
||||
|
||||
|
||||
)
|
||||
|
||||
ORDER BY
|
||||
""dsspesEncounterId"" ASC NULLS FIRST,
|
||||
""dsspesDischargeDateTime"" ASC NULLS FIRST,
|
||||
""dsspesEntityID"" ASC NULLS FIRST,
|
||||
""dsspesPhysicianId"" ASC NULLS FIRST,
|
||||
""dsspesPrimaryServiceLineId"" ASC NULLS FIRST,
|
||||
""dsspesCPTCodeCaseTypeFamilyId"" ASC NULLS FIRST,
|
||||
""dsspesMSDRGCaseTypeFamilyId"" ASC NULLS FIRST,
|
||||
""dsspesAPRDRGCaseTypeFamilyId"" ASC NULLS FIRST,
|
||||
""pblidDepartmentId"" ASC NULLS FIRST,
|
||||
""physPrimaryPhysicianSpecialtyId"" ASC NULLS FIRST,
|
||||
""ccChargeCodeID"" ASC NULLS FIRST,
|
||||
""ccCostDriver"" ASC NULLS FIRST,
|
||||
""encounterPatientTypeRollupPatientTypeRollup"" ASC NULLS FIRST";
|
||||
|
||||
// SQL returned by BuildSqlQuery for the tracking merge path with exploration population
|
||||
private const string _trackingFlexPopQuery = @"SELECT
|
||||
""dsspesEncounterId"",
|
||||
""dsspesDischargeDateTime"",
|
||||
""dsspesEntityID"",
|
||||
""dsspesPhysicianId"",
|
||||
""dsspesPrimaryServiceLineId"",
|
||||
""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
""dsspesMSDRGCaseTypeFamilyId"",
|
||||
""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
""pblidDepartmentId"",
|
||||
""physPrimaryPhysicianSpecialtyId"",
|
||||
""ccChargeCodeID"",
|
||||
""ccCostDriver"",
|
||||
""encounterPatientTypeRollupPatientTypeRollup"",
|
||||
ZEROIFNULL(""Units"") AS ""Units"",
|
||||
ZEROIFNULL(""Cost"") AS ""Cost""
|
||||
|
||||
FROM (
|
||||
SELECT
|
||||
""dsspesEncounterId"",
|
||||
""dsspesDischargeDateTime"",
|
||||
""dsspesEntityID"",
|
||||
""dsspesPhysicianId"",
|
||||
""dsspesPrimaryServiceLineId"",
|
||||
""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
""dsspesMSDRGCaseTypeFamilyId"",
|
||||
""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
""pblidDepartmentId"",
|
||||
""physPrimaryPhysicianSpecialtyId"",
|
||||
""ccChargeCodeID"",
|
||||
""ccCostDriver"",
|
||||
""encounterPatientTypeRollupPatientTypeRollup"",
|
||||
SUM(""Units"") AS ""Units"",
|
||||
SUM(""Cost"") AS ""Cost""
|
||||
|
||||
FROM (
|
||||
SELECT
|
||||
DSSPES.encounterid AS ""dsspesEncounterId"",
|
||||
DSSPES.dischargedatetime AS ""dsspesDischargeDateTime"",
|
||||
DSSPES.entityid AS ""dsspesEntityID"",
|
||||
DSSPES.primaryphysicianid AS ""dsspesPhysicianId"",
|
||||
DSSPES.servicelineid AS ""dsspesPrimaryServiceLineId"",
|
||||
DSSPES.cptcodecasetypefamilyid AS ""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
DSSPES.msdrgcasetypefamilyid AS ""dsspesMSDRGCaseTypeFamilyId"",
|
||||
DSSPES.aprdrgcasetypefamilyid AS ""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
PBLID.departmentid AS ""pblidDepartmentId"",
|
||||
PHYSPRIMARY.physicianspecialtyid AS ""physPrimaryPhysicianSpecialtyId"",
|
||||
CC.chargecodeid AS ""ccChargeCodeID"",
|
||||
IFNULL(CC.costdriver::TEXT, '') AS ""ccCostDriver"",
|
||||
IFNULL(ENCOUNTERPATIENTTYPEROLLUP.patienttyperollup::TEXT, '') AS ""encounterPatientTypeRollupPatientTypeRollup"",
|
||||
NULL AS ""Cost"",
|
||||
SUM(PBLID.unitsofservice) AS ""Units""
|
||||
|
||||
FROM dss.FactPatientBillingLineItemDetail AS PBLID
|
||||
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
|
||||
LEFT JOIN CCI.FACTEXPLORATIONPOPULATIONENCOUNTER AS EXPLORATIONPOPULATIONENCOUNTER ON EXPLORATIONPOPULATIONENCOUNTER.encounterid = PBLID.encounterid
|
||||
LEFT JOIN CCI.VIEWENCOUNTERPATIENTTYPEROLLUP AS ENCOUNTERPATIENTTYPEROLLUP ON ENCOUNTERPATIENTTYPEROLLUP.encounterid = PBLID.encounterid
|
||||
INNER JOIN FW.DIMCHARGECODE AS CC ON CC.chargecodeid = PBLID.chargecodeid
|
||||
INNER JOIN DSS.DIMPHYSICIAN AS PHYSPRIMARY ON PHYSPRIMARY.physicianid = DSSPES.primaryphysicianid
|
||||
|
||||
WHERE
|
||||
((DSSPES.dischargedatetime >= :dischargedatetime_Start_0 AND DSSPES.dischargedatetime <= :dischargedatetime_End_1) AND EXPLORATIONPOPULATIONENCOUNTER.explorationpopulationid = :explorationpopulationid_2)
|
||||
|
||||
GROUP BY
|
||||
""dsspesEncounterId"",
|
||||
""dsspesDischargeDateTime"",
|
||||
""dsspesEntityID"",
|
||||
""dsspesPhysicianId"",
|
||||
""dsspesPrimaryServiceLineId"",
|
||||
""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
""dsspesMSDRGCaseTypeFamilyId"",
|
||||
""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
""pblidDepartmentId"",
|
||||
""physPrimaryPhysicianSpecialtyId"",
|
||||
""ccChargeCodeID"",
|
||||
""ccCostDriver"",
|
||||
""encounterPatientTypeRollupPatientTypeRollup""
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
DSSPES.encounterid AS ""dsspesEncounterId"",
|
||||
DSSPES.dischargedatetime AS ""dsspesDischargeDateTime"",
|
||||
DSSPES.entityid AS ""dsspesEntityID"",
|
||||
DSSPES.primaryphysicianid AS ""dsspesPhysicianId"",
|
||||
DSSPES.servicelineid AS ""dsspesPrimaryServiceLineId"",
|
||||
DSSPES.cptcodecasetypefamilyid AS ""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
DSSPES.msdrgcasetypefamilyid AS ""dsspesMSDRGCaseTypeFamilyId"",
|
||||
DSSPES.aprdrgcasetypefamilyid AS ""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
PBLID.departmentid AS ""pblidDepartmentId"",
|
||||
PHYSPRIMARY.physicianspecialtyid AS ""physPrimaryPhysicianSpecialtyId"",
|
||||
CC.chargecodeid AS ""ccChargeCodeID"",
|
||||
IFNULL(CC.costdriver::TEXT, '') AS ""ccCostDriver"",
|
||||
IFNULL(ENCOUNTERPATIENTTYPEROLLUP.patienttyperollup::TEXT, '') AS ""encounterPatientTypeRollupPatientTypeRollup"",
|
||||
SUM(COSTDETAIL.variabledirectcost) AS ""Cost"",
|
||||
NULL AS ""Units""
|
||||
|
||||
FROM dss.FactPatientCostDetail AS COSTDETAIL
|
||||
RIGHT JOIN dss.FactPatientBillingLineItemDetail AS PBLID ON PBLID.rowid = COSTDETAIL.pblidrowid
|
||||
RIGHT JOIN clientdss.FactPatientEncounterSummary AS DSSPES ON DSSPES.encounterid = PBLID.encounterid
|
||||
LEFT JOIN CCI.FACTEXPLORATIONPOPULATIONENCOUNTER AS EXPLORATIONPOPULATIONENCOUNTER ON EXPLORATIONPOPULATIONENCOUNTER.encounterid = PBLID.encounterid
|
||||
LEFT JOIN CCI.VIEWENCOUNTERPATIENTTYPEROLLUP AS ENCOUNTERPATIENTTYPEROLLUP ON ENCOUNTERPATIENTTYPEROLLUP.encounterid = PBLID.encounterid
|
||||
INNER JOIN FW.DIMCHARGECODE AS CC ON CC.chargecodeid = PBLID.chargecodeid
|
||||
INNER JOIN DSS.DIMPHYSICIAN AS PHYSPRIMARY ON PHYSPRIMARY.physicianid = DSSPES.primaryphysicianid
|
||||
|
||||
WHERE
|
||||
((DSSPES.dischargedatetime >= :dischargedatetime_Start_3 AND DSSPES.dischargedatetime <= :dischargedatetime_End_4) AND EXPLORATIONPOPULATIONENCOUNTER.explorationpopulationid = :explorationpopulationid_5)
|
||||
|
||||
GROUP BY
|
||||
""dsspesEncounterId"",
|
||||
""dsspesDischargeDateTime"",
|
||||
""dsspesEntityID"",
|
||||
""dsspesPhysicianId"",
|
||||
""dsspesPrimaryServiceLineId"",
|
||||
""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
""dsspesMSDRGCaseTypeFamilyId"",
|
||||
""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
""pblidDepartmentId"",
|
||||
""physPrimaryPhysicianSpecialtyId"",
|
||||
""ccChargeCodeID"",
|
||||
""ccCostDriver"",
|
||||
""encounterPatientTypeRollupPatientTypeRollup""
|
||||
|
||||
|
||||
)
|
||||
|
||||
GROUP BY
|
||||
""dsspesEncounterId"",
|
||||
""dsspesDischargeDateTime"",
|
||||
""dsspesEntityID"",
|
||||
""dsspesPhysicianId"",
|
||||
""dsspesPrimaryServiceLineId"",
|
||||
""dsspesCPTCodeCaseTypeFamilyId"",
|
||||
""dsspesMSDRGCaseTypeFamilyId"",
|
||||
""dsspesAPRDRGCaseTypeFamilyId"",
|
||||
""pblidDepartmentId"",
|
||||
""physPrimaryPhysicianSpecialtyId"",
|
||||
""ccChargeCodeID"",
|
||||
""ccCostDriver"",
|
||||
""encounterPatientTypeRollupPatientTypeRollup""
|
||||
|
||||
|
||||
)
|
||||
|
||||
ORDER BY
|
||||
""dsspesEncounterId"" ASC NULLS FIRST,
|
||||
""dsspesDischargeDateTime"" ASC NULLS FIRST,
|
||||
""dsspesEntityID"" ASC NULLS FIRST,
|
||||
""dsspesPhysicianId"" ASC NULLS FIRST,
|
||||
""dsspesPrimaryServiceLineId"" ASC NULLS FIRST,
|
||||
""dsspesCPTCodeCaseTypeFamilyId"" ASC NULLS FIRST,
|
||||
""dsspesMSDRGCaseTypeFamilyId"" ASC NULLS FIRST,
|
||||
""dsspesAPRDRGCaseTypeFamilyId"" ASC NULLS FIRST,
|
||||
""pblidDepartmentId"" ASC NULLS FIRST,
|
||||
""physPrimaryPhysicianSpecialtyId"" ASC NULLS FIRST,
|
||||
""ccChargeCodeID"" ASC NULLS FIRST,
|
||||
""ccCostDriver"" ASC NULLS FIRST,
|
||||
""encounterPatientTypeRollupPatientTypeRollup"" ASC NULLS FIRST";
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Queries;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.StrategicOpportunities
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Unit"), Category("CCI")]
|
||||
internal class StrategicOpportunityGLDetailQueryBuilderTest
|
||||
{
|
||||
private StrategicOpportunityGLDetailQueryBuilder _queryBuilder;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_queryBuilder = new StrategicOpportunityGLDetailQueryBuilder();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildDeleteQuery_WithOpportunityId_EmbedsIdDirectly()
|
||||
{
|
||||
var result = _queryBuilder.BuildDeleteQuery(42);
|
||||
|
||||
result.Should().Contain("OpportunityId = 42");
|
||||
result.Should().NotContain(":opportunityId");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildDeleteQuery_WithoutOpportunityId_UsesParameterWithoutTrailingSemicolon()
|
||||
{
|
||||
var result = _queryBuilder.BuildDeleteQuery(null);
|
||||
|
||||
result.Should().Contain(":opportunityId");
|
||||
result.Should().NotContain(":opportunityId;");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildInsertQuery_WithTypedAccountIds_GeneratesAccountTypeMapCte()
|
||||
{
|
||||
var revenueIds = new List<int> { 100, 101 };
|
||||
var expenseIds = new List<int> { 200 };
|
||||
var statisticIds = new List<int> { 300 };
|
||||
|
||||
var result = _queryBuilder.BuildInsertQuery(
|
||||
opportunityId: 42,
|
||||
sourceTable: "fw.factglsampled",
|
||||
departmentIds: new List<int> { 1, 2 },
|
||||
revenueAccountIds: revenueIds,
|
||||
expenseAccountIds: expenseIds,
|
||||
statisticAccountIds: statisticIds,
|
||||
baselineStart: new DateOnly(2024, 1, 1),
|
||||
baselineEnd: new DateOnly(2024, 12, 31));
|
||||
|
||||
result.Should().Contain("(100, 0)");
|
||||
result.Should().Contain("(101, 0)");
|
||||
result.Should().Contain("(200, 1)");
|
||||
result.Should().Contain("(300, 2)");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildInsertQuery_WithTypedAccountIds_IncludesAccountTypeInInsertAndSelect()
|
||||
{
|
||||
var result = _queryBuilder.BuildInsertQuery(
|
||||
opportunityId: 42,
|
||||
sourceTable: "fw.factglsampled",
|
||||
departmentIds: new List<int> { 1 },
|
||||
revenueAccountIds: new List<int> { 100 },
|
||||
expenseAccountIds: new List<int> { 200 },
|
||||
statisticAccountIds: new List<int> { 300 },
|
||||
baselineStart: new DateOnly(2024, 1, 1),
|
||||
baselineEnd: new DateOnly(2024, 12, 31));
|
||||
|
||||
result.Should().Contain("AccountType,");
|
||||
result.Should().Contain("at.AccountType,");
|
||||
result.Should().Contain("at.AccountType;");
|
||||
result.Should().Contain("JOIN (SELECT AccountId, AccountType FROM (VALUES");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildMergeQuery_WithTypedAccountIds_IncludesAccountTypeInMergeConditionAndInsert()
|
||||
{
|
||||
var result = _queryBuilder.BuildMergeQuery(
|
||||
opportunityId: 42,
|
||||
sourceTable: "fw.factglsampled",
|
||||
departmentIds: new List<int> { 1 },
|
||||
revenueAccountIds: new List<int> { 100 },
|
||||
expenseAccountIds: new List<int> { 200 },
|
||||
statisticAccountIds: new List<int> { 300 },
|
||||
trackingStart: new DateOnly(2025, 1, 1),
|
||||
trackingEnd: new DateOnly(2025, 12, 31));
|
||||
|
||||
result.Should().Contain("AND t.AccountType = s.AccountType");
|
||||
result.Should().Contain("AccountId, AccountType, Amount");
|
||||
result.Should().Contain("s.AccountType, s.Amount");
|
||||
result.Should().Contain("at.AccountType,");
|
||||
result.Should().Contain("JOIN (SELECT AccountId, AccountType FROM (VALUES");
|
||||
}
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Dtos;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Queries;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
using VerifyNUnit;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.StrategicOpportunities
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Unit"), Category("CCI")]
|
||||
internal class StrategicOpportunityGLUpdateQueryBuilderTest : StrategicOpportunityTestsBase
|
||||
{
|
||||
private StrategicOpportunityGLUpdateQueryBuilder _queryBuilder;
|
||||
|
||||
private static readonly IEnumerable<string> Rows =
|
||||
[
|
||||
StrategicOpportunityDetailDimension.System.Value,
|
||||
StrategicOpportunityDetailDimension.Entity.Value,
|
||||
StrategicOpportunityDetailDimension.Department.Value,
|
||||
StrategicOpportunityDetailDimension.GLAccount.Value,
|
||||
];
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_queryBuilder = new StrategicOpportunityGLUpdateQueryBuilder();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BuildUpdateQuery_WithFourAdjustments_GeneratesCorrectSql()
|
||||
{
|
||||
// Arrange
|
||||
const long opportunityId = 42;
|
||||
|
||||
var adjustments = new List<StrategicOpportunityGLAdjustment>
|
||||
{
|
||||
// Root-level adjustment
|
||||
new() { NodeKey = "root", GoalAmountValue = 50000 },
|
||||
// Entity-level adjustment
|
||||
new() { NodeKey = "101", GoalAmountValue = 20000 },
|
||||
// Entity + Department level: null (passthrough)
|
||||
new() { NodeKey = "101|201", GoalAmountValue = null },
|
||||
// Entity + Department + GLAccount (lowest level)
|
||||
new() { NodeKey = "101|201|3001", GoalAmountValue = 15000 },
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = _queryBuilder.BuildUpdateQuery(adjustments, opportunityId, Rows).Query;
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result, VerifySettings);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildUpdateQuery_WithAccountTypeOnSecondAdjustment_IncludesAccountTypeFilterInSubquery()
|
||||
{
|
||||
// AccountType filters are embedded in the inner SELECT WHERE for non-first adjustments
|
||||
// (the first adjustment uses an outer WHERE clause that filters by hierarchy only)
|
||||
const long opportunityId = 42;
|
||||
|
||||
var adjustments = new List<StrategicOpportunityGLAdjustment>
|
||||
{
|
||||
new() { NodeKey = "root", GoalAmountValue = 50000 },
|
||||
new() { NodeKey = "101|201|3001", GoalAmountValue = 15000, AccountType = 0 },
|
||||
};
|
||||
|
||||
var (query, parameters) = _queryBuilder.BuildUpdateQuery(adjustments, opportunityId, Rows);
|
||||
|
||||
query.Should().Contain("AccountType = :AccountType_1");
|
||||
parameters.Should().ContainKey("AccountType_1");
|
||||
parameters["AccountType_1"].Should().Be((byte)0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildUpdateQuery_WithNullAccountType_OmitsAccountTypeFilter()
|
||||
{
|
||||
const long opportunityId = 42;
|
||||
|
||||
var adjustments = new List<StrategicOpportunityGLAdjustment>
|
||||
{
|
||||
new() { NodeKey = "root", GoalAmountValue = 50000 },
|
||||
new() { NodeKey = "101|201|3001", GoalAmountValue = 15000, AccountType = null },
|
||||
};
|
||||
|
||||
var (query, parameters) = _queryBuilder.BuildUpdateQuery(adjustments, opportunityId, Rows);
|
||||
|
||||
query.Should().NotContain("AccountType = :");
|
||||
parameters.Should().NotContainKey("AccountType_1");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildUpdateQuery_WithInvalidOpportunityId_ThrowsArgumentException()
|
||||
{
|
||||
var adjustments = new List<StrategicOpportunityGLAdjustment>
|
||||
{
|
||||
new() { NodeKey = "101", GoalAmountValue = 1000 },
|
||||
};
|
||||
|
||||
var act = () => _queryBuilder.BuildUpdateQuery(adjustments, opportunityId: 0, Rows);
|
||||
|
||||
act.Should().Throw<ArgumentException>().WithParameterName("opportunityId");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildUpdateQuery_WithEmptyNodeKey_ThrowsArgumentException()
|
||||
{
|
||||
var adjustments = new List<StrategicOpportunityGLAdjustment>
|
||||
{
|
||||
new() { NodeKey = "", GoalAmountValue = 1000 },
|
||||
};
|
||||
|
||||
var act = () => _queryBuilder.BuildUpdateQuery(adjustments, opportunityId: 42, Rows);
|
||||
|
||||
act.Should().Throw<ArgumentException>().WithParameterName("nodeKey");
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
using NUnit.Framework;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Strata.ContinuousImprovement.Biz.Test.Unit.Utilities;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VerifyTests;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.StrategicOpportunities
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture]
|
||||
public class StrategicOpportunityTestsBase
|
||||
{
|
||||
protected VerifySettings VerifySettings { get; set; }
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void TestFixtureSetup()
|
||||
{
|
||||
VerifySettings = TestExtensions.TestSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
using NUnit.Framework;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Dtos;
|
||||
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Queries;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
using VerifyNUnit;
|
||||
|
||||
namespace Strata.ContinuousImprovement.Biz.Test.Unit.StrategicOpportunities
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
[TestFixture, Category("Unit"), Category("CCI")]
|
||||
internal class StrategicOpportunityUpdateQueryBuilderTest : StrategicOpportunityTestsBase
|
||||
{
|
||||
private StrategicOpportunityUpdateQueryBuilder _queryBuilder;
|
||||
|
||||
private static readonly IEnumerable<string> Rows =
|
||||
[
|
||||
StrategicOpportunityDetailDimension.System.Value,
|
||||
StrategicOpportunityDetailDimension.Entity.Value,
|
||||
StrategicOpportunityDetailDimension.Department.Value,
|
||||
StrategicOpportunityDetailDimension.ChargeCode.Value,
|
||||
];
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_queryBuilder = new StrategicOpportunityUpdateQueryBuilder();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BuildUpdateQuery_WithFourAdjustments_GeneratesCorrectSql()
|
||||
{
|
||||
// Arrange
|
||||
const long opportunityId = 42;
|
||||
|
||||
var adjustments = new List<StrategicOpportunityChargeCodeAdjustment>
|
||||
{
|
||||
// Root-level adjustment: both units and costs
|
||||
new() { NodeKey = "root", UnitsValue = 1000m, CostsValue = 50000m },
|
||||
// Entity-level adjustment: costs only
|
||||
new() { NodeKey = "101", UnitsValue = null, CostsValue = 20000m },
|
||||
// Entity + Department level: units only
|
||||
new() { NodeKey = "101|201", UnitsValue = 300m, CostsValue = null },
|
||||
// Entity + Department + ChargeCode (lowest level): both units and costs
|
||||
new() { NodeKey = "101|201|3001", UnitsValue = 75m, CostsValue = 5000m },
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = _queryBuilder.BuildUpdateQuery(adjustments, opportunityId, Rows).Query;
|
||||
|
||||
// Assert
|
||||
await Verifier.Verify(result, VerifySettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user