Как перенести значения из комментария в дополнительный реквизит номенклатуры в 1С?

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

При переносе данных между различными конфигурациями (например, при переходе из 1С:Бухгалтерия 3.0 в 1С:Управление торговлей 11) или при реорганизации ведения учета часто возникает необходимость перенести информацию из стандартного текстового поля в дополнительные реквизиты. Типичный пример — перенос данных из стандартного поля Комментарий справочника Номенклатура в дополнительный реквизит (например, под названием "Кросс" или "Аналог").

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

В чем сложность работы с дополнительными реквизитами?

Для начала проанализируем, почему не работает интуитивный код вроде Объект.Кросс = Объект.Комментарий. В отличие от обычных реквизитов, которые создаются непосредственно в конфигураторе и физически представляют собой колонки в таблице базы данных, дополнительные реквизиты БСП устроены иначе.

Все дополнительные реквизиты хранятся в специальной табличной части справочника, которая называется ДополнительныеРеквизиты. Эта табличная часть имеет три основных реквизита:

  1. Свойство — ссылка на элемент плана видов характеристик ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.
  2. Значение — значение свойства (может быть строкой, числом, булево или ссылкой на специальный справочник значений).
  3. ТекстоваяСтрока — строковое представление, используемое для быстрого вывода информации.

Попытка обратиться напрямую к свойству объекта по его имени (например, Объект.Кросс) закономерно вызовет ошибку "Поле объекта не обнаружено", поскольку конфигурация физически ничего не знает о существовании такого поля в структуре метаданных справочника Номенклатура.

Разбор типичной ошибки при написании алгоритма

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


ДопРекв = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Кросс");
СтрокаТЧ = Объект.ДополнительныеРеквизиты.Найти(ДопРекв, "Свойство");
СтрокаТЧ.Значение = Объект.Комментарий;

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

Метод Найти() для табличной части ищет уже существующую строку. Если для конкретного товара дополнительный реквизит "Кросс" ранее никогда не заполнялся (а при массовом переносе это именно так), то строка в табличной части просто отсутствует. Метод возвращает значение Неопределено. Попытка обратиться к свойству .Значение у переменной со значением Неопределено и приводит к аварийному завершению работы алгоритма.

Правильный алгоритм: Сценарий А (Дополнительный реквизит имеет тип "Строка")

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


// Шаг 1. Находим целевой дополнительный реквизит по наименованию
СвойствоКросс = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Кросс");

Если ЗначениеЗаполнено(СвойствоКросс) Тогда
    
    // Шаг 2. Ищем существующую строку в табличной части номенклатуры
    СтрокаТЧ = Объект.ДополнительныеРеквизиты.Найти(СвойствоКросс, "Свойство");
    
    Если ЗначениеЗаполнено(Объект.Комментарий) Тогда
        // Шаг 3. Если строки нет, создаем новую
        Если СтрокаТЧ = Неопределено Тогда
            СтрокаТЧ = Объект.ДополнительныеРеквизиты.Добавить();
            СтрокаТЧ.Свойство = СвойствоКросс;
        КонецЕсли;
        
        // Шаг 4. Записываем значение комментария
        СтрокаТЧ.Значение = Объект.Комментарий;
    Иначе
        // Шаг 5. Очистка (опционально): если комментарий пуст, а строка в ТЧ была — удаляем её
        Если СтрокаТЧ <> Неопределено Тогда
            Объект.ДополнительныеРеквизиты.Удалить(СтрокаТЧ);
        КонецЕсли;
    КонецЕсли;
    
Иначе
    ВызватьИсключение "Дополнительный реквизит 'Кросс' не найден в системе! Убедитесь в правильности написания имени.";
КонецЕсли;

Правильный алгоритм: Сценарий Б (Дополнительный реквизит имеет тип "Дополнительное значение")

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

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


СвойствоКросс = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Кросс");

Если ЗначениеЗаполнено(СвойствоКросс) Тогда
    СтрокаТЧ = Объект.ДополнительныеРеквизиты.Найти(СвойствоКросс, "Свойство");
    
    Если ЗначениеЗаполнено(Объект.Комментарий) Тогда
        
        // Поиск существующего значения среди подчиненных элементов свойства "Кросс"
        ЗначениеРеквизита = Справочники.ЗначенияСвойствОбъектов.НайтиПоНаименованию(Объект.Комментарий, Истина, , СвойствоКросс);
        
        // Если такого значения еще нет в списке — создаем его программно
        Если Не ЗначениеЗаполнено(ЗначениеРеквизита) Тогда
            НовыйЭлемент = Справочники.ЗначенияСвойствОбъектов.СоздатьЭлемент();
            НовыйЭлемент.Владелец = СвойствоКросс;
            НовыйЭлемент.Наименование = Объект.Комментарий;
            НовыйЭлемент.Записать();
            ЗначениеРеквизита = НовыйЭлемент.Ссылка;
        КонецЕсли;
        
        Если СтрокаТЧ = Неопределено Тогда
            СтрокаТЧ = Объект.ДополнительныеРеквизиты.Добавить();
            СтрокаТЧ.Свойство = СвойствоКросс;
        КонецЕсли;
        
        СтрокаТЧ.Значение = ЗначениеРеквизита;
    Иначе
        Если СтрокаТЧ <> Неопределено Тогда
            Объект.ДополнительныеРеквизиты.Удалить(СтрокаТЧ);
        КонецЕсли;
    КонецЕсли;
КонецЕсли;

Пошаговая инструкция по выполнению переноса в 1С

Для выполнения этой задачи нам не обязательно привлекать программиста для написания отдельной внешней обработки. Мы можем воспользоваться встроенным инструментом "Групповое изменение реквизитов", который есть практически во всех современных конфигурациях на БСП.

  1. Перейдем в раздел Администрирование — Обслуживание — Корректировка данных и откроем Групповое изменение реквизитов.
  2. В поле "Изменять" выберем справочник Номенклатура. При необходимости настроим фильтр (например, выберем только определенную группу товаров).
  3. Нажмем кнопку Дополнительные параметры и установим флажок Показывать служебные реквизиты (это откроет нам доступ к выполнению произвольного алгоритма).
  4. В верхней части формы появится вкладка или кнопка Произвольный алгоритм. Перейдем туда.
  5. Вставим один из разработанных выше вариантов кода (в зависимости от типа вашего реквизита "Кросс"). Обратите внимание, что в контексте работы групповой обработки переменная Объект уже определена и инициализирована для каждой обрабатываемой строки номенклатуры.
  6. Нажмем кнопку Выполнить. Программа последовательно обойдет все выбранные элементы номенклатуры, обработает их табличные части и автоматически запишет изменения.

Важное примечание по оптимизации: Если в вашей базе десятки тысяч наименований номенклатуры, поиск свойства по наименованию ПланыВидовХарактеристик...НайтиПоНаименованию() внутри цикла обработки каждого объекта может снизить производительность. Для ускорения процесса при работе с очень большими базами данных рекомендуется кэшировать ссылку на дополнительный реквизит перед циклом или использовать специализированные внешние обработки с оптимизированными SQL-запросами.

← На главную