Files
tnsu-approval-route/backend/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepDto.cs
T
gorbenko.pavel 10c3057eff Несколько условий шага, объединяемых через И/ИЛИ
Раньше шаг маршрута мог нести ровно одно условие (field_name/operation/
value). Теперь условий может быть сколько угодно, а их объединение задаётся
явно — ConditionsLogic.And (все условия) или .Or (любое из условий).

Бэкенд:
- Новая сущность TemplateRouteStepCondition (дочерняя таблица
  template_route_step_conditions, FK на шаг, cascade delete) вместо трёх
  колонок на TemplateRouteStep. Миграция AddMultipleStepConditions переносит
  существующие условия шагов в новую таблицу перед удалением старых колонок,
  чтобы не потерять уже настроенные в деве/проде условия.
- TemplateRouteService.MatchesConditions: несколько условий шага объединяются
  по ConditionsLogic (Any/All); HasUnevaluatableManagerCondition проверяет
  условие на IsManager в списке условий, а не единственное поле.
- TemplateRouteStepService: дубликаты шагов теперь сравниваются по всему
  набору условий (без учёта порядка) и, если условий два и более, по логике
  их объединения — сравнение в памяти, а не в SQL, кандидатов на дубликат
  в пределах маршрута всегда мало.
- Валидация условия: раньше «все три поля или ни одного», теперь — в каждом
  элементе списка все три поля обязательны.

Фронтенд:
- RouteStepFormModal: условие — динамический список строк (добавить/удалить),
  у каждой строки свой набор допустимых операторов по типу колонки; селектор
  И/ИЛИ появляется, когда условий два и больше.
- describeStepCondition в таблице шагов склеивает все условия через « И »/« ИЛИ ».

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 09:56:36 +05:00

72 lines
2.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Tnsu.ApprovalRoute.Domain.Enums;
namespace Tnsu.ApprovalRoute.Application.TemplateRouteSteps;
// SlaHours/AskOverdueReason у шага перекрывают одноимённые поля типа заявки.
// null — «не задано, наследовать», поэтому оба поля nullable и необязательны
// (0 часов и false — это осознанный выбор, а не «пусто»). Добавлены в конец
// записей, чтобы не ломать существующие позиционные вызовы.
/// <summary>Одно условие появления шага — см. Domain.Entities.TemplateRouteStepCondition.</summary>
public sealed record RouteStepConditionDto(string FieldName, ComparisonOperator Operation, string Value);
public sealed record TemplateRouteStepDto(
int Id,
int OrderNo,
string? ExecutorEmail,
string? Position,
ProjectRole? ProjectRole,
HeadType? HeadType,
RouteStepStatus Status,
int RouteId,
bool AllowReassignToOtherApprovers,
IReadOnlyList<RouteStepConditionDto> Conditions,
ConditionsLogic ConditionsLogic,
string? Warning = null,
int? SlaHours = null,
bool? AskOverdueReason = null);
public sealed record CreateTemplateRouteStepRequest(
int OrderNo,
string? ExecutorEmail,
string? Position,
ProjectRole? ProjectRole,
HeadType? HeadType,
RouteStepStatus Status,
bool AllowReassignToOtherApprovers,
IReadOnlyList<RouteStepConditionDto>? Conditions,
ConditionsLogic ConditionsLogic = ConditionsLogic.And,
int? SlaHours = null,
bool? AskOverdueReason = null);
public sealed record UpdateTemplateRouteStepRequest(
int OrderNo,
string? ExecutorEmail,
string? Position,
ProjectRole? ProjectRole,
HeadType? HeadType,
RouteStepStatus Status,
bool AllowReassignToOtherApprovers,
IReadOnlyList<RouteStepConditionDto>? Conditions,
ConditionsLogic ConditionsLogic = ConditionsLogic.And,
int? SlaHours = null,
bool? AskOverdueReason = null);
/// <summary>
/// Как и остальные поля PATCH, Conditions=null означает «не менять». Отличить
/// «не менять» от «убрать все условия» здесь нельзя (тот же приём, что и раньше
/// для FieldName) — очистка условий делается через PUT с Conditions: [].
/// </summary>
public sealed record PatchTemplateRouteStepRequest(
int? OrderNo,
string? ExecutorEmail,
string? Position,
ProjectRole? ProjectRole,
HeadType? HeadType,
RouteStepStatus? Status,
bool? AllowReassignToOtherApprovers,
IReadOnlyList<RouteStepConditionDto>? Conditions,
ConditionsLogic? ConditionsLogic = null,
int? SlaHours = null,
bool? AskOverdueReason = null);