Друзья, давайте вместе разберем, как автоматизировать процесс заполнения табличной части справочника в 1С. Часто возникает задача, когда необходимо сформировать список дат, например, расписание периодического технического обслуживания, где каждая последующая дата зависит от предыдущей, а весь список должен умещаться в определенные временные рамки. Разберем эту задачу на примере заполнения расписания ТО для оборудования или техники.
Представим, что у нас есть справочник, в котором хранится информация об объекте (например, самолете), и нам нужно автоматически заполнить его табличную часть "РасписаниеПТО" датами проведения различных форм технического обслуживания (ТО).
Для решения этой задачи нам потребуются следующие исходные данные, которые, скорее всего, будут храниться в реквизитах основного объекта справочника:
ДатаПроизводства: Начальная дата, от которой мы будем отсчитывать первое ТО.НКСС (Назначенный Календарный Срок Службы): Числовой показатель, используемый для расчета ДатаИстечСЛГ.ДатаИстечСЛГ (Дата Истечения Срока Летной Годности): Конечная дата, до которой должны быть запланированы все ТО. После этой даты никакие работы не планируются.ФормаАинтервал, ФормаBинтервал и т.д.: Интервалы в днях для каждой конкретной формы ТО (например, Форма А проводится каждые 30 дней, Форма B - каждые 90 дней).ФормаАпродолж, ФормаBпродолж и т.д.: Продолжительность выполнения каждой формы ТО в часах.Основная проблема, с которой мы столкнемся, это необходимость рассчитывать каждую новую дату ТО, отталкиваясь не от даты производства, а от даты предыдущего ТО. Это требует использования итеративного подхода.
Для автоматического заполнения множества строк, количество которых заранее неизвестно (оно зависит от интервала и общего срока службы), наиболее подходящим инструментом является цикл Пока. Этот цикл будет продолжаться до тех пор, пока выполняется определенное условие.
Критически важно для итеративного расчета дат использовать дополнительную переменную. Мы назовем ее ТекущаяДатаНачалаТО. Эта переменная будет хранить дату начала последнего запланированного ТО. На каждой итерации цикла мы будем:
ТекущаяДатаНачалаТО для расчета следующей плановой даты ТО.ТекущаяДатаНачалаТО новым значением, чтобы оно послужило отправной точкой для следующей итерации.Условие завершения цикла будет основываться на дате истечения срока службы. Мы будем продолжать добавлять строки до тех пор, пока следующая рассчитанная дата ТО не превысит ДатаИстечСЛГ.
В 1С для работы с датами рекомендуется использовать специализированные функции, а не прямые арифметические операции с секундами (например, умножение на 86400 для добавления дня). Хотя арифметические операции с секундами могут работать, функции платформы более наглядны, надежны и корректно учитывают особенности календаря (количество дней в месяце, високосные годы и т.д.).
Вот несколько полезных функций:
ДобавитьДень(Дата, КоличествоДней): Добавляет или вычитает указанное количество дней к дате.ДобавитьМесяц(Дата, КоличествоМесяцев): Добавляет или вычитает указанное количество месяцев к дате, корректно обрабатывая конец месяца.ДобавитьГод(Дата, КоличествоЛет): Добавляет или вычитает указанное количество лет.Обратите внимание на граничные условия. Если нам нужно, чтобы последнее ТО не попадало точно на ДатаИстечСЛГ или за ее пределы, условие цикла должно быть строгим (<). В некоторых случаях может потребоваться небольшая корректировка ДатаИстечСЛГ, например, отнять от нее интервал или фиксированное количество дней (как было предложено в одном из сообщений, вычитая 30 дней в секундах: Объект.ДатаИстечСЛГ - 2592000). Это может быть полезно, если ТО должно быть завершено до определенной даты, а не в тот же день.
Давайте пошагово рассмотрим, как реализовать этот алгоритм в коде 1С.
Очистка табличной части: Прежде чем заполнять табличную часть, мы всегда должны ее очищать, чтобы избежать дублирования данных при повторном вызове процедуры. Это особенно важно, если процедура вызывается, например, при изменении даты производства, и мы хотим пересчитать все расписание.
Объект.РасписаниеПТО.Очистить();
Получение и инициализация исходных данных: Нам нужно получить значения из реквизитов объекта и инициализировать переменную, которая будет отслеживать текущую дату ТО.
НачальнаяДата = Объект.ДатаПроизводства;
ДатаОкончанияСЛГ = Объект.ДатаИстечСЛГ; // Или скорректированная ДатаИстечСЛГ - 2592000;
ИнтервалФормыАДней = Объект.ФормаАинтервал;
ПродолжительностьФормыАСек = Объект.ФормаАпродолж * 60 * 60; // Переводим часы в секунды
// Переменная для хранения даты начала следующего ТО
ТекущаяДатаНачалаТО = НачальнаяДата;
Организация цикла: Мы используем цикл Пока Истина Цикл с явным выходом по условию Прервать. Это обеспечивает гибкость, позволяя выполнять проверки внутри цикла перед добавлением новой строки.
Пока Истина Цикл
// ... логика расчета и добавления ...
Прервать; // Выход из цикла
КонецЦикла;
Расчет следующей даты начала ТО: Внутри цикла мы рассчитываем плановую дату начала следующего ТО, используя функцию ДобавитьДень() и текущую дату из нашей переменной.
СледующаяПлановаяДатаНачалаТО = ДобавитьДень(ТекущаяДатаНачалаТО, ИнтервалФормыАДней);
Проверка условия выхода из цикла: После расчета следующей даты, мы проверяем, не превышает ли она ДатаОкончанияСЛГ. Если превышает, то дальнейшие ТО планировать не нужно, и мы выходим из цикла.
Если СледующаяПлановаяДатаНачалаТО > ДатаОкончанияСЛГ Тогда
Прервать; // Выходим из цикла, если следующая дата превышает срок службы
КонецЕсли;
Добавление новой строки в табличную часть: Если условие выхода не сработало, значит, текущее ТО еще вписывается в срок службы. Мы добавляем новую строку и заполняем ее реквизиты.
НоваяСтрока = Объект.РасписаниеПТО.Добавить();
НоваяСтрока.ДатаНачТОплан = СледующаяПлановаяДатаНачалаТО;
НоваяСтрока.ПродолжТО = ПродолжительностьФормыАСек;
НоваяСтрока.ДатаОКТОплан = НоваяСтрока.ДатаНачТОплан + НоваяСтрока.ПродолжТО; // Исправлено: используем реквизиты НовойСтроки
НоваяСтрока.ФормаТО = "ФормаА";
Обратите внимание на строку НоваяСтрока.ДатаОКТОплан = НоваяСтрока.ДатаНачТОплан + НоваяСтрока.ПродолжТО;. Важно использовать реквизиты именно НоваяСтрока, а не какой-то внешней переменной ТекСтрока, которая может содержать неактуальные данные или вовсе не быть инициализирована в контексте цикла. Это было одной из распространенных ошибок в обсуждении.
Обновление переменной цикла: Самый важный шаг для корректной работы последовательного расчета дат – это обновление нашей отслеживающей переменной ТекущаяДатаНачалаТО. Для следующей итерации она должна принять значение только что запланированной даты начала ТО.
ТекущаяДатаНачалаТО = НоваяСтрока.ДатаНачТОплан;
С учетом всего вышесказанного, представим полный пример кода, который можно использовать в серверной процедуре. Мы также учтем необходимость вызова серверного кода из клиентского контекста, например, при изменении реквизита на форме.
Сначала определим серверную процедуру, которая будет выполнять основную логику заполнения:
&НаСервере
Процедура ЗаполнитьРасписаниеТОНаСервере()
// Очищаем табличную часть перед заполнением, чтобы избежать дублирования
Объект.РасписаниеПТО.Очистить();
// Получаем необходимые исходные данные из реквизитов объекта
НачальнаяДата = Объект.ДатаПроизводства;
ДатаОкончанияСЛГ = Объект.ДатаИстечСЛГ;
// Если требуется корректировка даты окончания для исключения последнего интервала
// Например, чтобы ТО всегда завершалось за N дней до ДатаИстечСЛГ
// ДатаОкончанияСЛГ = ДатаОкончанияСЛГ - 2592000; // Отнимаем 30 дней в секундах
ИнтервалФормыАДней = Объект.ФормаАинтервал;
ПродолжительностьФормыАСек = Объект.ФормаАпродолж * 60 * 60; // Переводим часы в секунды
// Переменная для хранения даты начала следующего ТО, инициализируем ее НачальнойДатой
ТекущаяДатаНачалаТО = НачальнаяДата;
// Цикл для заполнения расписания для Формы А
Пока Истина Цикл
// Рассчитываем следующую плановую дату начала ТО
// Отталкиваемся от ТекущаяДатаНачалаТО
СледующаяПлановаяДатаНачалаТО = ДобавитьДень(ТекущаяДатаНачалаТО, ИнтервалФормыАДней);
// Проверяем, не выходим ли за рамки срока службы
// Если следующая дата превышает ДатаОкончанияСЛГ, прерываем цикл
Если СледующаяПлановаяДатаНачалаТО > ДатаОкончанияСЛГ Тогда
Прервать;
КонецЕсли;
// Добавляем новую строку в табличную часть
НоваяСтрока = Объект.РасписаниеПТО.Добавить();
НоваяСтрока.ДатаНачТОплан = СледующаяПлановаяДатаНачалаТО;
НоваяСтрока.ПродолжТО = ПродолжительностьФормыАСек;
// Дата окончания ТО рассчитывается на основе только что установленной ДатаНачТОплан
НоваяСтрока.ДатаОКТОплан = НоваяСтрока.ДатаНачТОплан + НоваяСтрока.ПродолжТО;
НоваяСтрока.ФормаТО = "ФормаА";
// Обновляем ТекущаяДатаНачалаТО для следующей итерации
// Для следующего расчета мы будем отталкиваться от только что запланированной даты начала ТО
ТекущаяДатаНачалаТО = НоваяСтрока.ДатаНачТОплан;
КонецЦикла;
// После заполнения всех строк, при необходимости можно записать объект
// Объект.Записать();
КонецПроцедуры
Теперь создадим клиентскую процедуру, которая будет вызывать серверную. Например, это может быть обработчик события "ПриИзменении" для поля ДатаПроизводства на форме:
&НаКлиенте
Процедура ДатаПроизводстваПриИзменении(Элемент)
// Эта часть кода может быть перенесена в серверную процедуру ЗаполнитьРасписаниеТОНаСервере
// или остаться здесь, если она не требует доступа к данным, записываемым на сервере.
// Если ЗначениеЗаполнено(Объект.НКСС) Тогда
// Объект.ДатаИстечСЛГ = ДобавитьМесяц(Объект.ДатаПроизводства, 12 * Объект.НКСС);
// КонецЕсли;
// Вызываем серверную процедуру для выполнения основной логики заполнения табличной части
ЗаполнитьРасписаниеТОНаСервере();
КонецПроцедуры
Если у нас есть несколько форм ТО (например, Форма А, Форма B, Форма С, Форма D) с разными интервалами, есть несколько подходов к их заполнению:
Несколько отдельных циклов: Самый простой и понятный подход – это создать отдельный блок кода с циклом Пока для каждой формы ТО. Каждый такой блок будет заполнять свою часть расписания, используя свой интервал и продолжительность, до общей ДатаОкончанияСЛГ. При этом каждый цикл будет начинаться от Объект.ДатаПроизводства (или другой базовой даты) и работать независимо.
Этот метод легче для отладки и понимания, но может привести к дублированию кода, если логика заполнения очень похожа.
// ... Начало процедуры ЗаполнитьРасписаниеТОНаСервере ...
Объект.РасписаниеПТО.Очистить();
НачальнаяДата = Объект.ДатаПроизводства;
ДатаОкончанияСЛГ = Объект.ДатаИстечСЛГ;
// --- Заполнение для Формы А ---
ТекущаяДатаНачалаТО = НачальнаяДата;
ИнтервалФормыАДней = Объект.ФормаАинтервал;
ПродолжительностьФормыАСек = Объект.ФормаАпродолж * 60 * 60;
Пока Истина Цикл
СледующаяПлановаяДатаНачалаТО = ДобавитьДень(ТекущаяДатаНачалаТО, ИнтервалФормыАДней);
Если СледующаяПлановаяДатаНачалаТО > ДатаОкончанияСЛГ Тогда Прервать; КонецЕсли;
НоваяСтрока = Объект.РасписаниеПТО.Добавить();
НоваяСтрока.ДатаНачТОплан = СледующаяПлановаяДатаНачалаТО;
НоваяСтрока.ПродолжТО = ПродолжительностьФормыАСек;
НоваяСтрока.ДатаОКТОплан = НоваяСтрока.ДатаНачТОплан + НоваяСтрока.ПродолжТО;
НоваяСтрока.ФормаТО = "ФормаА";
ТекущаяДатаНачалаТО = НоваяСтрока.ДатаНачТОплан;
КонецЦикла;
// --- Заполнение для Формы B ---
ТекущаяДатаНачалаТО = НачальнаяДата; // Снова от НачальнойДаты
ИнтервалФормыBДней = Объект.ФормаBинтервал;
ПродолжительностьФормыBСек = Объект.ФормаBпродолж * 60 * 60;
Пока Истина Цикл
СледующаяПлановаяДатаНачалаТО = ДобавитьДень(ТекущаяДатаНачалаТО, ИнтервалФормыBДней);
Если СледующаяПлановаяДатаНачалаТО > ДатаОкончанияСЛГ Тогда Прервать; КонецЕсли;
НоваяСтрока = Объект.РасписаниеПТО.Добавить();
НоваяСтрока.ДатаНачТОплан = СледующаяПлановаяДатаНачалаТО;
НоваяСтрока.ПродолжТО = ПродолжительностьФормыBСек;
НоваяСтрока.ДатаОКТОплан = НоваяСтрока.ДатаНачТОплан + НоваяСтрока.ПродолжТО;
НоваяСтрока.ФормаТО = "ФормаB";
ТекущаяДатаНачалаТО = НоваяСтрока.ДатаНачТОплан;
КонецЦикла;
// ... и так далее для Форм C и D ...
// ... Конец процедуры ЗаполнитьРасписаниеТОНаСервере ...
Единый цикл со сложной логикой: Если формы ТО имеют сложную взаимосвязь, чередуются или зависят друг от друга по более сложным правилам, можно использовать единый цикл с внутренней логикой определения, какую форму ТО добавить следующей. Однако это значительно усложняет код и отладку.
В большинстве случаев подход с несколькими отдельными циклами является предпочтительным из-за его простоты и чистоты кода.
Типы данных: Убедитесь, что все реквизиты, используемые для интервалов и продолжительности, имеют правильные типы данных (например, Число). Даты должны быть типа "Дата".
Граничные условия: Тщательно проверьте условие СледующаяПлановаяДатаНачалаТО > ДатаОкончанияСЛГ. В зависимости от требований, возможно, потребуется использовать >= или скорректировать ДатаОкончанияСЛГ, чтобы последнее ТО точно попадало в срок или завершалось до него.
Пустые интервалы: Если какой-то интервал (например, Объект.ФормаАинтервал) может быть пустым или нулевым, это приведет к бесконечному циклу или ошибкам. Добавьте проверку Если ИнтервалФормыАДней <= 0 Тогда Продолжить; КонецЕсли; перед циклом для каждой формы, чтобы пропустить ее, если интервал некорректен.
Обратная связь: Для пользователя может быть полезно добавить сообщение о завершении заполнения или о возникших проблемах с помощью функции Сообщить().
Транзакция: Если заполнение табличной части является частью более сложной операции, может потребоваться использование транзакций для обеспечения целостности данных.
Таким образом, применяя переменные для отслеживания текущего состояния (даты последнего ТО) и корректно используя циклы и встроенные функции для работы с датами, мы можем эффективно решить задачу автоматического заполнения табличной части справочника в 1С даже для сложных сценариев с последовательными расчетами.