@@ -16,7 +16,6 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
builder.Services.AddApprovalRouteInfrastructure(builder.Configuration);
|
builder.Services.AddApprovalRouteInfrastructure(builder.Configuration);
|
||||||
builder.Services.AddApprovalRouteApplication();
|
builder.Services.AddApprovalRouteApplication();
|
||||||
builder.Services.AddApprovalRouteAuth(builder.Configuration);
|
builder.Services.AddApprovalRouteAuth(builder.Configuration);
|
||||||
builder.Services.AddHttpContextAccessor();
|
|
||||||
builder.Services.AddScoped<ICurrentUser, CurrentUserAccessor>();
|
builder.Services.AddScoped<ICurrentUser, CurrentUserAccessor>();
|
||||||
builder.Services.AddOpenApi(options =>
|
builder.Services.AddOpenApi(options =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,22 +6,22 @@ public sealed record TemplateRouteDto(
|
|||||||
int Id,
|
int Id,
|
||||||
RouteType RouteType,
|
RouteType RouteType,
|
||||||
int RequestTypeId,
|
int RequestTypeId,
|
||||||
string? Department,
|
Guid? DepartmentId,
|
||||||
string Company,
|
Guid CompanyId,
|
||||||
bool ByProject,
|
bool ByProject,
|
||||||
bool RequiresControl);
|
bool RequiresControl);
|
||||||
|
|
||||||
public sealed record CreateTemplateRouteRequest(
|
public sealed record CreateTemplateRouteRequest(
|
||||||
RouteType RouteType,
|
RouteType RouteType,
|
||||||
int RequestTypeId,
|
int RequestTypeId,
|
||||||
string? Department,
|
Guid? DepartmentId,
|
||||||
string Company,
|
Guid CompanyId,
|
||||||
bool ByProject,
|
bool ByProject,
|
||||||
bool RequiresControl);
|
bool RequiresControl);
|
||||||
|
|
||||||
public sealed record UpdateTemplateRouteRequest(
|
public sealed record UpdateTemplateRouteRequest(
|
||||||
RouteType RouteType,
|
RouteType RouteType,
|
||||||
string? Department,
|
Guid? DepartmentId,
|
||||||
string Company,
|
Guid CompanyId,
|
||||||
bool ByProject,
|
bool ByProject,
|
||||||
bool RequiresControl);
|
bool RequiresControl);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public sealed class TemplateRouteService(
|
|||||||
|
|
||||||
var items = await query
|
var items = await query
|
||||||
.Select(x => new TemplateRouteDto(
|
.Select(x => new TemplateRouteDto(
|
||||||
x.Id, x.RouteType, x.RequestTypeId, x.Department, x.Company, x.ByProject, x.RequiresControl))
|
x.Id, x.RouteType, x.RequestTypeId, x.DepartmentId, x.CompanyId, x.ByProject, x.RequiresControl))
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
return Result.Ok<IReadOnlyList<TemplateRouteDto>>(items);
|
return Result.Ok<IReadOnlyList<TemplateRouteDto>>(items);
|
||||||
@@ -43,8 +43,7 @@ public sealed class TemplateRouteService(
|
|||||||
if (!requestTypeExists)
|
if (!requestTypeExists)
|
||||||
return Result.Fail<TemplateRouteDto>(new NotFoundError("Тип заявки", r.RequestTypeId));
|
return Result.Fail<TemplateRouteDto>(new NotFoundError("Тип заявки", r.RequestTypeId));
|
||||||
|
|
||||||
var department = Normalize(r.Department);
|
var duplicate = await HasDuplicateAsync(r.RequestTypeId, r.DepartmentId, r.CompanyId, r.ByProject, excludeId: null, cancellationToken);
|
||||||
var duplicate = await HasDuplicateAsync(r.RequestTypeId, department, r.Company, r.ByProject, excludeId: null, cancellationToken);
|
|
||||||
if (duplicate)
|
if (duplicate)
|
||||||
return Result.Fail<TemplateRouteDto>(new ConflictError(
|
return Result.Fail<TemplateRouteDto>(new ConflictError(
|
||||||
"Маршрут с таким типом заявки, отделом, компанией и признаком «по проекту» уже существует."));
|
"Маршрут с таким типом заявки, отделом, компанией и признаком «по проекту» уже существует."));
|
||||||
@@ -53,8 +52,8 @@ public sealed class TemplateRouteService(
|
|||||||
{
|
{
|
||||||
RouteType = r.RouteType,
|
RouteType = r.RouteType,
|
||||||
RequestTypeId = r.RequestTypeId,
|
RequestTypeId = r.RequestTypeId,
|
||||||
Department = department,
|
DepartmentId = r.DepartmentId,
|
||||||
Company = r.Company,
|
CompanyId = r.CompanyId,
|
||||||
ByProject = r.ByProject,
|
ByProject = r.ByProject,
|
||||||
RequiresControl = r.RequiresControl
|
RequiresControl = r.RequiresControl
|
||||||
};
|
};
|
||||||
@@ -70,15 +69,14 @@ public sealed class TemplateRouteService(
|
|||||||
if (entity is null)
|
if (entity is null)
|
||||||
return Result.Fail<TemplateRouteDto>(new NotFoundError("Маршрут", id));
|
return Result.Fail<TemplateRouteDto>(new NotFoundError("Маршрут", id));
|
||||||
|
|
||||||
var department = Normalize(r.Department);
|
var duplicate = await HasDuplicateAsync(entity.RequestTypeId, r.DepartmentId, r.CompanyId, r.ByProject, excludeId: entity.Id, cancellationToken);
|
||||||
var duplicate = await HasDuplicateAsync(entity.RequestTypeId, department, r.Company, r.ByProject, excludeId: entity.Id, cancellationToken);
|
|
||||||
if (duplicate)
|
if (duplicate)
|
||||||
return Result.Fail<TemplateRouteDto>(new ConflictError(
|
return Result.Fail<TemplateRouteDto>(new ConflictError(
|
||||||
"Маршрут с таким типом заявки, отделом, компанией и признаком «по проекту» уже существует."));
|
"Маршрут с таким типом заявки, отделом, компанией и признаком «по проекту» уже существует."));
|
||||||
|
|
||||||
entity.RouteType = r.RouteType;
|
entity.RouteType = r.RouteType;
|
||||||
entity.Department = department;
|
entity.DepartmentId = r.DepartmentId;
|
||||||
entity.Company = r.Company;
|
entity.CompanyId = r.CompanyId;
|
||||||
entity.ByProject = r.ByProject;
|
entity.ByProject = r.ByProject;
|
||||||
entity.RequiresControl = r.RequiresControl;
|
entity.RequiresControl = r.RequiresControl;
|
||||||
await db.SaveChangesAsync(cancellationToken);
|
await db.SaveChangesAsync(cancellationToken);
|
||||||
@@ -116,23 +114,22 @@ public sealed class TemplateRouteService(
|
|||||||
new NotFoundError("Сотрудник в справочнике api.tnsu.kz по email", email));
|
new NotFoundError("Сотрудник в справочнике api.tnsu.kz по email", email));
|
||||||
|
|
||||||
var byProject = request.ProjectId is not null;
|
var byProject = request.ProjectId is not null;
|
||||||
var department = Normalize(employee.Department);
|
|
||||||
|
|
||||||
var route = await db.TemplateRoutes.AsNoTracking()
|
var route = await db.TemplateRoutes.AsNoTracking()
|
||||||
.Include(x => x.Steps)
|
.Include(x => x.Steps)
|
||||||
.Where(x =>
|
.Where(x =>
|
||||||
x.RequestTypeId == request.RequestTypeId &&
|
x.RequestTypeId == request.RequestTypeId &&
|
||||||
x.Company == employee.Company &&
|
x.CompanyId == employee.CompanyId &&
|
||||||
x.ByProject == byProject &&
|
x.ByProject == byProject &&
|
||||||
x.Department == department)
|
x.DepartmentId == employee.DepartmentId)
|
||||||
.FirstOrDefaultAsync(cancellationToken)
|
.FirstOrDefaultAsync(cancellationToken)
|
||||||
?? await db.TemplateRoutes.AsNoTracking()
|
?? await db.TemplateRoutes.AsNoTracking()
|
||||||
.Include(x => x.Steps)
|
.Include(x => x.Steps)
|
||||||
.Where(x =>
|
.Where(x =>
|
||||||
x.RequestTypeId == request.RequestTypeId &&
|
x.RequestTypeId == request.RequestTypeId &&
|
||||||
x.Company == employee.Company &&
|
x.CompanyId == employee.CompanyId &&
|
||||||
x.ByProject == byProject &&
|
x.ByProject == byProject &&
|
||||||
x.Department == null)
|
x.DepartmentId == null)
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
|
|
||||||
if (route is null)
|
if (route is null)
|
||||||
@@ -154,12 +151,12 @@ public sealed class TemplateRouteService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Task<bool> HasDuplicateAsync(
|
private Task<bool> HasDuplicateAsync(
|
||||||
int requestTypeId, string? department, string company, 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 &&
|
||||||
x.Department == department &&
|
x.DepartmentId == departmentId &&
|
||||||
x.Company == company &&
|
x.CompanyId == companyId &&
|
||||||
x.ByProject == byProject,
|
x.ByProject == byProject,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
@@ -181,8 +178,6 @@ public sealed class TemplateRouteService(
|
|||||||
step.ProjectRole, step.HeadType, step.Status, step.AllowReassignToOtherApprovers);
|
step.ProjectRole, step.HeadType, step.Status, step.AllowReassignToOtherApprovers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string? Normalize(string? value) => string.IsNullOrWhiteSpace(value) ? null : value;
|
|
||||||
|
|
||||||
private static TemplateRouteDto ToDto(TemplateRoute e) => new(
|
private static TemplateRouteDto ToDto(TemplateRoute e) => new(
|
||||||
e.Id, e.RouteType, e.RequestTypeId, e.Department, e.Company, e.ByProject, e.RequiresControl);
|
e.Id, e.RouteType, e.RequestTypeId, e.DepartmentId, e.CompanyId, e.ByProject, e.RequiresControl);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -7,7 +7,7 @@ public sealed class CreateTemplateRouteRequestValidator : AbstractValidator<Crea
|
|||||||
{
|
{
|
||||||
public CreateTemplateRouteRequestValidator()
|
public CreateTemplateRouteRequestValidator()
|
||||||
{
|
{
|
||||||
RuleFor(x => x.Company).NotEmpty().WithMessage("Компания обязательна.");
|
RuleFor(x => x.CompanyId).NotEqual(Guid.Empty).WithMessage("Компания обязательна.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ public sealed class UpdateTemplateRouteRequestValidator : AbstractValidator<Upda
|
|||||||
{
|
{
|
||||||
public UpdateTemplateRouteRequestValidator()
|
public UpdateTemplateRouteRequestValidator()
|
||||||
{
|
{
|
||||||
RuleFor(x => x.Company).NotEmpty().WithMessage("Компания обязательна.");
|
RuleFor(x => x.CompanyId).NotEqual(Guid.Empty).WithMessage("Компания обязательна.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ public class TemplateRoute
|
|||||||
public RouteType RouteType { get; set; } = RouteType.Sequential;
|
public RouteType RouteType { get; set; } = RouteType.Sequential;
|
||||||
public int RequestTypeId { get; set; }
|
public int RequestTypeId { get; set; }
|
||||||
|
|
||||||
// Компания и отдел инициатора — названия из справочника AD (api-tnsu-portal /Employee/ByEmail),
|
// Id компании/отдела инициатора из 1С/AD (api-tnsu-portal /Employee/ByEmail) — без FK в этой БД.
|
||||||
// хранятся строками без FK в этой БД. Отдел опционален, компания обязательна.
|
// Отдел опционален, компания обязательна.
|
||||||
public string? Department { get; set; }
|
public Guid? DepartmentId { get; set; }
|
||||||
public string Company { get; set; } = string.Empty;
|
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
@@ -13,6 +13,8 @@ public static class InfrastructureServiceCollectionExtensions
|
|||||||
public static IServiceCollection AddApprovalRouteInfrastructure(
|
public static IServiceCollection AddApprovalRouteInfrastructure(
|
||||||
this IServiceCollection services, IConfiguration configuration)
|
this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
|
services.AddHttpContextAccessor();
|
||||||
|
|
||||||
services.AddDbContext<ApprovalRouteDbContext>(opt =>
|
services.AddDbContext<ApprovalRouteDbContext>(opt =>
|
||||||
opt.UseNpgsql(configuration.GetConnectionString("Postgres"))
|
opt.UseNpgsql(configuration.GetConnectionString("Postgres"))
|
||||||
.UseSnakeCaseNamingConvention());
|
.UseSnakeCaseNamingConvention());
|
||||||
|
|||||||
@@ -22,10 +22,8 @@ public class ApprovalRouteDbContext(DbContextOptions<ApprovalRouteDbContext> opt
|
|||||||
{
|
{
|
||||||
e.HasKey(x => x.Id);
|
e.HasKey(x => x.Id);
|
||||||
e.Property(x => x.RouteType).HasConversion<string>().HasMaxLength(32).IsRequired();
|
e.Property(x => x.RouteType).HasConversion<string>().HasMaxLength(32).IsRequired();
|
||||||
e.Property(x => x.Company).HasMaxLength(256).IsRequired();
|
|
||||||
e.Property(x => x.Department).HasMaxLength(256);
|
|
||||||
e.HasOne(x => x.RequestType).WithMany(x => x.Routes).HasForeignKey(x => x.RequestTypeId);
|
e.HasOne(x => x.RequestType).WithMany(x => x.Routes).HasForeignKey(x => x.RequestTypeId);
|
||||||
e.HasIndex(x => new { x.RequestTypeId, x.Department, x.Company, x.ByProject }).IsUnique();
|
e.HasIndex(x => new { x.RequestTypeId, x.DepartmentId, x.CompanyId, x.ByProject }).IsUnique();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<TemplateRouteStep>(e =>
|
modelBuilder.Entity<TemplateRouteStep>(e =>
|
||||||
|
|||||||
+10
-12
@@ -1,4 +1,5 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
@@ -11,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("20260724085143_InitialCreate")]
|
[Migration("20260724114550_InitialCreate")]
|
||||||
partial class InitialCreate
|
partial class InitialCreate
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -64,16 +65,13 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
|||||||
.HasColumnType("boolean")
|
.HasColumnType("boolean")
|
||||||
.HasColumnName("by_project");
|
.HasColumnName("by_project");
|
||||||
|
|
||||||
b.Property<string>("Company")
|
b.Property<Guid>("CompanyId")
|
||||||
.IsRequired()
|
.HasColumnType("uuid")
|
||||||
.HasMaxLength(256)
|
.HasColumnName("company_id");
|
||||||
.HasColumnType("character varying(256)")
|
|
||||||
.HasColumnName("company");
|
|
||||||
|
|
||||||
b.Property<string>("Department")
|
b.Property<Guid?>("DepartmentId")
|
||||||
.HasMaxLength(256)
|
.HasColumnType("uuid")
|
||||||
.HasColumnType("character varying(256)")
|
.HasColumnName("department_id");
|
||||||
.HasColumnName("department");
|
|
||||||
|
|
||||||
b.Property<int>("RequestTypeId")
|
b.Property<int>("RequestTypeId")
|
||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
@@ -92,9 +90,9 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_template_routes");
|
.HasName("pk_template_routes");
|
||||||
|
|
||||||
b.HasIndex("RequestTypeId", "Department", "Company", "ByProject")
|
b.HasIndex("RequestTypeId", "DepartmentId", "CompanyId", "ByProject")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("ix_template_routes_request_type_id_department_company_by_proje");
|
.HasDatabaseName("ix_template_routes_request_type_id_department_id_company_id_by");
|
||||||
|
|
||||||
b.ToTable("template_routes", (string)null);
|
b.ToTable("template_routes", (string)null);
|
||||||
});
|
});
|
||||||
+6
-5
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
@@ -33,8 +34,8 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
|||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
route_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
route_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||||
request_type_id = table.Column<int>(type: "integer", nullable: false),
|
request_type_id = table.Column<int>(type: "integer", nullable: false),
|
||||||
department = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
department_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
company = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
company_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
by_project = table.Column<bool>(type: "boolean", nullable: false),
|
by_project = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
requires_control = table.Column<bool>(type: "boolean", nullable: false)
|
requires_control = table.Column<bool>(type: "boolean", nullable: false)
|
||||||
},
|
},
|
||||||
@@ -81,9 +82,9 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
|||||||
columns: new[] { "route_id", "order_no" });
|
columns: new[] { "route_id", "order_no" });
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "ix_template_routes_request_type_id_department_company_by_proje",
|
name: "ix_template_routes_request_type_id_department_id_company_id_by",
|
||||||
table: "template_routes",
|
table: "template_routes",
|
||||||
columns: new[] { "request_type_id", "department", "company", "by_project" },
|
columns: new[] { "request_type_id", "department_id", "company_id", "by_project" },
|
||||||
unique: true);
|
unique: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
+9
-11
@@ -1,4 +1,5 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
@@ -61,16 +62,13 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
|||||||
.HasColumnType("boolean")
|
.HasColumnType("boolean")
|
||||||
.HasColumnName("by_project");
|
.HasColumnName("by_project");
|
||||||
|
|
||||||
b.Property<string>("Company")
|
b.Property<Guid>("CompanyId")
|
||||||
.IsRequired()
|
.HasColumnType("uuid")
|
||||||
.HasMaxLength(256)
|
.HasColumnName("company_id");
|
||||||
.HasColumnType("character varying(256)")
|
|
||||||
.HasColumnName("company");
|
|
||||||
|
|
||||||
b.Property<string>("Department")
|
b.Property<Guid?>("DepartmentId")
|
||||||
.HasMaxLength(256)
|
.HasColumnType("uuid")
|
||||||
.HasColumnType("character varying(256)")
|
.HasColumnName("department_id");
|
||||||
.HasColumnName("department");
|
|
||||||
|
|
||||||
b.Property<int>("RequestTypeId")
|
b.Property<int>("RequestTypeId")
|
||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
@@ -89,9 +87,9 @@ namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_template_routes");
|
.HasName("pk_template_routes");
|
||||||
|
|
||||||
b.HasIndex("RequestTypeId", "Department", "Company", "ByProject")
|
b.HasIndex("RequestTypeId", "DepartmentId", "CompanyId", "ByProject")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("ix_template_routes_request_type_id_department_company_by_proje");
|
.HasDatabaseName("ix_template_routes_request_type_id_department_id_company_id_by");
|
||||||
|
|
||||||
b.ToTable("template_routes", (string)null);
|
b.ToTable("template_routes", (string)null);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public sealed class HttpTnsuPortalClient(
|
|||||||
if (employee is null)
|
if (employee is null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return new EmployeeDirectoryInfo(employee.Company ?? string.Empty, employee.Section ?? string.Empty);
|
return new EmployeeDirectoryInfo(employee.CompanyId ?? Guid.Empty, employee.DepartmentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IReadOnlyList<ProjectExecutorInfo>> GetProjectExecutorsAsync(Guid projectId, CancellationToken cancellationToken)
|
public async Task<IReadOnlyList<ProjectExecutorInfo>> GetProjectExecutorsAsync(Guid projectId, CancellationToken cancellationToken)
|
||||||
@@ -82,8 +82,8 @@ public sealed class HttpTnsuPortalClient(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private sealed record EmployeeResponse(
|
private sealed record EmployeeResponse(
|
||||||
[property: JsonPropertyName("company")] string? Company,
|
[property: JsonPropertyName("companyId")] Guid? CompanyId,
|
||||||
[property: JsonPropertyName("section")] string? Section);
|
[property: JsonPropertyName("departmentId")] Guid? DepartmentId);
|
||||||
|
|
||||||
private sealed record ProjectResponse(
|
private sealed record ProjectResponse(
|
||||||
[property: JsonPropertyName("projectManager")] string? ProjectManager,
|
[property: JsonPropertyName("projectManager")] string? ProjectManager,
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ using Tnsu.ApprovalRoute.Domain.Enums;
|
|||||||
|
|
||||||
namespace Tnsu.ApprovalRoute.Infrastructure.TnsuPortal;
|
namespace Tnsu.ApprovalRoute.Infrastructure.TnsuPortal;
|
||||||
|
|
||||||
public sealed record EmployeeDirectoryInfo(string Company, string Department);
|
public sealed record EmployeeDirectoryInfo(Guid CompanyId, Guid? DepartmentId);
|
||||||
|
|
||||||
public sealed record ProjectExecutorInfo(ProjectRole ProjectRole, string Email);
|
public sealed record ProjectExecutorInfo(ProjectRole ProjectRole, string Email);
|
||||||
|
|||||||
+13
-13
@@ -12,8 +12,8 @@ namespace Tnsu.ApprovalRoute.IntegrationTests.ApplicationServices;
|
|||||||
|
|
||||||
public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFixture>
|
public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFixture>
|
||||||
{
|
{
|
||||||
private const string Company = "ТОО TANSU Construction";
|
private static readonly Guid CompanyId = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
private const string Department = "IT";
|
private static readonly Guid DepartmentId = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
|
|
||||||
private readonly ServiceProvider _services;
|
private readonly ServiceProvider _services;
|
||||||
private readonly TemplateRouteService _service;
|
private readonly TemplateRouteService _service;
|
||||||
@@ -24,7 +24,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
var portalClient = new Mock<ITnsuPortalClient>();
|
var portalClient = new Mock<ITnsuPortalClient>();
|
||||||
portalClient
|
portalClient
|
||||||
.Setup(x => x.GetEmployeeByEmailAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
.Setup(x => x.GetEmployeeByEmailAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||||
.ReturnsAsync(new EmployeeDirectoryInfo(Company, Department));
|
.ReturnsAsync(new EmployeeDirectoryInfo(CompanyId, DepartmentId));
|
||||||
portalClient
|
portalClient
|
||||||
.Setup(x => x.GetProjectExecutorsAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
.Setup(x => x.GetProjectExecutorsAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
||||||
.ReturnsAsync([new ProjectExecutorInfo(ProjectRole.ProjectManager, "pm@tnsu.kz")]);
|
.ReturnsAsync([new ProjectExecutorInfo(ProjectRole.ProjectManager, "pm@tnsu.kz")]);
|
||||||
@@ -45,7 +45,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
|
|
||||||
// Action
|
// Action
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, Department, Company, ByProject: false, RequiresControl: true),
|
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, DepartmentId, CompanyId, ByProject: false, RequiresControl: true),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -54,7 +54,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
|
|
||||||
var fetched = await _service.GetAsync(created.Value.Id, CancellationToken.None);
|
var fetched = await _service.GetAsync(created.Value.Id, CancellationToken.None);
|
||||||
fetched.IsSuccess.ShouldBeTrue();
|
fetched.IsSuccess.ShouldBeTrue();
|
||||||
fetched.Value.Company.ShouldBe(Company);
|
fetched.Value.CompanyId.ShouldBe(CompanyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(DisplayName = "Создание маршрута с несуществующим типом заявки возвращает неуспешный результат")]
|
[Fact(DisplayName = "Создание маршрута с несуществующим типом заявки возвращает неуспешный результат")]
|
||||||
@@ -62,7 +62,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
{
|
{
|
||||||
// Action
|
// Action
|
||||||
var result = await _service.CreateAsync(
|
var result = await _service.CreateAsync(
|
||||||
new CreateTemplateRouteRequest(RouteType.Sequential, 999999, Department, Company, ByProject: false, RequiresControl: false),
|
new CreateTemplateRouteRequest(RouteType.Sequential, 999999, DepartmentId, CompanyId, ByProject: false, RequiresControl: false),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -74,7 +74,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var requestTypeId = await CreateRequestTypeAsync("Business trip");
|
var requestTypeId = await CreateRequestTypeAsync("Business trip");
|
||||||
var request = new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, Department, Company, ByProject: false, RequiresControl: false);
|
var request = new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, DepartmentId, CompanyId, ByProject: false, RequiresControl: false);
|
||||||
var first = await _service.CreateAsync(request, CancellationToken.None);
|
var first = await _service.CreateAsync(request, CancellationToken.None);
|
||||||
first.IsSuccess.ShouldBeTrue();
|
first.IsSuccess.ShouldBeTrue();
|
||||||
|
|
||||||
@@ -91,13 +91,13 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
// Arrange
|
// Arrange
|
||||||
var requestTypeId = await CreateRequestTypeAsync("Sick leave");
|
var requestTypeId = await CreateRequestTypeAsync("Sick leave");
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, Department, Company, ByProject: false, RequiresControl: false),
|
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, DepartmentId, CompanyId, ByProject: false, RequiresControl: false),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
var updated = await _service.UpdateAsync(
|
var updated = await _service.UpdateAsync(
|
||||||
created.Value.Id,
|
created.Value.Id,
|
||||||
new UpdateTemplateRouteRequest(RouteType.Parallel, Department, Company, ByProject: false, RequiresControl: true),
|
new UpdateTemplateRouteRequest(RouteType.Parallel, DepartmentId, CompanyId, ByProject: false, RequiresControl: true),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -112,7 +112,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
// Arrange
|
// Arrange
|
||||||
var requestTypeId = await CreateRequestTypeAsync("Remote work");
|
var requestTypeId = await CreateRequestTypeAsync("Remote work");
|
||||||
var created = await _service.CreateAsync(
|
var created = await _service.CreateAsync(
|
||||||
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, Department, Company, ByProject: false, RequiresControl: false),
|
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, DepartmentId, CompanyId, ByProject: false, RequiresControl: false),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
@@ -131,7 +131,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
// Arrange
|
// Arrange
|
||||||
var requestTypeId = await CreateRequestTypeAsync("Exact match request");
|
var requestTypeId = await CreateRequestTypeAsync("Exact match request");
|
||||||
var route = await _service.CreateAsync(
|
var route = await _service.CreateAsync(
|
||||||
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, Department, Company, ByProject: false, RequiresControl: true),
|
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, DepartmentId, CompanyId, ByProject: false, RequiresControl: true),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
@@ -158,7 +158,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
// Arrange
|
// Arrange
|
||||||
var requestTypeId = await CreateRequestTypeAsync("Project request");
|
var requestTypeId = await CreateRequestTypeAsync("Project request");
|
||||||
var route = await _service.CreateAsync(
|
var route = await _service.CreateAsync(
|
||||||
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, Department, Company, ByProject: true, RequiresControl: false),
|
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, DepartmentId, CompanyId, ByProject: true, RequiresControl: false),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
var stepService = _services.GetRequiredService<TemplateRouteStepService>();
|
||||||
@@ -184,7 +184,7 @@ public sealed class TemplateRouteServiceTests : IClassFixture<PostgresDatabaseFi
|
|||||||
// Arrange
|
// Arrange
|
||||||
var requestTypeId = await CreateRequestTypeAsync("Company-wide request");
|
var requestTypeId = await CreateRequestTypeAsync("Company-wide request");
|
||||||
var route = await _service.CreateAsync(
|
var route = await _service.CreateAsync(
|
||||||
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, Department: null, Company, ByProject: false, RequiresControl: false),
|
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, DepartmentId: null, CompanyId, ByProject: false, RequiresControl: false),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
|
|||||||
+2
-2
@@ -10,7 +10,7 @@ namespace Tnsu.ApprovalRoute.IntegrationTests.ApplicationServices;
|
|||||||
|
|
||||||
public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDatabaseFixture>
|
public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDatabaseFixture>
|
||||||
{
|
{
|
||||||
private const string Company = "ТОО TANSU Construction";
|
private static readonly Guid CompanyId = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
|
|
||||||
private readonly TemplateRouteStepService _service;
|
private readonly TemplateRouteStepService _service;
|
||||||
private readonly TemplateRouteService _routeService;
|
private readonly TemplateRouteService _routeService;
|
||||||
@@ -30,7 +30,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture<PostgresDataba
|
|||||||
new CreateRequestTypeRequest(requestTypeName, $"/{requestTypeName}"), CancellationToken.None)).Value.Id;
|
new CreateRequestTypeRequest(requestTypeName, $"/{requestTypeName}"), CancellationToken.None)).Value.Id;
|
||||||
|
|
||||||
var route = await _routeService.CreateAsync(
|
var route = await _routeService.CreateAsync(
|
||||||
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, null, Company, byProject, RequiresControl: false),
|
new CreateTemplateRouteRequest(RouteType.Sequential, requestTypeId, null, CompanyId, byProject, RequiresControl: false),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
return route.Value.Id;
|
return route.Value.Id;
|
||||||
|
|||||||
Reference in New Issue
Block a user