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

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

При разработке специализированных рабочих мест и внешних обработок в типовых конфигурациях «1С:Комплексная автоматизация 2» и «1С:ERP Управление предприятием» разработчики часто сталкиваются с задачей пакетного редактирования весогабаритных характеристик (ВГХ): веса единицы, длины или веса погонного метра. Если эти данные отображаются в обработке на основании среза последних регистра сведений ЦеныНоменклатуры, возникает путаница: что именно необходимо изменять — движения регистра цен или реквизиты справочников, и почему при вызове метода Записать() новые значения могут внезапно очищаться.

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

Где физически хранятся данные: разделяем регистр цен и справочники

Рассмотрим типовую структуру регистра сведений ЦеныНоменклатуры. Данный регистр является подчиненным регистратору (документу УстановкаЦенНоменклатуры), имеет периодичность в пределах секунды и содержит:

  1. Измерения: Номенклатура, Характеристика, ВидЦены.
  2. Ресурсы: Цена, Упаковка, Валюта.

Когда в запросе к срезу последних мы обращаемся через точку к полям ЦеныНоменклатурыСрезПоследних.Упаковка.Числитель или ЦеныНоменклатурыСрезПоследних.Номенклатура.ДлинаЗнаменатель, мы читаем не ресурсы самого регистра сведений, а реквизиты связанных справочников — Справочник.УпаковкиЕдиницыИзмерения и Справочник.Номенклатура.

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

Почему очищаются реквизиты при вызове метода Записать()

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


&НаСервере
Процедура НоменклатураСРеквизитамиВесПогонногоМетраПриИзмененииНаСервере(ВесПогонногоМетра, НоменклатураСсылка)
    
    ЭлемСпр = НоменклатураСсылка.ПолучитьОбъект();
    ЭлемСпр.ДлинаЗнаменатель = ВесПогонногоМетра;
    ЭлемСпр.Записать();
    
КонецПроцедуры

При выполнении этого кода значение реквизита ДлинаЗнаменатель успешно устанавливается в памяти объекта, однако сразу после строки ЭлемСпр.Записать() в базе данных реквизит оказывается пустым или равным нулю. Выясним причину такого поведения.

В типовых конфигурациях линейные размеры, площадь, объем и весогабаритные параметры хранятся в виде рациональных дробей (парных реквизитов: ДлинаЧислитель / ДлинаЗнаменатель, ВесЧислитель / ВесЗнаменатель). Это предотвращает накопление погрешностей округления при пересчете единиц.

В модуле объекта Справочник.Номенклатура в событии ПередЗаписью вызывается типовая процедура проверки:

Справочники.Номенклатура.ОтработатьЛогикуСвязиРеквизитов(ЭтотОбъект);

Эта процедура осуществляет строгий перекрестный контроль согласованности данных:

  1. Проверка признаков учета: включено ли для данного вида номенклатуры использование мерных характеристик (длина, площадь, вес). Если признак не установлен, дробные коэффициенты принудительно обнуляются.
  2. Проверка целостности дроби: если задан ДлинаЗнаменатель, но ДлинаЧислитель равен нулю или не заполнен, логика считает дробь некорректной и сбрасывает оба реквизита.
  3. Связь с базовыми единицами измерения: соответствие единиц хранения остатков и единиц для отчетов заданным коэффициентам пересчета.

Вариант 1. Корректное заполнение взаимосвязанных реквизитов (Рекомендуемый)

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


&НаСервере
Процедура ОбновитьПараметрыНоменклатурыНаСервере(НоменклатураСсылка, Длина, ВесПогонногоМетра)
    
    ОбъектНоменклатура = НоменклатураСсылка.ПолучитьОбъект();
    
    // Заполняем оба компонента рациональной дроби
    ОбъектНоменклатура.ДлинаЧислитель = ?(ЗначениеЗаполнено(Длина), Длина, 1);
    ОбъектНоменклатура.ДлинаЗнаменатель = ВесПогонногоМетра;
    
    // Если меняется упаковка, получаем и перезаписываем объект упаковки
    // Для упаковок Числитель и Знаменатель отвечают за коэффициент пересчета к базовой единице
    
    Попытка
        ОбъектНоменклатура.Записать();
    Исключение
        ВызватьИсключение "Ошибка при записи номенклатуры: " + ОписаниеОшибки();
    КонецПопытки;
    
КонецПроцедуры

Если требуется скорректировать вес единицы (реквизит Числитель упаковки), разберем работу с подчиненным справочником УпаковкиЕдиницыИзмерения:


&НаСервере
Процедура ОбновитьПараметрыУпаковкиНаСервере(УпаковкаСсылка, ВесЕдиницы)
    
    Если Не ЗначениеЗаполнено(УпаковкаСсылка) Тогда
        Возврат;
    КонецЕсли;
    
    ОбъектУпаковка = УпаковкаСсылка.ПолучитьОбъект();
    ОбъектУпаковка.Числитель = ВесЕдиницы;
    
    // Знаменатель коэффициента упаковки не должен быть равен 0
    Если ОбъектУпаковка.Знаменатель = 0 Тогда
        ОбъектУпаковка.Знаменатель = 1;
    КонецЕсли;
    
    ОбъектУпаковка.Записать();
    
КонецПроцедуры

Вариант 2. Использование режима загрузки данных (ОбменДанными.Загрузка)

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


&НаСервере
Процедура ЗаписатьРеквизитыБезПроверок(НоменклатураСсылка, ВесПогонногоМетра, Длина)
    
    ЭлемСпр = НоменклатураСсылка.ПолучитьОбъект();
    
    // Отключаем обработчики ПередЗаписью, ПриЗаписи и валидацию реквизитов
    ЭлемСпр.ОбменДанными.Загрузка = Истина;
    
    ЭлемСпр.ДлинаЧислитель = Длина;
    ЭлемСпр.ДлинаЗнаменатель = ВесПогонногоМетра;
    
    ЭлемСпр.Записать();
    
КонецПроцедуры

Важно: рассмотрим риски использования режима ОбменДанными.Загрузка = Истина на рабочей базе:

  1. Рассинхронизация логистических расчетов: модули адресного хранения (WMS), расчет веса и объема грузомест в заказах на доставку опираются на согласованные параметры ВГХ. Запись некорректных дробей приведет к делению на ноль или неверному расчету вместимости ячеек.
  2. Нарушение регламентов блокировки: в типовых решениях реквизиты, влияющие на ранее проведенные документы, блокируются подсистемой «Разрешение редактирования реквизитов объектов». Прямая запись в обход проверок может нарушить сопоставимость складских отчетов за прошлые периоды.

Итоги

Посмотрим на ключевые выводы при работе с весогабаритными параметрами и ценами номенклатуры:

  1. Если в отчете или обработке данные получены из среза последних регистра ЦеныНоменклатуры, но являются реквизитами ссылок Номенклатура или Упаковка, изменять движения регистра не нужно — редактируются сами элементы справочников.
  2. При программной записи номенклатуры необходимо учитывать работу процедуры ОтработатьЛогикуСвязиРеквизитов и передавать полные пары дробных значений (Числитель и Знаменатель).
  3. Режим ОбменДанными.Загрузка = Истина позволяет обойти очистку полей, но должен применяться с осторожностью во избежание логических ошибок в расчете складских и логистических операций.
← На главную