Несколько условий шага, объединяемых через И/ИЛИ #16

Merged
gorbenko.pavel merged 5 commits from several-ifs into main 2026-08-25 07:04:44 +00:00
4 changed files with 21 additions and 3 deletions
Showing only changes of commit ca8de91742 - Show all commits
@@ -13,6 +13,13 @@ namespace Tnsu.ApprovalRoute.Application.TemplateRoutes;
/// <param name="AskOverdueReason"> /// <param name="AskOverdueReason">
/// Спрашивать ли причину просрочки — так же с уже применённым наследованием. /// Спрашивать ли причину просрочки — так же с уже применённым наследованием.
/// </param> /// </param>
/// <param name="PositionCode">
/// HeadType шага (Chief/DirectionHead/DirectorTansu/ManagingDirectorTansu/DepartmentHead),
/// если исполнитель задан типом руководителя, иначе null. В отличие от <see cref="Position"/>
/// (реальная должность резолвнутого сотрудника из справочника api.tnsu.kz — свободный
/// текст), это код роли из самого шага маршрута — чтобы вызывающая сторона могла отличить,
/// например, «директора Tansu» от «руководителя направления», не разбирая Position текстом.
/// </param>
public sealed record ResolvedRouteStepDto( public sealed record ResolvedRouteStepDto(
int Id, int Id,
int OrderNo, int OrderNo,
@@ -21,7 +28,8 @@ public sealed record ResolvedRouteStepDto(
bool AllowReassignToOtherApprovers, bool AllowReassignToOtherApprovers,
string? Warning = null, string? Warning = null,
int? SlaHours = null, int? SlaHours = null,
bool? AskOverdueReason = null); bool? AskOverdueReason = null,
HeadType? PositionCode = null);
public sealed record ResolveTemplateRouteResultDto( public sealed record ResolveTemplateRouteResultDto(
int RouteId, int RouteId,
@@ -340,7 +340,8 @@ public sealed class TemplateRouteService(
step.AllowReassignToOtherApprovers, step.AllowReassignToOtherApprovers,
combinedWarning.Length == 0 ? null : combinedWarning, combinedWarning.Length == 0 ? null : combinedWarning,
step.SlaHours ?? requestType.SlaHours, step.SlaHours ?? requestType.SlaHours,
step.AskOverdueReason ?? requestType.AskOverdueReason); step.AskOverdueReason ?? requestType.AskOverdueReason,
step.HeadType is null or HeadType.None ? null : step.HeadType);
} }
/// <summary> /// <summary>
@@ -182,6 +182,8 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
resolved.Value.RouteId.ShouldBe(route.Value.Id); resolved.Value.RouteId.ShouldBe(route.Value.Id);
resolved.Value.Steps.ShouldHaveSingleItem(); resolved.Value.Steps.ShouldHaveSingleItem();
resolved.Value.Steps[0].ExecutorEmail.ShouldBe("approver@tnsu.kz"); resolved.Value.Steps[0].ExecutorEmail.ShouldBe("approver@tnsu.kz");
// Исполнитель задан email'ом, а не типом руководителя — PositionCode пуст.
resolved.Value.Steps[0].PositionCode.ShouldBeNull();
} }
// SLA приезжает в резолв уже с применённым наследованием — это осознанное // SLA приезжает в резолв уже с применённым наследованием — это осознанное
@@ -704,6 +706,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
resolved.IsSuccess.ShouldBeTrue(); resolved.IsSuccess.ShouldBeTrue();
resolved.Value.Steps.ShouldHaveSingleItem(); resolved.Value.Steps.ShouldHaveSingleItem();
resolved.Value.Steps[0].ExecutorEmail.ShouldBe("chief@tnsu.kz"); resolved.Value.Steps[0].ExecutorEmail.ShouldBe("chief@tnsu.kz");
resolved.Value.Steps[0].PositionCode.ShouldBe(HeadType.Chief);
} }
[Fact(DisplayName = "Резолв шага с типом руководителя DirectorTansu подставляет захардкоженный email из настроек")] [Fact(DisplayName = "Резолв шага с типом руководителя DirectorTansu подставляет захардкоженный email из настроек")]
@@ -2,7 +2,7 @@
// (Application/TemplateRoutes/ResolveTemplateRouteDto.cs). // (Application/TemplateRoutes/ResolveTemplateRouteDto.cs).
/* eslint-disable @rushstack/no-new-null -- поля 1:1 зеркалят nullable JSON-контракт бэкенда */ /* eslint-disable @rushstack/no-new-null -- поля 1:1 зеркалят nullable JSON-контракт бэкенда */
import { RouteStepStatus } from './enums'; import { HeadType, RouteStepStatus } from './enums';
/** Значение поля списка SharePoint (field_name шага) для проверки условий шагов. */ /** Значение поля списка SharePoint (field_name шага) для проверки условий шагов. */
export interface ResolveFieldValue { export interface ResolveFieldValue {
@@ -28,6 +28,12 @@ export interface ResolvedRouteStep {
allowReassignToOtherApprovers: boolean; allowReassignToOtherApprovers: boolean;
/** Причина, по которой исполнитель шага не определился (текст с бэкенда). */ /** Причина, по которой исполнитель шага не определился (текст с бэкенда). */
warning: string | null; warning: string | null;
/**
* HeadType шага (Chief/DirectionHead/...), если исполнитель задан типом
* руководителя, иначе null. В отличие от position (реальная должность
* резолвнутого сотрудника из справочника) — это код роли из самого шага.
*/
positionCode: HeadType | null;
} }
export interface ResolveTemplateRouteResult { export interface ResolveTemplateRouteResult {