diff --git a/AsbCloudApp/Services/DailyReport/IDailyReportExportService.cs b/AsbCloudApp/Services/DailyReport/IDailyReportExportService.cs
new file mode 100644
index 00000000..fb9a897a
--- /dev/null
+++ b/AsbCloudApp/Services/DailyReport/IDailyReportExportService.cs
@@ -0,0 +1,21 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AsbCloudApp.Services.DailyReport;
+
+///
+/// Сервис экспорта суточного отчёта
+///
+public interface IDailyReportExportService
+{
+ ///
+ /// Экспортировать
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task<(string FileName, Stream File)> ExportAsync(int idWell, DateTime dailyReportDateStart, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/Services/DailyReport/DailyReportExportService.cs b/AsbCloudInfrastructure/Services/DailyReport/DailyReportExportService.cs
new file mode 100644
index 00000000..8084c7c9
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/DailyReport/DailyReportExportService.cs
@@ -0,0 +1,259 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using AsbCloudApp.Data.DailyReport;
+using AsbCloudApp.Data.DailyReport.Blocks;
+using AsbCloudApp.Data.DailyReport.Blocks.Sign;
+using AsbCloudApp.Data.DailyReport.Blocks.Subsystems;
+using AsbCloudApp.Data.DailyReport.Blocks.TimeBalance;
+using AsbCloudApp.Data.DailyReport.Blocks.WellOperation;
+using AsbCloudApp.Services.DailyReport;
+using ClosedXML.Excel;
+
+namespace AsbCloudInfrastructure.Services.DailyReport;
+
+public class DailyReportExportService : IDailyReportExportService
+{
+ private const int rowStartScheduleBlock = 20;
+ private const int rowStartSubsystemBlock = 26;
+ private const int rowStartFactWellOperationBlock = 39;
+ private const int rowStartTimeBalanceBlock = 62;
+ private const int rowStartProcessMapWellDrillingBlock = 68;
+
+ private const int columnTimeBalanceDurationPlan = 3;
+ private const int columnTimeBalanceDurationFact = 4;
+ private const int columnTimeBalanceReasonDeviation = 8;
+ private const int columnTimeBalanceDrillingDeviationPerSection = 10;
+ private const int columnTimeBalanceDrillingDeviationPerDaily = 11;
+
+ private const int columnSheduleDriller = 3;
+ private const int columnSheduleShiftStart = 7;
+ private const int columnSheduleShiftEnd = 8;
+
+ private const int columnSubsystemName = 2;
+ private const int columnUseSubsystemPerDayUsedTimeHours = 3;
+ private const int columnUseSubsystemPerDaySumDepthInterval = 4;
+ private const int columnUseSubsystemPerDayKUsage = 5;
+
+ private const int columnUseSubsystemPerWellUsedTimeHours = 6;
+ private const int columnUseSubsystemPerWellSumDepthInterval = 7;
+ private const int columnUseSubsystemPerWellKUsage = 8;
+
+ private const int columnProcessMapWellDrillingBlockMode = 2;
+ private const int columnProcessMapWellDrillingBlockWellBoreDepth = 3;
+ private const int columnProcessMapWellDrillingBlockMechDrillingHours = 4;
+ private const int columnProcessMapWellDrillingBlockRopPlan = 5;
+ private const int columnProcessMapWellDrillingBlockRopFact = 6;
+
+ private const int columnWellOperationCategory = 2;
+ private const int columnWellOperationDurationHours = 4;
+
+ private const string cellCustomer = "C2";
+ private const string cellContractor = "C3";
+ private const string cellDeposit = "C5";
+ private const string cellCluster = "C6";
+ private const string cellWellName = "C7";
+ private const string cellWellType = "C8";
+ private const string cellDateStart = "C12";
+ private const string cellDateEnd = "D12";
+ private const string cellDepthStart = "C13";
+ private const string cellDepthEnd = "D13";
+
+ private const string cellTrajectoryBlockWellboreDepth = "B17";
+ private const string cellTrajectoryBlockVerticalDepth = "C17";
+ private const string cellTrajectoryBlockZenithAngle = "D17";
+ private const string cellTrajectoryBlockAzimuthGeo = "E17";
+
+ private const string cellTimeBalanceBlockSection = "C60";
+ private const string cellTimeBalanceBlockWellDepthPlan = "C61";
+ private const string cellDurationHoursDrillingPerSection = "F77";
+ private const string cellTimeBalanceBlockWellDepthFact = "F78";
+ private const string cellTimeBalanceBlockCountWellOperationSlipsTime = "F79";
+
+ private const string cellSubsystemComment = "D35";
+ private const string cellSubsystemMeasurementsPerDaily = "F80";
+ private const string cellSubsystemWellBoreDepth = "C9";
+ private const string cellSubsystemTotalRopPlan = "E70";
+
+ private const string cellSignDrillingMaster = "C84";
+ private const string cellSignSupervisor = "C85";
+
+ private readonly IDailyReportService dailyReportService;
+
+ public DailyReportExportService(IDailyReportService dailyReportService)
+ {
+ this.dailyReportService = dailyReportService;
+ }
+
+ public async Task<(string FileName, Stream File)> ExportAsync(int idWell, DateTime dailyReportDateStart, CancellationToken cancellationToken)
+ {
+ var dailyReport = await dailyReportService.GetAsync(idWell, dailyReportDateStart, cancellationToken);
+
+ var stream = await GenerateFileAsync(dailyReport, cancellationToken);
+
+ var fileName = $"Суточный_отчёт_по_скважине_{dailyReport.WellName}_куст_{dailyReport.Cluster}_от_{dailyReport.DateStart:yy-MM-dd}.xlsx";
+
+ return (fileName, stream);
+ }
+
+ private async Task GenerateFileAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
+ {
+ using var excelTemplateStream = await Assembly
+ .GetExecutingAssembly()
+ .GetTemplateCopyStreamAsync("DailyReportTemplate.xlsx", cancellationToken);
+
+ using var workbook = new XLWorkbook(excelTemplateStream, XLEventTracking.Disabled);
+
+ AddDailyReportToWorkBook(workbook, dailyReport);
+
+ var memoryStream = new MemoryStream();
+ workbook.SaveAs(memoryStream, new SaveOptions { });
+ memoryStream.Seek(0, SeekOrigin.Begin);
+ return memoryStream;
+ }
+
+ private static void AddDailyReportToWorkBook(IXLWorkbook workbook, DailyReportDto dailyReport)
+ {
+ const string sheetName = "Суточный отчёт";
+
+ var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetName)
+ ?? throw new FileFormatException($"Книга excel не содержит листа {sheetName}.");
+
+ sheet.Cell(cellCustomer).Value = dailyReport.Customer;
+ 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(cellWellType).Value = dailyReport.WellType;
+ sheet.Cell(cellDateStart).Value = dailyReport.DateStart;
+ sheet.Cell(cellDateEnd).Value = dailyReport.DateEnd;
+ sheet.Cell(cellDepthStart).Value = dailyReport.DepthStart;
+ sheet.Cell(cellDepthEnd).Value = dailyReport.DepthEnd;
+
+ if (dailyReport.TimeBalanceBlock is not null)
+ AddTimeBalanceBlockToSheet(sheet, dailyReport.TimeBalanceBlock);
+
+ if (dailyReport.SubsystemBlock is not null)
+ AddSubsystemBlockToSheet(sheet, dailyReport.SubsystemBlock);
+
+ if (dailyReport.SignBlock is not null)
+ AddSignBlockToSheet(sheet, dailyReport.SignBlock);
+
+ AddTrajectoryBlockToSheet(sheet, dailyReport.TrajectoryBlock);
+ AddScheduleBlockToSheet(sheet, dailyReport.ScheduleBlock);
+ AddProcessMapWellDrillingBlockToSheet(sheet, dailyReport.ProcessMapWellDrillingBlock);
+ AddFactWellOperationBlockToSheet(sheet, dailyReport.FactWellOperationBlock);
+ }
+
+ private static void AddTrajectoryBlockToSheet(IXLWorksheet sheet, TrajectoryBlockDto trajectoryBlock)
+ {
+ sheet.Cell(cellTrajectoryBlockWellboreDepth).Value = trajectoryBlock.WellboreDepth;
+ sheet.Cell(cellTrajectoryBlockVerticalDepth).Value = trajectoryBlock.VerticalDepth;
+ sheet.Cell(cellTrajectoryBlockZenithAngle).Value = trajectoryBlock.ZenithAngle;
+ sheet.Cell(cellTrajectoryBlockAzimuthGeo).Value = trajectoryBlock.AzimuthGeo;
+ }
+
+ private static void AddTimeBalanceBlockToSheet(IXLWorksheet sheet, TimeBalanceBlockDto timeBalanceBlock)
+ {
+ var rowCurrent = rowStartTimeBalanceBlock;
+
+ 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, columnTimeBalanceReasonDeviation).Value = wellOperation.ReasonDeviation;
+ sheet.Cell(rowCurrent, columnTimeBalanceDrillingDeviationPerSection).Value = wellOperation.DrillingDeviationPerSection;
+ sheet.Cell(rowCurrent, columnTimeBalanceDrillingDeviationPerDaily).Value = wellOperation.DrillingDeviationPerDaily;
+
+ rowCurrent++;
+ }
+
+ sheet.Cell(cellTimeBalanceBlockSection).Value = timeBalanceBlock.IdSection;
+ sheet.Cell(cellTimeBalanceBlockWellDepthPlan).Value = timeBalanceBlock.WellDepthPlan;
+ sheet.Cell(cellTimeBalanceBlockWellDepthFact).Value = timeBalanceBlock.WellDepthFact;
+ sheet.Cell(cellTimeBalanceBlockCountWellOperationSlipsTime).Value = timeBalanceBlock.CountWellOperationSlipsTime;
+ }
+
+ private static void AddSubsystemBlockToSheet(IXLWorksheet sheet, SubsystemBlockDto subsystemBlock)
+ {
+ var groupedModules = subsystemBlock.Modules.OrderBy(m => m.IdSubsystem)
+ .GroupBy(m => m.IdSubsystem);
+
+ var rowСurrent = rowStartSubsystemBlock;
+
+ foreach (var groupedModule in groupedModules)
+ {
+ var useSubsystemPerDay = groupedModule.FirstOrDefault(m => m.IdTimeInterval == 1);
+ var useSubsystemPerWell = groupedModule.FirstOrDefault(m => m.IdTimeInterval == 2);
+
+ sheet.Cell(rowСurrent, columnSubsystemName).Value = groupedModule.Key;
+
+ sheet.Cell(rowСurrent, columnUseSubsystemPerDayUsedTimeHours).Value = useSubsystemPerDay?.UsedTimeHours;
+ sheet.Cell(rowСurrent, columnUseSubsystemPerDaySumDepthInterval).Value = useSubsystemPerDay?.SumDepthInterval;
+ sheet.Cell(rowСurrent, columnUseSubsystemPerDayKUsage).Value = useSubsystemPerDay?.KUsage;
+
+ sheet.Cell(rowСurrent, columnUseSubsystemPerWellUsedTimeHours).Value = useSubsystemPerWell?.UsedTimeHours;
+ sheet.Cell(rowСurrent, columnUseSubsystemPerWellSumDepthInterval).Value = useSubsystemPerWell?.SumDepthInterval;
+ sheet.Cell(rowСurrent, columnUseSubsystemPerWellKUsage).Value = useSubsystemPerWell?.KUsage;
+
+ rowСurrent++;
+ }
+
+ sheet.Cell(cellSubsystemComment).Value = subsystemBlock.Comment;
+ sheet.Cell(cellSubsystemMeasurementsPerDaily).Value = subsystemBlock.MeasurementsPerDaily;
+ sheet.Cell(cellSubsystemWellBoreDepth).Value = subsystemBlock.WellBoreDepth;
+ sheet.Cell(cellSubsystemTotalRopPlan).Value = subsystemBlock.TotalRopPlan;
+ }
+
+ private static void AddScheduleBlockToSheet(IXLWorksheet sheet, IEnumerable scheduleBlock)
+ {
+ var rowCurrent = rowStartScheduleBlock;
+
+ foreach (var schedule in scheduleBlock.OrderBy(s => s.ShiftStart))
+ {
+ sheet.Cell(rowCurrent, columnSheduleDriller).Value = $"{schedule.Surname} {schedule.Name} {schedule.Patronymic}";
+ sheet.Cell(rowCurrent, columnSheduleShiftStart).Value = schedule.ShiftStart;
+ sheet.Cell(rowCurrent, columnSheduleShiftEnd).Value = schedule.ShiftEnd;
+
+ rowCurrent++;
+ }
+ }
+
+ private static void AddProcessMapWellDrillingBlockToSheet(IXLWorksheet sheet,
+ IEnumerable processMapWellDrillingBlock)
+ {
+ var rowCurrent = rowStartProcessMapWellDrillingBlock;
+
+ foreach (var processMapWellDrilling in processMapWellDrillingBlock.OrderBy(p => p.IdMode))
+ {
+ sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockMode).Value = processMapWellDrilling.IdMode;
+ sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockWellBoreDepth).Value = processMapWellDrilling.WellBoreDepth;
+ sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockMechDrillingHours).Value = processMapWellDrilling.MechDrillingHours;
+ sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockRopPlan).Value = processMapWellDrilling.Rop.Plan;
+ sheet.Cell(rowCurrent, columnProcessMapWellDrillingBlockRopFact).Value = processMapWellDrilling.Rop.Fact;
+
+ rowCurrent++;
+ }
+ }
+
+ private static void AddFactWellOperationBlockToSheet(IXLWorksheet sheet, WellOperationBlockDto factWellOperationBlock)
+ {
+ sheet.Cell(cellDurationHoursDrillingPerSection).Value = factWellOperationBlock.DurationHoursDrillingPerSection;
+
+ foreach (var factOperation in factWellOperationBlock.WellOperations.OrderBy(w => w.IdWellCategory))
+ {
+ sheet.Cell(rowStartFactWellOperationBlock, columnWellOperationCategory).Value = factOperation.IdWellCategory;
+ sheet.Cell(rowStartFactWellOperationBlock, columnWellOperationDurationHours).Value = factOperation.DurationHours;
+ }
+ }
+
+ private static void AddSignBlockToSheet(IXLWorksheet sheet, SignBlockDto signBlock)
+ {
+ sheet.Cell(cellSignDrillingMaster).Value = signBlock.DrillingMaster?.ToString();
+ sheet.Cell(cellSignSupervisor).Value = signBlock.Supervisor?.ToString();
+ }
+}
\ No newline at end of file