При разработке дополнительного функционала в конфигурации 1С:Управление торговлей 11 (а также в 1С:ERP и 1С:Комплексная автоматизация) разработчики часто сталкиваются с нестандартным поведением управляемых форм: после программного изменения и записи реквизита ЕдиницаИзмерения в справочнике Номенклатура, в табличной части открытого документа (например, Реализация товаров и услуг) продолжает отображаться старая единица измерения. Если же открыть карточку номенклатуры интерактивно и нажать «Записать и закрыть», данные в строке документа обновляются корректно.
Разберем детально, почему так происходит, как устроена архитектура хранения единиц в современных конфигурациях и какими методами платформы решается эта задача.
Для понимания сути проблемы проанализируем, как система хранит и отображает количественные показатели в табличных частях документов:
ТоварыНаСкладах, СебестоимостьТоваров) всегда ведется строго в базовой единице хранения остатков, заданной в реквизите ЕдиницаИзмерения карточки номенклатуры.Товары документов товародвижения отдельного реквизита «Единица измерения» физически нет. Отображаемая пользователю колонка является либо связанным реквизитом ссылки Номенклатура.ЕдиницаИзмерения, либо представлением выбранной упаковки.Упаковка — ссылка на справочник УпаковкиЕдиницыИзмерения. Если значение не заполнено, операция выполняется в базовой единице.КоличествоУпаковок — видимое пользователю количество в выбранных единицах или упаковках.Количество — количество в базовой единице хранения, рассчитываемое как КоличествоУпаковок * КоэффициентУпаковки.Когда мы программно модифицируем и записываем объект на сервере (например, меняем реквизит ЕдиницаИзмерения у элемента СправочникСсылка.Номенклатура), клиентское приложение об этом не знает. Платформа 1С:Предприятие 8.3 активно кэширует представления ссылок и реквизиты объектов на стороне клиента для снижения нагрузки на сеть и сервер.
При интерактивной записи элемента из его формы платформа автоматически отправляет системное оповещение об изменении объекта, сбрасывая кэш во всех открытых формах сеанса. При чисто программной записи на сервере такое оповещение не генерируется, и открытые формы продолжают брать старые значения из локального кэша.
Чтобы заставить управляемую форму перечитать измененные ссылочные данные и отобразить актуальную единицу измерения, после серверной записи объекта необходимо на клиенте вызвать процедуру ОповеститьОбИзменении().
Посмотрим на пример реализации обработчика команды на клиенте:
&НаКлиенте
Процедура ИзменитьЕдиницуИзмеренияКоманда(Команда)
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// Вызываем серверную процедуру для изменения данных в справочнике
НоменклатураСсылка = ИзменитьЕдиницуНоменклатурыНаСервере(ТекущиеДанные.Номенклатура);
// Сбрасываем кэш платформы для измененного объекта
ОповеститьОбИзменении(НоменклатураСсылка);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ИзменитьЕдиницуНоменклатурыНаСервере(НоменклатураСсылка)
НоменклатураОбъект = НоменклатураСсылка.ПолучитьОбъект();
// Устанавливаем новую единицу измерения (например, Штуки)
НоваяЕдиница = Справочники.УпаковкиЕдиницыИзмерения.НайтиПоНаименованию("шт");
НоменклатураОбъект.ЕдиницаИзмерения = НоваяЕдиница;
НоменклатураОбъект.Записать();
Возврат НоменклатураОбъект.Ссылка;
КонецФункции
Вызов метода ОповеститьОбИзменении(НоменклатураСсылка) или ОповеститьОбИзменении(Тип("СправочникСсылка.Номенклатура")) принудительно очищает клиентский кэш для указанной ссылки, после чего связанные поля в табличных частях мгновенно обновляют свои представления.
Рассмотрим другую частую практическую задачу: когда базовая единица в номенклатуре была изменена (например, с тонн на килограммы), и требуется обработать существующие документы, переведя их на использование упаковок (тонн), чтобы не исказить исходные объемы поставок.
Если просто изменить базовую единицу в карточке, в уже проведенных документах реквизит Количество останется неизменным, что приведет к занижению остатков в 1000 раз. Разберем по шагам алгоритм правильного пересчета строк документа:
Упаковка ссылкой на созданную упаковку.КоличествоУпаковок (количество тонн).Количество с учетом коэффициента упаковки.Посмотрим, как выглядит программный алгоритм групповой обработки документов:
Для Каждого Стр Из ОбъектДокумента.Товары Цикл
// Проверяем номенклатуру, у которой базовой единицей стали килограммы
Если Стр.Номенклатура.ЕдиницаИзмерения.Наименование = "кг" Тогда
// Находим упаковку "тонна" для текущей номенклатуры
УпаковкаТонна = Справочники.УпаковкиЕдиницыИзмерения.НайтиПоРеквизиту(
"Владелец",
Стр.Номенклатура
);
Если ЗначениеЗаполнено(УпаковкаТонна) Тогда
Стр.Упаковка = УпаковкаТонна;
// Если в строке было указано 5 (тонн), оставляем это значение для упаковок
// А базовое количество пересчитываем: 5 * 1000 = 5000 кг
Стр.Количество = Стр.КоличествоУпаковок * УпаковкаТонна.Коэффициент;
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Записываем и проводим документ для обновления движений в регистрах
ОбъектДокумента.Записать(РежимЗаписиДокумента.Проведение);
Изменение базовой единицы хранения остатков у номенклатуры, по которой уже зарегистрированы движения в закрытых периодах, несет серьезные учетные риски: