Новый виды условных шагов #6
@@ -4,20 +4,20 @@ public sealed record TemplateRouteDto(
|
|||||||
int Id,
|
int Id,
|
||||||
int RequestTypeId,
|
int RequestTypeId,
|
||||||
Guid? DepartmentId,
|
Guid? DepartmentId,
|
||||||
Guid CompanyId,
|
Guid? CompanyId,
|
||||||
bool ByProject,
|
bool ByProject,
|
||||||
bool RequiresControl);
|
bool RequiresControl);
|
||||||
|
|
||||||
public sealed record CreateTemplateRouteRequest(
|
public sealed record CreateTemplateRouteRequest(
|
||||||
int RequestTypeId,
|
int RequestTypeId,
|
||||||
Guid? DepartmentId,
|
Guid? DepartmentId,
|
||||||
Guid CompanyId,
|
Guid? CompanyId,
|
||||||
bool ByProject,
|
bool ByProject,
|
||||||
bool RequiresControl);
|
bool RequiresControl);
|
||||||
|
|
||||||
public sealed record UpdateTemplateRouteRequest(
|
public sealed record UpdateTemplateRouteRequest(
|
||||||
Guid? DepartmentId,
|
Guid? DepartmentId,
|
||||||
Guid CompanyId,
|
Guid? CompanyId,
|
||||||
bool ByProject,
|
bool ByProject,
|
||||||
bool RequiresControl);
|
bool RequiresControl);
|
||||||
|
|
||||||
|
|||||||
@@ -140,22 +140,11 @@ public sealed class TemplateRouteService(
|
|||||||
|
|
||||||
var byProject = request.ProjectId is not null;
|
var byProject = request.ProjectId is not null;
|
||||||
|
|
||||||
var route = await db.TemplateRoutes.AsNoTracking()
|
// Приоритет — маршрут с компанией инициатора; если для этой компании
|
||||||
.Include(x => x.Steps)
|
// своего маршрута нет, берём запасной маршрут без указанной компании
|
||||||
.Where(x =>
|
// (тот же принцип, что уже применяется к отделу внутри FindRouteAsync).
|
||||||
x.RequestTypeId == request.RequestTypeId &&
|
var route = await FindRouteAsync(request.RequestTypeId, employee.CompanyId, employee.DepartmentId, byProject, cancellationToken)
|
||||||
x.CompanyId == employee.CompanyId &&
|
?? await FindRouteAsync(request.RequestTypeId, null, employee.DepartmentId, byProject, cancellationToken);
|
||||||
x.ByProject == byProject &&
|
|
||||||
x.DepartmentId == employee.DepartmentId)
|
|
||||||
.FirstOrDefaultAsync(cancellationToken)
|
|
||||||
?? await db.TemplateRoutes.AsNoTracking()
|
|
||||||
.Include(x => x.Steps)
|
|
||||||
.Where(x =>
|
|
||||||
x.RequestTypeId == request.RequestTypeId &&
|
|
||||||
x.CompanyId == employee.CompanyId &&
|
|
||||||
x.ByProject == byProject &&
|
|
||||||
x.DepartmentId == null)
|
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
|
||||||
|
|
||||||
if (route is null)
|
if (route is null)
|
||||||
return Result.Fail<ResolveTemplateRouteResultDto>(new NotFoundError(
|
return Result.Fail<ResolveTemplateRouteResultDto>(new NotFoundError(
|
||||||
@@ -186,8 +175,33 @@ public sealed class TemplateRouteService(
|
|||||||
return Result.Ok(new ResolveTemplateRouteResultDto(route.Id, route.RequiresControl, collapsedSteps));
|
return Result.Ok(new ResolveTemplateRouteResultDto(route.Id, route.RequiresControl, collapsedSteps));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Маршрут для компании/отдела инициатора с фолбэком по отделу: сначала точное
|
||||||
|
/// совпадение отдела, если такого маршрута нет — запасной маршрут без отдела
|
||||||
|
/// (для той же компании). Компания при этом фиксирована — фолбэк по ней делает
|
||||||
|
/// вызывающий код (см. ResolveAsync: сначала с companyId сотрудника, затем null).
|
||||||
|
/// </summary>
|
||||||
|
private async Task<TemplateRoute?> FindRouteAsync(
|
||||||
|
int requestTypeId, Guid? companyId, Guid? departmentId, bool byProject, CancellationToken cancellationToken) =>
|
||||||
|
await db.TemplateRoutes.AsNoTracking()
|
||||||
|
.Include(x => x.Steps)
|
||||||
|
.Where(x =>
|
||||||
|
x.RequestTypeId == requestTypeId &&
|
||||||
|
x.CompanyId == companyId &&
|
||||||
|
x.ByProject == byProject &&
|
||||||
|
x.DepartmentId == departmentId)
|
||||||
|
.FirstOrDefaultAsync(cancellationToken)
|
||||||
|
?? await db.TemplateRoutes.AsNoTracking()
|
||||||
|
.Include(x => x.Steps)
|
||||||
|
.Where(x =>
|
||||||
|
x.RequestTypeId == requestTypeId &&
|
||||||
|
x.CompanyId == companyId &&
|
||||||
|
x.ByProject == byProject &&
|
||||||
|
x.DepartmentId == null)
|
||||||
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
|
|
||||||
private Task<bool> HasDuplicateAsync(
|
private Task<bool> HasDuplicateAsync(
|
||||||
int requestTypeId, Guid? departmentId, Guid companyId, bool byProject, int? excludeId, CancellationToken cancellationToken) =>
|
int requestTypeId, Guid? departmentId, Guid? companyId, bool byProject, int? excludeId, CancellationToken cancellationToken) =>
|
||||||
db.TemplateRoutes.AnyAsync(x =>
|
db.TemplateRoutes.AnyAsync(x =>
|
||||||
x.Id != excludeId &&
|
x.Id != excludeId &&
|
||||||
x.RequestTypeId == requestTypeId &&
|
x.RequestTypeId == requestTypeId &&
|
||||||
|
|||||||
+4
-24
@@ -3,30 +3,10 @@ using Tnsu.ApprovalRoute.Domain.Enums;
|
|||||||
|
|
||||||
namespace Tnsu.ApprovalRoute.Application.TemplateRoutes;
|
namespace Tnsu.ApprovalRoute.Application.TemplateRoutes;
|
||||||
|
|
||||||
public sealed class CreateTemplateRouteRequestValidator : AbstractValidator<CreateTemplateRouteRequest>
|
// Компания и отдел маршрута — оба опциональны, поэтому CreateTemplateRouteRequest/
|
||||||
{
|
// UpdateTemplateRouteRequest/PatchTemplateRouteRequest не нуждаются в отдельных
|
||||||
public CreateTemplateRouteRequestValidator()
|
// валидаторах: маршрут без компании служит запасным вариантом, когда для
|
||||||
{
|
// конкретной компании инициатора своего маршрута нет (см. ResolveAsync).
|
||||||
RuleFor(x => x.CompanyId).NotEqual(Guid.Empty).WithMessage("Компания обязательна.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class UpdateTemplateRouteRequestValidator : AbstractValidator<UpdateTemplateRouteRequest>
|
|
||||||
{
|
|
||||||
public UpdateTemplateRouteRequestValidator()
|
|
||||||
{
|
|
||||||
RuleFor(x => x.CompanyId).NotEqual(Guid.Empty).WithMessage("Компания обязательна.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class PatchTemplateRouteRequestValidator : AbstractValidator<PatchTemplateRouteRequest>
|
|
||||||
{
|
|
||||||
public PatchTemplateRouteRequestValidator()
|
|
||||||
{
|
|
||||||
When(x => x.CompanyId is not null, () =>
|
|
||||||
RuleFor(x => x.CompanyId!.Value).NotEqual(Guid.Empty).WithMessage("Компания обязательна."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class ResolveTemplateRouteRequestValidator : AbstractValidator<ResolveTemplateRouteRequest>
|
public sealed class ResolveTemplateRouteRequestValidator : AbstractValidator<ResolveTemplateRouteRequest>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ public class TemplateRoute
|
|||||||
public int RequestTypeId { get; set; }
|
public int RequestTypeId { get; set; }
|
||||||
|
|
||||||
public Guid? DepartmentId { get; set; }
|
public Guid? DepartmentId { get; set; }
|
||||||
public Guid CompanyId { get; set; }
|
public Guid? CompanyId { get; set; }
|
||||||
public bool ByProject { get; set; }
|
public bool ByProject { get; set; }
|
||||||
|
|
||||||
public bool RequiresControl { get; set; }
|
public bool RequiresControl { get; set; }
|
||||||
|
|||||||
+2
-2
@@ -12,7 +12,7 @@ using Tnsu.ApprovalRoute.Infrastructure.Persistence;
|
|||||||
namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(ApprovalRouteDbContext))]
|
[DbContext(typeof(ApprovalRouteDbContext))]
|
||||||
[Migration("20260731093159_AddRequestTypeListNameSectionAndStepConditionFields")]
|
[Migration("20260731111928_AddRequestTypeListNameSectionAndStepConditionFields")]
|
||||||
partial class AddRequestTypeListNameSectionAndStepConditionFields
|
partial class AddRequestTypeListNameSectionAndStepConditionFields
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -81,7 +81,7 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
|||||||
.HasColumnType("boolean")
|
.HasColumnType("boolean")
|
||||||
.HasColumnName("by_project");
|
.HasColumnName("by_project");
|
||||||
|
|
||||||
b.Property<Guid>("CompanyId")
|
b.Property<Guid?>("CompanyId")
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasColumnName("company_id");
|
.HasColumnName("company_id");
|
||||||
|
|
||||||
+20
-1
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
@@ -14,6 +15,14 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
|||||||
name: "route_type",
|
name: "route_type",
|
||||||
table: "template_routes");
|
table: "template_routes");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "company_id",
|
||||||
|
table: "template_routes",
|
||||||
|
type: "uuid",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "uuid");
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
migrationBuilder.AddColumn<string>(
|
||||||
name: "field_name",
|
name: "field_name",
|
||||||
table: "template_route_steps",
|
table: "template_route_steps",
|
||||||
@@ -85,6 +94,16 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
|||||||
name: "site_url",
|
name: "site_url",
|
||||||
table: "request_types");
|
table: "request_types");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "company_id",
|
||||||
|
table: "template_routes",
|
||||||
|
type: "uuid",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "uuid",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
migrationBuilder.AddColumn<string>(
|
||||||
name: "route_type",
|
name: "route_type",
|
||||||
table: "template_routes",
|
table: "template_routes",
|
||||||
+1
-1
@@ -78,7 +78,7 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
|||||||
.HasColumnType("boolean")
|
.HasColumnType("boolean")
|
||||||
.HasColumnName("by_project");
|
.HasColumnName("by_project");
|
||||||
|
|
||||||
b.Property<Guid>("CompanyId")
|
b.Property<Guid?>("CompanyId")
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasColumnName("company_id");
|
.HasColumnName("company_id");
|
||||||
|
|
||||||
|
|||||||
+42
@@ -583,6 +583,48 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
resolved.Value.RouteId.ShouldBe(route.Value.Id);
|
resolved.Value.RouteId.ShouldBe(route.Value.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact(DisplayName = "Резолв маршрута без компании инициатора использует запасной маршрут без указанной компании")]
|
||||||
|
public async Task ResolveAsync_NoCompanyRoute_FallsBackToRouteWithoutCompany()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requestTypeId = await CreateRequestTypeAsync("No company route request");
|
||||||
|
var route = await _service.CreateAsync(
|
||||||
|
new CreateTemplateRouteRequest(requestTypeId, DepartmentId: null, CompanyId: null, ByProject: false, RequiresControl: false),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
// Action
|
||||||
|
var resolved = await _service.ResolveAsync(
|
||||||
|
new ResolveTemplateRouteRequest(requestTypeId, "someone@tnsu.kz", null, RouteStepStatus.Execution),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
resolved.IsSuccess.ShouldBeTrue();
|
||||||
|
resolved.Value.RouteId.ShouldBe(route.Value.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact(DisplayName = "Резолв маршрута предпочитает маршрут с компанией инициатора маршруту без компании")]
|
||||||
|
public async Task ResolveAsync_RouteWithCompanyExists_PreferredOverRouteWithoutCompany()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requestTypeId = await CreateRequestTypeAsync("Company priority request");
|
||||||
|
var companyWideRoute = await _service.CreateAsync(
|
||||||
|
new CreateTemplateRouteRequest(requestTypeId, DepartmentId: null, CompanyId: null, ByProject: false, RequiresControl: false),
|
||||||
|
CancellationToken.None);
|
||||||
|
var companySpecificRoute = await _service.CreateAsync(
|
||||||
|
new CreateTemplateRouteRequest(requestTypeId, DepartmentId: null, CompanyId, ByProject: false, RequiresControl: true),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
// Action
|
||||||
|
var resolved = await _service.ResolveAsync(
|
||||||
|
new ResolveTemplateRouteRequest(requestTypeId, "someone@tnsu.kz", null, RouteStepStatus.Execution),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
resolved.IsSuccess.ShouldBeTrue();
|
||||||
|
resolved.Value.RouteId.ShouldBe(companySpecificRoute.Value.Id);
|
||||||
|
resolved.Value.RouteId.ShouldNotBe(companyWideRoute.Value.Id);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact(DisplayName = "Резолв маршрута с несуществующим типом заявки возвращает неуспешный результат")]
|
[Fact(DisplayName = "Резолв маршрута с несуществующим типом заявки возвращает неуспешный результат")]
|
||||||
public async Task ResolveAsync_UnknownRequestType_ReturnsFailedResult()
|
public async Task ResolveAsync_UnknownRequestType_ReturnsFailedResult()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ function toFormState(editing: TemplateRoute | undefined, initialRequestTypeId: s
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
requestTypeId: String(editing.requestTypeId),
|
requestTypeId: String(editing.requestTypeId),
|
||||||
companyId: editing.companyId,
|
companyId: editing.companyId ?? '',
|
||||||
departmentId: editing.departmentId ?? '',
|
departmentId: editing.departmentId ?? '',
|
||||||
byProject: editing.byProject,
|
byProject: editing.byProject,
|
||||||
requiresControl: editing.requiresControl
|
requiresControl: editing.requiresControl
|
||||||
@@ -73,22 +73,19 @@ export const RouteFormModal: React.FC<IRouteFormModalProps> = ({
|
|||||||
submit.fail(texts.routes.validationRequestType);
|
submit.fail(texts.routes.validationRequestType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!form.companyId.trim()) {
|
|
||||||
submit.fail(texts.routes.validationCompany);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const companyId = form.companyId.trim() ? form.companyId.trim() : null;
|
||||||
const departmentId = form.departmentId.trim() ? form.departmentId.trim() : null;
|
const departmentId = form.departmentId.trim() ? form.departmentId.trim() : null;
|
||||||
const request = editing
|
const request = editing
|
||||||
? api.templateRoutes.update(editing.id, {
|
? api.templateRoutes.update(editing.id, {
|
||||||
companyId: form.companyId.trim(),
|
companyId,
|
||||||
departmentId,
|
departmentId,
|
||||||
byProject: form.byProject,
|
byProject: form.byProject,
|
||||||
requiresControl: form.requiresControl
|
requiresControl: form.requiresControl
|
||||||
})
|
})
|
||||||
: api.templateRoutes.create({
|
: api.templateRoutes.create({
|
||||||
requestTypeId: Number(form.requestTypeId),
|
requestTypeId: Number(form.requestTypeId),
|
||||||
companyId: form.companyId.trim(),
|
companyId,
|
||||||
departmentId,
|
departmentId,
|
||||||
byProject: form.byProject,
|
byProject: form.byProject,
|
||||||
requiresControl: form.requiresControl
|
requiresControl: form.requiresControl
|
||||||
@@ -120,11 +117,13 @@ export const RouteFormModal: React.FC<IRouteFormModalProps> = ({
|
|||||||
onChange={e => setForm({ ...form, requestTypeId: e.target.value })}
|
onChange={e => setForm({ ...form, requestTypeId: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
<Field label={texts.routes.companyLabel} required full hint={texts.routes.companyHint}>
|
<Field label={texts.routes.companyLabel} full hint={texts.routes.companyHint}>
|
||||||
<SearchableSelect
|
<SearchableSelect
|
||||||
options={companies.map(c => ({ value: c.id, label: c.name }))}
|
options={companies.map(c => ({ value: c.id, label: c.name }))}
|
||||||
value={form.companyId}
|
value={form.companyId}
|
||||||
loading={dictionaryLoading}
|
loading={dictionaryLoading}
|
||||||
|
allowClear
|
||||||
|
clearLabel={texts.routes.companyClear}
|
||||||
placeholder={texts.routes.companyPlaceholder}
|
placeholder={texts.routes.companyPlaceholder}
|
||||||
searchPlaceholder={texts.routes.companySearchPlaceholder}
|
searchPlaceholder={texts.routes.companySearchPlaceholder}
|
||||||
onChange={companyId =>
|
onChange={companyId =>
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export const RoutesScreen: React.FC<IRoutesScreenProps> = ({ api, dictionary, on
|
|||||||
header: texts.routes.columnRequestType,
|
header: texts.routes.columnRequestType,
|
||||||
render: r => <PrimaryCell>{requestTypeName(r.requestTypeId)}</PrimaryCell>
|
render: r => <PrimaryCell>{requestTypeName(r.requestTypeId)}</PrimaryCell>
|
||||||
},
|
},
|
||||||
{ key: 'company', header: texts.routes.columnCompany, render: r => companyName(r.companyId) },
|
{ key: 'company', header: texts.routes.columnCompany, render: r => (r.companyId ? companyName(r.companyId) : texts.common.empty) },
|
||||||
{ key: 'department', header: texts.routes.columnDepartment, render: r => (r.departmentId ? departmentName(r.departmentId) : texts.common.empty) },
|
{ key: 'department', header: texts.routes.columnDepartment, render: r => (r.departmentId ? departmentName(r.departmentId) : texts.common.empty) },
|
||||||
{
|
{
|
||||||
key: 'byProject',
|
key: 'byProject',
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ export interface TemplateRoute {
|
|||||||
id: number;
|
id: number;
|
||||||
requestTypeId: number;
|
requestTypeId: number;
|
||||||
departmentId: string | null;
|
departmentId: string | null;
|
||||||
companyId: string;
|
/** Опционально — маршрут без компании служит запасным вариантом при resolve. */
|
||||||
|
companyId: string | null;
|
||||||
byProject: boolean;
|
byProject: boolean;
|
||||||
requiresControl: boolean;
|
requiresControl: boolean;
|
||||||
}
|
}
|
||||||
@@ -12,7 +13,7 @@ export interface TemplateRoute {
|
|||||||
export interface CreateTemplateRouteInput {
|
export interface CreateTemplateRouteInput {
|
||||||
requestTypeId: number;
|
requestTypeId: number;
|
||||||
departmentId: string | null;
|
departmentId: string | null;
|
||||||
companyId: string;
|
companyId: string | null;
|
||||||
byProject: boolean;
|
byProject: boolean;
|
||||||
requiresControl: boolean;
|
requiresControl: boolean;
|
||||||
}
|
}
|
||||||
@@ -20,14 +21,14 @@ export interface CreateTemplateRouteInput {
|
|||||||
// Тип заявки у существующего маршрута не меняется — его нет в update-контракте.
|
// Тип заявки у существующего маршрута не меняется — его нет в update-контракте.
|
||||||
export interface UpdateTemplateRouteInput {
|
export interface UpdateTemplateRouteInput {
|
||||||
departmentId: string | null;
|
departmentId: string | null;
|
||||||
companyId: string;
|
companyId: string | null;
|
||||||
byProject: boolean;
|
byProject: boolean;
|
||||||
requiresControl: boolean;
|
requiresControl: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PatchTemplateRouteInput {
|
export interface PatchTemplateRouteInput {
|
||||||
departmentId?: string | null;
|
departmentId?: string | null;
|
||||||
companyId?: string;
|
companyId?: string | null;
|
||||||
byProject?: boolean;
|
byProject?: boolean;
|
||||||
requiresControl?: boolean;
|
requiresControl?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,9 +152,10 @@ export const texts = {
|
|||||||
requestTypeLabel: 'Тип заявки',
|
requestTypeLabel: 'Тип заявки',
|
||||||
requestTypePlaceholder: 'Выберите тип заявки',
|
requestTypePlaceholder: 'Выберите тип заявки',
|
||||||
companyLabel: 'Компания',
|
companyLabel: 'Компания',
|
||||||
companyHint: 'Справочник 1С (api.tnsu.kz).',
|
companyHint: 'Необязательно — пусто означает запасной маршрут, если для компании инициатора своего маршрута нет.',
|
||||||
companyPlaceholder: 'Выберите компанию',
|
companyPlaceholder: 'Без компании (запасной маршрут)',
|
||||||
companySearchPlaceholder: 'Поиск по названию компании...',
|
companySearchPlaceholder: 'Поиск по названию компании...',
|
||||||
|
companyClear: 'Без компании (запасной маршрут)',
|
||||||
departmentLabel: 'Отдел',
|
departmentLabel: 'Отдел',
|
||||||
departmentHint: 'Необязательно — пусто означает маршрут «по умолчанию» для всей компании.',
|
departmentHint: 'Необязательно — пусто означает маршрут «по умолчанию» для всей компании.',
|
||||||
departmentHintNoCompany: 'Сначала выберите компанию.',
|
departmentHintNoCompany: 'Сначала выберите компанию.',
|
||||||
@@ -166,7 +167,6 @@ export const texts = {
|
|||||||
controlLabel: 'Контроль',
|
controlLabel: 'Контроль',
|
||||||
controlCheckbox: 'Требуется отдельный контроль исполнения',
|
controlCheckbox: 'Требуется отдельный контроль исполнения',
|
||||||
validationRequestType: 'Выберите тип заявки.',
|
validationRequestType: 'Выберите тип заявки.',
|
||||||
validationCompany: 'Компания обязательна (Guid из справочника api.tnsu.kz).',
|
|
||||||
deleteTitle: 'Удалить маршрут?',
|
deleteTitle: 'Удалить маршрут?',
|
||||||
deleteText: (routeId: number): string =>
|
deleteText: (routeId: number): string =>
|
||||||
`Маршрут #${routeId} и все его шаги будут удалены безвозвратно.`
|
`Маршрут #${routeId} и все его шаги будут удалены безвозвратно.`
|
||||||
|
|||||||
Reference in New Issue
Block a user