feat(api,esppScheduleSync): добавлена логика подстановки часового пояса рабочей группы ответственной за эк в расписании генератора
This commit is contained in:
@@ -12,11 +12,10 @@ using PARR.Common.Domain;
|
|||||||
using PARR.Constants;
|
using PARR.Constants;
|
||||||
using PARR.DAL.Contracts;
|
using PARR.DAL.Contracts;
|
||||||
using PARR.DAL.DomainServices.Shortcodes;
|
using PARR.DAL.DomainServices.Shortcodes;
|
||||||
using PARR.DAL.DomainServices.Shortcodes.Models;
|
|
||||||
using PARR.DAL.Models;
|
using PARR.DAL.Models;
|
||||||
using PARR.DAL.NextRunServices;
|
using PARR.DAL.NextRunServices;
|
||||||
using PARR.DAL.Services.Interfaces;
|
using PARR.DAL.Services.Interfaces;
|
||||||
using PARR.DAL.TransformServices;
|
using PARR.DAL.Services.Interfaces.Schedule;
|
||||||
|
|
||||||
namespace PARR.API.Controllers.V1
|
namespace PARR.API.Controllers.V1
|
||||||
{
|
{
|
||||||
@@ -32,6 +31,7 @@ namespace PARR.API.Controllers.V1
|
|||||||
private readonly IRobotHistoryService robotHistoryService;
|
private readonly IRobotHistoryService robotHistoryService;
|
||||||
private readonly IShortcodesService shortcodesService;
|
private readonly IShortcodesService shortcodesService;
|
||||||
private readonly INextRunService nextRunService;
|
private readonly INextRunService nextRunService;
|
||||||
|
private readonly IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService;
|
||||||
|
|
||||||
public RobotTaskController(
|
public RobotTaskController(
|
||||||
IMapper mapper,
|
IMapper mapper,
|
||||||
@@ -42,7 +42,8 @@ namespace PARR.API.Controllers.V1
|
|||||||
IClientService clientService,
|
IClientService clientService,
|
||||||
IRobotHistoryService robotHistoryService,
|
IRobotHistoryService robotHistoryService,
|
||||||
IShortcodesService shortcodesService,
|
IShortcodesService shortcodesService,
|
||||||
INextRunService nextRunService
|
INextRunService nextRunService,
|
||||||
|
IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.mapper = mapper;
|
this.mapper = mapper;
|
||||||
@@ -54,6 +55,7 @@ namespace PARR.API.Controllers.V1
|
|||||||
this.robotHistoryService = robotHistoryService;
|
this.robotHistoryService = robotHistoryService;
|
||||||
this.shortcodesService = shortcodesService;
|
this.shortcodesService = shortcodesService;
|
||||||
this.nextRunService = nextRunService;
|
this.nextRunService = nextRunService;
|
||||||
|
this.scheduleResponseAreaTimeOffsetService = scheduleResponseAreaTimeOffsetService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -198,38 +200,6 @@ namespace PARR.API.Controllers.V1
|
|||||||
{ //RobotTaskTemplateResponse
|
{ //RobotTaskTemplateResponse
|
||||||
var robotTaskTemplateResponse = mapper.Map<RobotTaskTemplateResponse>(task);
|
var robotTaskTemplateResponse = mapper.Map<RobotTaskTemplateResponse>(task);
|
||||||
|
|
||||||
//TODO Вынести в отдельный метод ShortcodesService
|
|
||||||
|
|
||||||
// Построим TemplateForShortcodes из уже загруженного task.Template
|
|
||||||
//var templateForShortcodes = new TemplateForShortcodes
|
|
||||||
//{
|
|
||||||
// Id = task.Template!.Id,
|
|
||||||
// Index = task.Template.Index,
|
|
||||||
// JobId = task.Template.JobId,
|
|
||||||
// UnitId = task.Template.UnitId,
|
|
||||||
// Job = task.Template.Job == null ? null : new JobForShortcodes
|
|
||||||
// {
|
|
||||||
// Group = task.Template.Job.Group == null ? null : new JobGroupForShortcodes
|
|
||||||
// {
|
|
||||||
// Id = task.Template.Job.Group.Id,
|
|
||||||
// GroupingUnitFieldId = task.Template.Job.Group.GroupingUnitFieldId,
|
|
||||||
// GroupType = task.Template.Job.Group.GroupType == null ? null : new JobGroupTypeForShortcodes
|
|
||||||
// {
|
|
||||||
// Code = task.Template.Job.Group.GroupType.Code
|
|
||||||
// },
|
|
||||||
// GroupName = task.Template.Job.Group.GroupName
|
|
||||||
// },
|
|
||||||
// Tnk = task.Template.Job.Tnk == null ? null : new TnkForShortcodes
|
|
||||||
// {
|
|
||||||
// Name = task.Template.Job.Tnk.Name,
|
|
||||||
// ShortName = task.Template.Job.Tnk.ShortName ?? ""
|
|
||||||
// },
|
|
||||||
// WorkName = task.Template.Job.WorkName,
|
|
||||||
// Name = task.Template.Job.Name
|
|
||||||
// },
|
|
||||||
// UnitsInTemplate = task.Template.UnitsInTemplate?.Select(uit => new UnitInTemplateForShortcodes { UnitId = uit.UnitId }).ToList() ?? new List<UnitInTemplateForShortcodes>()
|
|
||||||
//};
|
|
||||||
|
|
||||||
//if (shortcodesService.IsAnyShortcodes(robotTaskTemplateResponse.FullDescription))
|
//if (shortcodesService.IsAnyShortcodes(robotTaskTemplateResponse.FullDescription))
|
||||||
robotTaskTemplateResponse.FullDescription = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.FullDescription, task.Template!);
|
robotTaskTemplateResponse.FullDescription = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.FullDescription, task.Template!);
|
||||||
//if (shortcodesService.IsAnyShortcodes(robotTaskTemplateResponse.ShortDescription))
|
//if (shortcodesService.IsAnyShortcodes(robotTaskTemplateResponse.ShortDescription))
|
||||||
@@ -253,39 +223,13 @@ namespace PARR.API.Controllers.V1
|
|||||||
//RobotTaskScheduleResponse
|
//RobotTaskScheduleResponse
|
||||||
var robotTaskScheduleResponse = mapper.Map<RobotTaskScheduleResponse>(task);
|
var robotTaskScheduleResponse = mapper.Map<RobotTaskScheduleResponse>(task);
|
||||||
|
|
||||||
//// Построим TemplateForShortcodes из уже загруженного task.Template
|
robotTaskScheduleResponse.Timezone = await GetTimezoneForTemplateAsync(task.Template!);
|
||||||
//var templateForShortcodes = new TemplateForShortcodes
|
|
||||||
//{
|
|
||||||
// Id = task.Template!.Id,
|
|
||||||
// Index = task.Template.Index,
|
|
||||||
// JobId = task.Template.JobId,
|
|
||||||
// UnitId = task.Template.UnitId,
|
|
||||||
// Job = task.Template.Job == null ? null : new JobForShortcodes
|
|
||||||
// {
|
|
||||||
// Group = task.Template.Job.Group == null ? null : new JobGroupForShortcodes
|
|
||||||
// {
|
|
||||||
// GroupingUnitFieldId = task.Template.Job.Group.GroupingUnitFieldId,
|
|
||||||
// GroupType = task.Template.Job.Group.GroupType == null ? null : new JobGroupTypeForShortcodes
|
|
||||||
// {
|
|
||||||
// Code = task.Template.Job.Group.GroupType.Code
|
|
||||||
// },
|
|
||||||
// GroupName = task.Template.Job.Group.GroupName
|
|
||||||
// },
|
|
||||||
// Tnk = task.Template.Job.Tnk == null ? null : new TnkForShortcodes
|
|
||||||
// {
|
|
||||||
// Name = task.Template.Job.Tnk.Name,
|
|
||||||
// ShortName = task.Template.Job.Tnk.ShortName
|
|
||||||
// },
|
|
||||||
// WorkName = task.Template.Job.WorkName,
|
|
||||||
// Name = task.Template.Job.Name
|
|
||||||
// },
|
|
||||||
// UnitsInTemplate = task.Template.UnitsInTemplate?.Select(uit => new UnitInTemplateForShortcodes { UnitId = uit.UnitId }).ToList() ?? new List<UnitInTemplateForShortcodes>()
|
|
||||||
//};
|
|
||||||
|
|
||||||
//if (shortcodesService.IsAnyShortcodes(robotTaskScheduleResponse.WorkGroup))
|
|
||||||
robotTaskScheduleResponse.WorkGroup = await shortcodesService.ApplyShortcodesAsync(robotTaskScheduleResponse.WorkGroup, task.Template!);
|
robotTaskScheduleResponse.WorkGroup = await shortcodesService.ApplyShortcodesAsync(robotTaskScheduleResponse.WorkGroup, task.Template!);
|
||||||
robotTaskScheduleResponse.ResponseArea = await shortcodesService.ApplyShortcodesAsync(robotTaskScheduleResponse.ResponseArea, task.Template!);
|
robotTaskScheduleResponse.ResponseArea = await shortcodesService.ApplyShortcodesAsync(robotTaskScheduleResponse.ResponseArea, task.Template!);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return Ok(new Response<RobotTaskScheduleResponse>(robotTaskScheduleResponse, true));
|
return Ok(new Response<RobotTaskScheduleResponse>(robotTaskScheduleResponse, true));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -387,5 +331,33 @@ namespace PARR.API.Controllers.V1
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<string> GetTimezoneForTemplateAsync(Template template)
|
||||||
|
{
|
||||||
|
if (template.Job?.Group?.IsWorkGroupTimezone != true)
|
||||||
|
return settingsFromDb.ScheduleTimezone;
|
||||||
|
|
||||||
|
var responseArea = template.Unit?.BaseFields?.ResponseArea;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(responseArea))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"У шаблона Id={template.Id}, Name='{template.Name}' не задана ResponseArea в Unit.BaseFields, " +
|
||||||
|
"но включена настройка 'использовать часовой пояс рабочей группы'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var offsets = await scheduleResponseAreaTimeOffsetService
|
||||||
|
.Get()
|
||||||
|
.ToDictionaryAsync(t => t.ResponseArea, t => t.TimeOffset);
|
||||||
|
|
||||||
|
if (!offsets.TryGetValue(responseArea, out var offset))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Не найдено временное смещение для ResponseArea '{responseArea}' у шаблона Id={template.Id}, Name='{template.Name}'. " +
|
||||||
|
"Проверьте наличие записи в таблице ScheduleResponseAreaTimeOffset.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using PARR.BLL.Services.Interfaces;
|
|||||||
using PARR.DAL.Contracts;
|
using PARR.DAL.Contracts;
|
||||||
using PARR.DAL.Models;
|
using PARR.DAL.Models;
|
||||||
using PARR.DAL.Services.Interfaces;
|
using PARR.DAL.Services.Interfaces;
|
||||||
|
using PARR.DAL.Services.Interfaces.Schedule;
|
||||||
using PARR.DAL.TransformServices;
|
using PARR.DAL.TransformServices;
|
||||||
using PARR.EsppScheduleSync.Domain;
|
using PARR.EsppScheduleSync.Domain;
|
||||||
using PARR.EsppScheduleSync.Settings;
|
using PARR.EsppScheduleSync.Settings;
|
||||||
@@ -21,7 +22,9 @@ namespace PARR.EsppScheduleSync
|
|||||||
private readonly ISyncService<EsppObjectSchedule> syncService;
|
private readonly ISyncService<EsppObjectSchedule> syncService;
|
||||||
private readonly SettingsFromDb settingsFromDb;
|
private readonly SettingsFromDb settingsFromDb;
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
//private readonly INextRunModifierService nextRunModifierService;
|
private string noneExcludeCalendarEsppValue;
|
||||||
|
private Dictionary<string, string> responseAreaTimeOffsetDict;
|
||||||
|
|
||||||
|
|
||||||
public ScheduleSyncher(
|
public ScheduleSyncher(
|
||||||
ILogger<ScheduleSyncher> logger,
|
ILogger<ScheduleSyncher> logger,
|
||||||
@@ -30,7 +33,6 @@ namespace PARR.EsppScheduleSync
|
|||||||
ISyncService<EsppObjectSchedule> syncService,
|
ISyncService<EsppObjectSchedule> syncService,
|
||||||
SettingsFromDb settingsFromDb,
|
SettingsFromDb settingsFromDb,
|
||||||
IServiceProvider serviceProvider
|
IServiceProvider serviceProvider
|
||||||
//INextRunModifierService nextRunModifierService
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
@@ -39,17 +41,30 @@ namespace PARR.EsppScheduleSync
|
|||||||
this.syncService = syncService;
|
this.syncService = syncService;
|
||||||
this.settingsFromDb = settingsFromDb;
|
this.settingsFromDb = settingsFromDb;
|
||||||
this.serviceProvider = serviceProvider;
|
this.serviceProvider = serviceProvider;
|
||||||
//this.nextRunModifierService = nextRunModifierService;
|
|
||||||
|
this.responseAreaTimeOffsetDict = new Dictionary<string, string>();
|
||||||
|
this.noneExcludeCalendarEsppValue = string.Empty;
|
||||||
|
|
||||||
if (globalSettings.MqSettings == null)
|
if (globalSettings.MqSettings == null)
|
||||||
{
|
{
|
||||||
logger.LogError("Нет секции настроек хранилища. MqSettings, EsppTemplates");
|
logger.LogError("Нет секции настроек хранилища. MqSettings, EsppTemplates");
|
||||||
throw new Exception("Нет секции настроек хранилища. MqSettings, EsppTemplates");
|
throw new Exception("Нет секции настроек хранилища. MqSettings, EsppTemplates");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(globalSettings.ParsingSeparator))
|
||||||
|
throw new ArgumentException("ParsingSeparator не задан в GlobalSettings.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task StartAsync()
|
public async Task StartAsync()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(noneExcludeCalendarEsppValue))
|
||||||
|
await GetNoneExcludeCalendarEsppValueAsync();
|
||||||
|
|
||||||
|
if (responseAreaTimeOffsetDict.Count == 0)
|
||||||
|
await GetResponseAreaTimeOffsetsAsync();
|
||||||
|
|
||||||
var isConnected = await mqService.InitConsumerAsync(globalSettings!.MqSettings!, SyncScheduleAsync);
|
var isConnected = await mqService.InitConsumerAsync(globalSettings!.MqSettings!, SyncScheduleAsync);
|
||||||
|
|
||||||
if (!isConnected)
|
if (!isConnected)
|
||||||
@@ -58,6 +73,26 @@ namespace PARR.EsppScheduleSync
|
|||||||
logger.LogInformation("Запущена проверка очереди {QueueName}.", globalSettings.MqSettings!.QueueName);
|
logger.LogInformation("Запущена проверка очереди {QueueName}.", globalSettings.MqSettings!.QueueName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task GetResponseAreaTimeOffsetsAsync()
|
||||||
|
{
|
||||||
|
using var scope = serviceProvider.CreateScope();
|
||||||
|
var service = scope.ServiceProvider.GetRequiredService<IScheduleResponseAreaTimeOffsetService>();
|
||||||
|
|
||||||
|
responseAreaTimeOffsetDict = await service.Get().ToDictionaryAsync(t => t.ResponseArea, t => t.TimeOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GetNoneExcludeCalendarEsppValueAsync()
|
||||||
|
{
|
||||||
|
using var scope = serviceProvider.CreateScope();
|
||||||
|
var service = scope.ServiceProvider.GetRequiredService<IScheduleExcludeTypeService>();
|
||||||
|
|
||||||
|
var noneExcludeType = await service.Get().AsNoTracking().FirstOrDefaultAsync(t => t.Code == nameof(ScheduleExcludeTypeEnum.None));
|
||||||
|
|
||||||
|
if (noneExcludeType == null)
|
||||||
|
throw new InvalidOperationException($"Не найден тип исключения с кодом '{nameof(ScheduleExcludeTypeEnum.None)}'");
|
||||||
|
|
||||||
|
noneExcludeCalendarEsppValue = noneExcludeType.EsppValue;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task StopAsync()
|
public async Task StopAsync()
|
||||||
{
|
{
|
||||||
@@ -68,6 +103,8 @@ namespace PARR.EsppScheduleSync
|
|||||||
|
|
||||||
|
|
||||||
private async Task SyncScheduleAsync(string str)
|
private async Task SyncScheduleAsync(string str)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
const int expectedParts = 24;
|
const int expectedParts = 24;
|
||||||
var separator = globalSettings.ParsingSeparator;
|
var separator = globalSettings.ParsingSeparator;
|
||||||
@@ -172,6 +209,12 @@ namespace PARR.EsppScheduleSync
|
|||||||
// Передаём оригинальную строку в стандартный синхронизатор
|
// Передаём оригинальную строку в стандартный синхронизатор
|
||||||
await syncService.SyncEsppObjectAsync(str, ParseStrToEsppObject, ConvertDbObjToEsppObj);
|
await syncService.SyncEsppObjectAsync(str, ParseStrToEsppObject, ConvertDbObjToEsppObj);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Ошибка при синхронизации расписания из строки: {InputString}", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -194,7 +237,6 @@ namespace PARR.EsppScheduleSync
|
|||||||
nextRunByAccountRobotTimeZone = nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun);
|
nextRunByAccountRobotTimeZone = nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var esppObjectFromDb = new EsppObjectSchedule
|
var esppObjectFromDb = new EsppObjectSchedule
|
||||||
{
|
{
|
||||||
TemplateName = template.Name.ToUpper(),
|
TemplateName = template.Name.ToUpper(),
|
||||||
@@ -213,7 +255,7 @@ namespace PARR.EsppScheduleSync
|
|||||||
//BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun)),
|
//BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun)),
|
||||||
Scheduled = EsppScheduleHelpers.GetNextRun(nextRunByAccountRobotTimeZone),
|
Scheduled = EsppScheduleHelpers.GetNextRun(nextRunByAccountRobotTimeZone),
|
||||||
BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunByAccountRobotTimeZone),
|
BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunByAccountRobotTimeZone),
|
||||||
Timezone = settingsFromDb.ScheduleTimezone,
|
Timezone = GetTimezone(template),
|
||||||
//Мы решили, что для всех расписаний "Отсутствует дата завершения", если что-то поменяется, тут нужно переделать
|
//Мы решили, что для всех расписаний "Отсутствует дата завершения", если что-то поменяется, тут нужно переделать
|
||||||
TerminationType = settingsFromDb.ScheduleRepeatRange == "Отсутствует дата завершения" ? "forever" : "",
|
TerminationType = settingsFromDb.ScheduleRepeatRange == "Отсутствует дата завершения" ? "forever" : "",
|
||||||
CompleteAfter = ""
|
CompleteAfter = ""
|
||||||
@@ -223,6 +265,31 @@ namespace PARR.EsppScheduleSync
|
|||||||
return ClearOptionalFields(esppObjectFromDb);
|
return ClearOptionalFields(esppObjectFromDb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetTimezone(Template template)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (template.Job?.Group?.IsWorkGroupTimezone != true)
|
||||||
|
return settingsFromDb.ScheduleTimezone;
|
||||||
|
|
||||||
|
var responseArea = template.Unit?.BaseFields?.ResponseArea;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(responseArea))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"У шаблона Id={template.Id}, Name='{template.Name}' не задана ResponseArea в Unit.BaseFields, " +
|
||||||
|
"но включена настройка 'использовать часовой пояс рабочей группы'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!responseAreaTimeOffsetDict.TryGetValue(responseArea, out var offset))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Не найдено временное смещение для ResponseArea '{responseArea}' у шаблона Id={template.Id}, Name='{template.Name}'. " +
|
||||||
|
"Проверьте наличие записи в таблице ScheduleResponseAreaTimeOffset.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить исключение - Календарь
|
/// Получить исключение - Календарь
|
||||||
@@ -476,7 +543,9 @@ namespace PARR.EsppScheduleSync
|
|||||||
esppObject.ResponseArea = string.Empty;
|
esppObject.ResponseArea = string.Empty;
|
||||||
esppObject.WorkGroup = string.Empty;
|
esppObject.WorkGroup = string.Empty;
|
||||||
esppObject.CompleteAfter = string.Empty;
|
esppObject.CompleteAfter = string.Empty;
|
||||||
//esppObject.V60calendar = string.Empty;
|
|
||||||
|
if (esppObject.TypeV60calendar == noneExcludeCalendarEsppValue)
|
||||||
|
esppObject.V60calendar = string.Empty;
|
||||||
|
|
||||||
// В ЕСПП, при изменении "Повторять задачу", остаются предыдущие значения, их не нужно синхронизировать (касается только данных полученных из ЕСПП, в БД все ок)
|
// В ЕСПП, при изменении "Повторять задачу", остаются предыдущие значения, их не нужно синхронизировать (касается только данных полученных из ЕСПП, в БД все ок)
|
||||||
// т.е. если стояло Ежедненвно:понедельник, а изменили например на Еженедельно..., то в ежедневно значения останутся, но будут отрабатывать значения из Еженедельно.
|
// т.е. если стояло Ежедненвно:понедельник, а изменили например на Еженедельно..., то в ежедневно значения останутся, но будут отрабатывать значения из Еженедельно.
|
||||||
|
|||||||
Reference in New Issue
Block a user