feat(dal): NextRunServiceV2 - заготовка распределения шаблонов в рамках JobGroup
This commit is contained in:
14
PARR.DAL/NextRunServices/INextRunServiceV2.cs
Normal file
14
PARR.DAL/NextRunServices/INextRunServiceV2.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using PARR.DAL.NextRunServices.Models;
|
||||||
|
|
||||||
|
namespace PARR.DAL.NextRunServices
|
||||||
|
{
|
||||||
|
public interface INextRunServiceV2
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Распределить шаблоны в группе работ (получить список распределенных шаблонов с актуальными nextRun)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jobGroupId"></param>
|
||||||
|
/// <returns>Список TemplateId с NextRun и NextRunOld</returns>
|
||||||
|
Task<List<TemplateNextRunResultDto>?> GetNextRunForJobGroupWithAutoDistributionAsync(Guid jobGroupId);
|
||||||
|
}
|
||||||
|
}
|
||||||
240
PARR.DAL/NextRunServices/NextRunServiceV2.cs
Normal file
240
PARR.DAL/NextRunServices/NextRunServiceV2.cs
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using NodaTime;
|
||||||
|
using PARR.Constants;
|
||||||
|
using PARR.DAL.Contracts;
|
||||||
|
using PARR.DAL.DomainServices.Shortcodes;
|
||||||
|
using PARR.DAL.Models;
|
||||||
|
using PARR.DAL.Models.Job;
|
||||||
|
using PARR.DAL.NextRunServices.Models;
|
||||||
|
using PARR.DAL.NextRunServices.Subservices;
|
||||||
|
using PARR.DAL.Services.Interfaces;
|
||||||
|
using PARR.DAL.Services.Interfaces.Job;
|
||||||
|
using PARR.DAL.Services.Interfaces.Schedule;
|
||||||
|
|
||||||
|
namespace PARR.DAL.NextRunServices
|
||||||
|
{
|
||||||
|
internal class NextRunServiceV2 : INextRunServiceV2
|
||||||
|
{
|
||||||
|
private readonly ILogger<NextRunServiceV2> logger;
|
||||||
|
private readonly ITemplateService templateService;
|
||||||
|
private readonly IJobGroupService jobGroupService;
|
||||||
|
private readonly IEsppScheduleTransformService esppScheduleTransformService;
|
||||||
|
private readonly ITemplateDistributorV2 templateDistributor;
|
||||||
|
private readonly IShortcodesService shortcodesService;
|
||||||
|
private readonly IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService;
|
||||||
|
private readonly SettingsFromDb settingsFromDb;
|
||||||
|
|
||||||
|
public NextRunServiceV2(
|
||||||
|
ILogger<NextRunServiceV2> logger,
|
||||||
|
ITemplateService templateService,
|
||||||
|
IJobGroupService jobGroupService,
|
||||||
|
IEsppScheduleTransformService esppScheduleTransformService,
|
||||||
|
ITemplateDistributorV2 templateDistributor,
|
||||||
|
IShortcodesService shortcodesService,
|
||||||
|
IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService,
|
||||||
|
SettingsFromDb settingsFromDb
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
this.templateService = templateService;
|
||||||
|
this.jobGroupService = jobGroupService;
|
||||||
|
this.esppScheduleTransformService = esppScheduleTransformService;
|
||||||
|
this.templateDistributor = templateDistributor;
|
||||||
|
this.shortcodesService = shortcodesService;
|
||||||
|
this.scheduleResponseAreaTimeOffsetService = scheduleResponseAreaTimeOffsetService;
|
||||||
|
this.settingsFromDb = settingsFromDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<TemplateNextRunResultDto>?> GetNextRunForJobGroupWithAutoDistributionAsync(Guid jobGroupId)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Начинаю распределять шаблоны для группы работ {gobGroupId}", jobGroupId);
|
||||||
|
|
||||||
|
var jobGroup = await jobGroupService.Get()
|
||||||
|
.Include(t => t.DistributionConfig)
|
||||||
|
.ThenInclude(t => t.DistributionPeriod)
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(t => t.Id == jobGroupId);
|
||||||
|
|
||||||
|
if (jobGroup == null)
|
||||||
|
{
|
||||||
|
logger.LogError("Не найдена группа работа с id: {id}", jobGroupId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jobGroup.IsAutoDistributionEnabled || jobGroup.DistributionConfig == null)
|
||||||
|
{
|
||||||
|
logger.LogError("Группа работ id: {id} не подходит для автораспределения, у нее или отсутствуют настройки или не включено автораспределение. IsAutoDistributionEnabled: {IsAutoDistributionEnabled}. Есть конфиг: {DistributionConfig}", jobGroupId, jobGroup.IsAutoDistributionEnabled, jobGroup.DistributionConfig != null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogInformation("Параметры распределения. groupId: {groupId}, name: {groupName}, referenceDate: {referenceDate}, " +
|
||||||
|
"distributionPeriodName: {distributionPeriodName}, distributionPeriodDuration: {distributionPeriodDuration}, " +
|
||||||
|
"distributionPeriodType: {distributionPeriodType}, IsExcludeWeekends: {IsExcludeWeekends}, IsGroupingByWorkGroup: {IsGroupingByWorkGroup}, IsResponseAreaTimezone: {IsResponseAreaTimezone}",
|
||||||
|
jobGroupId, jobGroup.GroupName, jobGroup.ReferenceDate, jobGroup.DistributionConfig.DistributionPeriod.Name, jobGroup.DistributionConfig.DistributionPeriod.Duration, jobGroup.DistributionConfig.DistributionPeriod.Type,
|
||||||
|
jobGroup.DistributionConfig.IsExcludeWeekends, jobGroup.DistributionConfig.IsGroupingByWorkGroup, jobGroup.IsResponseAreaTimezone);
|
||||||
|
|
||||||
|
//получаем шаблоны только в статусе Used
|
||||||
|
var allTemplates = await templateService.Get()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Include(t => t.Job)
|
||||||
|
.Where(t =>
|
||||||
|
t.Job!.GroupId == jobGroupId
|
||||||
|
&& t.StatusTypeId == TemplateStatusTypeEnum.Used
|
||||||
|
).ToListAsync();
|
||||||
|
if (!allTemplates.Any())
|
||||||
|
{
|
||||||
|
logger.LogInformation("В группе c ИД {jobGroupId} отсутствуют шаблоны в статусе Used", jobGroupId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogDebug("Всего шаблонов для распределения в статусе Used: {count} шт.", allTemplates.Count);
|
||||||
|
|
||||||
|
|
||||||
|
// продолжительность в днях
|
||||||
|
var durationDays = GetDurationDays(jobGroup.DistributionConfig);
|
||||||
|
|
||||||
|
// группировать по РГ
|
||||||
|
var isGroupingByWorkGroup = jobGroup.DistributionConfig.IsGroupingByWorkGroup;
|
||||||
|
|
||||||
|
// использовать часовой пояс РГ
|
||||||
|
var isResponseAreaTimeZone = jobGroup.IsResponseAreaTimezone;
|
||||||
|
|
||||||
|
var distributedTemplates = new List<TemplateNextRunResultDto>();
|
||||||
|
|
||||||
|
if (isGroupingByWorkGroup)
|
||||||
|
{
|
||||||
|
// группировать по РГ
|
||||||
|
|
||||||
|
if (isResponseAreaTimeZone)
|
||||||
|
{
|
||||||
|
// использовать часовой пояс РГ
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// не нужно использовать часовой пояс РГ (используем часовой пояс УЗ ЕСПП)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//не нужно группировать по РГ
|
||||||
|
|
||||||
|
if (isResponseAreaTimeZone)
|
||||||
|
{
|
||||||
|
// использовать ЧАСОВОЙ ПОЯС РГ (нужно сгруппировать шаблоны по часовым поясам, затем отдельно распределить каждую группу)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// не нужно использовать часовой пояс РГ
|
||||||
|
|
||||||
|
var offset = GetOffsetForDistribution();
|
||||||
|
var templatesForDistribute = allTemplates.Select(t => new TemplateNextRunDto(t.Id, t.NextRun)).ToList();
|
||||||
|
var dateStart = GetDateStart(offset);
|
||||||
|
|
||||||
|
logger.LogInformation("Буду распределать шаблоны {count} шт, их НЕ НУЖНО группировать по РГ, НЕ НУЖНО использовать часовой пояс РГ. dateStart в целевом часовом поясе: {dateStart}, offset: {offset}",
|
||||||
|
templatesForDistribute.Count, dateStart, offset);
|
||||||
|
|
||||||
|
distributedTemplates = await templateDistributor.DistributeTemplatesAsync(dateStart, durationDays, jobGroup.ReferenceDate, offset, templatesForDistribute, jobGroup.DistributionConfig.IsExcludeWeekends);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return distributedTemplates;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить продолжительность в днях
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private int GetDurationDays(JobGroupDistributionConfig config)
|
||||||
|
{
|
||||||
|
var period = config.DistributionPeriod;
|
||||||
|
var periodType = period!.Type;
|
||||||
|
int.TryParse(period.Duration, out var duration);
|
||||||
|
|
||||||
|
var periodDays = duration;
|
||||||
|
|
||||||
|
if (periodType == DistributionPeriodTypeEnum.Day.ToString())
|
||||||
|
periodDays = duration;
|
||||||
|
|
||||||
|
|
||||||
|
if (periodType == DistributionPeriodTypeEnum.Month.ToString())
|
||||||
|
// в месяце 30 дней, duration*30
|
||||||
|
periodDays = duration * 30;
|
||||||
|
|
||||||
|
|
||||||
|
if (periodType == DistributionPeriodTypeEnum.Year.ToString())
|
||||||
|
// в году 365 дней, duration*365
|
||||||
|
periodDays = duration * 365;
|
||||||
|
|
||||||
|
|
||||||
|
logger.LogDebug("Период распределения: {name}, duration: {duration}, type: {type}. Итого в днях: {days}", period.Name, period.Duration, period.Type, periodDays);
|
||||||
|
|
||||||
|
if (periodDays == 0)
|
||||||
|
{
|
||||||
|
periodDays = 1;
|
||||||
|
logger.LogWarning("Полученный период распределения 0 дней. Неверный конфиг распределения в таблице {table}. {name}, duration: {duration}, type: {type}. Устанавливаем минимальный период распределения {periodDays} дней.", nameof(DistributionPeriod), period.Name, period.Duration, period.Type, periodDays);
|
||||||
|
}
|
||||||
|
|
||||||
|
return periodDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить часовую зону для распределения
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
private TimeSpan GetOffsetForDistribution(string? responseArea = null)
|
||||||
|
{
|
||||||
|
// или часова зона робота, или часовая зона РГ
|
||||||
|
if (responseArea != null)
|
||||||
|
{
|
||||||
|
// возвращаем часовой пояс ЗО
|
||||||
|
var responseAreaOffset = scheduleResponseAreaTimeOffsetService.GetByResponseAreaOrDefault(responseArea).UtcTimeOffset;
|
||||||
|
logger.LogDebug("Получил часовой пояс для ЗО: {responseArea}, UtcTimeOffset: {responseAreaOffset}", responseArea, responseAreaOffset);
|
||||||
|
|
||||||
|
return responseAreaOffset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// возвращаем часовой пояс робота
|
||||||
|
var esppOffset = GetEsppAccountOffset();
|
||||||
|
logger.LogDebug("Не передана ЗО, использую часовой пояс УЗ ЕСПП, UtcTimeOffset: {UtcTimeOffset}", esppOffset);
|
||||||
|
|
||||||
|
return esppOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить часовой пояс УЗ ЕСПП
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
private TimeSpan GetEsppAccountOffset()
|
||||||
|
{
|
||||||
|
var esppOffset = TimeSpan.FromHours(settingsFromDb.EsppRobotAccountTimeZoneHour);
|
||||||
|
logger.LogDebug("Оффсет УЗ ЕСПП: {esppOffset}", esppOffset);
|
||||||
|
|
||||||
|
return esppOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить дату начала распределения в целевом часовом поясе
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private DateOnly GetDateStart(TimeSpan offset)
|
||||||
|
{
|
||||||
|
var dateStart = DateOnly.FromDateTime(DateTimeOffset.UtcNow.ToOffset(offset).Date);
|
||||||
|
logger.LogDebug("Дата начала распределения (сегодня) в целевом часовом поясе {dateStart}, offset: {offset}", dateStart, offset);
|
||||||
|
|
||||||
|
return dateStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -142,6 +142,7 @@ namespace PARR.DAL
|
|||||||
services.AddTransient<INextRunService, NextRunService>();
|
services.AddTransient<INextRunService, NextRunService>();
|
||||||
services.AddTransient<ITemplateDistributor, TemplateDistributor>();
|
services.AddTransient<ITemplateDistributor, TemplateDistributor>();
|
||||||
services.AddTransient<ITemplateDistributorV2, TemplateDistributorV2>();
|
services.AddTransient<ITemplateDistributorV2, TemplateDistributorV2>();
|
||||||
|
services.AddTransient<INextRunServiceV2, NextRunServiceV2>();
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using PARR.DAL.Context;
|
using PARR.DAL.Context;
|
||||||
using PARR.DAL.Contracts;
|
using PARR.DAL.Contracts;
|
||||||
using PARR.DAL.Models.Schedule;
|
using PARR.DAL.Models.Schedule;
|
||||||
@@ -17,8 +18,9 @@ namespace PARR.DAL.Services.Implementations.Schedule
|
|||||||
/// Настройки, если не нашли в offsetList
|
/// Настройки, если не нашли в offsetList
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ScheduleResponseAreaTimeOffset defaultOffset;
|
private readonly ScheduleResponseAreaTimeOffset defaultOffset;
|
||||||
|
private readonly ILogger<ScheduleResponseAreaTimeOffsetService> logger;
|
||||||
|
|
||||||
public ScheduleResponseAreaTimeOffsetService(DataContext dataContext, SettingsFromDb settingsFromDb)
|
public ScheduleResponseAreaTimeOffsetService(DataContext dataContext, SettingsFromDb settingsFromDb, ILogger<ScheduleResponseAreaTimeOffsetService> logger)
|
||||||
{
|
{
|
||||||
offsetList = dataContext.ScheduleResponseAreaTimeOffsets
|
offsetList = dataContext.ScheduleResponseAreaTimeOffsets
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
@@ -36,6 +38,7 @@ namespace PARR.DAL.Services.Implementations.Schedule
|
|||||||
EsppValue = "",
|
EsppValue = "",
|
||||||
UtcTimeOffset = new TimeSpan(3, 0, 0)
|
UtcTimeOffset = new TimeSpan(3, 0, 0)
|
||||||
};
|
};
|
||||||
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -45,7 +48,20 @@ namespace PARR.DAL.Services.Implementations.Schedule
|
|||||||
|
|
||||||
public ScheduleResponseAreaTimeOffset GetByResponseAreaOrDefault(string responseArea)
|
public ScheduleResponseAreaTimeOffset GetByResponseAreaOrDefault(string responseArea)
|
||||||
{
|
{
|
||||||
return offsetList.TryGetValue(responseArea, out var value) ? value : defaultOffset;
|
offsetList.TryGetValue(responseArea, out var value);
|
||||||
|
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
logger.LogDebug("Получил часовой пояс по ЗО '{responseArea}', esppValue: {EsppValue}, utcTimeOffset: {utcTimeOffset}", responseArea, value.EsppValue, value.UtcTimeOffset);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogWarning("Не смог получить часовой пояс по ЗО '{responseArea}', вернул часово пояс по умолчанию: name: {name}, esppValue: {EsppValue}, utcTimeOffset: {utcTimeOffset}",
|
||||||
|
responseArea, defaultOffset.ResponseArea, defaultOffset.EsppValue, defaultOffset.UtcTimeOffset);
|
||||||
|
return defaultOffset;
|
||||||
|
}
|
||||||
|
//return offsetList.TryGetValue(responseArea, out var value) ? value : defaultOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,15 @@ namespace PARR.Test.NextRun
|
|||||||
private readonly ITemplateService templateService;
|
private readonly ITemplateService templateService;
|
||||||
private readonly IShortcodesService shortcodesService;
|
private readonly IShortcodesService shortcodesService;
|
||||||
private readonly ITemplateDistributorV2 templateDistributorV2;
|
private readonly ITemplateDistributorV2 templateDistributorV2;
|
||||||
|
private readonly INextRunServiceV2 nextRunServiceV2;
|
||||||
|
|
||||||
public NextRunTest(
|
public NextRunTest(
|
||||||
/*ITemplateDistributor templateDistributor, */
|
/*ITemplateDistributor templateDistributor, */
|
||||||
INextRunService nextRunService,
|
INextRunService nextRunService,
|
||||||
ITemplateService templateService,
|
ITemplateService templateService,
|
||||||
IShortcodesService shortcodesService,
|
IShortcodesService shortcodesService,
|
||||||
ITemplateDistributorV2 templateDistributorV2
|
ITemplateDistributorV2 templateDistributorV2,
|
||||||
|
INextRunServiceV2 nextRunServiceV2
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
//this.templateDistributor = templateDistributor;
|
//this.templateDistributor = templateDistributor;
|
||||||
@@ -27,29 +29,40 @@ namespace PARR.Test.NextRun
|
|||||||
this.templateService = templateService;
|
this.templateService = templateService;
|
||||||
this.shortcodesService = shortcodesService;
|
this.shortcodesService = shortcodesService;
|
||||||
this.templateDistributorV2 = templateDistributorV2;
|
this.templateDistributorV2 = templateDistributorV2;
|
||||||
|
this.nextRunServiceV2 = nextRunServiceV2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public async Task Test()
|
public async Task Test()
|
||||||
{
|
{
|
||||||
var duration = 2;
|
#region тестирование INextRunServiceV2
|
||||||
var offset = TimeSpan.FromHours(3);
|
|
||||||
|
|
||||||
var startDate = new DateOnly(2026, 2, 10);
|
var distributeResult = await nextRunServiceV2.GetNextRunForJobGroupWithAutoDistributionAsync(Guid.Parse("d25d8a9e-898f-417a-9bec-ef3a356e7c94"));
|
||||||
var referenceDate = new DateTimeOffset(2026, 2, 13, 23, 0, 0, TimeSpan.Zero);
|
|
||||||
|
|
||||||
var templates = GetTemplates();
|
#endregion
|
||||||
var templatesEmpty = new List<TemplateNextRunDto>();
|
|
||||||
var templatesToHandeler = templatesEmpty; // templatesEmpty // templates
|
|
||||||
|
|
||||||
// распределить шаблоны
|
|
||||||
var distributedTemplates = await templateDistributorV2.DistributeTemplatesAsync(startDate, duration, referenceDate, offset, templates, true);
|
|
||||||
|
|
||||||
// получить актуальный nextRun
|
#region Тестирование templateDistributorV2
|
||||||
var targetTemplate = new TemplateNextRunDto(Guid.Parse("DA151719-2742-4260-BFFE-012B61591053"), new DateTimeOffset(2026, 2, 15, 23, 30, 0, TimeSpan.Zero));
|
|
||||||
//var validNextRun = await templateDistributorV2.GetNextRunForTemplateAsync(startDate, duration, referenceDate, offset, targetTemplate, templatesToHandeler, true, isNew: false);
|
|
||||||
|
|
||||||
|
//var duration = 2;
|
||||||
|
//var offset = TimeSpan.FromHours(3);
|
||||||
|
|
||||||
|
//var startDate = new DateOnly(2026, 2, 10);
|
||||||
|
//var referenceDate = new DateTimeOffset(2026, 2, 13, 23, 0, 0, TimeSpan.Zero);
|
||||||
|
|
||||||
|
//var templates = GetTemplates();
|
||||||
|
//var templatesEmpty = new List<TemplateNextRunDto>();
|
||||||
|
//var templatesToHandeler = templatesEmpty; // templatesEmpty // templates
|
||||||
|
|
||||||
|
//// распределить шаблоны
|
||||||
|
//var distributedTemplates = await templateDistributorV2.DistributeTemplatesAsync(startDate, duration, referenceDate, offset, templates, true);
|
||||||
|
|
||||||
|
//// получить актуальный nextRun
|
||||||
|
//var targetTemplate = new TemplateNextRunDto(Guid.Parse("DA151719-2742-4260-BFFE-012B61591053"), new DateTimeOffset(2026, 2, 15, 23, 30, 0, TimeSpan.Zero));
|
||||||
|
////var validNextRun = await templateDistributorV2.GetNextRunForTemplateAsync(startDate, duration, referenceDate, offset, targetTemplate, templatesToHandeler, true, isNew: false);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
//var template = await templateService.Get().Include(t => t.Job).ThenInclude(t => t.Group).FirstAsync(t => t.Name == "ЭИТИ-ПАРР_ЦКИТ-ГВЦ_СХД ТО-1_СХД-AERODISK-432-2-4-U26-EN4SAG022-ГВЦ");
|
//var template = await templateService.Get().Include(t => t.Job).ThenInclude(t => t.Group).FirstAsync(t => t.Name == "ЭИТИ-ПАРР_ЦКИТ-ГВЦ_СХД ТО-1_СХД-AERODISK-432-2-4-U26-EN4SAG022-ГВЦ");
|
||||||
//var responseAreae = await shortcodesService.ApplyShortcodesAsync(template.Job.ResponseAreaMask, template);
|
//var responseAreae = await shortcodesService.ApplyShortcodesAsync(template.Job.ResponseAreaMask, template);
|
||||||
|
|||||||
Reference in New Issue
Block a user