Напоминания: убрать дубль ManagerEmail, ссылка из настроек; None первым во всех enum
Напоминания: - ProbationEndingReminderDto: ManagerEmail убран — это был унаследованный ReceiverEmail под вторым именем. - Ссылка для ProbationEnding больше не ведёт на саму заявку на приём (она уже закрыта, решать по ней нечего) — берётся из новой настройки RemindersOptions.ProbationReminderUrl и ведёт в раздел, где заводят новую заявку. Подстановок в ней нет: адрес конкретной формы в портал не передать, rptype без rpid/rpsource игнорируется (см. parseRoute в request-portal). Заодно ушли BuildProbationUrl и ненужные больше параметры type/listId. Стайлгайд (CLAUDE.md) — у enum первое значение None: - ReminderFeed: None=0 трактуется как ApprovalOverdue, чтобы вызов без явного выбора работал как раньше. - EmploymentStatus: заодно инвертирован switch в ReminderService — раньше «продолжить» было веткой по умолчанию, и новый None молча означал бы «сотрудник работает». Теперь по умолчанию пропуск с предупреждением. - ConditionsLogic: None — НЕвалидное значение, отвергается валидаторами шага (в том числе новым PatchTemplateRouteStepRequestValidator, его не было вовсе). Поэтому у ConditionsLogic в Create/Update-запросах убрано значение по умолчанию: System.Text.Json в .NET 10 уважает дефолты параметров конструктора, и пропущенное поле молча означало «И». Теперь пропущенное поле даёт None и упирается в валидатор. ЛОМАЮЩЕЕ ИЗМЕНЕНИЕ КОНТРАКТА: клиент, не присылающий conditionsLogic в POST/PUT шага, получит 400 вместо прежнего молчаливого «И». Админка не затронута — она это поле всегда отправляет.
This commit is contained in:
@@ -4,14 +4,21 @@ namespace Tnsu.ApprovalRoute.Application.Reminders;
|
|||||||
|
|
||||||
internal enum EmploymentStatus
|
internal enum EmploymentStatus
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Статус не определяли. EmploymentOf такого не возвращает — значение существует
|
||||||
|
/// только как значение по умолчанию (default(EmploymentStatus)); трактуется
|
||||||
|
/// наравне с <see cref="NotFound"/>, то есть «трудоустройство не подтверждено».
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
/// <summary>Сотрудника с таким ФИО в справочнике нет вовсе — трудоустройство не подтверждено.</summary>
|
/// <summary>Сотрудника с таким ФИО в справочнике нет вовсе — трудоустройство не подтверждено.</summary>
|
||||||
NotFound,
|
NotFound = 1,
|
||||||
|
|
||||||
/// <summary>Работает хотя бы в одной компании.</summary>
|
/// <summary>Работает хотя бы в одной компании.</summary>
|
||||||
Employed,
|
Employed = 2,
|
||||||
|
|
||||||
/// <summary>Есть в справочнике, но уволен из всех компаний, где числился.</summary>
|
/// <summary>Есть в справочнике, но уволен из всех компаний, где числился.</summary>
|
||||||
Dismissed,
|
Dismissed = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
namespace Tnsu.ApprovalRoute.Application.Reminders;
|
namespace Tnsu.ApprovalRoute.Application.Reminders;
|
||||||
|
|
||||||
/// <param name="ManagerEmail">
|
/// <param name="ReceiverEmail">
|
||||||
/// То же значение, что и унаследованный ReceiverEmail, — email руководителя
|
/// Email руководителя принимаемого сотрудника — кому адресовано напоминание.
|
||||||
/// принимаемого сотрудника, кому адресовано напоминание.
|
/// Отдельного ManagerEmail рядом нет намеренно: это было то же самое значение
|
||||||
|
/// под вторым именем.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="CandidateEmail">Email принимаемого сотрудника (см. ReminderService.GetProbationEndingAsync).</param>
|
/// <param name="CandidateEmail">Email принимаемого сотрудника (см. ReminderService.GetProbationEndingAsync).</param>
|
||||||
/// <param name="CandidateName">ФИО принимаемого сотрудника (колонка CandidateName списка данных).</param>
|
/// <param name="CandidateName">ФИО принимаемого сотрудника (колонка CandidateName списка данных).</param>
|
||||||
@@ -11,7 +12,7 @@ namespace Tnsu.ApprovalRoute.Application.Reminders;
|
|||||||
/// <param name="ProbationEndsAt">Дата окончания испытательного срока (HireDate + срок).</param>
|
/// <param name="ProbationEndsAt">Дата окончания испытательного срока (HireDate + срок).</param>
|
||||||
/// <param name="DaysUntilProbationEnds">Сколько дней осталось до конца испытательного срока.</param>
|
/// <param name="DaysUntilProbationEnds">Сколько дней осталось до конца испытательного срока.</param>
|
||||||
public sealed record ProbationEndingReminderDto(
|
public sealed record ProbationEndingReminderDto(
|
||||||
string ManagerEmail,
|
string ReceiverEmail,
|
||||||
string CandidateEmail,
|
string CandidateEmail,
|
||||||
string CandidateName,
|
string CandidateName,
|
||||||
string? ProbationPeriod,
|
string? ProbationPeriod,
|
||||||
@@ -20,4 +21,4 @@ public sealed record ProbationEndingReminderDto(
|
|||||||
int DaysUntilProbationEnds,
|
int DaysUntilProbationEnds,
|
||||||
string Url,
|
string Url,
|
||||||
string? ReceiverName = null)
|
string? ReceiverName = null)
|
||||||
: ReminderDto(ManagerEmail, ReceiverName, Url);
|
: ReminderDto(ReceiverEmail, ReceiverName, Url);
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
namespace Tnsu.ApprovalRoute.Application.Reminders;
|
namespace Tnsu.ApprovalRoute.Application.Reminders;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Какой вид напоминаний запросить у <see cref="ReminderService.GetRemindersAsync(DateTimeOffset, ReminderFeed, CancellationToken)"/>.
|
/// Какой вид напоминаний запросить у <see cref="ReminderService.GetRemindersAsync(ReminderFeed, CancellationToken)"/>.
|
||||||
///
|
///
|
||||||
/// GetRemindersAsync изначально (до появления ProbationEnding) отдавал только
|
/// <see cref="None"/> («вид не указан») трактуется как <see cref="ApprovalOverdue"/>:
|
||||||
/// просроченные шаги согласования — этот вид и остаётся значением по умолчанию
|
/// до появления ProbationEnding эндпоинт отдавал только просроченные шаги, и
|
||||||
/// (0), чтобы старые потребители (Power Automate), не указывающие feed явно,
|
/// вызов без явного выбора обязан работать как раньше.
|
||||||
/// продолжали получать ровно то же, что и раньше.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum ReminderFeed
|
public enum ReminderFeed
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
ApprovalOverdue = 1,
|
ApprovalOverdue = 1,
|
||||||
ProbationEnding = 2
|
ProbationEnding = 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -403,15 +403,20 @@ public sealed class ReminderService(
|
|||||||
var reminders = new List<ReminderDto>();
|
var reminders = new List<ReminderDto>();
|
||||||
foreach (var row in rows)
|
foreach (var row in rows)
|
||||||
{
|
{
|
||||||
var reminder = await TryBuildProbationReminderAsync(type, row, listId, directory, now, cancellationToken);
|
var reminder = await TryBuildProbationReminderAsync(row, directory, now, cancellationToken);
|
||||||
if (reminder is not null) reminders.Add(reminder);
|
if (reminder is not null) reminders.Add(reminder);
|
||||||
}
|
}
|
||||||
|
|
||||||
return reminders;
|
return reminders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ни type, ни listId здесь больше не нужны: ссылка в этом виде напоминаний —
|
||||||
|
/// константа из настроек (см. ProbationReminderUrl), а поля самой заявки
|
||||||
|
/// (RequestTypeId и т.п.) в ProbationEndingReminderDto не входят.
|
||||||
|
/// </summary>
|
||||||
private async Task<ReminderDto?> TryBuildProbationReminderAsync(
|
private async Task<ReminderDto?> TryBuildProbationReminderAsync(
|
||||||
RequestType type, HireRow row, string listId, EmployeeDirectorySnapshot directory,
|
HireRow row, EmployeeDirectorySnapshot directory,
|
||||||
DateTimeOffset now, CancellationToken cancellationToken)
|
DateTimeOffset now, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(row.CandidateName) || row.HireDate is not { } hireDate) return null;
|
if (string.IsNullOrWhiteSpace(row.CandidateName) || row.HireDate is not { } hireDate) return null;
|
||||||
@@ -425,11 +430,17 @@ public sealed class ReminderService(
|
|||||||
|
|
||||||
var candidateName = row.CandidateName.Trim();
|
var candidateName = row.CandidateName.Trim();
|
||||||
|
|
||||||
|
// Перечислены оба «продолжающих» исхода явно, а «пропустить» — это default:
|
||||||
|
// так любое НЕ подтверждённое трудоустройство (в том числе EmploymentStatus.None,
|
||||||
|
// если он когда-нибудь сюда доедет) приводит к пропуску с предупреждением, а не
|
||||||
|
// молча считается «работает».
|
||||||
switch (directory.EmploymentOf(candidateName))
|
switch (directory.EmploymentOf(candidateName))
|
||||||
{
|
{
|
||||||
|
case EmploymentStatus.Employed:
|
||||||
|
break;
|
||||||
case EmploymentStatus.Dismissed:
|
case EmploymentStatus.Dismissed:
|
||||||
return null; // уже уволен — напоминать не о чем.
|
return null; // уже уволен — напоминать не о чем.
|
||||||
case EmploymentStatus.NotFound:
|
default:
|
||||||
logger.LogWarning(
|
logger.LogWarning(
|
||||||
"Заявка на приём {RequestId}: кандидат «{Fio}» не найден в /Employee/FullBase — статус трудоустройства не подтверждён, напоминание пропущено",
|
"Заявка на приём {RequestId}: кандидат «{Fio}» не найден в /Employee/FullBase — статус трудоустройства не подтверждён, напоминание пропущено",
|
||||||
row.Id, candidateName);
|
row.Id, candidateName);
|
||||||
@@ -455,26 +466,17 @@ public sealed class ReminderService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new ProbationEndingReminderDto(
|
return new ProbationEndingReminderDto(
|
||||||
ManagerEmail: managerEmail,
|
ReceiverEmail: managerEmail,
|
||||||
CandidateEmail: candidateEmail,
|
CandidateEmail: candidateEmail,
|
||||||
CandidateName: candidateName,
|
CandidateName: candidateName,
|
||||||
ProbationPeriod: row.ProbationPeriod,
|
ProbationPeriod: row.ProbationPeriod,
|
||||||
HireDate: hireDate,
|
HireDate: hireDate,
|
||||||
ProbationEndsAt: probationEndsAt,
|
ProbationEndsAt: probationEndsAt,
|
||||||
DaysUntilProbationEnds: daysLeft,
|
DaysUntilProbationEnds: daysLeft,
|
||||||
Url: BuildProbationUrl(type, row, listId),
|
// Не ссылка на саму заявку на приём (она уже закрыта, решать по ней
|
||||||
|
// нечего) — константный адрес раздела, где руководитель заводит новую
|
||||||
|
// заявку по итогам испытательного срока. См. ProbationReminderUrl.
|
||||||
|
Url: options.Value.ProbationReminderUrl,
|
||||||
ReceiverName: directory.NameByEmail(managerEmail));
|
ReceiverName: directory.NameByEmail(managerEmail));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ссылка на саму заявку (не на шаг согласования — этого вида напоминаний
|
|
||||||
/// понятие "фаза" не касается), в том же формате, что и BuildUrl.
|
|
||||||
/// </summary>
|
|
||||||
private string BuildProbationUrl(RequestType type, HireRow row, string listId) =>
|
|
||||||
options.Value.RequestUrlTemplate
|
|
||||||
.Replace("{siteUrl}", type.SiteUrl!.TrimEnd('/'))
|
|
||||||
.Replace("{pageUrl}", type.PageUrl)
|
|
||||||
.Replace("{requestId}", row.Id.ToString())
|
|
||||||
.Replace("{listId}", Uri.EscapeDataString(listId))
|
|
||||||
.Replace("{nav}", "approvals");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,4 +34,19 @@ public sealed class RemindersOptions
|
|||||||
|
|
||||||
/// <summary>За сколько дней до конца испытательного срока присылать напоминание.</summary>
|
/// <summary>За сколько дней до конца испытательного срока присылать напоминание.</summary>
|
||||||
public int ProbationReminderDaysBeforeEnd { get; set; } = 3;
|
public int ProbationReminderDaysBeforeEnd { get; set; } = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Куда ведёт ссылка в напоминании об окончании испытательного срока — на
|
||||||
|
/// раздел «Подать заявку» портала, а НЕ на саму заявку на приём: от
|
||||||
|
/// руководителя по этому напоминанию требуется завести НОВУЮ заявку (решение
|
||||||
|
/// по итогам испытательного срока), а не открыть уже закрытую старую.
|
||||||
|
///
|
||||||
|
/// Поэтому ссылка константная, без подстановок: адрес конкретной формы в
|
||||||
|
/// портал вообще не передать — параметр rptype без rpid/rpsource
|
||||||
|
/// игнорируется, и маршрут сваливается в раздел «Подать заявку» (см.
|
||||||
|
/// parseRoute в request-portal/shared/routing.ts). Отдельная настройка, а не
|
||||||
|
/// хардкод, — чтобы адрес правился конфигурацией.
|
||||||
|
/// </summary>
|
||||||
|
public string ProbationReminderUrl { get; set; } =
|
||||||
|
"https://tnsukz.sharepoint.com/sites/requests?rpnav=new";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public sealed record CreateTemplateRouteStepRequest(
|
|||||||
RouteStepStatus Status,
|
RouteStepStatus Status,
|
||||||
bool AllowReassignToOtherApprovers,
|
bool AllowReassignToOtherApprovers,
|
||||||
IReadOnlyList<RouteStepConditionDto>? Conditions,
|
IReadOnlyList<RouteStepConditionDto>? Conditions,
|
||||||
ConditionsLogic ConditionsLogic = ConditionsLogic.And,
|
ConditionsLogic ConditionsLogic,
|
||||||
int? SlaHours = null,
|
int? SlaHours = null,
|
||||||
bool? AskOverdueReason = null);
|
bool? AskOverdueReason = null);
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ public sealed record UpdateTemplateRouteStepRequest(
|
|||||||
RouteStepStatus Status,
|
RouteStepStatus Status,
|
||||||
bool AllowReassignToOtherApprovers,
|
bool AllowReassignToOtherApprovers,
|
||||||
IReadOnlyList<RouteStepConditionDto>? Conditions,
|
IReadOnlyList<RouteStepConditionDto>? Conditions,
|
||||||
ConditionsLogic ConditionsLogic = ConditionsLogic.And,
|
ConditionsLogic ConditionsLogic,
|
||||||
int? SlaHours = null,
|
int? SlaHours = null,
|
||||||
bool? AskOverdueReason = null);
|
bool? AskOverdueReason = null);
|
||||||
|
|
||||||
|
|||||||
+35
@@ -8,6 +8,9 @@ public sealed class CreateTemplateRouteStepRequestValidator : AbstractValidator<
|
|||||||
public CreateTemplateRouteStepRequestValidator()
|
public CreateTemplateRouteStepRequestValidator()
|
||||||
{
|
{
|
||||||
RuleFor(x => x.Status).NotEqual(RouteStepStatus.None).WithMessage("Статус обязателен: Approval или Execution.");
|
RuleFor(x => x.Status).NotEqual(RouteStepStatus.None).WithMessage("Статус обязателен: Approval или Execution.");
|
||||||
|
RuleFor(x => x.ConditionsLogic)
|
||||||
|
.NotEqual(ConditionsLogic.None)
|
||||||
|
.WithMessage("Логика объединения условий обязательна: And или Or.");
|
||||||
RuleFor(x => x)
|
RuleFor(x => x)
|
||||||
.Must(x => TemplateRouteStepFields.CountFilled(x.ExecutorEmail, x.Position, x.ProjectRole, x.HeadType) == 1)
|
.Must(x => TemplateRouteStepFields.CountFilled(x.ExecutorEmail, x.Position, x.ProjectRole, x.HeadType) == 1)
|
||||||
.WithMessage("Должно быть заполнено ровно одно из полей: исполнитель, должность, роль в проекте или тип руководителя.");
|
.WithMessage("Должно быть заполнено ровно одно из полей: исполнитель, должность, роль в проекте или тип руководителя.");
|
||||||
@@ -28,6 +31,9 @@ public sealed class UpdateTemplateRouteStepRequestValidator : AbstractValidator<
|
|||||||
public UpdateTemplateRouteStepRequestValidator()
|
public UpdateTemplateRouteStepRequestValidator()
|
||||||
{
|
{
|
||||||
RuleFor(x => x.Status).NotEqual(RouteStepStatus.None).WithMessage("Статус обязателен: Approval или Execution.");
|
RuleFor(x => x.Status).NotEqual(RouteStepStatus.None).WithMessage("Статус обязателен: Approval или Execution.");
|
||||||
|
RuleFor(x => x.ConditionsLogic)
|
||||||
|
.NotEqual(ConditionsLogic.None)
|
||||||
|
.WithMessage("Логика объединения условий обязательна: And или Or.");
|
||||||
RuleFor(x => x)
|
RuleFor(x => x)
|
||||||
.Must(x => TemplateRouteStepFields.CountFilled(x.ExecutorEmail, x.Position, x.ProjectRole, x.HeadType) == 1)
|
.Must(x => TemplateRouteStepFields.CountFilled(x.ExecutorEmail, x.Position, x.ProjectRole, x.HeadType) == 1)
|
||||||
.WithMessage("Должно быть заполнено ровно одно из полей: исполнитель, должность, роль в проекте или тип руководителя.");
|
.WithMessage("Должно быть заполнено ровно одно из полей: исполнитель, должность, роль в проекте или тип руководителя.");
|
||||||
@@ -43,6 +49,35 @@ public sealed class UpdateTemplateRouteStepRequestValidator : AbstractValidator<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// У PATCH-запроса шага валидатора раньше не было вовсе — здесь он ровно про одно:
|
||||||
|
/// не дать протащить невалидные значения-заглушки в поля, которые запрос ЯВНО
|
||||||
|
/// задаёт. null остаётся «не менять» и не проверяется (см. PatchTemplateRouteStepRequest).
|
||||||
|
/// </summary>
|
||||||
|
public sealed class PatchTemplateRouteStepRequestValidator : AbstractValidator<PatchTemplateRouteStepRequest>
|
||||||
|
{
|
||||||
|
public PatchTemplateRouteStepRequestValidator()
|
||||||
|
{
|
||||||
|
When(x => x.Status is not null, () =>
|
||||||
|
RuleFor(x => x.Status)
|
||||||
|
.NotEqual(RouteStepStatus.None)
|
||||||
|
.WithMessage("Статус обязателен: Approval или Execution."));
|
||||||
|
When(x => x.ConditionsLogic is not null, () =>
|
||||||
|
RuleFor(x => x.ConditionsLogic)
|
||||||
|
.NotEqual(ConditionsLogic.None)
|
||||||
|
.WithMessage("Логика объединения условий обязательна: And или Or."));
|
||||||
|
RuleFor(x => x)
|
||||||
|
.Must(x => TemplateRouteStepFields.AreConditionsValid(x.Conditions))
|
||||||
|
.WithMessage("В каждом условии (field_name, operation, value) все три поля обязательны.");
|
||||||
|
// Отрицательный SLA сделал бы шаг просроченным уже в момент активации;
|
||||||
|
// ноль допустим — это осознанное «напоминать сразу».
|
||||||
|
RuleFor(x => x.SlaHours)
|
||||||
|
.GreaterThanOrEqualTo(0)
|
||||||
|
.When(x => x.SlaHours is not null)
|
||||||
|
.WithMessage("SLA в часах не может быть отрицательным.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal static class TemplateRouteStepFields
|
internal static class TemplateRouteStepFields
|
||||||
{
|
{
|
||||||
public static int CountFilled(string? executorEmail, string? position, ProjectRole? projectRole, HeadType? headType) =>
|
public static int CountFilled(string? executorEmail, string? position, ProjectRole? projectRole, HeadType? headType) =>
|
||||||
|
|||||||
@@ -6,9 +6,21 @@ namespace Tnsu.ApprovalRoute.Domain.Enums;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public enum ConditionsLogic
|
public enum ConditionsLogic
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Логика не задана — НЕвалидное значение (см. валидаторы шага), а не «как И».
|
||||||
|
/// Существует только как значение по умолчанию (default(ConditionsLogic)):
|
||||||
|
/// шаг с такой логикой не должен ни создаваться, ни сохраняться.
|
||||||
|
///
|
||||||
|
/// Именно поэтому у самой сущности и у запросов на создание/обновление
|
||||||
|
/// значение по умолчанию задано явно — <c>ConditionsLogic.And</c>
|
||||||
|
/// (см. TemplateRouteStep.ConditionsLogic и *TemplateRouteStepRequest):
|
||||||
|
/// пропущенное в запросе поле по-прежнему означает «И», как и раньше.
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
/// <summary>Шаг появляется, только если выполняются ВСЕ условия.</summary>
|
/// <summary>Шаг появляется, только если выполняются ВСЕ условия.</summary>
|
||||||
And,
|
And = 1,
|
||||||
|
|
||||||
/// <summary>Шаг появляется, если выполняется ХОТЯ БЫ ОДНО условие.</summary>
|
/// <summary>Шаг появляется, если выполняется ХОТЯ БЫ ОДНО условие.</summary>
|
||||||
Or
|
Or = 2
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-3
@@ -31,6 +31,11 @@ public sealed class ReminderServiceTests : IClassFixture<PostgresDatabaseFixture
|
|||||||
private const string DataListTitle = "Заявка тест — данные";
|
private const string DataListTitle = "Заявка тест — данные";
|
||||||
private const string ApprovalListTitle = "Заявка тест — согласование";
|
private const string ApprovalListTitle = "Заявка тест — согласование";
|
||||||
|
|
||||||
|
// Своё значение (не то, что по умолчанию в RemindersOptions) — так тест
|
||||||
|
// проверяет, что ссылка действительно берётся из настроек, а не совпадает
|
||||||
|
// с ожидаемой случайно.
|
||||||
|
private const string ProbationReminderUrl = "https://portal.test/новая-заявка";
|
||||||
|
|
||||||
private static readonly DateTimeOffset Now = new(2026, 8, 12, 12, 0, 0, TimeSpan.Zero);
|
private static readonly DateTimeOffset Now = new(2026, 8, 12, 12, 0, 0, TimeSpan.Zero);
|
||||||
|
|
||||||
// GetOverdueAsync/GetProbationEndingAsync по-прежнему берут now параметром — их
|
// GetOverdueAsync/GetProbationEndingAsync по-прежнему берут now параметром — их
|
||||||
@@ -355,7 +360,12 @@ public sealed class ReminderServiceTests : IClassFixture<PostgresDatabaseFixture
|
|||||||
_services.GetRequiredService<ListPartitionRegistry>(),
|
_services.GetRequiredService<ListPartitionRegistry>(),
|
||||||
portalClient.Object,
|
portalClient.Object,
|
||||||
Clock,
|
Clock,
|
||||||
Options.Create(new RemindersOptions { RecruitingHirePageUrl = pageUrl, ProbationReminderDaysBeforeEnd = daysBeforeEnd }),
|
Options.Create(new RemindersOptions
|
||||||
|
{
|
||||||
|
RecruitingHirePageUrl = pageUrl,
|
||||||
|
ProbationReminderDaysBeforeEnd = daysBeforeEnd,
|
||||||
|
ProbationReminderUrl = ProbationReminderUrl
|
||||||
|
}),
|
||||||
NullLogger<ReminderService>.Instance);
|
NullLogger<ReminderService>.Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,10 +404,13 @@ public sealed class ReminderServiceTests : IClassFixture<PostgresDatabaseFixture
|
|||||||
|
|
||||||
result.IsSuccess.ShouldBeTrue();
|
result.IsSuccess.ShouldBeTrue();
|
||||||
var reminder = result.Value.ShouldHaveSingleItem().ShouldBeOfType<ProbationEndingReminderDto>();
|
var reminder = result.Value.ShouldHaveSingleItem().ShouldBeOfType<ProbationEndingReminderDto>();
|
||||||
reminder.ManagerEmail.ShouldBe("manager@tnsu.kz");
|
reminder.ReceiverEmail.ShouldBe("manager@tnsu.kz");
|
||||||
reminder.CandidateEmail.ShouldBe("candidate@tnsu.kz");
|
reminder.CandidateEmail.ShouldBe("candidate@tnsu.kz");
|
||||||
reminder.CandidateName.ShouldBe("Иванов Иван Иванович");
|
reminder.CandidateName.ShouldBe("Иванов Иван Иванович");
|
||||||
reminder.DaysUntilProbationEnds.ShouldBe(3);
|
reminder.DaysUntilProbationEnds.ShouldBe(3);
|
||||||
|
// Ссылка ведёт не на саму заявку на приём, а на раздел, где заводят новую
|
||||||
|
// (константа из настроек, см. RemindersOptions.ProbationReminderUrl).
|
||||||
|
reminder.Url.ShouldBe(ProbationReminderUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(DisplayName = "Кандидат уже уволен (dismissalDate заполнен) — напоминания нет")]
|
[Fact(DisplayName = "Кандидат уже уволен (dismissalDate заполнен) — напоминания нет")]
|
||||||
@@ -558,7 +571,7 @@ public sealed class ReminderServiceTests : IClassFixture<PostgresDatabaseFixture
|
|||||||
|
|
||||||
var service = new ReminderService(
|
var service = new ReminderService(
|
||||||
db, sharePoint.Object, partitionRegistry, portalClient.Object, Clock,
|
db, sharePoint.Object, partitionRegistry, portalClient.Object, Clock,
|
||||||
Options.Create(new RemindersOptions { RecruitingHirePageUrl = pageUrl }),
|
Options.Create(new RemindersOptions { RecruitingHirePageUrl = pageUrl, ProbationReminderUrl = ProbationReminderUrl }),
|
||||||
NullLogger<ReminderService>.Instance);
|
NullLogger<ReminderService>.Instance);
|
||||||
|
|
||||||
var result = await service.GetProbationEndingAsync(Now, CancellationToken.None);
|
var result = await service.GetProbationEndingAsync(Now, CancellationToken.None);
|
||||||
|
|||||||
+28
-28
@@ -169,7 +169,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, "approver@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "approver@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -206,7 +206,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(
|
new CreateTemplateRouteStepRequest(
|
||||||
1, "approver@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, null,
|
1, "approver@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And,
|
||||||
SlaHours: stepSla, AskOverdueReason: stepAsk),
|
SlaHours: stepSla, AskOverdueReason: stepAsk),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
@@ -285,15 +285,15 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
|
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, "dup@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "dup@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(2, "other@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(2, "other@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(3, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(3, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -342,11 +342,11 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
|
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.DirectionHead, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.DirectionHead, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -377,7 +377,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.DepartmentHead, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.DepartmentHead, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action — departmentId в запросе не передан
|
// Action — departmentId в запросе не передан
|
||||||
@@ -419,7 +419,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.DepartmentHead, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.DepartmentHead, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -459,7 +459,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.DepartmentHead, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.DepartmentHead, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -488,7 +488,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, "big-amount@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }),
|
new CreateTemplateRouteStepRequest(1, "big-amount@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -515,11 +515,11 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, "big-amount@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }),
|
new CreateTemplateRouteStepRequest(1, "big-amount@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(2, "always@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(2, "always@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -546,7 +546,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, "big-amount@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }),
|
new CreateTemplateRouteStepRequest(1, "big-amount@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action — fieldValues не переданы вовсе
|
// Action — fieldValues не переданы вовсе
|
||||||
@@ -571,7 +571,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, "urgent@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Priority", ComparisonOperator.Equal, "Urgent") }),
|
new CreateTemplateRouteStepRequest(1, "urgent@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Priority", ComparisonOperator.Equal, "Urgent") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -605,7 +605,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, "text-condition@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Priority", operation, expected) }),
|
new CreateTemplateRouteStepRequest(1, "text-condition@tnsu.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Priority", operation, expected) }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -672,7 +672,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, ProjectRole.ProjectManager, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, ProjectRole.ProjectManager, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -710,7 +710,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, "Lead", null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, "Lead", null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -749,7 +749,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -785,7 +785,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.DirectorTansu, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.DirectorTansu, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -832,7 +832,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(5, null, null, null, HeadType.CompanyDirectors, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(5, null, null, null, HeadType.CompanyDirectors, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -883,7 +883,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.CompanyDirectors, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.CompanyDirectors, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -949,7 +949,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.CompanyDirectors, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.CompanyDirectors, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -998,11 +998,11 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(5, null, null, null, HeadType.CompanyDirectors, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(5, null, null, null, HeadType.CompanyDirectors, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(10, null, null, null, HeadType.DirectorTansu, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(10, null, null, null, HeadType.DirectorTansu, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action — тестовая конфигурация не переопределяет TnsuPortal:DirectorTansuEmail,
|
// Action — тестовая конфигурация не переопределяет TnsuPortal:DirectorTansuEmail,
|
||||||
@@ -1036,7 +1036,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -1065,7 +1065,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, "Несуществующая должность", null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, "Несуществующая должность", null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -1094,7 +1094,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
await stepService.CreateAsync(
|
await stepService.CreateAsync(
|
||||||
route.Value.Id,
|
route.Value.Id,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, ProjectRole.Security, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, ProjectRole.Security, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
|
|||||||
+42
-36
@@ -43,7 +43,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
// Action
|
// Action
|
||||||
var result = await _service.CreateAsync(
|
var result = await _service.CreateAsync(
|
||||||
999999,
|
999999,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -59,7 +59,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
// Action
|
// Action
|
||||||
var result = await _service.CreateAsync(
|
var result = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.ProjectManager, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", ProjectRole.ProjectManager, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -71,14 +71,14 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var routeId = await CreateRouteAsync("Route with duplicate step", byProject: false);
|
var routeId = await CreateRouteAsync("Route with duplicate step", byProject: false);
|
||||||
var request = new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, HeadType.Chief, RouteStepStatus.Approval, false, null);
|
var request = new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, HeadType.Chief, RouteStepStatus.Approval, false, null, ConditionsLogic.And);
|
||||||
var first = await _service.CreateAsync(routeId, request, CancellationToken.None);
|
var first = await _service.CreateAsync(routeId, request, CancellationToken.None);
|
||||||
first.IsSuccess.ShouldBeTrue();
|
first.IsSuccess.ShouldBeTrue();
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
var duplicate = await _service.CreateAsync(
|
var duplicate = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(2, "a@b.kz", "Engineer", null, HeadType.Chief, RouteStepStatus.Approval, true, null),
|
new CreateTemplateRouteStepRequest(2, "a@b.kz", "Engineer", null, HeadType.Chief, RouteStepStatus.Approval, true, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -153,14 +153,14 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var routeId = await CreateRouteAsync("Route with duplicate head type", byProject: false);
|
var routeId = await CreateRouteAsync("Route with duplicate head type", byProject: false);
|
||||||
var first = await _service.CreateAsync(
|
var first = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
first.IsSuccess.ShouldBeTrue();
|
first.IsSuccess.ShouldBeTrue();
|
||||||
|
|
||||||
// Action — другой статус (Execution вместо Approval), но тот же HeadType.Chief
|
// Action — другой статус (Execution вместо Approval), но тот же HeadType.Chief
|
||||||
var second = await _service.CreateAsync(
|
var second = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.Chief, RouteStepStatus.Execution, true, null),
|
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.Chief, RouteStepStatus.Execution, true, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -174,14 +174,14 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var routeId = await CreateRouteAsync("Route with duplicate department head", byProject: false);
|
var routeId = await CreateRouteAsync("Route with duplicate department head", byProject: false);
|
||||||
var first = await _service.CreateAsync(
|
var first = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.DepartmentHead, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.DepartmentHead, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
first.IsSuccess.ShouldBeTrue();
|
first.IsSuccess.ShouldBeTrue();
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
var second = await _service.CreateAsync(
|
var second = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.DepartmentHead, RouteStepStatus.Execution, false, null),
|
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.DepartmentHead, RouteStepStatus.Execution, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -197,11 +197,11 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
// Action
|
// Action
|
||||||
var chief = await _service.CreateAsync(
|
var chief = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
var directorTansu = await _service.CreateAsync(
|
var directorTansu = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.DirectorTansu, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.DirectorTansu, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -219,11 +219,11 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
// Action
|
// Action
|
||||||
var small = await _service.CreateAsync(
|
var small = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.LessThan, "1000") }),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.LessThan, "1000") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
var big = await _service.CreateAsync(
|
var big = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }),
|
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -240,11 +240,11 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
// Action
|
// Action
|
||||||
var small = await _service.CreateAsync(
|
var small = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.LessThan, "1000") }),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.LessThan, "1000") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
var big = await _service.CreateAsync(
|
var big = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(2, "a@b.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }),
|
new CreateTemplateRouteStepRequest(2, "a@b.kz", null, null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -259,14 +259,14 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var routeId = await CreateRouteAsync("Route with head type same condition", byProject: false, listName: "Requests");
|
var routeId = await CreateRouteAsync("Route with head type same condition", byProject: false, listName: "Requests");
|
||||||
var first = await _service.CreateAsync(
|
var first = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
first.IsSuccess.ShouldBeTrue();
|
first.IsSuccess.ShouldBeTrue();
|
||||||
|
|
||||||
// Action — то же условие, другой статус
|
// Action — то же условие, другой статус
|
||||||
var second = await _service.CreateAsync(
|
var second = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.Chief, RouteStepStatus.Execution, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }),
|
new CreateTemplateRouteStepRequest(2, null, null, null, HeadType.Chief, RouteStepStatus.Execution, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -280,14 +280,14 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var routeId = await CreateRouteAsync("Route to update head type on itself", byProject: false);
|
var routeId = await CreateRouteAsync("Route to update head type on itself", byProject: false);
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action — HasDuplicateHeadTypeAsync должен исключать сам обновляемый шаг (excludeId)
|
// Action — HasDuplicateHeadTypeAsync должен исключать сам обновляемый шаг (excludeId)
|
||||||
var updated = await _service.UpdateAsync(
|
var updated = await _service.UpdateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
created.Value.Id,
|
created.Value.Id,
|
||||||
new UpdateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Execution, true, null),
|
new UpdateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Execution, true, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -303,7 +303,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
// Action
|
// Action
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -321,7 +321,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
// Action
|
// Action
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -341,7 +341,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
// Action
|
// Action
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -359,7 +359,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
// Action
|
// Action
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "", "", ProjectRole.ProjectManager, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "", "", ProjectRole.ProjectManager, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -374,14 +374,14 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var routeId = await CreateRouteAsync("Route to update step", byProject: false);
|
var routeId = await CreateRouteAsync("Route to update step", byProject: false);
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
var updated = await _service.UpdateAsync(
|
var updated = await _service.UpdateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
created.Value.Id,
|
created.Value.Id,
|
||||||
new UpdateTemplateRouteStepRequest(2, "b@b.kz", "Senior Engineer", null, HeadType.Chief, RouteStepStatus.Execution, true, null),
|
new UpdateTemplateRouteStepRequest(2, "b@b.kz", "Senior Engineer", null, HeadType.Chief, RouteStepStatus.Execution, true, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -398,7 +398,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var routeId = await CreateRouteAsync("Route to patch step", byProject: false);
|
var routeId = await CreateRouteAsync("Route to patch step", byProject: false);
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", null, null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", null, null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -422,7 +422,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var routeId = await CreateRouteAsync("Route to patch step into conflict", byProject: false);
|
var routeId = await CreateRouteAsync("Route to patch step into conflict", byProject: false);
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, null, null, null, HeadType.Chief, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -443,7 +443,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var routeId = await CreateRouteAsync("Route to patch step status none", byProject: false);
|
var routeId = await CreateRouteAsync("Route to patch step status none", byProject: false);
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", null, null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", null, null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -457,6 +457,12 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
patched.IsFailed.ShouldBeTrue();
|
patched.IsFailed.ShouldBeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConditionsLogic здесь всюду передаётся явно, потому что он обязателен: у
|
||||||
|
// Create/Update-запроса значения по умолчанию нет вовсе, а пропущенное в JSON
|
||||||
|
// поле превращается в ConditionsLogic.None и отвергается валидатором на слое
|
||||||
|
// API (TemplateRouteStepValidators + ValidationActionFilter). Отдельным тестом
|
||||||
|
// это не покрыто — сервис валидаторы не вызывает.
|
||||||
|
|
||||||
[Fact(DisplayName = "Явные ProjectRole.None и HeadType.None при патче считаются незаполненными, как и null")]
|
[Fact(DisplayName = "Явные ProjectRole.None и HeadType.None при патче считаются незаполненными, как и null")]
|
||||||
public async Task PatchAsync_ExplicitNoneValues_TreatedAsUnfilled()
|
public async Task PatchAsync_ExplicitNoneValues_TreatedAsUnfilled()
|
||||||
{
|
{
|
||||||
@@ -464,7 +470,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var routeId = await CreateRouteAsync("Route with explicit None values on patch", byProject: false);
|
var routeId = await CreateRouteAsync("Route with explicit None values on patch", byProject: false);
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", null, null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", null, null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -489,7 +495,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var result = await _service.CreateAsync(
|
var result = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false,
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false,
|
||||||
new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "") }),
|
new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -505,7 +511,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
// Action
|
// Action
|
||||||
var result = await _service.CreateAsync(
|
var result = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -521,7 +527,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
// Action
|
// Action
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, new[] { new RouteStepConditionDto("Amount", ComparisonOperator.GreaterThan, "1000") }, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -539,7 +545,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var routeId = await CreateRouteAsync("Route to patch condition into conflict", byProject: false, listName: "Requests");
|
var routeId = await CreateRouteAsync("Route to patch condition into conflict", byProject: false, listName: "Requests");
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -560,7 +566,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var routeId = await CreateRouteAsync("Route to delete step", byProject: false);
|
var routeId = await CreateRouteAsync("Route to delete step", byProject: false);
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
routeId,
|
routeId,
|
||||||
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "a@b.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -581,16 +587,16 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
var routeBId = await CreateRouteAsync("Route B for executor search", byProject: false);
|
var routeBId = await CreateRouteAsync("Route B for executor search", byProject: false);
|
||||||
await _service.CreateAsync(
|
await _service.CreateAsync(
|
||||||
routeAId,
|
routeAId,
|
||||||
new CreateTemplateRouteStepRequest(1, "Target@Example.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(1, "Target@Example.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
await _service.CreateAsync(
|
await _service.CreateAsync(
|
||||||
routeBId,
|
routeBId,
|
||||||
new CreateTemplateRouteStepRequest(1, "target@example.kz", "Manager", null, null, RouteStepStatus.Execution, false, null),
|
new CreateTemplateRouteStepRequest(1, "target@example.kz", "Manager", null, null, RouteStepStatus.Execution, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
// Другой исполнитель — не должен попасть в выдачу.
|
// Другой исполнитель — не должен попасть в выдачу.
|
||||||
await _service.CreateAsync(
|
await _service.CreateAsync(
|
||||||
routeAId,
|
routeAId,
|
||||||
new CreateTemplateRouteStepRequest(2, "other@example.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null),
|
new CreateTemplateRouteStepRequest(2, "other@example.kz", "Engineer", null, null, RouteStepStatus.Approval, false, null, ConditionsLogic.And),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
|
|||||||
Reference in New Issue
Block a user