При изучении основ разработки в системе 1С по методике М.Г. Радченко многие начинающие разработчики сталкиваются с ситуацией, когда механизмы, описанные в учебнике, работают корректно только при создании нового документа. Типичный пример — занятие №9, где настраивается автоматическое получение цены из регистра сведений при выборе номенклатуры. Однако часто возникает вопрос: почему, если мы меняем дату уже созданного документа, цены в табличной части не обновляются автоматически, хотя в регистре на новую дату могут быть установлены совсем другие значения?
В этой статье мы подробно разберем, как работает событийная модель 1С, почему платформа не делает «магических» пересчетов сама по себе и как программно реализовать обновление цен во всех строках документа при смене периода.
Для начала проанализируем ситуацию. В учебном примере код для получения цены прописывается в обработчике события ПриИзменении поля Номенклатура, которое находится внутри табличной части. Это означает, что процедура срабатывает только в тот момент, когда пользователь физически выбирает другой товар в конкретной строке.
Когда же мы меняем реквизит Дата в шапке документа, срабатывает событие самого поля даты. Платформа 1С не знает о том, что данные в табличной части (цены, суммы) зависят от этой даты, пока разработчик явно не укажет это в коде. В системе 1С нет встроенного интеллекта, который бы автоматически пересчитывал все связанные данные при изменении даты, так как в реальных бизнес-задачах это может привести к нежелательному изменению финансовых показателей прошлых периодов без ведома пользователя.
Давайте вспомним, какой код мы вносим в модуль формы документа согласно учебнику для обработки изменения номенклатуры:
// Обработчик изменения номенклатуры в строке табличной части
&НаКлиенте
Процедура ПереченьНоменклатурыНоменклатураПриИзменении(Элемент)
// Получить текущую строку табличной части.
СтрокаТабличнойЧасти = Элементы.ПереченьНоменклатуры.ТекущиеДанные;
// Установить цену, используя функцию из общего модуля.
// Передаем дату документа и выбранную номенклатуру.
СтрокаТабличнойЧасти.Цена = РаботаСоСправочниками.РозничнаяЦена(Объект.Дата, СтрокаТабличнойЧасти.Номенклатура);
// Пересчитать сумму строки
РаботаСДокументами.РассчитатьСумму(СтрокаТабличнойЧасти);
КонецПроцедуры
В данном коде ключевым моментом является использование метода Объект.Дата. Именно этот параметр определяет, на какой «срез времени» функция РозничнаяЦена будет искать данные в регистре сведений. Если мы изменим дату в шапке, значение Объект.Дата поменяется, но обработчик ПриИзменении для номенклатуры не запустится, поэтому старая цена останется в строке.
Чтобы реализовать пересчет цен во всем документе при изменении даты, нам необходимо выполнить следующие действия:
ОказаниеУслуги. Найдем в реквизитах объекта поле Дата, перейдем в его свойства и создадим обработчик события ПриИзменении на клиенте.ТекущиеДанные (одной строкой), при изменении даты нам нужно обновить все строки табличной части.Рассмотрим, как должен выглядеть программный код этого обработчика в модуле формы:
&НаКлиенте
Процедура ДатаПриИзменении(Элемент)
// Выясним причину: нам нужно пройти по всей коллекции строк табличной части
Для Каждого СтрокаТЧ Из Объект.ПереченьНоменклатуры Цикл
// Для каждой строки получаем актуальную цену на новую дату документа
// Используем ту же экспортную функцию из общего модуля
НоваяЦена = РаботаСоСправочниками.РозничнаяЦена(Объект.Дата, СтрокаТЧ.Номенклатура);
// Проверяем, изменилась ли цена, чтобы не перезаписывать данные лишний раз
Если СтрокаТЧ.Цена <> НоваяЦена Тогда
СтрокаТЧ.Цена = НоваяЦена;
// После изменения цены обязательно вызываем пересчет суммы
РаботаСДокументами.РассчитатьСумму(СтрокаТЧ);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Проанализируем работу функции РозничнаяЦена. Внутри она выполняет запрос к виртуальной таблице СрезПоследних регистра сведений. Параметр «Период» в этом запросе критически важен. Если бы в коде вместо Объект.Дата передавалась пустая дата или ТекущаяДата(), то система всегда возвращала бы самую последнюю цену, игнорируя дату документа. В учебнике Радченко грамотно используется передача даты именно из реквизита документа, что позволяет соблюдать историческую точность учета.
В реальных типовых конфигурациях (например, в 1С:Управлении торговлей или ERP) автоматический пересчет цен при случайном изменении даты может быть опасен. Представьте, что бухгалтер случайно изменил дату в документе годовой давности, и все цены «поехали». Поэтому профессиональные разработчики часто добавляют вопрос пользователю: «Дата документа изменилась. Желаете ли вы пересчитать цены в соответствии с новой датой?».
Также стоит учитывать производительность. Если в табличной части сотни строк, вызов функции РозничнаяЦена в цикле (который внутри делает запрос к БД) может замедлить работу формы. В таких случаях программисты пишут один запрос, который получает цены сразу для всего списка номенклатуры из документа на определенную дату. Однако для учебных целей и небольших документов подход с циклом, предложенный выше, является абсолютно корректным и наглядным.
Мы выяснили, что для решения проблемы «застывших» цен необходимо связать событие изменения даты с логикой обновления табличной части. Помните, что в 1С 8.3 управляемые формы работают по принципу «событие — реакция». Если вы хотите, чтобы изменение одного реквизита влияло на другие, вы всегда должны прописывать эту связь программно в соответствующем обработчике события.