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