diff --git a/backend/Directory.Packages.props b/backend/Directory.Packages.props index 1d529ff..55b277f 100644 --- a/backend/Directory.Packages.props +++ b/backend/Directory.Packages.props @@ -13,7 +13,6 @@ - diff --git a/backend/src/Tnsu.ApprovalRoute.Api/Controllers/RequestTypesController.cs b/backend/src/Tnsu.ApprovalRoute.Api/Controllers/RequestTypesController.cs index fda5461..9c0644a 100644 --- a/backend/src/Tnsu.ApprovalRoute.Api/Controllers/RequestTypesController.cs +++ b/backend/src/Tnsu.ApprovalRoute.Api/Controllers/RequestTypesController.cs @@ -1,4 +1,3 @@ -using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tnsu.ApprovalRoute.Api.Common; @@ -9,25 +8,25 @@ namespace Tnsu.ApprovalRoute.Api.Controllers; [ApiController] [Authorize] [Route("api/request-types")] -public sealed class RequestTypesController(ISender mediator) : ControllerBase +public sealed class RequestTypesController(RequestTypeService service) : ControllerBase { [HttpGet] public async Task List(CancellationToken ct) => - (await mediator.Send(new ListRequestTypesQuery(), ct)).ToActionResult(); + (await service.ListAsync(ct)).ToActionResult(); [HttpGet("{id:guid}")] public async Task Get(Guid id, CancellationToken ct) => - (await mediator.Send(new GetRequestTypeQuery(id), ct)).ToActionResult(); + (await service.GetAsync(id, ct)).ToActionResult(); [HttpPost] public async Task Create(CreateRequestTypeRequest body, CancellationToken ct) => - (await mediator.Send(new CreateRequestTypeCommand(body.Name, body.PageUrl), ct)).ToActionResult(); + (await service.CreateAsync(body.Name, body.PageUrl, ct)).ToActionResult(); [HttpPut("{id:guid}")] public async Task Update(Guid id, UpdateRequestTypeRequest body, CancellationToken ct) => - (await mediator.Send(new UpdateRequestTypeCommand(id, body.Name, body.PageUrl), ct)).ToActionResult(); + (await service.UpdateAsync(id, body.Name, body.PageUrl, ct)).ToActionResult(); [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken ct) => - (await mediator.Send(new DeleteRequestTypeCommand(id), ct)).ToActionResult(); + (await service.DeleteAsync(id, ct)).ToActionResult(); } diff --git a/backend/src/Tnsu.ApprovalRoute.Api/Controllers/TemplateRouteStepsController.cs b/backend/src/Tnsu.ApprovalRoute.Api/Controllers/TemplateRouteStepsController.cs index f069f64..57d9e79 100644 --- a/backend/src/Tnsu.ApprovalRoute.Api/Controllers/TemplateRouteStepsController.cs +++ b/backend/src/Tnsu.ApprovalRoute.Api/Controllers/TemplateRouteStepsController.cs @@ -1,4 +1,3 @@ -using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tnsu.ApprovalRoute.Api.Common; @@ -9,25 +8,25 @@ namespace Tnsu.ApprovalRoute.Api.Controllers; [ApiController] [Authorize] [Route("api/template-routes/{routeId:guid}/steps")] -public sealed class TemplateRouteStepsController(ISender mediator) : ControllerBase +public sealed class TemplateRouteStepsController(TemplateRouteStepService service) : ControllerBase { [HttpGet] public async Task List(Guid routeId, CancellationToken ct) => - (await mediator.Send(new ListTemplateRouteStepsQuery(routeId), ct)).ToActionResult(); + (await service.ListAsync(routeId, ct)).ToActionResult(); [HttpGet("{id:guid}")] public async Task Get(Guid routeId, Guid id, CancellationToken ct) => - (await mediator.Send(new GetTemplateRouteStepQuery(routeId, id), ct)).ToActionResult(); + (await service.GetAsync(routeId, id, ct)).ToActionResult(); [HttpPost] public async Task Create(Guid routeId, CreateTemplateRouteStepRequest body, CancellationToken ct) => - (await mediator.Send(new CreateTemplateRouteStepCommand(routeId, body), ct)).ToActionResult(); + (await service.CreateAsync(routeId, body, ct)).ToActionResult(); [HttpPut("{id:guid}")] public async Task Update(Guid routeId, Guid id, UpdateTemplateRouteStepRequest body, CancellationToken ct) => - (await mediator.Send(new UpdateTemplateRouteStepCommand(routeId, id, body), ct)).ToActionResult(); + (await service.UpdateAsync(routeId, id, body, ct)).ToActionResult(); [HttpDelete("{id:guid}")] public async Task Delete(Guid routeId, Guid id, CancellationToken ct) => - (await mediator.Send(new DeleteTemplateRouteStepCommand(routeId, id), ct)).ToActionResult(); + (await service.DeleteAsync(routeId, id, ct)).ToActionResult(); } diff --git a/backend/src/Tnsu.ApprovalRoute.Api/Controllers/TemplateRoutesController.cs b/backend/src/Tnsu.ApprovalRoute.Api/Controllers/TemplateRoutesController.cs index 7946471..473034f 100644 --- a/backend/src/Tnsu.ApprovalRoute.Api/Controllers/TemplateRoutesController.cs +++ b/backend/src/Tnsu.ApprovalRoute.Api/Controllers/TemplateRoutesController.cs @@ -1,4 +1,3 @@ -using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tnsu.ApprovalRoute.Api.Common; @@ -10,7 +9,7 @@ namespace Tnsu.ApprovalRoute.Api.Controllers; [ApiController] [Authorize] [Route("api/template-routes")] -public sealed class TemplateRoutesController(ISender mediator) : ControllerBase +public sealed class TemplateRoutesController(TemplateRouteService service) : ControllerBase { [HttpGet("resolve")] public async Task Resolve( @@ -19,25 +18,25 @@ public sealed class TemplateRoutesController(ISender mediator) : ControllerBase [FromQuery] Guid? projectId, [FromQuery] RouteStepStatus status, CancellationToken ct) => - (await mediator.Send(new ResolveTemplateRouteQuery(requestTypeId, userEmail, projectId, status), ct)).ToActionResult(); + (await service.ResolveAsync(requestTypeId, userEmail, projectId, status, ct)).ToActionResult(); [HttpGet] public async Task List([FromQuery] Guid? requestTypeId, CancellationToken ct) => - (await mediator.Send(new ListTemplateRoutesQuery(requestTypeId), ct)).ToActionResult(); + (await service.ListAsync(requestTypeId, ct)).ToActionResult(); [HttpGet("{id:guid}")] public async Task Get(Guid id, CancellationToken ct) => - (await mediator.Send(new GetTemplateRouteQuery(id), ct)).ToActionResult(); + (await service.GetAsync(id, ct)).ToActionResult(); [HttpPost] public async Task Create(CreateTemplateRouteRequest body, CancellationToken ct) => - (await mediator.Send(new CreateTemplateRouteCommand(body), ct)).ToActionResult(); + (await service.CreateAsync(body, ct)).ToActionResult(); [HttpPut("{id:guid}")] public async Task Update(Guid id, UpdateTemplateRouteRequest body, CancellationToken ct) => - (await mediator.Send(new UpdateTemplateRouteCommand(id, body), ct)).ToActionResult(); + (await service.UpdateAsync(id, body, ct)).ToActionResult(); [HttpDelete("{id:guid}")] public async Task Delete(Guid id, CancellationToken ct) => - (await mediator.Send(new DeleteTemplateRouteCommand(id), ct)).ToActionResult(); + (await service.DeleteAsync(id, ct)).ToActionResult(); } diff --git a/backend/src/Tnsu.ApprovalRoute.Api/Tnsu.ApprovalRoute.Api.csproj b/backend/src/Tnsu.ApprovalRoute.Api/Tnsu.ApprovalRoute.Api.csproj index 72b635e..a941843 100644 --- a/backend/src/Tnsu.ApprovalRoute.Api/Tnsu.ApprovalRoute.Api.csproj +++ b/backend/src/Tnsu.ApprovalRoute.Api/Tnsu.ApprovalRoute.Api.csproj @@ -7,7 +7,6 @@ - all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/backend/src/Tnsu.ApprovalRoute.Application/ApplicationServiceCollectionExtensions.cs b/backend/src/Tnsu.ApprovalRoute.Application/ApplicationServiceCollectionExtensions.cs index 51291b7..63a6f1b 100644 --- a/backend/src/Tnsu.ApprovalRoute.Application/ApplicationServiceCollectionExtensions.cs +++ b/backend/src/Tnsu.ApprovalRoute.Application/ApplicationServiceCollectionExtensions.cs @@ -1,6 +1,7 @@ -using System.Reflection; -using MediatR; using Microsoft.Extensions.DependencyInjection; +using Tnsu.ApprovalRoute.Application.RequestTypes; +using Tnsu.ApprovalRoute.Application.TemplateRoutes; +using Tnsu.ApprovalRoute.Application.TemplateRouteSteps; namespace Tnsu.ApprovalRoute.Application; @@ -8,7 +9,10 @@ public static class ApplicationServiceCollectionExtensions { public static IServiceCollection AddApprovalRouteApplication(this IServiceCollection services) { - services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly())); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + return services; } } diff --git a/backend/src/Tnsu.ApprovalRoute.Application/RequestTypes/RequestTypeCommands.cs b/backend/src/Tnsu.ApprovalRoute.Application/RequestTypes/RequestTypeCommands.cs deleted file mode 100644 index a2449cb..0000000 --- a/backend/src/Tnsu.ApprovalRoute.Application/RequestTypes/RequestTypeCommands.cs +++ /dev/null @@ -1,75 +0,0 @@ -using FluentResults; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Tnsu.ApprovalRoute.Application.Common.Errors; -using Tnsu.ApprovalRoute.Domain.Entities; -using Tnsu.ApprovalRoute.Infrastructure.Persistence; - -namespace Tnsu.ApprovalRoute.Application.RequestTypes; - -public sealed record CreateRequestTypeCommand(string Name, string PageUrl) : IRequest>; - -public sealed record UpdateRequestTypeCommand(Guid Id, string Name, string PageUrl) : IRequest>; - -public sealed record DeleteRequestTypeCommand(Guid Id) : IRequest; - -public sealed class CreateRequestTypeHandler(ApprovalRouteDbContext db) - : IRequestHandler> -{ - public async Task> Handle(CreateRequestTypeCommand request, CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(request.Name)) - return Result.Fail(new ValidationError("Имя типа заявки обязательно.")); - if (string.IsNullOrWhiteSpace(request.PageUrl)) - return Result.Fail(new ValidationError("Url страницы обязателен.")); - - var entity = new RequestType { Name = request.Name, PageUrl = request.PageUrl }; - db.RequestTypes.Add(entity); - await db.SaveChangesAsync(cancellationToken); - - return Result.Ok(RequestTypeMapper.ToDto(entity)); - } -} - -public sealed class UpdateRequestTypeHandler(ApprovalRouteDbContext db) - : IRequestHandler> -{ - public async Task> Handle(UpdateRequestTypeCommand request, CancellationToken cancellationToken) - { - var entity = await db.RequestTypes.FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken); - if (entity is null) - return Result.Fail(new NotFoundError("Тип заявки", request.Id)); - - if (string.IsNullOrWhiteSpace(request.Name)) - return Result.Fail(new ValidationError("Имя типа заявки обязательно.")); - if (string.IsNullOrWhiteSpace(request.PageUrl)) - return Result.Fail(new ValidationError("Url страницы обязателен.")); - - entity.Name = request.Name; - entity.PageUrl = request.PageUrl; - await db.SaveChangesAsync(cancellationToken); - - return Result.Ok(RequestTypeMapper.ToDto(entity)); - } -} - -public sealed class DeleteRequestTypeHandler(ApprovalRouteDbContext db) - : IRequestHandler -{ - public async Task Handle(DeleteRequestTypeCommand request, CancellationToken cancellationToken) - { - var entity = await db.RequestTypes.FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken); - if (entity is null) - return Result.Fail(new NotFoundError("Тип заявки", request.Id)); - - db.RequestTypes.Remove(entity); - await db.SaveChangesAsync(cancellationToken); - - return Result.Ok(); - } -} - -internal static class RequestTypeMapper -{ - public static RequestTypeDto ToDto(RequestType e) => new(e.Id, e.Name, e.PageUrl); -} diff --git a/backend/src/Tnsu.ApprovalRoute.Application/RequestTypes/RequestTypeQueries.cs b/backend/src/Tnsu.ApprovalRoute.Application/RequestTypes/RequestTypeQueries.cs deleted file mode 100644 index 93b75cd..0000000 --- a/backend/src/Tnsu.ApprovalRoute.Application/RequestTypes/RequestTypeQueries.cs +++ /dev/null @@ -1,39 +0,0 @@ -using FluentResults; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Tnsu.ApprovalRoute.Application.Common.Errors; -using Tnsu.ApprovalRoute.Infrastructure.Persistence; - -namespace Tnsu.ApprovalRoute.Application.RequestTypes; - -public sealed record GetRequestTypeQuery(Guid Id) : IRequest>; - -public sealed record ListRequestTypesQuery : IRequest>>; - -public sealed class GetRequestTypeHandler(ApprovalRouteDbContext db) - : IRequestHandler> -{ - public async Task> Handle(GetRequestTypeQuery request, CancellationToken cancellationToken) - { - var entity = await db.RequestTypes.AsNoTracking() - .FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken); - - return entity is null - ? Result.Fail(new NotFoundError("Тип заявки", request.Id)) - : Result.Ok(RequestTypeMapper.ToDto(entity)); - } -} - -public sealed class ListRequestTypesHandler(ApprovalRouteDbContext db) - : IRequestHandler>> -{ - public async Task>> Handle(ListRequestTypesQuery request, CancellationToken cancellationToken) - { - var items = await db.RequestTypes.AsNoTracking() - .OrderBy(x => x.Name) - .Select(x => new RequestTypeDto(x.Id, x.Name, x.PageUrl)) - .ToListAsync(cancellationToken); - - return Result.Ok>(items); - } -} diff --git a/backend/src/Tnsu.ApprovalRoute.Application/RequestTypes/RequestTypeService.cs b/backend/src/Tnsu.ApprovalRoute.Application/RequestTypes/RequestTypeService.cs new file mode 100644 index 0000000..6be017c --- /dev/null +++ b/backend/src/Tnsu.ApprovalRoute.Application/RequestTypes/RequestTypeService.cs @@ -0,0 +1,75 @@ +using FluentResults; +using Microsoft.EntityFrameworkCore; +using Tnsu.ApprovalRoute.Application.Common.Errors; +using Tnsu.ApprovalRoute.Domain.Entities; +using Tnsu.ApprovalRoute.Infrastructure.Persistence; + +namespace Tnsu.ApprovalRoute.Application.RequestTypes; + +public sealed class RequestTypeService(ApprovalRouteDbContext db) +{ + public async Task>> ListAsync(CancellationToken cancellationToken) + { + var items = await db.RequestTypes.AsNoTracking() + .OrderBy(x => x.Name) + .Select(x => new RequestTypeDto(x.Id, x.Name, x.PageUrl)) + .ToListAsync(cancellationToken); + + return Result.Ok>(items); + } + + public async Task> GetAsync(Guid id, CancellationToken cancellationToken) + { + var entity = await db.RequestTypes.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id, cancellationToken); + + return entity is null + ? Result.Fail(new NotFoundError("Тип заявки", id)) + : Result.Ok(ToDto(entity)); + } + + public async Task> CreateAsync(string name, string pageUrl, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(name)) + return Result.Fail(new ValidationError("Имя типа заявки обязательно.")); + if (string.IsNullOrWhiteSpace(pageUrl)) + return Result.Fail(new ValidationError("Url страницы обязателен.")); + + var entity = new RequestType { Name = name, PageUrl = pageUrl }; + db.RequestTypes.Add(entity); + await db.SaveChangesAsync(cancellationToken); + + return Result.Ok(ToDto(entity)); + } + + public async Task> UpdateAsync(Guid id, string name, string pageUrl, CancellationToken cancellationToken) + { + var entity = await db.RequestTypes.FirstOrDefaultAsync(x => x.Id == id, cancellationToken); + if (entity is null) + return Result.Fail(new NotFoundError("Тип заявки", id)); + + if (string.IsNullOrWhiteSpace(name)) + return Result.Fail(new ValidationError("Имя типа заявки обязательно.")); + if (string.IsNullOrWhiteSpace(pageUrl)) + return Result.Fail(new ValidationError("Url страницы обязателен.")); + + entity.Name = name; + entity.PageUrl = pageUrl; + await db.SaveChangesAsync(cancellationToken); + + return Result.Ok(ToDto(entity)); + } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken) + { + var entity = await db.RequestTypes.FirstOrDefaultAsync(x => x.Id == id, cancellationToken); + if (entity is null) + return Result.Fail(new NotFoundError("Тип заявки", id)); + + db.RequestTypes.Remove(entity); + await db.SaveChangesAsync(cancellationToken); + + return Result.Ok(); + } + + private static RequestTypeDto ToDto(RequestType e) => new(e.Id, e.Name, e.PageUrl); +} diff --git a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepQueries.cs b/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepQueries.cs deleted file mode 100644 index 2ddce86..0000000 --- a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepQueries.cs +++ /dev/null @@ -1,41 +0,0 @@ -using FluentResults; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Tnsu.ApprovalRoute.Application.Common.Errors; -using Tnsu.ApprovalRoute.Infrastructure.Persistence; - -namespace Tnsu.ApprovalRoute.Application.TemplateRouteSteps; - -public sealed record GetTemplateRouteStepQuery(Guid RouteId, Guid Id) : IRequest>; - -public sealed record ListTemplateRouteStepsQuery(Guid RouteId) : IRequest>>; - -public sealed class GetTemplateRouteStepHandler(ApprovalRouteDbContext db) - : IRequestHandler> -{ - public async Task> Handle(GetTemplateRouteStepQuery request, CancellationToken cancellationToken) - { - var entity = await db.TemplateRouteSteps.AsNoTracking() - .FirstOrDefaultAsync(x => x.Id == request.Id && x.RouteId == request.RouteId, cancellationToken); - - return entity is null - ? Result.Fail(new NotFoundError("Шаг маршрута", request.Id)) - : Result.Ok(TemplateRouteStepMapper.ToDto(entity)); - } -} - -public sealed class ListTemplateRouteStepsHandler(ApprovalRouteDbContext db) - : IRequestHandler>> -{ - public async Task>> Handle(ListTemplateRouteStepsQuery request, CancellationToken cancellationToken) - { - var items = await db.TemplateRouteSteps.AsNoTracking() - .Where(x => x.RouteId == request.RouteId) - .OrderBy(x => x.OrderNo) - .Select(x => new TemplateRouteStepDto( - x.Id, x.OrderNo, x.ExecutorEmail, x.Position, x.ProjectRole, x.HeadType, x.Status, x.RouteId, x.AllowReassignToOtherApprovers)) - .ToListAsync(cancellationToken); - - return Result.Ok>(items); - } -} diff --git a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepCommands.cs b/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepService.cs similarity index 50% rename from backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepCommands.cs rename to backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepService.cs index fa858a8..ae8c55f 100644 --- a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepCommands.cs +++ b/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepService.cs @@ -1,5 +1,4 @@ using FluentResults; -using MediatR; using Microsoft.EntityFrameworkCore; using Tnsu.ApprovalRoute.Application.Common.Errors; using Tnsu.ApprovalRoute.Domain.Entities; @@ -8,25 +7,37 @@ using Tnsu.ApprovalRoute.Infrastructure.Persistence; namespace Tnsu.ApprovalRoute.Application.TemplateRouteSteps; -public sealed record CreateTemplateRouteStepCommand(Guid RouteId, CreateTemplateRouteStepRequest Request) - : IRequest>; - -public sealed record UpdateTemplateRouteStepCommand(Guid RouteId, Guid Id, UpdateTemplateRouteStepRequest Request) - : IRequest>; - -public sealed record DeleteTemplateRouteStepCommand(Guid RouteId, Guid Id) : IRequest; - -public sealed class CreateTemplateRouteStepHandler(ApprovalRouteDbContext db) - : IRequestHandler> +public sealed class TemplateRouteStepService(ApprovalRouteDbContext db) { - public async Task> Handle(CreateTemplateRouteStepCommand command, CancellationToken cancellationToken) + public async Task>> ListAsync(Guid routeId, CancellationToken cancellationToken) { - var route = await db.TemplateRoutes.FirstOrDefaultAsync(x => x.Id == command.RouteId, cancellationToken); - if (route is null) - return Result.Fail(new NotFoundError("Маршрут", command.RouteId)); + var items = await db.TemplateRouteSteps.AsNoTracking() + .Where(x => x.RouteId == routeId) + .OrderBy(x => x.OrderNo) + .Select(x => new TemplateRouteStepDto( + x.Id, x.OrderNo, x.ExecutorEmail, x.Position, x.ProjectRole, x.HeadType, x.Status, x.RouteId, x.AllowReassignToOtherApprovers)) + .ToListAsync(cancellationToken); - var r = command.Request; - var fieldsError = TemplateRouteStepValidation.ValidateFields(r.ExecutorEmail, r.Position, r.ProjectRole, route.ByProject); + return Result.Ok>(items); + } + + public async Task> GetAsync(Guid routeId, Guid id, CancellationToken cancellationToken) + { + var entity = await db.TemplateRouteSteps.AsNoTracking() + .FirstOrDefaultAsync(x => x.Id == id && x.RouteId == routeId, cancellationToken); + + return entity is null + ? Result.Fail(new NotFoundError("Шаг маршрута", id)) + : Result.Ok(ToDto(entity)); + } + + public async Task> CreateAsync(Guid routeId, CreateTemplateRouteStepRequest r, CancellationToken cancellationToken) + { + var route = await db.TemplateRoutes.FirstOrDefaultAsync(x => x.Id == routeId, cancellationToken); + if (route is null) + return Result.Fail(new NotFoundError("Маршрут", routeId)); + + var fieldsError = ValidateFields(r.ExecutorEmail, r.Position, r.ProjectRole, route.ByProject); if (fieldsError is not null) return Result.Fail(fieldsError); @@ -44,27 +55,22 @@ public sealed class CreateTemplateRouteStepHandler(ApprovalRouteDbContext db) db.TemplateRouteSteps.Add(entity); await db.SaveChangesAsync(cancellationToken); - var result = Result.Ok(TemplateRouteStepMapper.ToDto(entity)); - return await TemplateRouteStepValidation.AppendMissingProjectRoleWarningAsync(db, route, result, cancellationToken); + var result = Result.Ok(ToDto(entity)); + return await AppendMissingProjectRoleWarningAsync(route, result, cancellationToken); } -} -public sealed class UpdateTemplateRouteStepHandler(ApprovalRouteDbContext db) - : IRequestHandler> -{ - public async Task> Handle(UpdateTemplateRouteStepCommand command, CancellationToken cancellationToken) + public async Task> UpdateAsync( + Guid routeId, Guid id, UpdateTemplateRouteStepRequest r, CancellationToken cancellationToken) { - var route = await db.TemplateRoutes.FirstOrDefaultAsync(x => x.Id == command.RouteId, cancellationToken); + var route = await db.TemplateRoutes.FirstOrDefaultAsync(x => x.Id == routeId, cancellationToken); if (route is null) - return Result.Fail(new NotFoundError("Маршрут", command.RouteId)); + return Result.Fail(new NotFoundError("Маршрут", routeId)); - var entity = await db.TemplateRouteSteps - .FirstOrDefaultAsync(x => x.Id == command.Id && x.RouteId == command.RouteId, cancellationToken); + var entity = await db.TemplateRouteSteps.FirstOrDefaultAsync(x => x.Id == id && x.RouteId == routeId, cancellationToken); if (entity is null) - return Result.Fail(new NotFoundError("Шаг маршрута", command.Id)); + return Result.Fail(new NotFoundError("Шаг маршрута", id)); - var r = command.Request; - var fieldsError = TemplateRouteStepValidation.ValidateFields(r.ExecutorEmail, r.Position, r.ProjectRole, route.ByProject); + var fieldsError = ValidateFields(r.ExecutorEmail, r.Position, r.ProjectRole, route.ByProject); if (fieldsError is not null) return Result.Fail(fieldsError); @@ -77,31 +83,23 @@ public sealed class UpdateTemplateRouteStepHandler(ApprovalRouteDbContext db) entity.AllowReassignToOtherApprovers = r.AllowReassignToOtherApprovers; await db.SaveChangesAsync(cancellationToken); - var result = Result.Ok(TemplateRouteStepMapper.ToDto(entity)); - return await TemplateRouteStepValidation.AppendMissingProjectRoleWarningAsync(db, route, result, cancellationToken); + var result = Result.Ok(ToDto(entity)); + return await AppendMissingProjectRoleWarningAsync(route, result, cancellationToken); } -} -public sealed class DeleteTemplateRouteStepHandler(ApprovalRouteDbContext db) - : IRequestHandler -{ - public async Task Handle(DeleteTemplateRouteStepCommand request, CancellationToken cancellationToken) + public async Task DeleteAsync(Guid routeId, Guid id, CancellationToken cancellationToken) { - var entity = await db.TemplateRouteSteps - .FirstOrDefaultAsync(x => x.Id == request.Id && x.RouteId == request.RouteId, cancellationToken); + var entity = await db.TemplateRouteSteps.FirstOrDefaultAsync(x => x.Id == id && x.RouteId == routeId, cancellationToken); if (entity is null) - return Result.Fail(new NotFoundError("Шаг маршрута", request.Id)); + return Result.Fail(new NotFoundError("Шаг маршрута", id)); db.TemplateRouteSteps.Remove(entity); await db.SaveChangesAsync(cancellationToken); return Result.Ok(); } -} -internal static class TemplateRouteStepValidation -{ - public static IError? ValidateFields(string executorEmail, string position, ProjectRole projectRole, bool routeByProject) + private static IError? ValidateFields(string executorEmail, string position, ProjectRole projectRole, bool routeByProject) { if (string.IsNullOrWhiteSpace(executorEmail) && string.IsNullOrWhiteSpace(position) && projectRole == ProjectRole.None) return new ValidationError("Исполнитель, должность и роль в проекте не могут быть пустыми одновременно."); @@ -112,8 +110,8 @@ internal static class TemplateRouteStepValidation return null; } - public static async Task> AppendMissingProjectRoleWarningAsync( - ApprovalRouteDbContext db, TemplateRoute route, Result result, CancellationToken cancellationToken) + private async Task> AppendMissingProjectRoleWarningAsync( + TemplateRoute route, Result result, CancellationToken cancellationToken) { if (!route.ByProject) return result; @@ -125,10 +123,7 @@ internal static class TemplateRouteStepValidation ? result : result.WithReason(new Warning("Должен быть хотя бы один шаг с ролью в проекте.")); } -} -internal static class TemplateRouteStepMapper -{ - public static TemplateRouteStepDto ToDto(TemplateRouteStep e) => new( + private static TemplateRouteStepDto ToDto(TemplateRouteStep e) => new( e.Id, e.OrderNo, e.ExecutorEmail, e.Position, e.ProjectRole, e.HeadType, e.Status, e.RouteId, e.AllowReassignToOtherApprovers); } diff --git a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRoutes/ResolveTemplateRouteQuery.cs b/backend/src/Tnsu.ApprovalRoute.Application/TemplateRoutes/ResolveTemplateRouteQuery.cs deleted file mode 100644 index 5e83386..0000000 --- a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRoutes/ResolveTemplateRouteQuery.cs +++ /dev/null @@ -1,95 +0,0 @@ -using FluentResults; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Tnsu.ApprovalRoute.Application.Common.Errors; -using Tnsu.ApprovalRoute.Application.Common.Interfaces; -using Tnsu.ApprovalRoute.Domain.Entities; -using Tnsu.ApprovalRoute.Domain.Enums; -using Tnsu.ApprovalRoute.Infrastructure.Persistence; -using Tnsu.ApprovalRoute.Infrastructure.TnsuPortal; - -namespace Tnsu.ApprovalRoute.Application.TemplateRoutes; - -public sealed record ResolveTemplateRouteQuery( - Guid RequestTypeId, - string? UserEmail, - Guid? ProjectId, - RouteStepStatus Status) : IRequest>; - -public sealed class ResolveTemplateRouteHandler( - ApprovalRouteDbContext db, - HttpTnsuPortalClient portalClient, - ICurrentUser currentUser) - : IRequestHandler> -{ - public async Task> Handle(ResolveTemplateRouteQuery request, CancellationToken cancellationToken) - { - if (request.Status == RouteStepStatus.None) - return Result.Fail( - new ValidationError("Статус обязателен: Approval или Execution.")); - - var email = string.IsNullOrWhiteSpace(request.UserEmail) ? currentUser.Email : request.UserEmail; - if (string.IsNullOrWhiteSpace(email)) - return Result.Fail( - new ValidationError("Не удалось определить email инициатора: не передан явно и отсутствует в токене.")); - - var requestTypeExists = await db.RequestTypes.AnyAsync(x => x.Id == request.RequestTypeId, cancellationToken); - if (!requestTypeExists) - return Result.Fail(new NotFoundError("Тип заявки", request.RequestTypeId)); - - var employee = await portalClient.GetEmployeeByEmailAsync(email, cancellationToken); - var byProject = request.ProjectId is not null; - - var route = await db.TemplateRoutes.AsNoTracking() - .Include(x => x.Steps) - .Where(x => - x.RequestTypeId == request.RequestTypeId && - x.CompanyId == employee.CompanyId && - 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) - return Result.Fail(new NotFoundError( - "Маршрут для этого типа заявки, отдела/компании инициатора и признака «по проекту»", - request.RequestTypeId)); - - IReadOnlyList projectExecutors = []; - if (request.ProjectId is { } projectId) - projectExecutors = await portalClient.GetProjectExecutorsAsync(projectId, cancellationToken); - - var steps = route.Steps - .Where(s => s.Status == request.Status) - .OrderBy(s => s.OrderNo) - .Select(s => ResolveStep(s, projectExecutors)) - .ToList(); - - return Result.Ok(new ResolveTemplateRouteResultDto(route.Id, route.RouteType, route.RequiresControl, steps)); - } - - private static ResolvedRouteStepDto ResolveStep(TemplateRouteStep step, IReadOnlyList projectExecutors) - { - // Для шагов с ролью в проекте фактический исполнитель приходит из данных проекта (api.tnsu.kz), - // а не из статичного ExecutorEmail шаблона — тот используется только для шагов без привязки к проекту. - if (step.ProjectRole != ProjectRole.None) - { - var projectExecutor = projectExecutors.FirstOrDefault(e => e.ProjectRole == step.ProjectRole); - if (projectExecutor is not null) - return new ResolvedRouteStepDto( - step.Id, step.OrderNo, projectExecutor.Email, projectExecutor.Position, - step.ProjectRole, step.HeadType, step.Status, step.AllowReassignToOtherApprovers); - } - - return new ResolvedRouteStepDto( - step.Id, step.OrderNo, step.ExecutorEmail, step.Position, - step.ProjectRole, step.HeadType, step.Status, step.AllowReassignToOtherApprovers); - } -} diff --git a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteCommands.cs b/backend/src/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteCommands.cs deleted file mode 100644 index 19c54a0..0000000 --- a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteCommands.cs +++ /dev/null @@ -1,122 +0,0 @@ -using FluentResults; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Tnsu.ApprovalRoute.Application.Common.Errors; -using Tnsu.ApprovalRoute.Domain.Entities; -using Tnsu.ApprovalRoute.Infrastructure.Persistence; - -namespace Tnsu.ApprovalRoute.Application.TemplateRoutes; - -public sealed record CreateTemplateRouteCommand(CreateTemplateRouteRequest Request) : IRequest>; - -public sealed record UpdateTemplateRouteCommand(Guid Id, UpdateTemplateRouteRequest Request) : IRequest>; - -public sealed record DeleteTemplateRouteCommand(Guid Id) : IRequest; - -public sealed class CreateTemplateRouteHandler(ApprovalRouteDbContext db) - : IRequestHandler> -{ - public async Task> Handle(CreateTemplateRouteCommand command, CancellationToken cancellationToken) - { - var r = command.Request; - - if (r.CompanyId == Guid.Empty) - return Result.Fail(new ValidationError("Компания обязательна.")); - - var requestTypeExists = await db.RequestTypes.AnyAsync(x => x.Id == r.RequestTypeId, cancellationToken); - if (!requestTypeExists) - return Result.Fail(new NotFoundError("Тип заявки", r.RequestTypeId)); - - var duplicate = await TemplateRouteUniqueness.HasDuplicateAsync( - db, r.RequestTypeId, r.DepartmentId, r.CompanyId, r.ByProject, excludeId: null, cancellationToken); - if (duplicate) - return Result.Fail(new ConflictError( - "Маршрут с таким типом заявки, отделом, компанией и признаком «по проекту» уже существует.")); - - var entity = new TemplateRoute - { - RouteType = r.RouteType, - RequestTypeId = r.RequestTypeId, - DepartmentId = r.DepartmentId, - CompanyId = r.CompanyId, - ByProject = r.ByProject, - RequiresControl = r.RequiresControl - }; - db.TemplateRoutes.Add(entity); - await db.SaveChangesAsync(cancellationToken); - - return Result.Ok(TemplateRouteMapper.ToDto(entity)); - } -} - -public sealed class UpdateTemplateRouteHandler(ApprovalRouteDbContext db) - : IRequestHandler> -{ - public async Task> Handle(UpdateTemplateRouteCommand command, CancellationToken cancellationToken) - { - var entity = await db.TemplateRoutes.FirstOrDefaultAsync(x => x.Id == command.Id, cancellationToken); - if (entity is null) - return Result.Fail(new NotFoundError("Маршрут", command.Id)); - - var r = command.Request; - - if (r.CompanyId == Guid.Empty) - return Result.Fail(new ValidationError("Компания обязательна.")); - - var duplicate = await TemplateRouteUniqueness.HasDuplicateAsync( - db, entity.RequestTypeId, r.DepartmentId, r.CompanyId, r.ByProject, excludeId: entity.Id, cancellationToken); - if (duplicate) - return Result.Fail(new ConflictError( - "Маршрут с таким типом заявки, отделом, компанией и признаком «по проекту» уже существует.")); - - entity.RouteType = r.RouteType; - entity.DepartmentId = r.DepartmentId; - entity.CompanyId = r.CompanyId; - entity.ByProject = r.ByProject; - entity.RequiresControl = r.RequiresControl; - await db.SaveChangesAsync(cancellationToken); - - return Result.Ok(TemplateRouteMapper.ToDto(entity)); - } -} - -public sealed class DeleteTemplateRouteHandler(ApprovalRouteDbContext db) - : IRequestHandler -{ - public async Task Handle(DeleteTemplateRouteCommand request, CancellationToken cancellationToken) - { - var entity = await db.TemplateRoutes.FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken); - if (entity is null) - return Result.Fail(new NotFoundError("Маршрут", request.Id)); - - db.TemplateRoutes.Remove(entity); - await db.SaveChangesAsync(cancellationToken); - - return Result.Ok(); - } -} - -internal static class TemplateRouteUniqueness -{ - public static Task HasDuplicateAsync( - ApprovalRouteDbContext db, - Guid requestTypeId, - Guid? departmentId, - Guid companyId, - bool byProject, - Guid? excludeId, - CancellationToken cancellationToken) => - db.TemplateRoutes.AnyAsync(x => - x.Id != excludeId && - x.RequestTypeId == requestTypeId && - x.DepartmentId == departmentId && - x.CompanyId == companyId && - x.ByProject == byProject, - cancellationToken); -} - -internal static class TemplateRouteMapper -{ - public static TemplateRouteDto ToDto(TemplateRoute e) => new( - e.Id, e.RouteType, e.RequestTypeId, e.DepartmentId, e.CompanyId, e.ByProject, e.RequiresControl); -} diff --git a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteQueries.cs b/backend/src/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteQueries.cs deleted file mode 100644 index 54e417a..0000000 --- a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteQueries.cs +++ /dev/null @@ -1,43 +0,0 @@ -using FluentResults; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Tnsu.ApprovalRoute.Application.Common.Errors; -using Tnsu.ApprovalRoute.Infrastructure.Persistence; - -namespace Tnsu.ApprovalRoute.Application.TemplateRoutes; - -public sealed record GetTemplateRouteQuery(Guid Id) : IRequest>; - -public sealed record ListTemplateRoutesQuery(Guid? RequestTypeId) : IRequest>>; - -public sealed class GetTemplateRouteHandler(ApprovalRouteDbContext db) - : IRequestHandler> -{ - public async Task> Handle(GetTemplateRouteQuery request, CancellationToken cancellationToken) - { - var entity = await db.TemplateRoutes.AsNoTracking() - .FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken); - - return entity is null - ? Result.Fail(new NotFoundError("Маршрут", request.Id)) - : Result.Ok(TemplateRouteMapper.ToDto(entity)); - } -} - -public sealed class ListTemplateRoutesHandler(ApprovalRouteDbContext db) - : IRequestHandler>> -{ - public async Task>> Handle(ListTemplateRoutesQuery request, CancellationToken cancellationToken) - { - var query = db.TemplateRoutes.AsNoTracking().AsQueryable(); - if (request.RequestTypeId is { } requestTypeId) - query = query.Where(x => x.RequestTypeId == requestTypeId); - - var items = await query - .Select(x => new TemplateRouteDto( - x.Id, x.RouteType, x.RequestTypeId, x.DepartmentId, x.CompanyId, x.ByProject, x.RequiresControl)) - .ToListAsync(cancellationToken); - - return Result.Ok>(items); - } -} diff --git a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteService.cs b/backend/src/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteService.cs new file mode 100644 index 0000000..4fa96d8 --- /dev/null +++ b/backend/src/Tnsu.ApprovalRoute.Application/TemplateRoutes/TemplateRouteService.cs @@ -0,0 +1,189 @@ +using FluentResults; +using Microsoft.EntityFrameworkCore; +using Tnsu.ApprovalRoute.Application.Common.Errors; +using Tnsu.ApprovalRoute.Application.Common.Interfaces; +using Tnsu.ApprovalRoute.Domain.Entities; +using Tnsu.ApprovalRoute.Domain.Enums; +using Tnsu.ApprovalRoute.Infrastructure.Persistence; +using Tnsu.ApprovalRoute.Infrastructure.TnsuPortal; + +namespace Tnsu.ApprovalRoute.Application.TemplateRoutes; + +public sealed class TemplateRouteService( + ApprovalRouteDbContext db, + HttpTnsuPortalClient portalClient, + ICurrentUser currentUser) +{ + public async Task>> ListAsync(Guid? requestTypeId, CancellationToken cancellationToken) + { + var query = db.TemplateRoutes.AsNoTracking().AsQueryable(); + if (requestTypeId is { } id) + query = query.Where(x => x.RequestTypeId == id); + + var items = await query + .Select(x => new TemplateRouteDto( + x.Id, x.RouteType, x.RequestTypeId, x.DepartmentId, x.CompanyId, x.ByProject, x.RequiresControl)) + .ToListAsync(cancellationToken); + + return Result.Ok>(items); + } + + public async Task> GetAsync(Guid id, CancellationToken cancellationToken) + { + var entity = await db.TemplateRoutes.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id, cancellationToken); + + return entity is null + ? Result.Fail(new NotFoundError("Маршрут", id)) + : Result.Ok(ToDto(entity)); + } + + public async Task> CreateAsync(CreateTemplateRouteRequest r, CancellationToken cancellationToken) + { + if (r.CompanyId == Guid.Empty) + return Result.Fail(new ValidationError("Компания обязательна.")); + + var requestTypeExists = await db.RequestTypes.AnyAsync(x => x.Id == r.RequestTypeId, cancellationToken); + if (!requestTypeExists) + return Result.Fail(new NotFoundError("Тип заявки", r.RequestTypeId)); + + var duplicate = await HasDuplicateAsync(r.RequestTypeId, r.DepartmentId, r.CompanyId, r.ByProject, excludeId: null, cancellationToken); + if (duplicate) + return Result.Fail(new ConflictError( + "Маршрут с таким типом заявки, отделом, компанией и признаком «по проекту» уже существует.")); + + var entity = new TemplateRoute + { + RouteType = r.RouteType, + RequestTypeId = r.RequestTypeId, + DepartmentId = r.DepartmentId, + CompanyId = r.CompanyId, + ByProject = r.ByProject, + RequiresControl = r.RequiresControl + }; + db.TemplateRoutes.Add(entity); + await db.SaveChangesAsync(cancellationToken); + + return Result.Ok(ToDto(entity)); + } + + public async Task> UpdateAsync(Guid id, UpdateTemplateRouteRequest r, CancellationToken cancellationToken) + { + var entity = await db.TemplateRoutes.FirstOrDefaultAsync(x => x.Id == id, cancellationToken); + if (entity is null) + return Result.Fail(new NotFoundError("Маршрут", id)); + + if (r.CompanyId == Guid.Empty) + return Result.Fail(new ValidationError("Компания обязательна.")); + + var duplicate = await HasDuplicateAsync(entity.RequestTypeId, r.DepartmentId, r.CompanyId, r.ByProject, excludeId: entity.Id, cancellationToken); + if (duplicate) + return Result.Fail(new ConflictError( + "Маршрут с таким типом заявки, отделом, компанией и признаком «по проекту» уже существует.")); + + entity.RouteType = r.RouteType; + entity.DepartmentId = r.DepartmentId; + entity.CompanyId = r.CompanyId; + entity.ByProject = r.ByProject; + entity.RequiresControl = r.RequiresControl; + await db.SaveChangesAsync(cancellationToken); + + return Result.Ok(ToDto(entity)); + } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken) + { + var entity = await db.TemplateRoutes.FirstOrDefaultAsync(x => x.Id == id, cancellationToken); + if (entity is null) + return Result.Fail(new NotFoundError("Маршрут", id)); + + db.TemplateRoutes.Remove(entity); + await db.SaveChangesAsync(cancellationToken); + + return Result.Ok(); + } + + public async Task> ResolveAsync( + Guid requestTypeId, string? userEmail, Guid? projectId, RouteStepStatus status, CancellationToken cancellationToken) + { + if (status == RouteStepStatus.None) + return Result.Fail( + new ValidationError("Статус обязателен: Approval или Execution.")); + + var email = string.IsNullOrWhiteSpace(userEmail) ? currentUser.Email : userEmail; + if (string.IsNullOrWhiteSpace(email)) + return Result.Fail( + new ValidationError("Не удалось определить email инициатора: не передан явно и отсутствует в токене.")); + + var requestTypeExists = await db.RequestTypes.AnyAsync(x => x.Id == requestTypeId, cancellationToken); + if (!requestTypeExists) + return Result.Fail(new NotFoundError("Тип заявки", requestTypeId)); + + var employee = await portalClient.GetEmployeeByEmailAsync(email, cancellationToken); + var byProject = projectId is not null; + + var route = await db.TemplateRoutes.AsNoTracking() + .Include(x => x.Steps) + .Where(x => + x.RequestTypeId == requestTypeId && + x.CompanyId == employee.CompanyId && + x.ByProject == byProject && + x.DepartmentId == employee.DepartmentId) + .FirstOrDefaultAsync(cancellationToken) + ?? await db.TemplateRoutes.AsNoTracking() + .Include(x => x.Steps) + .Where(x => + x.RequestTypeId == requestTypeId && + x.CompanyId == employee.CompanyId && + x.ByProject == byProject && + x.DepartmentId == null) + .FirstOrDefaultAsync(cancellationToken); + + if (route is null) + return Result.Fail(new NotFoundError( + "Маршрут для этого типа заявки, отдела/компании инициатора и признака «по проекту»", + requestTypeId)); + + IReadOnlyList projectExecutors = []; + if (projectId is { } id) + projectExecutors = await portalClient.GetProjectExecutorsAsync(id, cancellationToken); + + var steps = route.Steps + .Where(s => s.Status == status) + .OrderBy(s => s.OrderNo) + .Select(s => ResolveStep(s, projectExecutors)) + .ToList(); + + return Result.Ok(new ResolveTemplateRouteResultDto(route.Id, route.RouteType, route.RequiresControl, steps)); + } + + private Task HasDuplicateAsync( + Guid requestTypeId, Guid? departmentId, Guid companyId, bool byProject, Guid? excludeId, CancellationToken cancellationToken) => + db.TemplateRoutes.AnyAsync(x => + x.Id != excludeId && + x.RequestTypeId == requestTypeId && + x.DepartmentId == departmentId && + x.CompanyId == companyId && + x.ByProject == byProject, + cancellationToken); + + private static ResolvedRouteStepDto ResolveStep(TemplateRouteStep step, IReadOnlyList projectExecutors) + { + // Для шагов с ролью в проекте фактический исполнитель приходит из данных проекта (api.tnsu.kz), + // а не из статичного ExecutorEmail шаблона — тот используется только для шагов без привязки к проекту. + if (step.ProjectRole != ProjectRole.None) + { + var projectExecutor = projectExecutors.FirstOrDefault(e => e.ProjectRole == step.ProjectRole); + if (projectExecutor is not null) + return new ResolvedRouteStepDto( + step.Id, step.OrderNo, projectExecutor.Email, projectExecutor.Position, + step.ProjectRole, step.HeadType, step.Status, step.AllowReassignToOtherApprovers); + } + + return new ResolvedRouteStepDto( + step.Id, step.OrderNo, step.ExecutorEmail, step.Position, + step.ProjectRole, step.HeadType, step.Status, step.AllowReassignToOtherApprovers); + } + + private static TemplateRouteDto ToDto(TemplateRoute e) => new( + e.Id, e.RouteType, e.RequestTypeId, e.DepartmentId, e.CompanyId, e.ByProject, e.RequiresControl); +} diff --git a/backend/src/Tnsu.ApprovalRoute.Application/Tnsu.ApprovalRoute.Application.csproj b/backend/src/Tnsu.ApprovalRoute.Application/Tnsu.ApprovalRoute.Application.csproj index 409b6fe..9988106 100644 --- a/backend/src/Tnsu.ApprovalRoute.Application/Tnsu.ApprovalRoute.Application.csproj +++ b/backend/src/Tnsu.ApprovalRoute.Application/Tnsu.ApprovalRoute.Application.csproj @@ -5,7 +5,6 @@ -