diff --git a/AsbCloudApp/Services/DailyReport/IDailyReportService.cs b/AsbCloudApp/Services/DailyReport/IDailyReportService.cs
new file mode 100644
index 00000000..c07a5c06
--- /dev/null
+++ b/AsbCloudApp/Services/DailyReport/IDailyReportService.cs
@@ -0,0 +1,60 @@
+using System;
+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 GetAsync(int idWell, DateTime dateStart, CancellationToken cancellationToken);
+
+ ///
+ /// Получить список суточных отчётов по скважине
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> GetAsync(int idWell, FileReportRequest request,
+ CancellationToken cancellationToken);
+
+ ///
+ /// Получить диапазон дат по которым возможно сформировать суточный отчёты
+ ///
+ ///
+ ///
+ ///
+ Task GetDatesRangeAsync(int idWell, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs b/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs
index abb88b14..adc52883 100644
--- a/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs
+++ b/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs
@@ -1,241 +1,378 @@
-using AsbCloudApp.Data;
-using AsbCloudApp.Data.DailyReport;
-using AsbCloudApp.Data.User;
+using AsbCloudApp.Data.DailyReport;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
-using AsbCloudApp.Requests;
using AsbCloudApp.Services;
-using AsbCloudDb.Model;
-using AsbCloudDb.Model.DailyReport;
-using Mapster;
-using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using AsbCloudApp.Data;
+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.Data.DailyReport.Blocks.WellOperation;
+using AsbCloudApp.Requests;
+using AsbCloudApp.Services.DailyReport;
+using AsbCloudApp.Services.ProcessMaps.WellDrilling;
+using AsbCloudApp.Services.Subsystems;
+using AsbCloudDb.Model;
+using Mapster;
-namespace AsbCloudInfrastructure.Services.DailyReport
+namespace AsbCloudInfrastructure.Services.DailyReport;
+
+public class DailyReportService : IDailyReportService
{
+ private readonly IWellService wellService;
+ private readonly ITrajectoryFactRepository trajectoryFactRepository;
+ private readonly IDailyReportRepository dailyReportRepository;
+ private readonly IScheduleRepository scheduleRepository;
+ private readonly IWellOperationRepository wellOperationRepository;
+ private readonly ISubsystemOperationTimeService subsystemOperationTimeService;
+ private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingService;
+ private readonly IDetectedOperationService detectedOperationService;
- public class DailyReportService : IDailyReportService
- {
- private readonly IAsbCloudDbContext db;
- private readonly IUserRepository userRepository;
- private readonly IWellOperationRepository wellOperationRepository;
- private readonly IWellService wellService;
- private readonly DailyReportMakerExcel dailyReportMaker = new DailyReportMakerExcel();
+ public DailyReportService(IWellService wellService,
+ ITrajectoryFactRepository trajectoryFactRepository,
+ IDailyReportRepository dailyReportRepository,
+ IScheduleRepository scheduleRepository,
+ IWellOperationRepository wellOperationRepository,
+ ISubsystemOperationTimeService subsystemOperationTimeService,
+ IProcessMapReportWellDrillingService processMapReportWellDrillingService,
+ IDetectedOperationService detectedOperationService)
+ {
+ this.wellService = wellService;
+ this.trajectoryFactRepository = trajectoryFactRepository;
+ this.dailyReportRepository = dailyReportRepository;
+ this.scheduleRepository = scheduleRepository;
+ this.wellOperationRepository = wellOperationRepository;
+ this.subsystemOperationTimeService = subsystemOperationTimeService;
+ this.processMapReportWellDrillingService = processMapReportWellDrillingService;
+ this.detectedOperationService = detectedOperationService;
+ }
- public DailyReportService(
- IAsbCloudDbContext db,
- IWellService wellService,
- IUserRepository userRepository,
- IWellOperationRepository wellOperationRepository)
- {
- this.db = db;
- this.wellService = wellService;
- this.userRepository = userRepository;
- this.wellOperationRepository = wellOperationRepository;
+ 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,
+ };
- public async Task> GetListAsync(int idWell, DateOnly? begin, DateOnly? end, CancellationToken token)
- {
- var well = wellService.GetOrDefault(idWell)
- ?? throw new ArgumentInvalidException(nameof(idWell), "idWell doesn`t exist");
+ return await dailyReportRepository.InsertAsync(dailyReport, cancellationToken);
+ }
- var query = db.DailyReports.Where(r => r.IdWell == idWell);
+ public async Task UpdateBlockAsync(int idDailyReport, int idUser, TBlock editableBlock,
+ CancellationToken cancellationToken)
+ where TBlock : EditableBlock
+ {
+ var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idDailyReport, cancellationToken)
+ ?? throw new ArgumentInvalidException(nameof(idDailyReport), $"Суточный отчёт с Id: {idDailyReport} не найден");
- if (begin is not null)
- {
- query = query.Where(d => d.StartDate >= begin);
- }
+ editableBlock.IdUser = idUser;
- if (end is not null)
- {
- query = query.Where(d => d.StartDate <= end);
- }
+ dailyReport.DateLastUpdate = DateTime.UtcNow;
- var entities = await query.OrderByDescending(e => e.StartDate)
- .AsNoTracking()
- .ToArrayAsync(token)
- .ConfigureAwait(false);
+ switch (editableBlock)
+ {
+ case SubsystemBlockDto subsystemBlock:
+ dailyReport.SubsystemBlock = subsystemBlock;
+ break;
+ case TimeBalanceBlockDto timeBalanceBlock:
+ dailyReport.TimeBalanceBlock = timeBalanceBlock;
+ break;
+ case SignBlockDto signBlock:
+ dailyReport.SignBlock = signBlock;
+ break;
+ }
- var factOperationsForDtos = await GetFactOperationsForDailyReportAsync(idWell, token);
- var userDtos = await userRepository.GetAllAsync(token);
+ return await dailyReportRepository.UpdateAsync(dailyReport, cancellationToken);
+ }
- var dtos = entities.Select(entity => Convert(entity, factOperationsForDtos, userDtos));
- return dtos;
- }
+ public async Task GetAsync(int idWell, DateTime dateStart, CancellationToken cancellationToken)
+ {
+ var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken);
- ///
- /// Получение фактических операций для суточного рапорта
- ///
- ///
- ///
- ///
- private async Task> GetFactOperationsForDailyReportAsync(int idWell, CancellationToken token)
- {
- var request = new WellOperationRequest()
- {
- IdWell = idWell,
- OperationType = WellOperation.IdOperationTypeFact,
- };
+ if (well is null)
+ throw new ArgumentInvalidException($"Скважина с Id: {idWell} не найдена", nameof(idWell));
- var factOperations = await wellOperationRepository.GetAsync(request, token);
- return factOperations;
- }
+ var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idWell, dateStart, cancellationToken) ?? new DailyReportDto
+ {
+ DateStart = dateStart.Date,
+ IdWell = well.Id
+ };
- public async Task AddAsync(int idWell, DateOnly startDate, int idUser, CancellationToken token)
- {
- var well = wellService.GetOrDefault(idWell)
- ?? throw new ArgumentInvalidException(nameof(idWell), "idWell doesn`t exist");
+ var factWellOperations = await GetFactWellOperationsAsync(idWell, dailyReport.DateStart, dailyReport.DateEnd,
+ cancellationToken);
- var hasEntity = await db.DailyReports
- .AnyAsync(r => r.IdWell == idWell && r.StartDate == startDate, token);
- if (hasEntity)
- throw new ArgumentInvalidException(nameof(startDate), $"daily report on {startDate} already exists");
+ dailyReport.WellName = 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;
+ dailyReport.DepthStart = factWellOperations.Min(o => o.DepthStart);
+ dailyReport.DepthEnd = factWellOperations.Max(o => o.DepthEnd);
- var entity = new AsbCloudDb.Model.DailyReport.DailyReport
- {
- IdWell = idWell,
- StartDate = startDate,
- Info = new DailyReportInfo()
- {
- Head = CreateHeadDailyReportBlock(well, startDate, idUser)
- }
- };
- db.DailyReports.Add(entity);
- var result = await db.SaveChangesAsync(token);
- return result;
- }
+ await UpdateTimeBalanceBlockAsync(dailyReport, factWellOperations, cancellationToken);
+ await UpdateSubsystemBlockAsync(dailyReport, cancellationToken);
+
+ await AddTrajectoryBlockAsync(dailyReport, cancellationToken);
+ await AddScheduleBlockAsync(dailyReport, cancellationToken);
+ await AddProcessMapWellDrillingBlockAsync(dailyReport, cancellationToken);
- public async Task UpdateBlockAsync(int idWell, DateOnly startDate, ItemInfoDto dto, CancellationToken token)
- {
- var well = wellService.GetOrDefault(idWell)
- ?? throw new ArgumentInvalidException(nameof(idWell), "idWell doesn`t exist");
+ AddFactWellOperationBlock(dailyReport, factWellOperations);
- var entity = await db.DailyReports.FirstOrDefaultAsync(r => r.IdWell == idWell && r.StartDate == startDate, token)
- ?? throw new ArgumentInvalidException(nameof(startDate), "Daily report doesn`t exist");
+ return dailyReport;
+ }
- dto.LastUpdateDate = DateTimeOffset.Now;
- if (dto is HeadDto headDto)
- entity.Info.Head = headDto.Adapt();
- if (dto is BhaDto bhaDto)
- entity.Info.Bha = bhaDto.Adapt();
- if (dto is NoDrillingDto noDrillingDto)
- entity.Info.NoDrilling = noDrillingDto.Adapt();
- if (dto is SaubDto saubDto)
- entity.Info.Saub = saubDto.Adapt();
- if (dto is SignDto signDto)
- entity.Info.Sign = signDto.Adapt();
+ public async Task> GetAsync(int idWell, FileReportRequest request,
+ CancellationToken cancellationToken)
+ {
+ var result = new PaginationContainer
+ {
+ Skip = request.Skip ?? 0,
+ Take = request.Take ?? 10,
+ Items = Enumerable.Empty()
+ };
- db.DailyReports.Update(entity);
- var result = await db.SaveChangesAsync(token);
- return result;
- }
+ var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken)
+ ?? throw new ArgumentInvalidException(nameof(idWell), "Скважина не найдена");
- public async Task MakeReportAsync(int idWell, DateOnly date, CancellationToken token)
- {
- var stageIds = WellOperationCategory.WorkStages.Select(w => w.Id).ToArray();
- var wellOperationCategories = wellOperationRepository.GetCategories(true)
- .Where(o => o.IdParent is not null)
- .Where(o => stageIds.Contains(o.IdParent!.Value));
+ var datesRange = await GetDatesRangeAsync(idWell, cancellationToken);
- var dailyReportDto = await GetOrDefaultAsync(idWell, date, token);
- if (dailyReportDto is null)
- return null;
+ if (datesRange is null)
+ return result;
- var memoryStream = dailyReportMaker.MakeReportFromBlocks(dailyReportDto, wellOperationCategories);
+ var dailyReports = new List();
- return memoryStream;
- }
+ var existingDailyReports = await dailyReportRepository.GetAsync(idWell, request, cancellationToken);
- private async Task GetOrDefaultAsync(int idWell, DateOnly date, CancellationToken token)
- {
- var entity = await db.DailyReports
- .FirstOrDefaultAsync(r => r.IdWell == idWell && r.StartDate == date, token)
- ?? throw new ArgumentInvalidException(nameof(date), "Daily report doesn`t exist");
+ if (request.GeDate.HasValue)
+ {
+ var startDate = new DateTime(request.GeDate.Value.Year, request.GeDate.Value.Month,
+ request.GeDate.Value.Day);
- var factOperationsForDtos = await GetFactOperationsForDailyReportAsync(idWell, token);
- var userDtos = await userRepository.GetAllAsync(token);
- var dto = Convert(entity, factOperationsForDtos, userDtos);
- return dto;
- }
+ if (startDate.Date >= datesRange.From.Date)
+ datesRange.From = startDate;
+ }
- ///
- /// конвертация данных из модели базы данных в dto
- ///
- /// модель базы данных
- /// список фактичских операций для формирования суточного рапорта
- /// список пользователей для нахождения последнего изменившего запись
- ///
- private DailyReportDto Convert(
- AsbCloudDb.Model.DailyReport.DailyReport entity,
- IEnumerable factOperationsForDtos,
- IEnumerable users)
- {
- var dto = entity.Info.Adapt();
- dto.StartDate = entity.StartDate;
+ if (request.LeDate.HasValue)
+ {
+ var finishDate = new DateTime(request.LeDate.Value.Year, request.LeDate.Value.Month,
+ request.LeDate.Value.Day);
- var dailyFactOperations = factOperationsForDtos
- .Where(o => DateOnly.FromDateTime(o.DateStart) == dto.StartDate)
- .Where(o => o.IdParentCategory is not null);
+ if (finishDate.Date <= datesRange.To.Date)
+ datesRange.To = finishDate;
+ }
- var lastDailyFactOperation = dailyFactOperations
- .OrderByDescending(o => o.LastUpdateDate)
- .FirstOrDefault();
- dto.TimeBalance.IdUser = lastDailyFactOperation?.IdUser;
- dto.TimeBalance.LastUpdateDate = lastDailyFactOperation?.LastUpdateDate;
- dto.TimeBalance.OperationsStat = dailyFactOperations
- .GroupBy(o => o.IdParentCategory!.Value)
- .ToDictionary(g => g.Key, g => g.Sum(o => o.DurationHours));
+ if (datesRange.From.AddDays(result.Skip) <= datesRange.To)
+ result.Count = (int)(Math.Ceiling((datesRange.To - DateTime.UnixEpoch).TotalDays) -
+ Math.Floor((datesRange.From - DateTime.UnixEpoch).TotalDays));
- var blocks = new ItemInfoDto[] {
- dto.Head,
- dto.Bha,
- dto.NoDrilling,
- dto.TimeBalance,
- dto.Saub,
- dto.Sign
- };
- foreach (var block in blocks)
- {
- if (block.IdUser is not null)
- {
- block.UserName = users.FirstOrDefault(u => u.Id == block.IdUser.Value)?.MakeDisplayName()
- ?? $"userId:{block.IdUser.Value}";
- }
- }
+ if (request.SortFields?.Contains("DateStart desc") == true)
+ {
+ for (var day = result.Skip; day - result.Skip < result.Take && datesRange.To.AddDays(-day) >= datesRange.From; day++)
+ {
+ var dateStart = datesRange.To.AddDays(-day);
- return dto;
- }
+ AddDailyReport(dateStart);
+ }
+ }
+ else
+ {
+ for (var day = result.Skip; day - result.Skip < result.Take && datesRange.From.AddDays(day) <= datesRange.To; day++)
+ {
+ var dateStart = datesRange.From.AddDays(day);
- ///
- /// Создание блока "Заголовок" по умолчанию
- ///
- ///
- ///
- ///
- ///
- private Head CreateHeadDailyReportBlock(WellDto well, DateOnly startDate, int idUser)
- {
- var customer = well.Companies.FirstOrDefault(company => company.IdCompanyType == 1);
- var contractor = well.Companies.FirstOrDefault(company => company.IdCompanyType == 2);
+ AddDailyReport(dateStart);
+ }
+ }
- return new Head()
- {
- ReportDate = startDate,
- WellName = well.Caption,
- ClusterName = well?.Cluster ?? string.Empty,
- Customer = customer?.Caption ?? string.Empty,
- Contractor = contractor?.Caption ?? string.Empty,
- IdUser = idUser,
- LastUpdateDate = DateTimeOffset.Now,
- };
- }
- }
+ result.Items = dailyReports;
-}
+ return result;
+
+ void AddDailyReport(DateTime dateStart)
+ {
+ var existingDailyReport = existingDailyReports.FirstOrDefault(d => d.IdWell == idWell &&
+ d.DateStart == dateStart);
+
+ if (existingDailyReport is not null)
+ {
+ dailyReports.Add(existingDailyReport);
+ return;
+ }
+
+ dailyReports.Add(new DailyReportDto
+ {
+ DateStart = dateStart,
+ IdWell = well.Id
+ });
+ }
+ }
+
+ public async Task GetDatesRangeAsync(int idWell, CancellationToken cancellationToken)
+ {
+ var factOperations = await GetFactWellOperationsAsync(idWell, null, null,
+ cancellationToken);
+
+ if (!factOperations.Any())
+ return null;
+
+ return new DatesRangeDto
+ {
+ From = factOperations.Min(o => o.DateStart).Date,
+ To = factOperations.Max(o => o.DateStart).Date
+ };
+ }
+
+ private async Task UpdateTimeBalanceBlockAsync(DailyReportDto dailyReport, IEnumerable factWellOperations,
+ CancellationToken cancellationToken)
+ {
+ const int idWellOperationSlipsTime = 5011;
+
+ if (dailyReport.TimeBalanceBlock is not null)
+ {
+ dailyReport.TimeBalanceBlock.CountWellOperationSlipsTime = (await detectedOperationService.GetAsync(
+ new DetectedOperationRequest
+ {
+ IdsCategories = new[] { idWellOperationSlipsTime },
+ IdWell = dailyReport.IdWell,
+ GtDate = dailyReport.DateStart,
+ LtDate = dailyReport.DateEnd
+ }, cancellationToken))?.Stats.Sum(s => s.Count);
+
+ dailyReport.TimeBalanceBlock.WellDepthFact = factWellOperations
+ .Where(o => o.IdWellSectionType == dailyReport.TimeBalanceBlock.IdSection)
+ .Sum(o => o.DepthEnd = o.DepthStart);
+ }
+ }
+
+ private async Task AddTrajectoryBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
+ {
+ var trajectory = (await trajectoryFactRepository.GetAsync(new TrajectoryGeoFactRequest
+ {
+ IdWell = dailyReport.IdWell,
+ GeDate = dailyReport.DateStart,
+ LtDate = dailyReport.DateEnd
+ }, cancellationToken)).LastOrDefault();
+
+ dailyReport.TrajectoryBlock = new TrajectoryBlockDto
+ {
+ WellboreDepth = trajectory?.WellboreDepth,
+ VerticalDepth = trajectory?.VerticalDepth,
+ ZenithAngle = trajectory?.ZenithAngle,
+ AzimuthGeo = trajectory?.AzimuthGeo
+ };
+ }
+
+ private async Task AddScheduleBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken) =>
+ dailyReport.ScheduleBlock = (await scheduleRepository.GetAsync(dailyReport.IdWell, dailyReport.DateStart, cancellationToken))
+ .Select(s => new ScheduleRecordDto
+ {
+ ShiftStart = s.ShiftStart,
+ ShiftEnd = s.ShiftEnd,
+ Name = s.Driller?.Name,
+ Surname = s.Driller?.Surname,
+ Patronymic = s.Driller?.Patronymic
+ });
+
+ private async Task UpdateSubsystemBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
+ {
+ dailyReport.SubsystemBlock ??= new SubsystemBlockDto();
+
+ dailyReport.SubsystemBlock.Modules = await GetModulesAsync();
+
+ async Task> GetModulesAsync()
+ {
+ var modules = new List();
+
+ var statSubsystemOperationTimePerDaily = (await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest
+ {
+ IdWell = dailyReport.IdWell,
+ GtDate = dailyReport.DateStart,
+ LtDate = dailyReport.DateEnd
+ }, cancellationToken)).Select(s =>
+ {
+ var subsystemRecord = s.Adapt();
+ subsystemRecord.IdTimeInterval = 1;
+
+ return subsystemRecord;
+ });
+
+ var statSubsystemOperationTimePerWell = (await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest
+ {
+ IdWell = dailyReport.IdWell
+ }, cancellationToken)).Select(s =>
+ {
+ var subsystemRecord = s.Adapt();
+ subsystemRecord.IdTimeInterval = 2;
+
+ return subsystemRecord;
+ });
+
+ modules.AddRange(statSubsystemOperationTimePerDaily);
+ modules.AddRange(statSubsystemOperationTimePerWell);
+
+ if (dailyReport.SubsystemBlock?.Modules != null && dailyReport.SubsystemBlock.Modules.Any())
+ modules.AddRange(dailyReport.SubsystemBlock.Modules);
+
+ return modules;
+ }
+ }
+
+ 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 &&
+ p.IdMode.HasValue)
+ .GroupBy(p => p.IdMode)
+ .Select(g => new ProcessMapWellDrillingRecordDto
+ {
+ IdMode = g.Key!.Value,
+ WellBoreDepth = g.Sum(p => p.DeltaDepth),
+ Rop = new PlanFactDto
+ {
+ Plan = g.Sum(p => p.RopPlan),
+ Fact = g.Sum(p => p.RopFact)
+ },
+ MechDrillingHours = g.Sum(p => p.MechDrillingHours)
+ });
+ }
+
+ private void AddFactWellOperationBlock(DailyReportDto dailyReport, IEnumerable factWellOperations)
+ {
+ const int idWellOperationCategoryDrilling = 4001;
+
+ dailyReport.FactWellOperationBlock = new WellOperationBlockDto
+ {
+ WellOperations = factWellOperations.GroupBy(o => o.IdParentCategory)
+ .Select(g => new WellOperationRecordDto
+ {
+ IdWellCategory = g.Key,
+ DurationHours = g.Sum(o => o.DurationHours)
+ }),
+
+ DurationHoursDrillingPerSection = factWellOperations
+ .Where(o => o.IdParentCategory is idWellOperationCategoryDrilling)
+ .Sum(o => o.DurationHours)
+ };
+ }
+
+ private Task> GetFactWellOperationsAsync(int idWell, DateTime? dailyReportDateStart,
+ DateTime? dailyReportDateEnd, CancellationToken cancellationToken) =>
+ wellOperationRepository.GetAsync(new WellOperationRequest
+ {
+ IdWell = idWell,
+ OperationType = WellOperation.IdOperationTypeFact,
+ GeDate = dailyReportDateStart,
+ LtDate = dailyReportDateEnd
+ }, cancellationToken);
+}
\ No newline at end of file
diff --git a/AsbCloudWebApi.Tests/ServicesTests/DailyReportServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DailyReportServiceTest.cs
new file mode 100644
index 00000000..0d47e35c
--- /dev/null
+++ b/AsbCloudWebApi.Tests/ServicesTests/DailyReportServiceTest.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using AsbCloudApp.Data;
+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.Repositories;
+using AsbCloudApp.Services;
+using AsbCloudApp.Services.ProcessMaps.WellDrilling;
+using AsbCloudApp.Services.Subsystems;
+using AsbCloudInfrastructure.Services.DailyReport;
+using NSubstitute;
+using Xunit;
+
+namespace AsbCloudWebApi.Tests.ServicesTests;
+
+public class DailyReportServiceTest
+{
+ private const int idDailyReport = 1;
+ private const int idWell = 2;
+ private const int idUser = 3;
+
+ private readonly DateTime dateStart = new DateOnly(2023, 10, 26).ToDateTime(TimeOnly.MinValue);
+
+ private readonly IWellService wellServiceMock = Substitute.For();
+ private readonly ITrajectoryFactRepository trajectoryFactRepositoryMock = Substitute.For();
+ private readonly IDailyReportRepository dailyReportRepositoryMock = Substitute.For();
+ private readonly IScheduleRepository scheduleRepositoryMock = Substitute.For();
+ private readonly IWellOperationRepository wellOperationRepositoryMock = Substitute.For();
+ 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;
+
+ public DailyReportServiceTest()
+ {
+ fakeDailyReport = new()
+ {
+ Id = idDailyReport,
+ IdWell = idWell,
+ DateStart = dateStart
+ };
+
+ dailyReportService = new DailyReportService(wellServiceMock,
+ trajectoryFactRepositoryMock,
+ dailyReportRepositoryMock,
+ scheduleRepositoryMock,
+ wellOperationRepositoryMock,
+ subsystemOperationTimeServiceMock,
+ processMapReportWellDrillingServiceMock,
+ detectedOperationServiceMock);
+
+ dailyReportRepositoryMock.InsertAsync(Arg.Any(), Arg.Any())
+ .ReturnsForAnyArgs(idDailyReport);
+
+ dailyReportRepositoryMock.GetOrDefaultAsync(idDailyReport, Arg.Any())
+ .Returns(fakeDailyReport);
+
+ dailyReportRepositoryMock.UpdateAsync(Arg.Any(), Arg.Any())
+ .ReturnsForAnyArgs(idDailyReport);
+ }
+
+ [Fact]
+ public async Task InsertAsync_ShouldReturn_ExceptionAboutDuplicate()
+ {
+ //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);
+
+ //assert
+ Assert.Equal(idDailyReport, result);
+ }
+
+ [Fact]
+ public async Task UpdateSubsystemBlock_ShouldReturn_Success()
+ {
+ //arrange
+ var fakeSubsystemBlock = new SubsystemBlockDto
+ {
+ IdUser = idUser,
+ WellBoreDepth = 999,
+ MeasurementsPerDaily = 999,
+ TotalRopPlan = 999,
+ Comment = "Увеличить обороты",
+ Modules = new[]
+ {
+ new SubsystemRecordDto
+ {
+ IdSubsystem = 10000,
+ IdTimeInterval = 1,
+ UsedTimeHours = 24,
+ SumDepthInterval = 1500,
+ KUsage = 100
+ }
+ }
+ };
+
+ //act
+ var result = await dailyReportService.UpdateBlockAsync(idDailyReport, idUser, fakeSubsystemBlock, CancellationToken.None);
+
+ //assert
+ 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);
+
+ //assert
+ Assert.NotNull(fakeDailyReport.DateLastUpdate);
+ Assert.Equal(fakeDailyReport.SignBlock, fakeSignBlock);
+ Assert.Equal(idDailyReport, result);
+ }
+
+ [Fact]
+ public async Task UpdateTimeBalance_ShouldReturn_Success()
+ {
+ //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);
+
+ //assert
+ Assert.NotNull(fakeDailyReport.DateLastUpdate);
+ Assert.Equal(fakeDailyReport.TimeBalanceBlock, fakeTimeBalanceBlock);
+ Assert.Equal(idDailyReport, result);
+ }
+}
\ No newline at end of file