diff --git a/PARR.API/Contracts/V1/Requests/TnkRequest.cs b/PARR.API/Contracts/V1/Requests/TnkRequest.cs
new file mode 100644
index 00000000..46ba6884
--- /dev/null
+++ b/PARR.API/Contracts/V1/Requests/TnkRequest.cs
@@ -0,0 +1,9 @@
+namespace PARR.API.Contracts.V1.Requests
+{
+ public class TnkRequest
+ {
+ public required string Name { get; set; }
+ public int EsppId { get; set; }
+ public Guid SubprocessId { get; set; }
+ }
+}
diff --git a/PARR.API/Controllers/V1/TnkController.cs b/PARR.API/Controllers/V1/TnkController.cs
new file mode 100644
index 00000000..09e6084f
--- /dev/null
+++ b/PARR.API/Controllers/V1/TnkController.cs
@@ -0,0 +1,205 @@
+using AutoMapper;
+using FluentValidation;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Cors.Infrastructure;
+using Microsoft.AspNetCore.Mvc;
+using PARR.API.Contracts.V1;
+using PARR.API.Contracts.V1.Requests;
+using PARR.API.Contracts.V1.Requests.Queries;
+using PARR.API.Contracts.V1.Responses.Base;
+using PARR.API.Contracts.V1.Responses;
+using PARR.API.Controllers.V1.Base;
+using PARR.API.Services.Interfaces;
+using PARR.Constants;
+using PARR.DAL.DomainModels;
+using PARR.DAL.Services.Interfaces;
+using PARR.DAL.Models;
+using Microsoft.EntityFrameworkCore;
+using PARR.API.Extensions;
+
+namespace PARR.API.Controllers.V1
+{
+ ///
+ /// Управление ТНК
+ ///
+ [Authorize(Roles = ParrRoles.Administrator.Role)]
+ public class TnkController : BaseApiController
+ {
+ private readonly ILogger logger;
+ private readonly IMapper mapper;
+ private readonly ITnkService tnkService;
+ private readonly IValidator validator;
+ private readonly IUriService uriService;
+
+ public TnkController(
+ ILogger logger,
+ IMapper mapper,
+ ITnkService tnkService,
+ IValidator validator,
+ IUriService uriService
+ )
+ {
+ this.logger = logger;
+ this.mapper = mapper;
+ this.tnkService = tnkService;
+ this.validator = validator;
+ this.uriService = uriService;
+ }
+
+
+ ///
+ /// Создать ТНК
+ ///
+ ///
+ ///
+ [HttpPost(ApiRoutes.Tnk.Create)]
+ public async Task Create([FromBody] TnkRequest request)
+ {
+ var resultValidate = await validator.ValidateAsync(request);
+ if (!resultValidate.IsValid)
+ return BadRequest(new Response(resultValidate.Errors));
+
+ var existName = await tnkService.Get().FirstOrDefaultAsync(t => t.Name == request.Name);
+ if (existName != null)
+ return BadRequest(new Response(false, new List { new ErrorModel { FieldName = nameof(request.Name), Message = $"Вид работы с именем \"{request.Name}\" уже существует." } }));
+
+ var tnk = new Tnk
+ {
+ Id = Guid.NewGuid(),
+ Name = request.Name,
+ EsppId = request.EsppId,
+ SubprocessId = request.SubprocessId,
+ };
+
+ if (!await tnkService.CreateAsync(tnk) || !await tnkService.CommitAsync())
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = "Ошибка при записи новой ТНК в базу данных" } }));
+
+ logger.LogInformation($"Пользователь {User.Identity?.Name} добавил вид работ: {tnk.Name}, {tnk.EsppId}, {tnk.SubprocessId}");
+
+ var locationUri = uriService.GetUri(ApiRoutes.Tnk.Get, ApiRoutes.Tnk.getParam, tnk.Id);
+
+ return Created(locationUri, new Response(mapper.Map(tnk), true));
+ }
+
+
+ ///
+ /// Список ТНК постранично
+ ///
+ ///
+ ///
+ [HttpGet(ApiRoutes.Tnk.GetAll)]
+ public async Task GetAll([FromQuery] PaginationQuery paginationQuery)
+ {
+ var paginationFilter = mapper.Map(paginationQuery);
+
+ IQueryable query = tnkService.Get()
+ //.Include(t=>t.Subprocess).ThenInclude(t=>t.Process)
+ .OrderBy(t => t.Name);
+
+ var tnks = await tnkService.GetPage(query, paginationFilter).ToListAsync();
+
+ if (!tnks.Any())
+ return NoContent();
+
+ var tnkResponse = mapper.Map>(tnks);
+ var paginationResponse = new PagedResponse(tnkResponse, true).GetPaginatedProps(paginationFilter, query);
+
+ return Ok(paginationResponse);
+ }
+
+
+ ///
+ /// Получить ТНК по id
+ ///
+ ///
+ ///
+ [HttpGet(ApiRoutes.Tnk.Get)]
+ public async Task GetById([FromRoute] Guid id)
+ {
+ var tnk = await tnkService.Get()
+ .FirstOrDefaultAsync(t => t.Id == id);
+
+ if (tnk == null)
+ return NotFound();
+
+ var response = mapper.Map(tnk);
+
+ return Ok(new Response(response, true));
+ }
+
+
+ ///
+ /// Получить связанные с ТНК работы
+ ///
+ ///
+ ///
+ [HttpGet(ApiRoutes.Tnk.GetWorks)]
+ public async Task Get([FromRoute] Guid id)
+ {
+ var tnk = await tnkService.Get()
+ .Include(w => w.Works)
+ .FirstOrDefaultAsync(t => t.Id == id);
+
+ if (tnk == null)
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Не найдена ТНК с id: {id}" } }));
+
+ var response = mapper.Map>(tnk.Works.OrderBy(t=>t.Name).ToList());//Roles.Select(t => t.Role)).OrderBy(t => t.Description).ToList();
+
+ return Ok(new Response>(response, true));
+ }
+
+
+ ///
+ /// Обновить ТНК
+ ///
+ ///
+ ///
+ ///
+ [HttpPut(ApiRoutes.Tnk.Update)]
+ public async Task Update([FromRoute] Guid id, [FromBody] TnkRequest request)
+ {
+ var resultValidate = await validator.ValidateAsync(request);
+ if (!resultValidate.IsValid)
+ return BadRequest(new Response(resultValidate.Errors));
+
+ var orig = await tnkService.Get()
+ .FirstOrDefaultAsync(t => t.Id == id);
+
+ if (orig == null)
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при изменении ТНК. Не найдена ТНК Id: {id}" } }));
+
+ orig.Name = request.Name;
+ orig.EsppId = request.EsppId;
+ orig.SubprocessId = request.SubprocessId;
+
+ if (!await tnkService.CommitAsync())
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = "Ошибка записи в базу данных изменений ТНК." } }));
+
+ logger.LogInformation($"Пользователь {User.Identity?.Name} обновил ТНК: {orig.Id}, {orig.Name}, {orig.EsppId}, {orig.SubprocessId}");
+
+ return Ok(new Response(mapper.Map(orig), true));
+ }
+
+
+ ///
+ /// Удалить ТНК
+ ///
+ ///
+ ///
+ [HttpDelete(ApiRoutes.Tnk.Delete)]
+ public async Task Delete([FromRoute] Guid id)
+ {
+ var tnk = await tnkService.GetAsync(id);
+
+ if (tnk == null)
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при удалении ТНК. Не найдена ТНК Id: {id}" } }));
+
+ if (!tnkService.Delete(tnk) || !await tnkService.CommitAsync())
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при удалении ТНК из базы данных" } }));
+
+ logger.LogInformation($"Пользователь {User.Identity?.Name} удалил ТНК: {tnk.Id}, {tnk.Name}, {tnk.EsppId}, {tnk.SubprocessId}");
+
+ return NoContent();
+ }
+ }
+}
diff --git a/PARR.API/Controllers/V1/WorkController.cs b/PARR.API/Controllers/V1/WorkController.cs
index 8ba3b89e..d21a0a20 100644
--- a/PARR.API/Controllers/V1/WorkController.cs
+++ b/PARR.API/Controllers/V1/WorkController.cs
@@ -143,7 +143,7 @@ namespace PARR.API.Controllers.V1
.FirstOrDefaultAsync(t => t.Id == id);
if (orig == null)
- return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при изменении вида работ. Не найдена работа имющая Id {id}" } }));
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при изменении вида работ. Не найдена работа Id: {id}" } }));
orig.Name = request.Name;
orig.EsppId = request.EsppId;
@@ -169,7 +169,7 @@ namespace PARR.API.Controllers.V1
var work = await workService.GetAsync(id);
if (work == null)
- return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при удалении вида работ. Не найдена работа с id: {id}" } }));
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при удалении вида работ. Не найдена работа Id: {id}" } }));
if (!workService.Delete(work) || !await workService.CommitAsync())
return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при удалении вида работ." } }));
diff --git a/PARR.API/Validators/TnkRequestValidator.cs b/PARR.API/Validators/TnkRequestValidator.cs
new file mode 100644
index 00000000..327ac90b
--- /dev/null
+++ b/PARR.API/Validators/TnkRequestValidator.cs
@@ -0,0 +1,40 @@
+using FluentValidation;
+using PARR.API.Contracts.V1.Requests;
+using PARR.DAL.Services.Interfaces;
+
+namespace PARR.API.Validators
+{
+ public class TnkRequestValidator : AbstractValidator
+ {
+ private readonly ISubprocessService subprocessService;
+ private bool? isValid = null;
+
+ public TnkRequestValidator(
+ ISubprocessService subprocessService
+ )
+ {
+ this.subprocessService = subprocessService;
+
+ RuleFor(t => t.Name).NotEmpty().NotNull().WithMessage("Имя ТНК не может быть пустым");
+
+ RuleFor(t => t.EsppId).NotEmpty().NotNull().WithMessage("EsppId не может быть пустым");
+
+ RuleFor(t => t.SubprocessId).NotEmpty().NotNull().WithMessage("ТНК должна быть связана с подпроцессом. SubprocessId не может быть пустым");
+
+ RuleFor(t => t.SubprocessId)
+ .MustAsync(async (entity, value, c) => await IsSubprocessExist(entity))
+ .WithMessage("У данной ТНК указан несуществующий Id подпроцесса");
+ }
+
+ private async Task IsSubprocessExist(TnkRequest request)
+ {
+ //чтобы не делать 2 раза валидацию
+ if (isValid.HasValue)
+ return isValid.Value;
+
+ isValid = await subprocessService.GetAsync(request.SubprocessId) != null;
+
+ return isValid.Value;
+ }
+ }
+}
diff --git a/PARR.API/Validators/WorkRequestValidator.cs b/PARR.API/Validators/WorkRequestValidator.cs
index e0161c55..f31601ed 100644
--- a/PARR.API/Validators/WorkRequestValidator.cs
+++ b/PARR.API/Validators/WorkRequestValidator.cs
@@ -24,7 +24,7 @@ namespace PARR.API.Validators
RuleFor(t => t.TnkId)
.MustAsync(async (entity, value, c) => await IsTnkExist(entity))
- .WithMessage("У данной работы указан не существующий Id ТНК");
+ .WithMessage("У данной работы указан несуществующий Id ТНК");
}
private async Task IsTnkExist(WorkRequest request)