В практике разработчика 1С часто возникает задача массовой корректировки данных, например, заполнение пустых единиц измерения в справочнике Номенклатура после некорректного импорта из внешних систем. Казалось бы, задача тривиальна: прочитать файл Excel, найти ссылку на товар и записать новое значение в реквизит. Однако в современных конфигурациях, таких как 1С:Розница 2.3, Управление торговлей 11 или ERP 2, архитектура хранения данных о единицах измерения значительно сложнее, чем в старых решениях на обычных формах.
Разберем по шагам, с какими трудностями можно столкнуться при написании кода, проанализируем типичные ошибки и выясним, почему система может "молча" игнорировать ваши попытки записи данных.
Рассмотрим ситуацию: программист пытается загрузить единицы измерения (шт) для списка номенклатуры из табличного документа. Код выглядит следующим образом:
ЕИ = Справочники.БазовыеЕдиницыИзмерения.НайтиПоНаименованию("шт");
Для НомерСтроки = 1 По (КоличествоСтрок) Цикл
Ном = Справочники.Номенклатура.НайтиПоКоду(ТабДок.Область("R" + Формат(НомерСтроки, "ЧГ=0") + "C" + 1).Текст);
НомОбъект = Ном.ПолучитьОбъект();
НомОбъект.ЕдиницаИзмерения = ЕИ;
Попытка
НомОбъект.Записать();
Исключение
Сообщить(НомОбъект.Наименование + " " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Проанализируем ситуацию: у части товаров единица измерения заполняется, а у части — нет, при этом блок Исключение не срабатывает, и ошибки не выводятся. Это классический симптом того, что срабатывает внутренняя логика конфигурации в процедуре ПередЗаписью или ПриЗаписи, которая либо отменяет изменения, либо логика поиска не находит нужный объект, но не сообщает об этом.
Выясним главную причину, с которой столкнулся автор темы. В 1С:Розница 2.3 существует реквизит НаборУпаковок. Если этот реквизит заполнен, система переходит в режим использования упаковок. В этом случае ЕдиницаИзмерения (базовая единица по ОКЕИ) жестко связана с иерархией упаковок. Если у товара установлен конкретный набор упаковок, программная попытка изменить базовую единицу измерения может конфликтовать с уже настроенными коэффициентами в этом наборе.
Важный нюанс: если в карточке товара включено использование индивидуальных упаковок, то базовая единица становится фундаментом для всех расчетов коэффициентов. Изменение фундамента при наличии "надстроек" (упаковок) может быть заблокировано программными проверками в модуле объекта номенклатуры, чтобы не допустить нарушения логики учета (например, когда 1 коробка внезапно станет состоять не из 10 штук, а из 10 килограммов).
Посмотрим на строку поиска единицы измерения: Справочники.БазовыеЕдиницыИзмерения.НайтиПоНаименованию("шт"). Профессиональная разработка требует избегать поиска по наименованию в государственных классификаторах. Почему это плохо?
Рассмотрим правильный вариант поиска:
ЕИ = Справочники.БазовыеЕдиницыИзмерения.НайтиПоКоду("796");
Если ЕИ.Пустая() Тогда
// Если по коду не нашли, можно попробовать найти по международному сокращению или создать
// Но лучше убедиться, что классификатор заполнен
КонецЕсли;
В современных конфигурациях реализован механизм "Разрешение редактирования реквизитов объектов". Если у номенклатуры есть движения по регистрам (остатки, продажи), система блокирует изменение ключевых полей, таких как ЕдиницаИзмерения, ТипНоменклатуры или СтатусУказанияСерий.
При использовании метода Записать() в коде, если не приняты специальные меры, могут сработать проверки, которые просто "откатят" значение реквизита к исходному в момент выполнения подписок на события. Чтобы обойти эти проверки (если вы уверены в своих действиях), необходимо использовать флаг обмена данными.
Проанализируем исправленный и безопасный подход к решению задачи. Нам нужно не просто присвоить значение, но и гарантировать его запись, учитывая специфику "Розницы 2.3".
Всегда проверяйте, найдена ли ссылка функцией НайтиПоКоду или НайтиПоНаименованию. Если ссылка пустая, метод ПолучитьОбъект() вернет Неопределено или вызовет ошибку.
Для массового изменения реквизитов в существующих карточках используйте свойство ОбменДанными.Загрузка = Истина. Это отключает большинство проверок в модулях объекта и подписках, позволяя записать данные даже если по товару уже были движения. Внимание: используйте это осторожно, так как это может привести к несоответствию единиц измерения в документах прошлых периодов и текущих остатках.
Если вы меняете единицу измерения, убедитесь, что реквизит НаборУпаковок либо пуст, либо соответствует новой единице. В идеале, если набор упаковок заполнен, его нужно проанализировать или очистить.
Рассмотрим улучшенный пример кода:
// Предварительно найдем единицу измерения по коду ОКЕИ
СсылкаЕИ = Справочники.БазовыеЕдиницыИзмерения.НайтиПоКоду("796");
Если СсылкаЕИ.Пустая() Тогда
Сообщить("Единица измерения 'шт' (код 796) не найдена в классификаторе!");
Возврат;
КонецЕсли;
Для НомерСтроки = 1 По КоличествоСтрок Цикл
ТекстКода = ТабДок.Область("R" + Формат(НомерСтроки, "ЧГ=0") + "C1").Текст;
СсылкаНом = Справочники.Номенклатура.НайтиПоКоду(ТекстКода);
Если СсылкаНом.Пустая() Тогда
Сообщить("Товар с кодом " + ТекстКода + " не найден");
Продолжить;
КонецЕсли;
ОбъектНом = СсылкаНом.ПолучитьОбъект();
// Проверяем, не заполнено ли уже правильное значение
Если ОбъектНом.ЕдиницаИзмерения = СсылкаЕИ Тогда
Продолжить;
КонецЕсли;
ОбъектНом.ЕдиницаИзмерения = СсылкаЕИ;
// Если мешает НаборУпаковок, как в случае автора темы, его можно очистить
// (Только если это допустимо бизнес-логикой!)
// ОбъектНом.НаборУпаковок = Справочники.НаборыУпаковок.ПустаяСсылка();
// Включаем режим загрузки для обхода проверок защиты реквизитов
ОбъектНом.ОбменДанными.Загрузка = Истина;
Попытка
ОбъектНом.Записать();
Исключение
Сообщить("Ошибка записи товара '" + ОбъектНом.Наименование + "': " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Проанализируем, к чему может привести такое изменение в живой базе 1С:Розница. Единица измерения в карточке товара — это не просто текст, это ссылка, которая используется в:
Таким образом, мы выяснили, что причина "неудачных" записей в 1С:Розница 2.3 кроется во взаимосвязи объектов. Рекомендуем всегда проверять заполнение реквизитов НаборУпаковок и использовать ОбменДанными.Загрузка = Истина при массовых корректировках справочников, предварительно сделав резервную копию базы данных.