добавлен новый тип менеджера и условие новое
Tests / frontend-test (pull_request) Successful in 43s
Tests / backend-test (pull_request) Successful in 4m0s

This commit is contained in:
gorbenko.pavel
2026-08-13 12:16:22 +05:00
parent d076f4e98d
commit f4542c7b39
7 changed files with 74 additions and 13 deletions
@@ -34,6 +34,7 @@
"ClientId": "9232d9e2-9807-4753-808f-07250e5474b0",
"ClientSecret": "",
"Scope": "",
"DirectorTansuEmail": "a.samayeva@tnsu.kz"
"DirectorTansuEmail": "a.samayeva@tnsu.kz",
"ManagingDirectorTansuEmail": "nkim@tnsu.kz"
}
}
@@ -163,6 +163,11 @@ public sealed class TemplateRouteService(
.GroupBy(f => f.FieldName, StringComparer.OrdinalIgnoreCase)
.ToDictionary(g => g.Key, g => g.First().Value, StringComparer.OrdinalIgnoreCase);
// Синтетическое поле условия (не пришедшее из request.FieldValues, а вычисленное
// здесь): должность в справочнике — свободный текст, поэтому «руководитель или
// нет» определяем по ключевым словам в её названии, а не по отдельному флагу.
fieldValues["IsManager"] = IsManagerPosition(employee.Position) ? "true" : "false";
var matchingSteps = route.Steps
.Where(s => s.Status == request.Status)
.Where(s => MatchesCondition(s, fieldValues))
@@ -264,6 +269,7 @@ public sealed class TemplateRouteService(
HeadType.Chief => await portalClient.GetManagerAsync(initiatorEmail, "chief", cancellationToken),
HeadType.DirectionHead => await portalClient.GetManagerAsync(initiatorEmail, "directionHead", cancellationToken),
HeadType.DirectorTansu => portalOptions.Value.DirectorTansuEmail,
HeadType.ManagingDirectorTansu => portalOptions.Value.ManagingDirectorTansuEmail,
_ => null
};
if (string.IsNullOrWhiteSpace(resolvedEmail))
@@ -328,6 +334,21 @@ public sealed class TemplateRouteService(
private static bool TryParseNumber(string? value, out double number) =>
double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out number);
/// <summary>
/// Должность в справочнике api.tnsu.kz — свободный текст без структуры (нет
/// отдельного флага «это руководитель»), поэтому определяем это по ключевым
/// словам в названии должности. Список подобран по фактическим должностям
/// в справочнике — Начальник/Руководитель/Директор/Заместитель/Главный/
/// Заведующий складом. «Ведущий», «Старший» и «Менеджер» намеренно не входят:
/// это старший специалист, а не должность с подчинёнными.
/// </summary>
private static readonly string[] ManagerPositionKeywords =
["начальник", "руководитель", "директор", "заместитель", "главный", "заведующий склад"];
private static bool IsManagerPosition(string? position) =>
!string.IsNullOrWhiteSpace(position) &&
ManagerPositionKeywords.Any(keyword => position.Contains(keyword, StringComparison.OrdinalIgnoreCase));
private static IReadOnlyList<ResolvedRouteStepDto> CollapseByExecutorEmail(IReadOnlyList<ResolvedRouteStepDto> steps)
{
var seenEmails = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
@@ -6,6 +6,7 @@ public enum HeadType
Chief,
DirectionHead,
DirectorTansu,
ManagingDirectorTansu,
/// <summary>
/// Руководитель отдела, указанного явно в запросе на resolve (см.
@@ -8,4 +8,5 @@ public sealed class TnsuPortalOptions
public string ClientSecret { get; set; } = string.Empty;
public string Scope { get; set; } = string.Empty;
public string DirectorTansuEmail { get; set; } = "director.tansu@tnsu.kz";
public string ManagingDirectorTansuEmail { get; set; } = "nkim@tnsu.kz";
}