diff --git a/AsbCloudApp/Services/DailyReport/IDailyReportService.cs b/AsbCloudApp/Services/DailyReport/IDailyReportService.cs index c07a5c06..44283a44 100644 --- a/AsbCloudApp/Services/DailyReport/IDailyReportService.cs +++ b/AsbCloudApp/Services/DailyReport/IDailyReportService.cs @@ -3,42 +3,36 @@ using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; using AsbCloudApp.Data.DailyReport; -using AsbCloudApp.Data.DailyReport.Blocks; using AsbCloudApp.Requests; namespace AsbCloudApp.Services.DailyReport; +/// +/// Суточные отчёты +/// public interface IDailyReportService { /// - /// Создать отчёт + /// Обновить или создать суточный отчёт /// - /// - /// - /// - /// - Task InsertAsync(int idWell, DateTime dateStart, CancellationToken cancellationToken); - - /// - /// Обновить блок - /// - /// + /// /// /// /// - /// + /// /// - Task UpdateBlockAsync(int idDailyReport, int idUser, TBlock editableBlock, CancellationToken cancellationToken) - where TBlock : EditableBlock; + Task UpdateOrInsertAsync(int idWell, DateTime dateDailyReport, int idUser, TBlock editableBlock, + CancellationToken cancellationToken) + where TBlock : ItemInfoDto; /// /// Получить сформированный суточный отчёт /// /// - /// + /// /// /// - Task GetAsync(int idWell, DateTime dateStart, CancellationToken cancellationToken); + Task GetAsync(int idWell, DateTime dateDailyReport, CancellationToken cancellationToken); /// /// Получить список суточных отчётов по скважине @@ -47,8 +41,7 @@ public interface IDailyReportService /// /// /// - Task> GetAsync(int idWell, FileReportRequest request, - CancellationToken cancellationToken); + Task> GetAsync(int idWell, FileReportRequest request, CancellationToken cancellationToken); /// /// Получить диапазон дат по которым возможно сформировать суточный отчёты diff --git a/AsbCloudInfrastructure/Repository/DailyReportRepository.cs b/AsbCloudInfrastructure/Repository/DailyReportRepository.cs index 147cae3e..add98668 100644 --- a/AsbCloudInfrastructure/Repository/DailyReportRepository.cs +++ b/AsbCloudInfrastructure/Repository/DailyReportRepository.cs @@ -32,10 +32,16 @@ public class DailyReportRepository : CrudRepositoryBase d.IdWell == idWell); if (request.GeDate.HasValue) - query = query.Where(d => d.Date >= request.GeDate.Value.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc)); + { + var geDate = request.GeDate.Value.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc); + query = query.Where(d => d.Date >= geDate); + } if (request.LeDate.HasValue) - query = query.Where(d => d.Date <= request.LeDate.Value.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc)); + { + var leDate = request.LeDate.Value.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc); + query = query.Where(d => d.Date <= leDate); + } if (request.SortFields?.Any() == true) { diff --git a/AsbCloudInfrastructure/Services/DailyReport/DailyReportExportService.cs b/AsbCloudInfrastructure/Services/DailyReport/DailyReportExportService.cs index 6fbc0c12..53bb24f3 100644 --- a/AsbCloudInfrastructure/Services/DailyReport/DailyReportExportService.cs +++ b/AsbCloudInfrastructure/Services/DailyReport/DailyReportExportService.cs @@ -28,7 +28,7 @@ public class DailyReportExportService : IDailyReportExportService private const int columnTimeBalanceDurationFact = 4; private const int columnTimeBalanceReasonDeviation = 8; private const int columnTimeBalanceDrillingDeviationPerSection = 10; - private const int columnTimeBalanceDrillingDeviationPerDaily = 11; + private const int columnTimeBalanceDrillingDeviationPerDay = 11; private const int columnSheduleDriller = 3; private const int columnSheduleShiftStart = 7; @@ -56,10 +56,9 @@ public class DailyReportExportService : IDailyReportExportService private const string cellContractor = "C3"; private const string cellDeposit = "C5"; private const string cellCluster = "C6"; - private const string cellWellName = "C7"; + private const string cellWellCaption = "C7"; private const string cellWellType = "C8"; - private const string cellDateStart = "C12"; - private const string cellDateEnd = "D12"; + private const string cellDate = "C12"; private const string cellDepthStart = "C13"; private const string cellDepthEnd = "D13"; @@ -95,7 +94,7 @@ public class DailyReportExportService : IDailyReportExportService var stream = await GenerateFileAsync(dailyReport, cancellationToken); - var fileName = $"Суточный_рапорт_по_скважине_{dailyReport.WellName}_куст_{dailyReport.Cluster}_от_{dailyReport.DateStart:yy-MM-dd}.xlsx"; + var fileName = $"Суточный_рапорт_по_скважине_{dailyReport.WellCaption}_куст_{dailyReport.Cluster}_от_{dailyReport.Date:yy-MM-dd}.xlsx"; return (fileName, stream); } @@ -127,10 +126,9 @@ public class DailyReportExportService : IDailyReportExportService sheet.Cell(cellContractor).Value = dailyReport.Contractor; sheet.Cell(cellDeposit).Value = dailyReport.Deposit; sheet.Cell(cellCluster).Value = dailyReport.Cluster; - sheet.Cell(cellWellName).Value = dailyReport.WellName; + sheet.Cell(cellWellCaption).Value = dailyReport.WellCaption; sheet.Cell(cellWellType).Value = dailyReport.WellType; - sheet.Cell(cellDateStart).Value = dailyReport.DateStart; - sheet.Cell(cellDateEnd).Value = dailyReport.DateEnd; + sheet.Cell(cellDate).Value = dailyReport.Date; sheet.Cell(cellDepthStart).Value = dailyReport.DepthStart; sheet.Cell(cellDepthEnd).Value = dailyReport.DepthEnd; @@ -163,18 +161,18 @@ public class DailyReportExportService : IDailyReportExportService foreach (var wellOperation in timeBalanceBlock.WellOperations.OrderBy(w => w.IdWellOperation)) { - sheet.Cell(rowCurrent, columnTimeBalanceDurationPlan).Value = wellOperation.DurationHours?.Plan; - sheet.Cell(rowCurrent, columnTimeBalanceDurationFact).Value = wellOperation.DurationHours?.Fact; + sheet.Cell(rowCurrent, columnTimeBalanceDurationPlan).Value = wellOperation.DurationHours.Plan; + sheet.Cell(rowCurrent, columnTimeBalanceDurationFact).Value = wellOperation.DurationHours.Fact; sheet.Cell(rowCurrent, columnTimeBalanceReasonDeviation).Value = wellOperation.ReasonDeviation; sheet.Cell(rowCurrent, columnTimeBalanceDrillingDeviationPerSection).Value = wellOperation.DrillingDeviationPerSection; - sheet.Cell(rowCurrent, columnTimeBalanceDrillingDeviationPerDaily).Value = wellOperation.DrillingDeviationPerDaily; + sheet.Cell(rowCurrent, columnTimeBalanceDrillingDeviationPerDay).Value = wellOperation.DrillingDeviationPerDay; rowCurrent++; } sheet.Cell(cellTimeBalanceBlockSection).Value = timeBalanceBlock.SectionName; - sheet.Cell(cellTimeBalanceBlockWellDepthPlan).Value = timeBalanceBlock.WellDepthPlan; - sheet.Cell(cellTimeBalanceBlockWellDepthFact).Value = timeBalanceBlock.WellDepthFact; + sheet.Cell(cellTimeBalanceBlockWellDepthPlan).Value = timeBalanceBlock.WellDepth.Plan; + sheet.Cell(cellTimeBalanceBlockWellDepthFact).Value = timeBalanceBlock.WellDepth.Fact; sheet.Cell(cellTimeBalanceBlockWellOperationSlipsTimeCount).Value = timeBalanceBlock.WellOperationSlipsTimeCount; } diff --git a/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs b/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs index 95d42411..97cc4e85 100644 --- a/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs +++ b/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs @@ -52,31 +52,16 @@ public class DailyReportService : IDailyReportService this.detectedOperationService = detectedOperationService; } - public async Task InsertAsync(int idWell, DateTime dateStart, CancellationToken cancellationToken) - { - if (await dailyReportRepository.AnyAsync(idWell, dateStart, cancellationToken)) - throw new ArgumentInvalidException(nameof(dateStart), "Суточный отчёт уже существует"); - - var dailyReport = new DailyReportDto - { - IdWell = idWell, - DateStart = dateStart, - }; - - return await dailyReportRepository.InsertAsync(dailyReport, cancellationToken); - } - - public async Task UpdateBlockAsync(int idDailyReport, int idUser, TBlock editableBlock, + public async Task UpdateOrInsertAsync(int idWell, DateTime dateDailyReport, int idUser, TBlock editableBlock, CancellationToken cancellationToken) - where TBlock : EditableBlock + where TBlock : ItemInfoDto { - var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idDailyReport, cancellationToken) - ?? throw new ArgumentInvalidException(nameof(idDailyReport), $"Суточный отчёт с Id: {idDailyReport} не найден"); - - editableBlock.IdUser = idUser; - - editableBlock.DateLastUpdate = DateTime.UtcNow; - dailyReport.DateLastUpdate = DateTime.UtcNow; + var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idWell, dateDailyReport, cancellationToken) ?? + new DailyReportDto + { + IdWell = idWell, + Date = dateDailyReport + }; switch (editableBlock) { @@ -89,39 +74,46 @@ public class DailyReportService : IDailyReportService case SignBlockDto signBlock: dailyReport.SignBlock = signBlock; break; + default: + throw new InvalidOperationException("Unexpected type of editableBlock"); } + editableBlock.IdUser = idUser; + editableBlock.LastUpdateDate = DateTime.UtcNow; + + dailyReport.DateLastUpdate = DateTime.UtcNow; + + if (dailyReport.Id == 0) + return await dailyReportRepository.InsertAsync(dailyReport, cancellationToken); + return await dailyReportRepository.UpdateAsync(dailyReport, cancellationToken); } - public async Task GetAsync(int idWell, DateTime dateStart, CancellationToken cancellationToken) + public async Task GetAsync(int idWell, DateTime dateDailyReport, CancellationToken cancellationToken) { var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken); if (well is null) throw new ArgumentInvalidException($"Скважина с Id: {idWell} не найдена", nameof(idWell)); - var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idWell, dateStart, cancellationToken) ?? new DailyReportDto + var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idWell, dateDailyReport, cancellationToken) ?? new DailyReportDto { - DateStart = dateStart.Date, + Date = dateDailyReport.Date, IdWell = well.Id }; - var factWellOperations = await GetFactWellOperationsAsync(idWell, dailyReport.DateStart, dailyReport.DateEnd, - cancellationToken); + var factWellOperations = (await GetFactWellOperationsAsync(idWell, dailyReport.Date, cancellationToken)) + .OrderBy(o => o.DateStart) + .ThenBy(o => o.DepthStart); - dailyReport.WellName = well.Caption; + dailyReport.WellCaption = well.Caption; dailyReport.WellType = well.WellType; dailyReport.Cluster = well.Cluster; dailyReport.Deposit = well.Deposit; dailyReport.Customer = well.Companies.FirstOrDefault(c => c.IdCompanyType == 1)?.Caption; dailyReport.Contractor = well.Companies.FirstOrDefault(c => c.IdCompanyType == 2)?.Caption; - - if (factWellOperations.Any()) - { - dailyReport.DepthStart = factWellOperations.Min(o => o.DepthStart); - dailyReport.DepthEnd = factWellOperations.Max(o => o.DepthEnd); - } + dailyReport.DepthStart = factWellOperations.FirstOrDefault()?.DepthStart; + dailyReport.DepthEnd = factWellOperations.LastOrDefault()?.DepthEnd; await UpdateTimeBalanceBlockAsync(dailyReport, factWellOperations, cancellationToken); await UpdateSubsystemBlockAsync(dailyReport, cancellationToken); @@ -184,18 +176,18 @@ public class DailyReportService : IDailyReportService { for (var day = result.Skip; day - result.Skip < result.Take && datesRange.To.AddDays(-day) >= datesRange.From; day++) { - var dateStart = datesRange.To.AddDays(-day); + var dateDailyReport = datesRange.To.AddDays(-day).Date; - AddDailyReport(dateStart); + AddDailyReport(dateDailyReport); } } else { for (var day = result.Skip; day - result.Skip < result.Take && datesRange.From.AddDays(day) <= datesRange.To; day++) { - var dateStart = datesRange.From.AddDays(day); + var dateDailyReport = datesRange.From.AddDays(day).Date; - AddDailyReport(dateStart); + AddDailyReport(dateDailyReport); } } @@ -203,10 +195,9 @@ public class DailyReportService : IDailyReportService return result; - void AddDailyReport(DateTime dateStart) + void AddDailyReport(DateTime date) { - var existingDailyReport = existingDailyReports.FirstOrDefault(d => d.IdWell == idWell && - d.DateStart == dateStart); + var existingDailyReport = existingDailyReports.FirstOrDefault(d => d.IdWell == idWell && d.Date == date); if (existingDailyReport is not null) { @@ -216,7 +207,7 @@ public class DailyReportService : IDailyReportService dailyReports.Add(new DailyReportDto { - DateStart = dateStart, + Date = date, IdWell = well.Id }); } @@ -224,16 +215,18 @@ public class DailyReportService : IDailyReportService public async Task GetDatesRangeAsync(int idWell, CancellationToken cancellationToken) { - var factOperations = await GetFactWellOperationsAsync(idWell, null, null, - cancellationToken); + var factOperations = await GetFactWellOperationsAsync(idWell, null, cancellationToken); if (!factOperations.Any()) return null; + var minDateStart = factOperations.Min(o => o.DateStart); + var maxDateStart = factOperations.Max(o => o.DateStart); + return new DatesRangeDto { - From = factOperations.Min(o => o.DateStart).Date, - To = factOperations.Max(o => o.DateStart).Date + From = minDateStart.Date.AddDays(1) <= DateTime.UtcNow ? minDateStart : DateTime.UtcNow.Date.AddDays(-1), + To = maxDateStart.AddDays(1) <= DateTime.UtcNow ? maxDateStart : DateTime.UtcNow.Date.AddDays(-1) }; } @@ -241,24 +234,24 @@ public class DailyReportService : IDailyReportService CancellationToken cancellationToken) { const int idWellOperationSlipsTime = 5011; - + if (dailyReport.TimeBalanceBlock is not null) { dailyReport.TimeBalanceBlock.SectionName = wellOperationRepository.GetSectionTypes() .FirstOrDefault(s => s.Id == dailyReport.TimeBalanceBlock.IdSection)?.Caption; - + dailyReport.TimeBalanceBlock.WellOperationSlipsTimeCount = (await detectedOperationService.GetAsync( new DetectedOperationRequest { IdsCategories = new[] { idWellOperationSlipsTime }, IdWell = dailyReport.IdWell, - GtDate = dailyReport.DateStart, - LtDate = dailyReport.DateEnd + GtDate = dailyReport.Date, + LtDate = dailyReport.Date.AddHours(24) }, cancellationToken))?.Stats.Sum(s => s.Count); - dailyReport.TimeBalanceBlock.WellDepthFact = factWellOperations + dailyReport.TimeBalanceBlock.WellDepth.Fact = factWellOperations .Where(o => o.IdWellSectionType == dailyReport.TimeBalanceBlock.IdSection) - .Sum(o => o.DepthEnd = o.DepthStart); + .Sum(o => o.DepthEnd - o.DepthStart); } } @@ -267,9 +260,9 @@ public class DailyReportService : IDailyReportService var trajectory = (await trajectoryFactRepository.GetAsync(new TrajectoryGeoFactRequest { IdWell = dailyReport.IdWell, - GeDate = dailyReport.DateStart, - LeDate = dailyReport.DateEnd - }, cancellationToken)).LastOrDefault(); + GeDate = dailyReport.Date, + LeDate = dailyReport.Date.AddHours(24) + }, cancellationToken)).MaxBy(t => t.WellboreDepth); dailyReport.TrajectoryBlock = new TrajectoryBlockDto { @@ -281,7 +274,7 @@ public class DailyReportService : IDailyReportService } private async Task AddScheduleBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken) => - dailyReport.ScheduleBlock = (await scheduleRepository.GetAsync(dailyReport.IdWell, dailyReport.DateStart, cancellationToken)) + dailyReport.ScheduleBlock = (await scheduleRepository.GetAsync(dailyReport.IdWell, dailyReport.Date, cancellationToken)) .Select(s => new ScheduleRecordDto { ShiftStart = s.ShiftStart, @@ -299,13 +292,13 @@ public class DailyReportService : IDailyReportService async Task> GetSubsystemsAsync() { - var modules = new List(); + var subsystems = new List(); - var statSubsystemOperationTimePerDaily = (await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest + var statSubsystemOperationTimePerDay = (await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest { IdWell = dailyReport.IdWell, - GtDate = dailyReport.DateStart, - LtDate = dailyReport.DateEnd + GtDate = dailyReport.Date, + LtDate = dailyReport.Date.AddHours(24) }, cancellationToken)).Select(s => { var subsystemRecord = s.Adapt(); @@ -325,21 +318,21 @@ public class DailyReportService : IDailyReportService return subsystemRecord; }); - modules.AddRange(statSubsystemOperationTimePerDaily); - modules.AddRange(statSubsystemOperationTimePerWell); + subsystems.AddRange(statSubsystemOperationTimePerDay); + subsystems.AddRange(statSubsystemOperationTimePerWell); if (dailyReport.SubsystemBlock?.Subsystems != null && dailyReport.SubsystemBlock.Subsystems.Any()) - modules.AddRange(dailyReport.SubsystemBlock.Subsystems); + subsystems.AddRange(dailyReport.SubsystemBlock.Subsystems); - return modules; + return subsystems.OrderBy(s => s.SubsystemName); } } private async Task AddProcessMapWellDrillingBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken) { dailyReport.ProcessMapWellDrillingBlock = (await processMapReportWellDrillingService.GetAsync(dailyReport.IdWell, - cancellationToken)).Where(p => p.DateStart >= dailyReport.DateStart && - p.DateStart <= dailyReport.DateEnd) + cancellationToken)).Where(p => p.DateStart >= dailyReport.Date && + p.DateStart <= dailyReport.Date.AddHours(24)) .GroupBy(p => p.DrillingMode) .Select(g => new ProcessMapWellDrillingRecordDto { @@ -354,7 +347,7 @@ public class DailyReportService : IDailyReportService }); } - private void AddFactWellOperationBlock(DailyReportDto dailyReport, IEnumerable factWellOperations) + private static void AddFactWellOperationBlock(DailyReportDto dailyReport, IEnumerable factWellOperations) { const int idWellOperationCategoryDrilling = 4001; @@ -373,13 +366,12 @@ public class DailyReportService : IDailyReportService }; } - private Task> GetFactWellOperationsAsync(int idWell, DateTime? dailyReportDateStart, - DateTime? dailyReportDateEnd, CancellationToken cancellationToken) => + private Task> GetFactWellOperationsAsync(int idWell, DateTime? dateDailyReport, CancellationToken cancellationToken) => wellOperationRepository.GetAsync(new WellOperationRequest { IdWell = idWell, OperationType = WellOperation.IdOperationTypeFact, - GeDate = dailyReportDateStart, - LtDate = dailyReportDateEnd + GeDate = dateDailyReport, + LtDate = dateDailyReport?.AddHours(24) }, cancellationToken); } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/DailyReport/DailyReportTemplate.xlsx b/AsbCloudInfrastructure/Services/DailyReport/DailyReportTemplate.xlsx index 6bf8da35..7594507c 100644 Binary files a/AsbCloudInfrastructure/Services/DailyReport/DailyReportTemplate.xlsx and b/AsbCloudInfrastructure/Services/DailyReport/DailyReportTemplate.xlsx differ diff --git a/AsbCloudWebApi.Tests/Services/DailyReportServiceTest.cs b/AsbCloudWebApi.Tests/Services/DailyReportServiceTest.cs index 7e7046a8..a8447fa2 100644 --- a/AsbCloudWebApi.Tests/Services/DailyReportServiceTest.cs +++ b/AsbCloudWebApi.Tests/Services/DailyReportServiceTest.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; @@ -6,11 +8,15 @@ using AsbCloudApp.Data.DailyReport; using AsbCloudApp.Data.DailyReport.Blocks.Sign; using AsbCloudApp.Data.DailyReport.Blocks.Subsystems; using AsbCloudApp.Data.DailyReport.Blocks.TimeBalance; -using AsbCloudApp.Exceptions; +using AsbCloudApp.Data.DetectedOperation; +using AsbCloudApp.Data.ProcessMaps.Report; +using AsbCloudApp.Data.Subsystems; using AsbCloudApp.Repositories; +using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudApp.Services.ProcessMaps.WellDrilling; using AsbCloudApp.Services.Subsystems; +using AsbCloudDb.Model; using AsbCloudInfrastructure.Services.DailyReport; using NSubstitute; using Xunit; @@ -20,10 +26,169 @@ namespace AsbCloudWebApi.Tests.Services; public class DailyReportServiceTest { private const int idDailyReport = 1; - private const int idWell = 2; private const int idUser = 3; + private const int idWell = 2; - private readonly DateTime dateStart = new DateOnly(2023, 10, 26).ToDateTime(TimeOnly.MinValue); + private readonly DateTime dateDailyReport = new DateOnly(2023, 10, 26).ToDateTime(TimeOnly.MinValue); + + private readonly SubsystemBlockDto fakeSubsystemBlock = new() + { + IdUser = idUser, + WellBore = 999, + MeasurementsPerDay = 999, + TotalRopPlan = 999, + Comment = "Увеличить обороты", + Subsystems = new[] + { + new SubsystemRecordDto + { + SubsystemName = "АвтоСПО", + IdTimeInterval = 2, + UsedTimeHours = 24, + SumDepthInterval = 1500, + KUsage = 100 + } + } + }; + + private readonly SignBlockDto fakeSignBlock = new() + { + IdUser = idUser, + DrillingMaster = new SignRecordDto + { + Name = "Иван", + Patronymic = "Иванович", + Surname = "Иванов" + }, + Supervisor = new SignRecordDto() + { + Name = "Илья", + Patronymic = "Ильич", + Surname = "Бурилов" + } + }; + + private readonly TimeBalanceBlockDto fakeTimeBalanceBlock = new() + { + IdUser = idUser, + IdSection = 1, + WellDepth = new PlanFactDto + { + Plan = 2000 + }, + WellOperations = new[] + { + new TimeBalanceRecordDto + { + IdWellOperation = 1, + DurationHours = new PlanFactDto + { + Fact = 100, + Plan = 150, + }, + DrillingDeviationPerSection = 90, + DrillingDeviationPerDay = 100, + ReasonDeviation = "Отклонение" + } + } + }; + + private readonly DetectedOperationListDto fakeWellOperationSlipsTime = new() + { + Stats = new[] + { + new DetectedOperationDrillersStatDto + { + Count = 40 + } + } + }; + + private readonly ProcessMapReportWellDrillingDto fakeProcessMapReportWellDrilling = new() + { + DrillingMode = "Ротор", + DateStart = new DateTime(2023, 10, 26), + DeltaDepth = 500, + Rop = new PlanFactDto + { + Plan = 300, + Fact = 500 + }, + MechDrillingHours = 100 + }; + + private readonly WellSectionTypeDto fakeSectionType = new() + { + Id = 1, + Caption = "Пилотный ствол", + }; + + private readonly TrajectoryGeoFactDto fakeLastFactTrajectory = new() + { + WellboreDepth = 100, + VerticalDepth = 150, + ZenithAngle = 3, + AzimuthGeo = 5 + }; + + private readonly CompanyDto fakeCustomer = new() + { + Caption = "Тестовый заказчик", + IdCompanyType = 1 + }; + + private readonly CompanyDto fakeContractor = new() + { + Caption = "Тестовый подрядчик", + IdCompanyType = 2 + }; + + private readonly WellOperationDto fakeFirstFactWellOperation = new() + { + IdWell = idWell, + IdParentCategory = 4001, + IdWellSectionType = 1, + CategoryName = "Механическое. бурение", + DateStart = new DateTime(2023, 10, 26), + DepthStart = 80, + DepthEnd = 150, + DurationHours = 8, + }; + + private readonly WellOperationDto fakeLastFactWellOperation = new() + { + IdWell = idWell, + CategoryName = "Механическое. бурение", + IdWellSectionType = 1, + IdParentCategory = 4001, + DateStart = new DateTime(2023, 10, 26), + DepthStart = 150, + DepthEnd = 200, + DurationHours = 8, + }; + + private readonly ScheduleDto fakeShedule = new() + { + IdWell = idWell, + ShiftStart = new TimeDto(1), + ShiftEnd = new TimeDto(5), + DrillStart = new DateTime(2023, 01, 26), + DrillEnd = new DateTime(2023, 12, 26), + Driller = new() + { + Name = "Иван", + Surname = "Иванов", + Patronymic = "Бурила" + } + }; + + private readonly SubsystemStatDto fakeStatSubsystemOperationTimePerDay = new() + { + SubsystemName = "АПД", + SumDepthInterval = 250, + UsedTimeHours = 200, + KUsage = 30 + }; private readonly IWellService wellServiceMock = Substitute.For(); private readonly ITrajectoryFactRepository trajectoryFactRepositoryMock = Substitute.For(); @@ -33,18 +198,30 @@ public class DailyReportServiceTest private readonly ISubsystemOperationTimeService subsystemOperationTimeServiceMock = Substitute.For(); private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingServiceMock = Substitute.For(); private readonly IDetectedOperationService detectedOperationServiceMock = Substitute.For(); - + private readonly DailyReportService dailyReportService; private readonly DailyReportDto fakeDailyReport; + private readonly WellDto fakeWell; public DailyReportServiceTest() { - fakeDailyReport = new() + fakeDailyReport = new DailyReportDto { Id = idDailyReport, IdWell = idWell, - DateStart = dateStart + Date = dateDailyReport, + DateLastUpdate = null + }; + + fakeWell = new WellDto + { + Id = idWell, + Caption = "Тестовое название", + WellType = "Горизонтальная", + Cluster = "Тестовый куст", + Deposit = "Тестовое месторождение", + Companies = new[] { fakeCustomer, fakeContractor } }; dailyReportService = new DailyReportService(wellServiceMock, @@ -59,139 +236,276 @@ public class DailyReportServiceTest dailyReportRepositoryMock.InsertAsync(Arg.Any(), Arg.Any()) .ReturnsForAnyArgs(idDailyReport); - dailyReportRepositoryMock.GetOrDefaultAsync(idDailyReport, Arg.Any()) - .Returns(fakeDailyReport); + dailyReportRepositoryMock.GetOrDefaultAsync(Arg.Any(), Arg.Any(), Arg.Any()) + .ReturnsForAnyArgs(fakeDailyReport); dailyReportRepositoryMock.UpdateAsync(Arg.Any(), Arg.Any()) .ReturnsForAnyArgs(idDailyReport); + + wellServiceMock.GetOrDefaultAsync(Arg.Any(), Arg.Any()) + .ReturnsForAnyArgs(fakeWell); + + trajectoryFactRepositoryMock.GetAsync(Arg.Any(), Arg.Any()) + .ReturnsForAnyArgs(new[] { fakeLastFactTrajectory }); + + wellOperationRepositoryMock.GetAsync(Arg.Any(), Arg.Any()) + .ReturnsForAnyArgs(new[] { fakeFirstFactWellOperation, fakeLastFactWellOperation }); + + wellOperationRepositoryMock.GetSectionTypes() + .ReturnsForAnyArgs(new[] { fakeSectionType }); + + detectedOperationServiceMock.GetAsync(Arg.Any(), Arg.Any()) + .ReturnsForAnyArgs(fakeWellOperationSlipsTime); + + subsystemOperationTimeServiceMock.GetStatAsync(Arg.Any(), Arg.Any()) + .ReturnsForAnyArgs(new[] { fakeStatSubsystemOperationTimePerDay }); + + scheduleRepositoryMock.GetAsync(idWell, dateDailyReport, Arg.Any()) + .ReturnsForAnyArgs(new[] { fakeShedule }); + + processMapReportWellDrillingServiceMock.GetAsync(idWell, Arg.Any()) + .ReturnsForAnyArgs(new[] { fakeProcessMapReportWellDrilling }); } [Fact] - public async Task InsertAsync_ShouldReturn_ExceptionAboutDuplicate() + public async Task UpdateOrInsertAsync_ShouldReturn_UpdatedSubsystemBlock() { - //arrange - dailyReportRepositoryMock.AnyAsync(Arg.Any(), Arg.Any(), Arg.Any()) - .ReturnsForAnyArgs(true); - //act - Task Result() => dailyReportService.InsertAsync(idWell, dateStart, CancellationToken.None); - - //assert - var exception = await Assert.ThrowsAsync(Result); - Assert.Equal("Суточный отчёт уже существует", exception.Message); - } - - [Fact] - public async Task InsertAsync_ShouldReturn_PositiveId() - { - //arrange - dailyReportRepositoryMock.AnyAsync(Arg.Any(), Arg.Any(), Arg.Any()) - .ReturnsForAnyArgs(false); - - //act - var result = await dailyReportService.InsertAsync(idWell, dateStart, CancellationToken.None); + var result = await dailyReportService.UpdateOrInsertAsync(idWell, dateDailyReport, idUser, fakeSubsystemBlock, CancellationToken.None); //assert + Assert.NotNull(fakeSubsystemBlock.LastUpdateDate); + Assert.NotNull(fakeDailyReport.DateLastUpdate); + Assert.Equal(fakeSubsystemBlock.IdUser, idUser); + Assert.Equal(fakeDailyReport.SubsystemBlock, fakeSubsystemBlock); Assert.Equal(idDailyReport, result); } [Fact] - public async Task UpdateSubsystemBlock_ShouldReturn_Success() + public async Task UpdateOrInsertAsync_ShouldReturn_UpdatedSignBlock() { - //arrange - var fakeSubsystemBlock = new SubsystemBlockDto - { - IdUser = idUser, - WellBore = 999, - MeasurementsPerDay = 999, - TotalRopPlan = 999, - Comment = "Увеличить обороты", - Subsystems = new[] - { - new SubsystemRecordDto - { - SubsystemName = "АвтоСПО", - IdTimeInterval = 1, - UsedTimeHours = 24, - SumDepthInterval = 1500, - KUsage = 100 - } - } - }; - //act - var result = await dailyReportService.UpdateBlockAsync(idDailyReport, idUser, fakeSubsystemBlock, CancellationToken.None); - - //assert - Assert.NotNull(fakeSubsystemBlock.DateLastUpdate); - Assert.NotNull(fakeDailyReport.DateLastUpdate); - Assert.Equal(fakeDailyReport.SubsystemBlock, fakeSubsystemBlock); - Assert.Equal(idDailyReport, result); - } - - [Fact] - public async Task UpdateSignBlock_ShouldReturn_Success() - { - //arrange - var fakeSignBlock = new SignBlockDto - { - IdUser = idUser, - DrillingMaster = new SignRecordDto() - { - Name = "Иван", - Patronymic = "Иванович", - Surname = "Иванов" - }, - Supervisor = new SignRecordDto() - { - Name = "Илья", - Patronymic = "Ильич", - Surname = "Бурилов" - } - }; - - //act - var result = await dailyReportService.UpdateBlockAsync(idDailyReport, idUser, fakeSignBlock, CancellationToken.None); + var result = await dailyReportService.UpdateOrInsertAsync(idWell, dateDailyReport, idUser, fakeSignBlock, CancellationToken.None); //assert - Assert.NotNull(fakeSignBlock.DateLastUpdate); + Assert.NotNull(fakeSignBlock.LastUpdateDate); Assert.NotNull(fakeDailyReport.DateLastUpdate); + Assert.Equal(fakeSignBlock.IdUser, idUser); Assert.Equal(fakeDailyReport.SignBlock, fakeSignBlock); Assert.Equal(idDailyReport, result); } [Fact] - public async Task UpdateTimeBalance_ShouldReturn_Success() + public async Task UpdateOrInsertAsync_ShouldReturn_UpdatedTimeBalanceBlock() { - //arrange - var fakeTimeBalanceBlock = new TimeBalanceBlockDto - { - IdUser = idUser, - IdSection = 1, - WellDepthPlan = 2000, - WellOperations = new[] - { - new TimeBalanceRecordDto() - { - DurationHours = new PlanFactDto() - { - Fact = 100, - Plan = 150, - }, - DrillingDeviationPerSection = 90, - DrillingDeviationPerDaily = 100, - ReasonDeviation = "Отклонение" - } - } - }; - //act - var result = await dailyReportService.UpdateBlockAsync(idDailyReport, idUser, fakeTimeBalanceBlock, CancellationToken.None); + var result = await dailyReportService.UpdateOrInsertAsync(idWell, dateDailyReport, idUser, fakeTimeBalanceBlock, + CancellationToken.None); //assert - Assert.NotNull(fakeTimeBalanceBlock.DateLastUpdate); + Assert.NotNull(fakeTimeBalanceBlock.LastUpdateDate); Assert.NotNull(fakeDailyReport.DateLastUpdate); + Assert.Equal(fakeTimeBalanceBlock.IdUser, idUser); Assert.Equal(fakeDailyReport.TimeBalanceBlock, fakeTimeBalanceBlock); Assert.Equal(idDailyReport, result); } + + [Fact] + public async Task GetAsync_ShouldReturn_AddedWellInfo() + { + //act + var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None); + + //assert + Assert.Equal(result.IdWell, fakeWell.Id); + Assert.Equal(result.WellCaption, fakeWell.Caption); + Assert.Equal(result.WellType, fakeWell.WellType); + Assert.Equal(result.Cluster, fakeWell.Cluster); + Assert.Equal(result.Deposit, fakeWell.Deposit); + Assert.Equal(result.Customer, fakeCustomer.Caption); + Assert.Equal(result.Contractor, fakeContractor.Caption); + Assert.Equal(result.DepthStart, fakeFirstFactWellOperation.DepthStart); + Assert.Equal(result.DepthEnd, fakeLastFactWellOperation.DepthEnd); + } + + [Fact] + public async Task GetAsync_ShouldReturn_AddedTrajectoryBlock() + { + //act + var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None); + + //assert + Assert.Equal(fakeLastFactTrajectory.WellboreDepth, result.TrajectoryBlock.WellboreDepth); + Assert.Equal(fakeLastFactTrajectory.VerticalDepth, result.TrajectoryBlock.VerticalDepth); + Assert.Equal(fakeLastFactTrajectory.ZenithAngle, result.TrajectoryBlock.ZenithAngle); + Assert.Equal(fakeLastFactTrajectory.AzimuthGeo, result.TrajectoryBlock.AzimuthGeo); + } + + [Fact] + public async Task GetAsync_ShouldReturn_AddedFactWellOperationBlock() + { + //act + var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None); + + //assert + Assert.Equal(16, result.FactWellOperationBlock.SectionDrillingHours); + Assert.Single(result.FactWellOperationBlock.WellOperations); + + var wellOperation = result.FactWellOperationBlock.WellOperations.Single(); + + Assert.Equal("Механическое. бурение", wellOperation.CategoryName); + Assert.Equal(16, wellOperation.DurationHours); + } + + [Fact] + public async Task GetAsync_ShouldReturn_AddedScheduleBlock() + { + //act + var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None); + + //assert + Assert.Single(result.ScheduleBlock); + + var sheduleRecord = result.ScheduleBlock.Single(); + + Assert.Equal(fakeShedule.ShiftStart, sheduleRecord.ShiftStart); + Assert.Equal(fakeShedule.ShiftEnd, sheduleRecord.ShiftEnd); + Assert.Equal(fakeShedule.Driller?.Name, sheduleRecord.Name); + Assert.Equal(fakeShedule.Driller?.Surname, sheduleRecord.Surname); + Assert.Equal(fakeShedule.Driller?.Patronymic, sheduleRecord.Patronymic); + } + + [Fact] + public async Task GetAsync_ShouldReturn_UpdatedTimeBalanceBlock() + { + //arrange + fakeDailyReport.TimeBalanceBlock = fakeTimeBalanceBlock; + + //act + var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None); + + //assert + Assert.NotNull(result.TimeBalanceBlock); + Assert.Equal(fakeSectionType.Id, result.TimeBalanceBlock.IdSection); + Assert.Equal(fakeSectionType.Caption, result.TimeBalanceBlock.SectionName); + Assert.Equal(2000, result.TimeBalanceBlock?.WellDepth.Plan); + Assert.Equal(120, result.TimeBalanceBlock?.WellDepth.Fact); + Assert.Equal(40, result.TimeBalanceBlock?.WellOperationSlipsTimeCount); + } + + [Fact] + public async Task GetAsync_ShouldReturn_AddedProcessMapWellDrillingBlock() + { + //act + var result = await dailyReportService.GetAsync(idWell, dateDailyReport, CancellationToken.None); + + //assert + Assert.Single(result.ProcessMapWellDrillingBlock); + + var processMapWellDrillingRecord = result.ProcessMapWellDrillingBlock.Single(); + + Assert.Equal(fakeProcessMapReportWellDrilling.DrillingMode, processMapWellDrillingRecord.DrillingMode); + Assert.Equal(fakeProcessMapReportWellDrilling.Rop.Plan, processMapWellDrillingRecord.Rop.Plan); + Assert.Equal(fakeProcessMapReportWellDrilling.Rop.Fact, processMapWellDrillingRecord.Rop.Fact); + Assert.Equal(fakeProcessMapReportWellDrilling.DeltaDepth, processMapWellDrillingRecord.WellBoreDepth); + Assert.Equal(fakeProcessMapReportWellDrilling.MechDrillingHours, processMapWellDrillingRecord.MechDrillingHours); + } + + [Fact] + public async Task GetAsync_ShouldReturn_UpdatedSubsystemBlock() + { + //arrange + fakeDailyReport.SubsystemBlock = fakeSubsystemBlock; + + //act + var result = await dailyReportService.GetAsync(idDailyReport, dateDailyReport, CancellationToken.None); + + //assert + Assert.NotNull(result.SubsystemBlock); + Assert.Equal(3, result.SubsystemBlock?.Subsystems.Count()); + + var subsystemRecord0 = result.SubsystemBlock?.Subsystems.ElementAt(0); + + Assert.Equal("АвтоСПО", subsystemRecord0?.SubsystemName); + Assert.Equal(2, subsystemRecord0?.IdTimeInterval); + Assert.Equal(24, subsystemRecord0?.UsedTimeHours); + Assert.Equal(1500, subsystemRecord0?.SumDepthInterval); + Assert.Equal(100, subsystemRecord0?.KUsage); + + var subsystemRecord1 = result.SubsystemBlock?.Subsystems.ElementAt(1); + + Assert.Equal(fakeStatSubsystemOperationTimePerDay.SubsystemName, subsystemRecord1?.SubsystemName); + Assert.Equal(1, subsystemRecord1?.IdTimeInterval); + Assert.Equal(fakeStatSubsystemOperationTimePerDay.UsedTimeHours, subsystemRecord1?.UsedTimeHours); + Assert.Equal(fakeStatSubsystemOperationTimePerDay.SumDepthInterval, subsystemRecord1?.SumDepthInterval); + Assert.Equal(fakeStatSubsystemOperationTimePerDay.KUsage, subsystemRecord1?.KUsage); + } + + [Fact] + public async Task GetAsync_ShouldReturn_FictiveDailyReport() + { + //act + var result = await dailyReportService.GetAsync(idWell, new FileReportRequest(), CancellationToken.None); + + //assert + Assert.True((fakeLastFactWellOperation.DateStart - fakeFirstFactWellOperation.DateStart).Days == result.Count); + } + + [Theory] + [MemberData(nameof(FactWellOperationDates))] + public async Task GetDatesRangeAsync_ShouldReturn_DateRangeByFactWellOperations(IEnumerable factWellOperationDates) + { + //arrange + wellOperationRepositoryMock.GetAsync(Arg.Any(), CancellationToken.None) + .ReturnsForAnyArgs(factWellOperationDates.Select(dateStart => new WellOperationDto { DateStart = dateStart })); + + //act + var result = await dailyReportService.GetDatesRangeAsync(idWell, CancellationToken.None); + + //assert + Assert.NotNull(result); + Assert.True(result.From <= result.To); + Assert.True(result.To < DateTime.UtcNow.Date); + } + + public static IEnumerable FactWellOperationDates() + { + yield return new object[] + { + new[] + { + new DateTime(2023, 11, 1), + new DateTime(2023, 11, 9), + DateTime.UtcNow + } + }; + + yield return new object[] + { + new[] + { + new DateTime(2023, 11, 1), + new DateTime(2023, 11, 1) + } + }; + + yield return new object[] + { + new[] + { + DateTime.UtcNow, + DateTime.UtcNow + } + }; + + yield return new object[] + { + new[] + { + new DateTime(2023, 11, 1), + new DateTime(2023, 11, 9), + new DateTime(2023, 11, 11) + } + }; + } } \ No newline at end of file diff --git a/AsbCloudWebApi/Controllers/DailyReportController.cs b/AsbCloudWebApi/Controllers/DailyReportController.cs index 222eb74c..0a73a881 100644 --- a/AsbCloudWebApi/Controllers/DailyReportController.cs +++ b/AsbCloudWebApi/Controllers/DailyReportController.cs @@ -5,7 +5,6 @@ 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; @@ -57,39 +56,11 @@ public class DailyReportController : ControllerBase } } - /// - /// Создать суточный отчёт - /// - /// Id скважины - /// Дата формирования суточного отчёта - /// - /// - [HttpPost] - [Permission] - [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] - public async Task InsertAsync(int idWell, DateOnly dateStart, CancellationToken cancellationToken) - { - var dateStartToDateTime = dateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc); - - var datesRange = await dailyReportService.GetDatesRangeAsync(idWell, cancellationToken); - - if (dateStartToDateTime < datesRange?.From || dateStartToDateTime > datesRange?.To) - throw new ArgumentInvalidException("Невозможно сформировать суточный отчёт", nameof(dateStart)); - - await AssertUserAccessToWell(idWell, cancellationToken); - - var id = await dailyReportService.InsertAsync(idWell, dateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), - cancellationToken); - - return Ok(id); - } - /// /// Обновить подпись /// /// Id скважины - /// Id суточного отчёта + /// Дата суточного отчёта /// Обновляемый блок /// /// @@ -97,14 +68,15 @@ public class DailyReportController : ControllerBase [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); + public Task UpdateSignBlockAsync(int idWell, DateOnly dateDailyReport, SignBlockDto signBlock, + CancellationToken cancellationToken) => + UpdateOrInsertAsync(idWell, dateDailyReport, signBlock, cancellationToken); /// /// Обновить наработку подсистем /// /// Id скважины - /// Id суточного отчёта + /// Дата суточного отчёта /// Обновляемый блок /// /// @@ -112,7 +84,7 @@ public class DailyReportController : ControllerBase [Permission] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] - public Task UpdateSubsystemBlockAsync(int idWell, int idDailyReport, SubsystemBlockDto subsystemBlock, + public Task UpdateSubsystemBlockAsync(int idWell, DateOnly dateDailyReport, SubsystemBlockDto subsystemBlock, CancellationToken cancellationToken) { var validSubsystemNames = new[] { "АвтоСПО", "Автопроработка" }; @@ -121,14 +93,14 @@ public class DailyReportController : ControllerBase throw new ArgumentInvalidException($"Возможно добавить подсистемы с именами {string.Join(", ", validSubsystemNames)}", nameof(subsystemBlock.Subsystems)); - return UpdateBlockAsync(idWell, idDailyReport, subsystemBlock, cancellationToken); + return UpdateOrInsertAsync(idWell, dateDailyReport, subsystemBlock, cancellationToken); } /// /// Обновить баланс времени /// /// Id скважины - /// Id суточного отчёта + /// Дата суточного отчёта /// Обновляемый блок /// /// @@ -136,7 +108,7 @@ public class DailyReportController : ControllerBase [Permission] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] - public Task UpdateTimeBalanceBlockAsync(int idWell, int idDailyReport, TimeBalanceBlockDto timeBalanceBlock, + public Task UpdateTimeBalanceBlockAsync(int idWell, DateOnly dateDailyReport, TimeBalanceBlockDto timeBalanceBlock, CancellationToken cancellationToken) { var validWellOperationsIds = new[] { 1, 2, 3, 4 }; @@ -144,13 +116,13 @@ public class DailyReportController : ControllerBase if (timeBalanceBlock.WellOperations.Any(o => !validWellOperationsIds.Contains(o.IdWellOperation))) throw new ArgumentInvalidException($"Возможно добавить операции только с Id: {string.Join(", ", validWellOperationsIds)}", nameof(timeBalanceBlock.WellOperations)); - + var wellSections = wellOperationRepository.GetSectionTypes(); if (wellSections.All(s => s.Id != timeBalanceBlock.IdSection)) throw new ArgumentInvalidException($"Секция с Id: {timeBalanceBlock.IdSection} не найдена", nameof(timeBalanceBlock.IdSection)); - return UpdateBlockAsync(idWell, idDailyReport, timeBalanceBlock, cancellationToken); + return UpdateOrInsertAsync(idWell, dateDailyReport, timeBalanceBlock, cancellationToken); } /// @@ -193,36 +165,37 @@ public class DailyReportController : ControllerBase /// Экспорт суточного рапорта /// /// Id скважины - /// Дата формирования суточного отчёта + /// Дата формирования суточного отчёта /// /// - [HttpGet("{dateStart}")] + [HttpGet("{dateDailyReport}")] [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] - public async Task ExportAsync(int idWell, DateOnly dateStart, CancellationToken cancellationToken) + public async Task ExportAsync(int idWell, DateOnly dateDailyReport, CancellationToken cancellationToken) { - var dateStartToDateTime = dateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc); - + var dateStartToDateTime = dateDailyReport.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc); + var datesRange = await dailyReportService.GetDatesRangeAsync(idWell, cancellationToken); if (dateStartToDateTime < datesRange?.From || dateStartToDateTime > datesRange?.To) - throw new ArgumentInvalidException("Невозможно получить суточный отчёт", nameof(dateStart)); - + throw new ArgumentInvalidException("Невозможно получить суточный отчёт", nameof(dateDailyReport)); + await AssertUserAccessToWell(idWell, cancellationToken); var dailyReport = await dailyReportExportService.ExportAsync(idWell, - dateStart.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), cancellationToken); + dateDailyReport.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, + private async Task UpdateOrInsertAsync(int idWell, DateOnly dateDailyReport, TBlock block, CancellationToken cancellationToken) - where TBlock : EditableBlock + where TBlock : ItemInfoDto { await AssertUserAccessToWell(idWell, cancellationToken); - - var id = await dailyReportService.UpdateBlockAsync(idDailyReport, IdUser, block, cancellationToken); + + var id = await dailyReportService.UpdateOrInsertAsync(idWell, dateDailyReport.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc), IdUser, + block, cancellationToken); return Ok(id); }