diff --git a/AsbCloudWebApi/Controllers/DailyReportController.cs b/AsbCloudWebApi/Controllers/DailyReportController.cs
index dc740619..4041d732 100644
--- a/AsbCloudWebApi/Controllers/DailyReportController.cs
+++ b/AsbCloudWebApi/Controllers/DailyReportController.cs
@@ -1,196 +1,224 @@
-using AsbCloudApp.Data;
-using AsbCloudApp.Data.DailyReport;
-using AsbCloudApp.Exceptions;
-using AsbCloudApp.Services;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
+using System;
+using System.Linq;
+using System.Net;
using System.Threading;
using System.Threading.Tasks;
+using AsbCloudApp.Data;
+using AsbCloudApp.Data.DailyReport;
+using AsbCloudApp.Data.DailyReport.Blocks;
+using AsbCloudApp.Data.DailyReport.Blocks.Sign;
+using AsbCloudApp.Data.DailyReport.Blocks.Subsystems;
+using AsbCloudApp.Data.DailyReport.Blocks.TimeBalance;
+using AsbCloudApp.Exceptions;
+using AsbCloudApp.Repositories;
+using AsbCloudApp.Requests;
+using AsbCloudApp.Services;
+using AsbCloudApp.Services.DailyReport;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
-namespace AsbCloudWebApi.Controllers
+namespace AsbCloudWebApi.Controllers;
+
+///
+/// Суточный отчёт
+///
+[ApiController]
+[Route("api/well/{idWell:int}/[controller]")]
+[Authorize]
+public class DailyReportController : ControllerBase
{
+ private readonly IWellOperationRepository wellOperationRepository;
+ private readonly IDailyReportService dailyReportService;
+ private readonly IDailyReportExportService dailyReportExportService;
+ private readonly IWellService wellService;
- ///
- /// Суточный рапорт
- ///
- [Route("api/well/{idWell}/[controller]")]
- [ApiController]
- [Authorize]
- public class DailyReportController : ControllerBase
- {
- private readonly IDailyReportService dailyReportService;
- private readonly IWellService wellService;
+ public DailyReportController(IWellOperationRepository wellOperationRepository,
+ IDailyReportService dailyReportService,
+ IDailyReportExportService dailyReportExportService,
+ IWellService wellService)
+ {
+ this.wellOperationRepository = wellOperationRepository;
+ this.dailyReportService = dailyReportService;
+ this.dailyReportExportService = dailyReportExportService;
+ this.wellService = wellService;
+ }
- public DailyReportController(
- IDailyReportService dailyReportService,
- IWellService wellService)
- {
- this.dailyReportService = dailyReportService;
- this.wellService = wellService;
- }
+ private int IdUser
+ {
+ get
+ {
+ var idUser = User.GetUserId();
- ///
- /// Список наборов данных для формирования рапорта
- ///
- ///
- ///
- ///
- ///
- ///
- [HttpGet]
- [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)]
- public async Task GetListAsync(int idWell, DateOnly? begin, DateOnly? end, CancellationToken token)
- {
- var result = await dailyReportService.GetListAsync(idWell, begin, end, token);
- return Ok(result);
- }
+ if (!idUser.HasValue)
+ throw new ForbidException("Неизвестный пользователь");
- ///
- /// Создание суточного рапорта
- ///
- ///
- ///
- ///
- ///
- [HttpPost]
- [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
- public async Task AddAsync(int idWell, [Required] DateOnly startDate, CancellationToken token)
- {
- if (!await UserHasAccesToWellAsync(idWell, token))
- return Forbid();
+ return idUser.Value;
+ }
+ }
- var idUser = User.GetUserId()!.Value;
+ ///
+ /// Создать суточный отчёт
+ ///
+ /// Id скважины
+ /// Дата формирования суточного отчёта
+ ///
+ ///
+ [HttpPost]
+ [Permission]
+ [ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
+ public async Task InsertAsync(int idWell, DateOnly dateStart, CancellationToken cancellationToken)
+ {
+ await AssertUserAccessToWell(idWell, cancellationToken);
- var result = await dailyReportService.AddAsync(idWell, startDate, idUser, token);
- return Ok(result);
- }
+ var id = await dailyReportService.InsertAsync(idWell, dateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc),
+ cancellationToken);
- ///
- /// Сохранение изменений набора данных для формирования рапорта (заголовок)
- ///
- ///
- /// Дата без учета времени
- ///
- ///
- ///
- [HttpPut("{date}/head")]
- [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
- public Task UpdateHeadAsync(int idWell, [Required] DateOnly date, [Required] HeadDto dto, CancellationToken token)
- => UpdateReportBlockAsync(idWell, date, dto, token);
+ return Ok(id);
+ }
- ///
- /// Сохранение изменений набора данных для формирования рапорта (блок КНБК)
- ///
- ///
- /// Дата без учета времени
- ///
- ///
- ///
- [HttpPut("{date}/bha")]
- [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
- public Task UpdateBhaAsync(int idWell, [Required] DateOnly date, [Required] BhaDto dto, CancellationToken token)
- => UpdateReportBlockAsync(idWell, date, dto, token);
+ ///
+ /// Обновить подпись
+ ///
+ /// Id скважины
+ /// Id суточного отчёта
+ /// Обновляемый блок
+ ///
+ ///
+ [HttpPut("sign")]
+ [Permission]
+ [ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
+ public Task
+ UpdateSignBlockAsync(int idWell, int idDailyReport, SignBlockDto signBlock, CancellationToken cancellationToken) =>
+ UpdateBlockAsync(idWell, idDailyReport, signBlock, cancellationToken);
- ///
- /// Сохранение изменений набора данных для формирования рапорта (безметражные работы)
- ///
- ///
- /// Дата без учета времени
- ///
- ///
- ///
- [HttpPut("{date}/noDrilling")]
- [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
- public Task UpdateNoDrillingAsync(int idWell, [Required] DateOnly date, [Required] NoDrillingDto dto, CancellationToken token)
- => UpdateReportBlockAsync(idWell, date, dto, token);
+ ///
+ /// Обновить наработку подсистем
+ ///
+ /// Id скважины
+ /// Id суточного отчёта
+ /// Обновляемый блок
+ ///
+ ///
+ [HttpPut("subsystem")]
+ [Permission]
+ [ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
+ public Task UpdateSubsystemBlockAsync(int idWell, int idDailyReport, SubsystemBlockDto subsystemBlock,
+ CancellationToken cancellationToken)
+ {
+ var validSubsystemIds = new[] { 100000, 100001 };
- ///
- /// Сохранение изменений набора данных для формирования рапорта (САУБ)
- ///
- ///
- /// Дата без учета времени
- ///
- ///
- ///
- [HttpPut("{date}/saub")]
- [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
- public Task UpdateSaubAsync(int idWell, [Required] DateOnly date, [Required] SaubDto dto, CancellationToken token)
- => UpdateReportBlockAsync(idWell, date, dto, token);
+ if (subsystemBlock.Modules.Any(m => !validSubsystemIds.Contains(m.IdSubsystem)))
+ throw new ArgumentInvalidException($"Возможно добавить модули только с Id: {string.Join(", ", validSubsystemIds)}",
+ nameof(subsystemBlock.Modules));
- ///
- /// Сохранение изменений набора данных для формирования рапорта (подпись)
- ///
- ///
- /// Дата без учета времени
- ///
- ///
- ///
- [HttpPut("{date}/sign")]
- [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
- public Task UpdateSignAsync(int idWell, [Required] DateOnly date, [Required] SignDto dto, CancellationToken token)
- => UpdateReportBlockAsync(idWell, date, dto, token);
+ return UpdateBlockAsync(idWell, idDailyReport, subsystemBlock, cancellationToken);
+ }
- ///
- /// Обновление блока суточного рапорта
- ///
- /// ключ скважины
- /// дата суточного рапорта
- ///
- ///
- ///
- private async Task UpdateReportBlockAsync(int idWell, DateOnly date, ItemInfoDto dto, CancellationToken token)
- {
- if (!await UserHasAccesToWellAsync(idWell, token))
- return Forbid();
+ ///
+ /// Обновить баланс времени
+ ///
+ /// Id скважины
+ /// Id суточного отчёта
+ /// Обновляемый блок
+ ///
+ ///
+ [HttpPut("timeBalance")]
+ [Permission]
+ [ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
+ public Task UpdateTimeBalanceBlockAsync(int idWell, int idDailyReport, TimeBalanceBlockDto timeBalanceBlock,
+ CancellationToken cancellationToken)
+ {
+ var validWellOperationsIds = new[] { 4001, 4002, 4004, 4012 };
- dto.IdUser = User.GetUserId();
- dto.LastUpdateDate = DateTimeOffset.Now;
+ if (timeBalanceBlock.WellOperations.Any(o => !validWellOperationsIds.Contains(o.IdWellOperation)))
+ throw new ArgumentInvalidException($"Возможно добавить операции только с Id: {string.Join(",", validWellOperationsIds)}",
+ nameof(timeBalanceBlock.WellOperations));
+
+ var wellSections = wellOperationRepository.GetSectionTypes();
- var result = await dailyReportService.UpdateBlockAsync(idWell, date, dto, token);
- return Ok(result);
- }
+ if (wellSections.All(s => s.Id != timeBalanceBlock.IdSection))
+ throw new ArgumentInvalidException($"Секция с Id: {timeBalanceBlock.IdSection} не найдена", nameof(timeBalanceBlock.IdSection));
- ///
- /// Сформировать и скачать рапорт в формате excel
- ///
- ///
- ///
- ///
- ///
- [HttpGet("{date}/excel")]
- [ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK)]
- [ProducesResponseType(StatusCodes.Status204NoContent)]
- public async Task DownloadAsync(int idWell, DateOnly date, CancellationToken token)
- {
- if (!await UserHasAccesToWellAsync(idWell, token))
- return Forbid();
+ return UpdateBlockAsync(idWell, idDailyReport, timeBalanceBlock, cancellationToken);
+ }
- var well = await wellService.GetOrDefaultAsync(idWell, token);
- if (well is null)
- return this.ValidationBadRequest(nameof(idWell), $"Скважина c id:{idWell} не найдена");
+ ///
+ /// Получить список суточных отчётов по скважине
+ ///
+ /// Идентификатор скважины
+ /// Параметры запроса
+ ///
+ ///
+ [HttpGet]
+ [ProducesResponseType(typeof(PaginationContainer), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
+ public async Task GetAsync(int idWell, [FromQuery] FileReportRequest request, CancellationToken cancellationToken)
+ {
+ await AssertUserAccessToWell(idWell, cancellationToken);
- var stream = await dailyReportService.MakeReportAsync(idWell, date, token);
- if (stream is null)
- return NoContent();
-
- var fileName = $"Суточный рапорт по скважине {well.Caption} куст {well.Cluster}.xlsx";
- return File(stream, "application/octet-stream", fileName);
+ var dailyReports = await dailyReportService.GetAsync(idWell, request, cancellationToken);
- }
+ return Ok(dailyReports);
+ }
+
+ ///
+ /// Получить диапазон дат по которым возможно сформировать суточный отчёты
+ ///
+ /// Id скважины
+ ///
+ ///
+ [HttpGet("datesRange")]
+ [ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)]
+ public async Task GetDatesRangeAsync(int idWell, CancellationToken cancellationToken)
+ {
+ await AssertUserAccessToWell(idWell, cancellationToken);
- protected async Task UserHasAccesToWellAsync(int idWell, CancellationToken token)
- {
- var idCompany = User.GetCompanyId();
- if (idCompany is not null &&
- await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, idWell, token)
- .ConfigureAwait(false))
- return true;
- return false;
- }
- }
+ var datesRanges = await dailyReportService.GetDatesRangeAsync(idWell, cancellationToken);
-}
+ return Ok(datesRanges);
+ }
+
+ ///
+ /// Экспорт суточного рапорта
+ ///
+ /// Id скважины
+ /// Дата формирования суточного отчёта
+ ///
+ ///
+ [HttpGet("{dailyReportDateStart}")]
+ [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
+ public async Task ExportAsync(int idWell, DateOnly dailyReportDateStart, CancellationToken cancellationToken)
+ {
+ await AssertUserAccessToWell(idWell, cancellationToken);
+
+ var dailyReport = await dailyReportExportService.ExportAsync(idWell,
+ dailyReportDateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), cancellationToken);
+
+ return File(dailyReport.File, "application/octet-stream", dailyReport.FileName);
+ }
+
+ private async Task UpdateBlockAsync(int idWell, int idDailyReport, TBlock block,
+ CancellationToken cancellationToken)
+ where TBlock : EditableBlock
+ {
+ await AssertUserAccessToWell(idWell, cancellationToken);
+
+ var id = await dailyReportService.UpdateBlockAsync(idDailyReport, IdUser, block, cancellationToken);
+
+ return Ok(id);
+ }
+
+ private async Task AssertUserAccessToWell(int idWell, CancellationToken cancellationToken)
+ {
+ var idCompany = User.GetCompanyId();
+
+ if (!idCompany.HasValue || !await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value, idWell, cancellationToken))
+ throw new ForbidException("Нет доступа к скважине");
+ }
+}
\ No newline at end of file