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>
42 lines
1.9 KiB
C#
42 lines
1.9 KiB
C#
using Tnsu.ApprovalRoute.Domain.Enums;
|
|
|
|
namespace Tnsu.ApprovalRoute.Domain.Entities;
|
|
|
|
public class TemplateRouteStep
|
|
{
|
|
public int Id { get; set; }
|
|
public int OrderNo { get; set; }
|
|
|
|
public string? ExecutorEmail { get; set; }
|
|
public string? Position { get; set; }
|
|
public ProjectRole? ProjectRole { get; set; }
|
|
public HeadType? HeadType { get; set; }
|
|
|
|
public RouteStepStatus Status { get; set; } = RouteStepStatus.Approval;
|
|
public int RouteId { get; set; }
|
|
public bool AllowReassignToOtherApprovers { get; set; }
|
|
|
|
/// <summary>
|
|
/// SLA этого шага в часах — перекрывает <see cref="RequestType.SlaHours"/>.
|
|
/// null означает ИМЕННО «не задано, наследовать от типа заявки», а 0 —
|
|
/// осознанное «напоминать сразу», поэтому поле обязано быть nullable и не
|
|
/// должно получать значение по умолчанию.
|
|
/// </summary>
|
|
public int? SlaHours { get; set; }
|
|
|
|
/// <summary>
|
|
/// Спрашивать ли причину просрочки на этом шаге — перекрывает
|
|
/// <see cref="RequestType.AskOverdueReason"/>. Здесь тоже три состояния:
|
|
/// null — наследовать от типа, true/false — явный выбор.
|
|
/// </summary>
|
|
public bool? AskOverdueReason { get; set; }
|
|
|
|
/// <summary>Как объединяются <see cref="Conditions"/> между собой — не действует, если условий меньше двух.</summary>
|
|
public ConditionsLogic ConditionsLogic { get; set; } = ConditionsLogic.And;
|
|
|
|
/// <summary>Условия появления шага. Пусто — шаг применяется всегда.</summary>
|
|
public List<TemplateRouteStepCondition> Conditions { get; set; } = [];
|
|
|
|
public TemplateRoute? Route { get; set; }
|
|
}
|