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