diff --git a/backend/Tnsu.ApprovalRoute.Application/TemplateRoutes/ResolveTemplateRouteDto.cs b/backend/Tnsu.ApprovalRoute.Application/TemplateRoutes/ResolveTemplateRouteDto.cs
index 1aba714..2029871 100644
--- a/backend/Tnsu.ApprovalRoute.Application/TemplateRoutes/ResolveTemplateRouteDto.cs
+++ b/backend/Tnsu.ApprovalRoute.Application/TemplateRoutes/ResolveTemplateRouteDto.cs
@@ -23,10 +23,18 @@ public sealed record ResolvedRouteStepDto(
int? SlaHours = null,
bool? AskOverdueReason = null);
+///
+/// Предупреждение уровня всего резолва (не привязано к конкретному шагу) — например,
+/// в маршруте есть шаг с условием по полю IsManager, а в заявке не передано значение
+/// поля Position, из которого IsManager вычисляется. Это не ошибка (резолв всё равно
+/// отработал, IsManager просто посчитался как «false»), но может означать, что
+/// вызывающая сторона забыла передать поле, и шаг из-за этого не появился, хотя должен был.
+///
public sealed record ResolveTemplateRouteResultDto(
int RouteId,
bool RequiresControl,
- IReadOnlyList Steps);
+ IReadOnlyList Steps,
+ string? Warning = null);
/// Значение поля списка SharePoint (field_name шага) для проверки условий шагов при resolve.
public sealed record ResolveFieldValueDto(string FieldName, string Value);
diff --git a/backend/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteService.cs b/backend/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteService.cs
index d29eea0..dd5dd0d 100644
--- a/backend/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteService.cs
+++ b/backend/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteService.cs
@@ -163,10 +163,29 @@ 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";
+ // Синтетическое поле условия — переопределяет то, что мог прислать вызывающий
+ // код под тем же именем. Считаем НЕ по должности инициатора из справочника
+ // api.tnsu.kz (employee.Position — она у самого сотрудника чаще всего не
+ // «руководитель»), а по полю Position, заполненному В САМОЙ АНКЕТЕ (например,
+ // «Должность» в Обходном листе — там указывают должность увольняемого, а не
+ // того, кто отправил заявку). Это свободный текст, поэтому определяем
+ // «руководитель или нет» по ключевым словам в нём. Нет такого поля в заявке —
+ // считаем «нет» (а не подставляем должность инициатора как фолбэк).
+ fieldValues.TryGetValue("Position", out var formPosition);
+ fieldValues["IsManager"] = IsManagerPosition(formPosition) ? "true" : "false";
+
+ // Предупреждение уровня резолва, а не шага: условие по IsManager есть в маршруте
+ // (хотя бы на одном шаге, независимо от статуса — фильтр по s.Status ниже сузил бы
+ // проверку до конкретной фазы, а «забыли передать Position» это не про фазу), но
+ // Position в заявке не пришёл вовсе. Если поле пришло, но пустой строкой или не
+ // похоже на руководящую должность — это осознанный «false», предупреждать не о чем.
+ string? generalWarning = null;
+ if (string.IsNullOrWhiteSpace(formPosition) &&
+ route.Steps.Any(s => string.Equals(s.FieldName, "IsManager", StringComparison.OrdinalIgnoreCase)))
+ {
+ generalWarning = "В маршруте есть шаг с условием по полю «IsManager», но в заявке не передано значение " +
+ "поля «Position» (должность) — условие считается невыполненным.";
+ }
var matchingSteps = route.Steps
.Where(s => s.Status == request.Status)
@@ -181,7 +200,7 @@ public sealed class TemplateRouteService(
var collapsedSteps = CollapseByExecutorEmail(steps);
- return Result.Ok(new ResolveTemplateRouteResultDto(route.Id, route.RequiresControl, collapsedSteps));
+ return Result.Ok(new ResolveTemplateRouteResultDto(route.Id, route.RequiresControl, collapsedSteps, generalWarning));
}
///
diff --git a/frontend/src/webparts/approvalAdmin/components/screens/RouteStepFormModal.tsx b/frontend/src/webparts/approvalAdmin/components/screens/RouteStepFormModal.tsx
index 80b73fb..c8787f2 100644
--- a/frontend/src/webparts/approvalAdmin/components/screens/RouteStepFormModal.tsx
+++ b/frontend/src/webparts/approvalAdmin/components/screens/RouteStepFormModal.tsx
@@ -78,8 +78,10 @@ const OPERATION_OPTIONS = Object.values(ComparisonOperator)
/**
* Служебное поле условия, не привязанное к списку SharePoint — вычисляется на бэкенде
- * из должности инициатора (см. TemplateRouteService.ResolveAsync), а не из данных заявки.
- * Поэтому оно доступно всегда, даже если у типа заявки не настроен list_name.
+ * (см. TemplateRouteService.ResolveAsync) по значению поля Position В САМОЙ ЗАЯВКЕ
+ * («Должность» — например, у увольняемого в обходном листе), а не по должности того,
+ * кто отправил заявку. Поэтому поле доступно всегда, даже если у типа заявки не
+ * настроен list_name — источник данных не сам список, а fieldValues resolve-запроса.
*/
const IS_MANAGER_FIELD = 'IsManager';
const SYNTHETIC_FIELD_OPTIONS = [{ value: IS_MANAGER_FIELD, label: texts.routeSteps.conditionFieldIsManagerLabel }];
diff --git a/frontend/src/webparts/approvalAdmin/texts.ts b/frontend/src/webparts/approvalAdmin/texts.ts
index 7a010d2..0ff23df 100644
--- a/frontend/src/webparts/approvalAdmin/texts.ts
+++ b/frontend/src/webparts/approvalAdmin/texts.ts
@@ -236,7 +236,7 @@ export const texts = {
conditionCheckbox: 'Показывать шаг только при выполнении условия',
conditionDisabledHint:
'У типа заявки не указан список SharePoint (list_name) — доступно только служебное поле «Инициатор — руководитель».',
- conditionFieldIsManagerLabel: 'Инициатор — руководитель (IsManager)',
+ conditionFieldIsManagerLabel: 'Должность в заявке — руководящая (IsManager)',
conditionFieldNameLabel: 'Поле списка',
conditionFieldNamePlaceholder: 'Выберите столбец списка',
conditionFieldNameLoading: 'Загрузка столбцов списка...',