Краткие тексты предупреждений: «Не найден руководитель направления»
Tests / frontend-test (pull_request) Successful in 1m37s
Tests / backend-test (pull_request) Successful in 2m22s

Вместо длинных пояснений — короткие фразы: «Не найден руководитель
направления.», «В проекте не найден руководитель проекта.», «Не проставлен
директор Tansu.». Для роли «Безопасность» — «сотрудник службы
безопасности», чтобы не получилось «найден безопасность».

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
gorbenko.pavel
2026-10-16 10:44:21 +05:00
parent 547c51ca3c
commit 2eccd304bb
3 changed files with 18 additions and 6 deletions
@@ -20,6 +20,18 @@ public static class StepLabels
_ => headType.ToString() _ => headType.ToString()
}; };
/// <summary>Название с маленькой буквы — для середины фразы («Не найден руководитель направления»).</summary>
public static string InSentence(HeadType headType) => Lower(Of(headType));
/// <summary>
/// Все названия мужского рода («найден руководитель проекта»), кроме «Безопасность» —
/// для неё нужна отдельная формулировка, иначе выйдет «найден безопасность».
/// </summary>
public static string InSentence(ProjectRole projectRole) =>
projectRole == ProjectRole.Security ? "сотрудник службы безопасности" : Lower(Of(projectRole));
private static string Lower(string label) => char.ToLowerInvariant(label[0]) + label[1..];
public static string Of(ProjectRole projectRole) => projectRole switch public static string Of(ProjectRole projectRole) => projectRole switch
{ {
ProjectRole.ProjectManager => "Руководитель проекта", ProjectRole.ProjectManager => "Руководитель проекта",
@@ -438,7 +438,7 @@ public sealed class TemplateRouteService(
{ {
resolvedEmail = projectExecutors.FirstOrDefault(e => e.ProjectRole == step.ProjectRole)?.Email; resolvedEmail = projectExecutors.FirstOrDefault(e => e.ProjectRole == step.ProjectRole)?.Email;
if (string.IsNullOrWhiteSpace(resolvedEmail)) if (string.IsNullOrWhiteSpace(resolvedEmail))
warning = $"Не найден исполнитель с ролью в проекте «{StepLabels.Of(step.ProjectRole.Value)}»."; warning = $"В проекте не найден {StepLabels.InSentence(step.ProjectRole.Value)}.";
} }
// DepartmentHead — единственный HeadType, который резолвится НЕ по цепочке // DepartmentHead — единственный HeadType, который резолвится НЕ по цепочке
// подчинённости инициатора, а по отделу, переданному явно в запросе (см. // подчинённости инициатора, а по отделу, переданному явно в запросе (см.
@@ -471,12 +471,12 @@ public sealed class TemplateRouteService(
}; };
if (string.IsNullOrWhiteSpace(resolvedEmail)) if (string.IsNullOrWhiteSpace(resolvedEmail))
{ {
var headLabel = StepLabels.Of(step.HeadType.Value); var headLabel = StepLabels.InSentence(step.HeadType.Value);
// Директор Tansu и управляющий директор берутся из настроек сервиса, а не из // Директор Tansu и управляющий директор берутся из настроек сервиса, а не из
// справочника сотрудников — там email не «не найден», а «не проставлен». // справочника сотрудников — там email не «не найден», а «не проставлен».
warning = step.HeadType is HeadType.DirectorTansu or HeadType.ManagingDirectorTansu warning = step.HeadType is HeadType.DirectorTansu or HeadType.ManagingDirectorTansu
? $"Не проставлен «{headLabel}»: email не задан в настройках сервиса." ? $"Не проставлен {headLabel}."
: $"Не найден руководитель «{headLabel}»: не указан у инициатора в справочнике сотрудников."; : $"Не найден {headLabel}.";
} }
} }
else else
@@ -1051,7 +1051,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
resolved.Value.Steps[0].ExecutorEmail.ShouldBeNull(); resolved.Value.Steps[0].ExecutorEmail.ShouldBeNull();
var warning = resolved.Value.Steps[0].Warning; var warning = resolved.Value.Steps[0].Warning;
warning.ShouldNotBeNull(); warning.ShouldNotBeNull();
warning.ShouldContain("руководитель"); warning.ShouldBe("Не найден непосредственный руководитель.");
} }
[Fact(DisplayName = "Резолв шага с должностью возвращает warning, если сотрудник по должности не найден")] [Fact(DisplayName = "Резолв шага с должностью возвращает warning, если сотрудник по должности не найден")]
@@ -1109,7 +1109,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
resolved.Value.Steps[0].ExecutorEmail.ShouldBeNull(); resolved.Value.Steps[0].ExecutorEmail.ShouldBeNull();
var warning = resolved.Value.Steps[0].Warning; var warning = resolved.Value.Steps[0].Warning;
warning.ShouldNotBeNull(); warning.ShouldNotBeNull();
warning.ShouldContain("роль"); warning.ShouldBe("В проекте не найден сотрудник службы безопасности.");
} }
[Fact(DisplayName = "Резолв маршрута откатывается на маршрут без отдела, если точного совпадения по отделу нет")] [Fact(DisplayName = "Резолв маршрута откатывается на маршрут без отдела, если точного совпадения по отделу нет")]