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