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>
72 lines
2.8 KiB
C#
72 lines
2.8 KiB
C#
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);
|