Как правильно отслеживать изменение реквизитов связанных объектов и актуализировать цены в 1С?

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

В процессе разработки конфигураций на платформе 1С:Предприятие часто возникает задача синхронизации данных между связанными объектами. Типичный пример: в документе есть табличная часть, где пользователь выбирает изделие (или номенклатуру), и система должна автоматически подтянуть актуальную цену этого изделия из другого документа или справочника. Но что делать, если цена в объекте-источнике изменилась после того, как документ-приемник был заполнен или открыт? Как правильно отследить это изменение и обновить данные?

В этой статье мы подробно разберем, почему прямая привязка реквизитов документов друг к другу является архитектурной ошибкой, и рассмотрим методически верные паттерны проектирования, которые используются в профессиональной разработке на платформе 1С:Предприятие 8.

Почему прямая синхронизация между документами — это антипаттерн?

Попытка «на лету» отслеживать изменения в одном документе и сразу же перезаписывать данные в других связанных документах приводит к ряду серьезных проблем:

  1. Нарушение последовательности учета: Если мы автоматически и без ведома пользователя изменим цену в уже проведенном документе за прошлый месяц, у нас «поплывет» вся финансовая отчетность и закрытие периода.
  2. Блокировки базы данных: При одновременной работе множества пользователей попытка массово перезаписать связанные документы при изменении одного реквизита гарантированно вызовет взаимоблокировки (deadlocks) в СУБД.
  3. Избыточная нагрузка: Постоянный мониторинг изменений через обработчики ожидания или бесконечные фоновые задания перегружает сервер.

Выясним, как решить эту задачу правильно с точки зрения архитектуры 1С.

---

Решение 1. Использование регистров сведений для хранения цен

Связывать цену в документе напрямую с реквизитом другого документа или справочника методически неверно. Для хранения цен, тарифов и других изменяющихся во времени характеристик в 1С всегда используются периодические регистры сведений (например, ЦеныНоменклатуры).

Рассмотрим по шагам, как должна работать эта схема:

  1. Документ-источник (например, «Установка цен») при проведении записывает новые значения цен в периодический регистр сведений.
  2. В документе-приемнике при изменении реквизита «Изделие» (или «Номенклатура») мы делаем быстрый неконтекстный серверный запрос к регистру сведений, чтобы получить срез последних цен на дату нашего документа.

Посмотрим на пример реализации получения цены на клиенте при изменении реквизита табличной части:


&НаКлиенте
Процедура ТоварыИзделиеПриИзменении(Элемент)
    
    СтрокаТЧ = Элементы.Товары.ТекущиеДанные;
    Если СтрокаТЧ <> Неопределено Тогда
        // Получаем актуальную цену неконтекстным серверным вызовом
        СтрокаТЧ.Цена = ПолучитьЦенуИзделияНаСервере(СтрокаТЧ.Изделие, Объект.Дата);
        // Пересчитываем сумму строки
        ПересчитатьСуммуСтроки(СтрокаТЧ);
    КонецЕсли;
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьЦенуИзделияНаСервере(Знач СсылкаНаИзделие, Знач ДатаДокумента)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ЦеныИзделийСрезПоследних.Цена КАК Цена
        |ИЗ
        |	РегистрСведений.ЦеныИзделий.СрезПоследних(&Период, Изделие = &Изделие) КАК ЦеныИзделийСрезПоследних";
        
    Запрос.УстановитьПараметр("Период", ДатаДокумента);
    Запрос.УстановитьПараметр("Изделие", СсылкаНаИзделие);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Возврат Выборка.Цена;
    КонецЕсли;
    
    Возврат 0;
    
КонецФункции

---

Решение 2. Контроль актуальности данных в транзакции записи

Проанализируем ситуацию: пользователь открыл документ в 9:00, заполнил его по актуальным ценам, но оставил открытым и ушел на обед. В 12:00 другой менеджер утвердил новые прайс-листы. В 14:00 первый пользователь вернулся и нажал кнопку «Провести». На этот момент цены в документе уже неактуальны.

