При активной работе с торговыми каталогами в программе «1С:Управление торговлей, редакция 10.3» пользователи часто сталкиваются с ситуацией, когда после автоматической загрузки данных из внешних источников (например, сайтов, файлов Excel или XML) цены на характеристики товаров отображаются некорректно. Типичный симптом проблемы: в регистре сведений ЦеныНоменклатуры все записи присутствуют, но в форме списка справочника Номенклатура или в окне подбора цены видны только для части характеристик. При этом, если зайти в документ УстановкаЦенНоменклатуры, вручную перевыбрать «проблемную» характеристику и перепровести документ, цена сразу же начинает отображаться в справочнике.
В этой статье мы подробно проанализируем причины возникновения данной проблемы, разберем внутренние механизмы платформы «1С:Предприятие 8», которые приводят к такому поведению системы, и рассмотрим практические способы ее решения с примерами программного кода.
Для того чтобы понять, почему возникает эта ошибка, нам необходимо обратиться к архитектуре метаданных конфигурации. Справочник ХарактеристикиНоменклатуры является подчиненным по отношению к справочнику Номенклатура. Это означает, что у каждого элемента характеристики в базе данных существует обязательный стандартный реквизит Владелец, в котором хранится ссылка на конкретный товар.
Когда заполнение справочников и документов производится вручную интерактивно через интерфейс программы, платформа строго контролирует эту связь благодаря свойству поля ввода СвязьПоВладельцу. Вы физически не сможете выбрать для «Товара А» характеристику, которая принадлежит «Товару Б».
Однако при использовании программного заполнения (через внешние обработки загрузки данных) разработчики часто совершают логическую ошибку в алгоритмах поиска или создания характеристик. Рассмотрим типичные сценарии сбоя:
ХарактеристикиНоменклатуры просто по текстовому названию (например, «Красный» или «XL»). Если в базе данных уже существует характеристика «Красный», принадлежащая другому товару, обработка находит ее ссылку и подставляет в документ УстановкаЦенНоменклатуры для текущего товара. В результате в строке документа оказывается связка: Номенклатура = «Товар А», а ХарактеристикаНоменклатуры = «Красный (Владелец: Товар Б)».ХарактеристикиНоменклатуры разработчик забывает указать реквизит Владелец. Характеристика записывается в базу «сиротой», без привязки к конкретному товару.Теперь проанализируем, почему при этом данные двоятся: в регистре цены есть, а в справочнике их не видно.
Регистр сведений ЦеныНоменклатуры представляет собой плоскую таблицу базы данных. При проведении документа УстановкаЦенНоменклатуры платформа просто переносит значения из табличной части документа в измерения и ресурсы регистра. Для СУБД связка [Товар А] — [Характеристика от Товара Б] — [Цена] является абсолютно валидной записью. Платформа не выполняет автоматическую проверку соответствия владельца характеристики и номенклатуры при записи движений регистра.
Однако при выводе данных в форму списка справочника Номенклатура или в форму подбора товаров система динамически строит сложный запрос. Чтобы отобразить актуальные цены, платформа выполняет левое соединение (LEFT JOIN) списка «родных» характеристик текущего товара (у которых реквизит Владелец равен отображаемому товару) со срезом последних регистра сведений ЦеныНоменклатуры по двум полям одновременно: Номенклатура и Характеристика. Поскольку в регистре записана чужая характеристика (принадлежащая «Товару Б»), левое соединение не находит соответствия для «родной» характеристики «Товара А». В результате колонка с ценой в интерфейсе пользователя остается пустой.
Когда вы открываете документ УстановкаЦенНоменклатуры, заходите в ячейку с некорректной характеристикой и нажимаете кнопку выбора, платформа отрабатывает штатно. Срабатывает настроенное свойство связи по владельцу. Программа открывает список характеристик, автоматически отфильтрованный по значению в колонке Номенклатура текущей строки.
Интерактивно вы видите список «родных» характеристик товара. Выбирая визуально точно такую же строчку (например, «Размер 44»), вы на самом деле заменяете «чужую» ссылку (с неправильным владельцем) на правильную ссылку (где владельцем является текущий товар). После перепроведения документа в регистр сведений записываются корректные данные, и левое соединение в динамическом списке справочника начинает отрабатывать правильно.
Чтобы исключить появление подобных ошибок при последующих загрузках данных, необходимо внести изменения в код обработки, которая отвечает за импорт номенклатуры и цен. Разберем по шагам, как правильно реализовать поиск и создание характеристик.
При поиске существующей характеристики обязательно передавайте ссылку на номенклатуру-владельца в качестве четвертого параметра метода НайтиПоНаименованию. Рассмотрим пример кода:
// ПРИМЕР ПРАВИЛЬНОГО ПОИСКА ХАРАКТЕРИСТИКИ
// СсылкаНаНоменклатуру - ссылка на элемент справочника Номенклатура
// ИскомоеНаименование - текстовое представление характеристики (например, "Синий")
ХарактеристикаСсылка = Справочники.ХарактеристикиНоменклатуры.НайтиПоНаименованию(ИскомоеНаименование, Истина, , СсылкаНаНоменклатуру);
Если ХарактеристикаСсылка.Пустая() Тогда
// Если характеристика не найдена у данного владельца, создаем новую
НоваяХарактеристика = Справочники.ХарактеристикиНоменклатуры.СоздатьЭлемент();
НоваяХарактеристика.Наименование = ИскомоеНаименование;
НоваяХарактеристика.Владелец = СсылкаНаНоменклатуру; // Обязательное заполнение владельца!
Попытка
НоваяХарактеристика.Записать();
ХарактеристикаСсылка = НоваяХарактеристика.Ссылка;
Исключение
Сообщить("Не удалось записать характеристику: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
КонецЕсли;
Использование владельца в качестве контекста поиска гарантирует, что система найдет или создаст характеристику, принадлежащую именно импортируемому товару, исключая дублирование и привязку к чужим объектам.
Если некорректных документов в информационной базе скопилось много, исправлять их вручную интерактивно — слишком трудоемкий процесс. Для автоматического исправления существующих связей мы можем написать и запустить простую сервисную обработку.
Проанализируем алгоритм, который должна выполнять такая обработка:
УстановкаЦенНоменклатуры за период, в котором происходила некорректная загрузка данных.Товары.Номенклатура и реквизит Владелец у выбранной ХарактеристикиНоменклатуры.Ниже представлен пример программного кода для реализации такой корректирующей процедуры:
Процедура ИсправитьОшибкиВДокументахУстановкиЦен() Экспорт
// Выбираем проведенные документы за интересующий период
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| УстановкаЦен.Ссылка КАК Ссылка
|ИЗ
| Документ.УстановкаЦенНоменклатуры КАК УстановкаЦен
|ГДЕ
| УстановкаЦен.Проведен = ИСТИНА
| И УстановкаЦен.Дата МЕЖДУ &ДатаНачала И &ДатаКонца";
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(РабочаяДата)); // Настройте период под свои нужды
Запрос.УстановитьПараметр("ДатаКонца", КонецДня(РабочаяДата));
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Сообщить("Документы для исправления не найдены.");
Возврат;
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
ДокументОбъект = Выборка.Ссылка.ПолучитьОбъект();
Модифицирован = Ложь;
Для Каждого СтрокаТЧ Из ДокументОбъект.Товары Цикл
// Проверяем наличие характеристики в строке
Если НЕ ЗначениеЗаполнено(СтрокаТЧ.ХарактеристикаНоменклатуры) Тогда
Продолжить;
КонецЕсли;
ТекущийВладелец = СтрокаТЧ.ХарактеристикаНоменклатуры.Владелец;
// Если владелец характеристики не совпадает с номенклатурой в строке
Если ТекущийВладелец <> СтрокаТЧ.Номенклатура Тогда
НаименованиеХарактеристики = СтрокаТЧ.ХарактеристикаНоменклатуры.Наименование;
// Ищем родную характеристику для нашей номенклатуры
РоднаяХарактеристика = Справочники.ХарактеристикиНоменклатуры.НайтиПоНаименованию(
НаименованиеХарактеристики, Истина, , СтрокаТЧ.Номенклатура
);
// Если родной характеристики нет, создаем её
Если РоднаяХарактеристика.Пустая() Тогда
НовыйЭлемент = Справочники.ХарактеристикиНоменклатуры.СоздатьЭлемент();
НовыйЭлемент.Наименование = НаименованиеХарактеристики;
НовыйЭлемент.Владелец = СтрокаТЧ.Номенклатура;
Попытка
НовыйЭлемент.Записать();
РоднаяХарактеристика = НовыйЭлемент.Ссылка;
Сообщить("Создана недостающая характеристика: " + НаименованиеХарактеристики + " для " + СтрокаТЧ.Номенклатура);
Исключение
Сообщить("Ошибка создания характеристики: " + ОписаниеОшибки(), СтатусСообщения.Важное);
Продолжить;
КонецПопытки;
КонецЕсли;
// Производим замену ссылки на корректную
СтрокаТЧ.ХарактеристикаНоменклатуры = РоднаяХарактеристика;
Модифицирован = Истина;
КонецЕсли;
КонецЦикла;
// Если в документе были исправления, записываем и проводим его
Если Модифицирован Тогда
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Сообщить("Документ " + ДокументОбъект.Ссылка + " успешно исправлен и перепроведен.");
Исключение
Сообщить("Не удалось провести документ " + ДокументОбъект.Ссылка + ": " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Использование данной обработки позволит быстро привести базу данных в порядок. После выполнения процедуры все внутренние связи между номенклатурой, характеристиками и записями регистра сведений будут восстановлены, и цены корректно отобразятся в формах списков и подбора программы «1С:Управление торговлей 10.3».