Почему не обновляется единица измерения при программном изменении номенклатуры и как это исправить

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

При разработке дополнительного функционала в конфигурации 1С:Управление торговлей 11 (а также в 1С:ERP и 1С:Комплексная автоматизация) разработчики часто сталкиваются с нестандартным поведением управляемых форм: после программного изменения и записи реквизита ЕдиницаИзмерения в справочнике Номенклатура, в табличной части открытого документа (например, Реализация товаров и услуг) продолжает отображаться старая единица измерения. Если же открыть карточку номенклатуры интерактивно и нажать «Записать и закрыть», данные в строке документа обновляются корректно.

Разберем детально, почему так происходит, как устроена архитектура хранения единиц в современных конфигурациях и какими методами платформы решается эта задача.

Архитектура хранения единиц измерения и упаковок в УТ 11

Для понимания сути проблемы проанализируем, как система хранит и отображает количественные показатели в табличных частях документов:

  1. Базовая единица хранения: Учет остатков и движений в регистрах накопления (таких как ТоварыНаСкладах, СебестоимостьТоваров) всегда ведется строго в базовой единице хранения остатков, заданной в реквизите ЕдиницаИзмерения карточки номенклатуры.
  2. Реквизиты табличной части: В табличной части Товары документов товародвижения отдельного реквизита «Единица измерения» физически нет. Отображаемая пользователю колонка является либо связанным реквизитом ссылки Номенклатура.ЕдиницаИзмерения, либо представлением выбранной упаковки.
  3. Упаковки и коэффициенты: В строке документа хранятся реквизиты:
    1. Упаковка — ссылка на справочник УпаковкиЕдиницыИзмерения. Если значение не заполнено, операция выполняется в базовой единице.
    2. КоличествоУпаковок — видимое пользователю количество в выбранных единицах или упаковках.
    3. Количество — количество в базовой единице хранения, рассчитываемое как КоличествоУпаковок * КоэффициентУпаковки.

Причина проблемы: кэширование ссылочных данных на клиенте

Когда мы программно модифицируем и записываем объект на сервере (например, меняем реквизит ЕдиницаИзмерения у элемента СправочникСсылка.Номенклатура), клиентское приложение об этом не знает. Платформа 1С:Предприятие 8.3 активно кэширует представления ссылок и реквизиты объектов на стороне клиента для снижения нагрузки на сеть и сервер.

При интерактивной записи элемента из его формы платформа автоматически отправляет системное оповещение об изменении объекта, сбрасывая кэш во всех открытых формах сеанса. При чисто программной записи на сервере такое оповещение не генерируется, и открытые формы продолжают брать старые значения из локального кэша.

Решение 1: Оповещение клиентского приложения об изменении ссылки

Чтобы заставить управляемую форму перечитать измененные ссылочные данные и отобразить актуальную единицу измерения, после серверной записи объекта необходимо на клиенте вызвать процедуру ОповеститьОбИзменении().

Посмотрим на пример реализации обработчика команды на клиенте:


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

&НаСервереБезКонтекста
Функция ИзменитьЕдиницуНоменклатурыНаСервере(НоменклатураСсылка)
    
    НоменклатураОбъект = НоменклатураСсылка.ПолучитьОбъект();
    
    // Устанавливаем новую единицу измерения (например, Штуки)
    НоваяЕдиница = Справочники.УпаковкиЕдиницыИзмерения.НайтиПоНаименованию("шт");
    НоменклатураОбъект.ЕдиницаИзмерения = НоваяЕдиница;
    
    НоменклатураОбъект.Записать();
    
    Возврат НоменклатураОбъект.Ссылка;
    
КонецФункции

Вызов метода ОповеститьОбИзменении(НоменклатураСсылка) или ОповеститьОбИзменении(Тип("СправочникСсылка.Номенклатура")) принудительно очищает клиентский кэш для указанной ссылки, после чего связанные поля в табличных частях мгновенно обновляют свои представления.

Решение 2: Корректная программная смена единиц и упаковок в документах

Рассмотрим другую частую практическую задачу: когда базовая единица в номенклатуре была изменена (например, с тонн на килограммы), и требуется обработать существующие документы, переведя их на использование упаковок (тонн), чтобы не исказить исходные объемы поставок.

Если просто изменить базовую единицу в карточке, в уже проведенных документах реквизит Количество останется неизменным, что приведет к занижению остатков в 1000 раз. Разберем по шагам алгоритм правильного пересчета строк документа:

  1. Убедимся, что для номенклатуры создана соответствующая упаковка (например, «т» с коэффициентом 1000 к базовой единице «кг»).
  2. Заполним в строке табличной части реквизит Упаковка ссылкой на созданную упаковку.
  3. Сохраним исходное значение в КоличествоУпаковок (количество тонн).
  4. Пересчитаем базовое поле Количество с учетом коэффициента упаковки.

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


Для Каждого Стр Из ОбъектДокумента.Товары Цикл
    
    // Проверяем номенклатуру, у которой базовой единицей стали килограммы
    Если Стр.Номенклатура.ЕдиницаИзмерения.Наименование = "кг" Тогда
        
        // Находим упаковку "тонна" для текущей номенклатуры
        УпаковкаТонна = Справочники.УпаковкиЕдиницыИзмерения.НайтиПоРеквизиту(
            "Владелец", 
            Стр.Номенклатура
        );
        
        Если ЗначениеЗаполнено(УпаковкаТонна) Тогда
            Стр.Упаковка = УпаковкаТонна;
            
            // Если в строке было указано 5 (тонн), оставляем это значение для упаковок
            // А базовое количество пересчитываем: 5 * 1000 = 5000 кг
            Стр.Количество = Стр.КоличествоУпаковок * УпаковкаТонна.Коэффициент;
        КонецЕсли;
        
    КонецЕсли;
    
КонецЦикла;

// Записываем и проводим документ для обновления движений в регистрах
ОбъектДокумента.Записать(РежимЗаписиДокумента.Проведение);

Важные предостережения при смене единиц измерения

Изменение базовой единицы хранения остатков у номенклатуры, по которой уже зарегистрированы движения в закрытых периодах, несет серьезные учетные риски:

  1. Искажение остатков прошлых периодов: Регистры накопления хранят только числовые значения количества. Смена единицы задним числом приведет к тому, что старые движения будут интерпретироваться в новых единицах.
  2. Ошибки расчета себестоимости: Процедура закрытия месяца и расчет партий могут выдать некорректные финансовые результаты из-за несоответствия цен и объемов.
  3. Рекомендуемый подход: Если по позиции уже велся активный учет, безопаснее создать новую карточку номенклатуры с правильной базовой единицей, перенести остатки документом инвентаризации/корректировки, а старую позицию пометить на удаление или установить ей признак запрета к использованию.
← На главную