Чтобы избежать коллизий, методически верно выполнять финальную проверку цен непосредственно в транзакции записи/проведения документа на сервере. Если цены в табличной части не совпадают с актуальными ценами в базе данных, транзакцию необходимо отменить и выдать пользователю информативное сообщение.

Запрограммируем этот контроль в модуле объекта документа:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Выполняем проверку только при проведении документа
    Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
        
        Для Каждого СтрокаТЧ Из Товары Цикл
            
            // Получаем актуальную цену на момент проведения
            АктуальнаяЦена = ПолучитьЦенуИзделияНаСервере(СтрокаТЧ.Изделие, Дата);
            
            Если СтрокаТЧ.Цена <> АктуальнаяЦена Тогда
                
                // Выводим сообщение пользователю и отменяем проведение
                Сообщение = Новый СообщениеПользователю;
                Сообщение.Текст = "Внимание! Цена по изделию """ + СтрокаТЧ.Изделие + """ изменилась в базе данных. " +
                                  "В документе: " + СтрокаТЧ.Цена + ", Актуальная цена: " + АктуальнаяЦена + ". " +
                                  "Пожалуйста, обновите цены в документе.";
                Сообщение.Поле = "Товары[" + (Товары.Индекс(СтрокаТЧ)) + "].Цена";
                Сообщение.УстановитьДанные(ЭтотОбъект);
                Сообщение.Сообщить();
                
                Отказ = Истина; // Отмена транзакции
                
            КонецЕсли;
            
        КонецЦикла;
        
    КонецЕсли;
    
КонецПроцедуры

В этом случае мы гарантируем, что в систему не попадут некорректные или устаревшие данные, а пользователь получит четкое указание, в какой именно строке возникло расхождение.

---

Решение 3. Интерактивное обновление открытых форм через оповещения

Если вам все же требуется предоставить пользователю возможность интерактивно реагировать на изменения в реальном времени (например, если документы редактируются параллельно в рамках одного сеанса), мы можем использовать встроенный в платформу 1С механизм оповещений.

Для реализации этого сценария применим связку методов Оповестить() и ОбработкаОповещения():

Шаг 1. В форме документа-источника (где меняется цена) в обработчике события ПослеЗаписи отправляем глобальное оповещение по всей системе:


&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    // Оповещаем систему, что цена конкретного изделия изменилась
    Оповестить("ИзменениеЦеныИзделия", Объект.Ссылка);
КонецПроцедуры

Шаг 2. В форме нашего документа-приемника настраиваем обработчик события формы ОбработкаОповещения. Данное событие срабатывает на клиенте при вызове метода Оповестить в любой другой форме:


&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    
    Если ИмяСобытия = "ИзменениеЦеныИзделия" Тогда
        // Параметр содержит ссылку на измененное изделие
        СсылкаНаИзделие = Параметр;
        
        // Проверяем, есть ли это изделие в нашей табличной части
        Для Каждого СтрокаТЧ Из Объект.Товары Цикл
            Если СтрокаТЧ.Изделие = СсылкаНаИзделие Тогда
                // Запрашиваем новую цену с сервера и обновляем форму
                СтрокаТЧ.Цена = ПолучитьЦенуИзделияНаСервере(СсылкаНаИзделие, Объект.Target);
                ПересчитатьСуммуСтроки(СтрокаТЧ);
                ПоказатьОповещениеПользователя("Обновление данных", , "Цена изделия была успешно обновлена!");
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    
КонецПроцедуры

Этот метод отлично работает в рамках одного клиентского приложения (одного сеанса пользователя). Если же пользователи работают в разных сеансах (на разных компьютерах), для аналогичного интерактивного взаимодействия рекомендуется использовать современный механизм платформы — Систему взаимодействия.

---

Резюме и рекомендации

Подводя итоги, сформулируем основные правила работы с зависимыми реквизитами в 1С:

← На главную