Схлопывание шагов резолва: PositionCode не теряется, конфликт — с предупреждением
CollapseByExecutorEmail просто выкидывал дубликаты по email, оставляя первую строку как есть — если у неё PositionCode был пуст (исполнитель задан email/должностью), а у отброшенного дубликата задан (пришёл через HeadType), тот терялся молча. Теперь: PositionCode пустой у оставленной строки — переносится с дубликата. Задан у обеих и они РАЗНЫЕ (один и тот же человек получен через два несовместимых HeadType, например Chief и DirectionHead одновременно) — оставляем первый попавшийся и добавляем предупреждение с обоими значениями, а не выбираем молча. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+55
@@ -306,10 +306,65 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
||||
resolved.Value.Steps.Count.ShouldBe(2);
|
||||
resolved.Value.Steps[0].ExecutorEmail.ShouldBe("dup@tnsu.kz");
|
||||
resolved.Value.Steps[0].OrderNo.ShouldBe(1);
|
||||
// Схлопнутый шаг #1 задан по email (PositionCode пуст), но дубликат — шаг #3
|
||||
// по HeadType.Chief — принёс PositionCode. Он не должен потеряться при схлопывании.
|
||||
resolved.Value.Steps[0].PositionCode.ShouldBe(HeadType.Chief);
|
||||
resolved.Value.Steps[1].ExecutorEmail.ShouldBe("other@tnsu.kz");
|
||||
resolved.Value.Steps[1].OrderNo.ShouldBe(2);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Резолв маршрута: один email через два разных HeadType — предупреждение, PositionCode первого из них")]
|
||||
public async Task ResolveAsync_StepsResolveToSameEmailWithDifferentPositionCode_AddsWarningAndKeepsFirst()
|
||||
{
|
||||
// Arrange — оба типа руководителя резолвятся в один и тот же email.
|
||||
var portalClient = new Mock<ITnsuPortalClient>();
|
||||
portalClient
|
||||
.Setup(x => x.GetEmployeeByEmailAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new List<EmployeeDirectoryInfo> { new(CompanyId, DepartmentId, "ТОО TANSU Construction", "Тестовая должность") });
|
||||
portalClient
|
||||
.Setup(x => x.GetManagerAsync(It.IsAny<string>(), "chief", It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync("same@tnsu.kz");
|
||||
portalClient
|
||||
.Setup(x => x.GetManagerAsync(It.IsAny<string>(), "directionHead", It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync("same@tnsu.kz");
|
||||
|
||||
var services = TestServiceProviderFactory.Build(_connectionString, portalClient: portalClient.Object);
|
||||
var routeService = services.GetRequiredService<TemplateRouteService>();
|
||||
var requestTypeService = services.GetRequiredService<RequestTypeService>();
|
||||
var stepService = services.GetRequiredService<TemplateRouteStepService>();
|
||||
|
||||
var requestTypeId = (await requestTypeService.CreateAsync(
|
||||
new CreateRequestTypeRequest("Collapse conflicting position code request", "/collapse-conflicting-position-code", null, null, "General"),
|
||||
CancellationToken.None)).Value.Id;
|
||||
var route = await routeService.CreateAsync(
|
||||
new CreateTemplateRouteRequest(requestTypeId, DepartmentId, CompanyId, ByProject: false, RequiresControl: false),
|
||||
CancellationToken.None);
|
||||
|
||||
await stepService.CreateAsync(
|
||||
route.Value.Id,
|
||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null),
|
||||
CancellationToken.None);
|
||||
await stepService.CreateAsync(
|
||||
route.Value.Id,
|
||||
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.DirectionHead, RouteStepStatus.Approval, false, null),
|
||||
CancellationToken.None);
|
||||
|
||||
// Action
|
||||
var resolved = await routeService.ResolveAsync(
|
||||
new ResolveTemplateRouteRequest(requestTypeId, "someone@tnsu.kz", null, RouteStepStatus.Approval),
|
||||
CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
resolved.IsSuccess.ShouldBeTrue();
|
||||
resolved.Value.Steps.ShouldHaveSingleItem();
|
||||
resolved.Value.Steps[0].ExecutorEmail.ShouldBe("same@tnsu.kz");
|
||||
resolved.Value.Steps[0].PositionCode.ShouldBe(HeadType.Chief);
|
||||
var warning = resolved.Value.Steps[0].Warning;
|
||||
warning.ShouldNotBeNull();
|
||||
warning.ShouldContain("Chief");
|
||||
warning.ShouldContain("DirectionHead");
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Резолв маршрута с шагом «Руководитель указанного отдела» без departmentId в запросе — маршрут формируется, шаг с предупреждением")]
|
||||
public async Task ResolveAsync_DepartmentHeadStepWithoutDepartmentId_ReturnsWarningAndNullExecutor()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user