Как программно заполнить документ «Установка цен номенклатуры» несколькими типами цен для каждого товара?

Программист 1С v8.3 (Обычные формы) 1С:Комплексная автоматизация Управленческий учет Торговля и дистрибуция
← На главную

При автоматизации ценообразования в типовых конфигурациях 1С (таких как Управление торговлей 10.3, Комплексная автоматизация 1.1 или Управление производственным предприятием 1.3) разработчики часто сталкиваются с трудностями при программном создании документа УстановкаЦенНоменклатуры. В пользовательском интерфейсе цены отображаются в удобной матричной форме, где каждой номенклатуре соответствует одна строка, а цены разнесены по отдельным колонкам. Однако физическая структура хранения данных в базе 1С устроена совершенно иначе.

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

Архитектура документа и логика хранения данных

Проанализируем структуру объекта в конфигураторе. Документ содержит две ключевые табличные части:

  1. ТипыЦен — список всех видов цен, значения которых регистрируются данным документом (например, Базовая цена поставщика, Дилерская, Розничная).
  2. Товары — строки с конкретными позициями номенклатуры и числовыми значениями цен.

Главная особенность заключается в том, что табличная часть Товары хранится в базе данных линейно. Реквизит ТипЦен является обязательным полем каждой строки табличной части Товары. Это означает, что если для одного товара регистрируется три вида цен, в табличную часть Товары необходимо добавить три отдельные строки с одинаковой номенклатурой, но разными ссылками на типы цен.

Если внутри цикла по типам цен не создавать новую строку методом Добавить(), а просто перезаписывать значения в одной и той же строке, документ сохранит только последнюю обработанную цену, затерев все предыдущие.

Назначение реквизита «ИндексСтрокиТаблицыЦен»

Обратим внимание на специальный реквизит табличной части Товары — ИндексСтрокиТаблицыЦен. Этот реквизит служит целочисленным ключом группировки строк для визуальной сборки таблицы на экранной форме документа:

  1. Все строки табличной части Товары, относящиеся к одной номенклатурной позиции (и одной характеристике), должны иметь одинаковый индекс (начиная с 0 или 1).
  2. При переходе к следующему товару значение индекса увеличивается на 1.

Важно: При интерактивной работе в форме документа этот индекс рассчитывается автоматически платформой при записи. Однако при чисто программном создании документа в фоновом режиме или внешней обработке модуль объекта в типовых конфигурациях обычных форм не пересчитывает данный индекс. Если оставить реквизит ИндексСтрокиТаблицыЦен незаполненным, движения по регистрам могут сформироваться успешно, но при открытии документа пользователем форма рухнет с ошибкой либо отобразит строки ступенчатой «лесенкой».

Обязательные сопутствующие реквизиты

Чтобы документ корректно провелся по регистру сведений ЦеныНоменклатуры, недостаточно заполнить только номенклатуру, тип цены и сумму. Обязательно требуется указывать следующие поля в каждой строке табличной части Товары:

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

Оптимизация: избавляемся от поиска в циклах

Типичной ошибкой начинающих разработчиков является вызов метода НайтиПоНаименованию() внутри циклов по строкам исходных данных. Это генерирует сотни и тысячи лишних запросов к СУБД, сильно замедляя выполнение алгоритма.

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

Пошаговый пример правильного программного кода

Посмотрим на практическую реализацию создания и проведения документа с корректным заполнением двух табличных частей и расчетом индексов строк:


ДокУЦ = Документы.УстановкаЦенНоменклатуры.СоздатьДокумент();
ДокУЦ.Дата = ТекущаяДата();
ДокУЦ.Ответственный = Пользователи.ТекущийПользователь();

// Шаг 1. Предварительно получаем ссылки на типы цен (без поиска внутри циклов)
ТипБазовая = Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Базовая цена поставщика");
ТипДилерская = Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Дилерская цена прихода");

// Шаг 2. Заполняем состав типов цен документа
СтрТЧВидЦены = ДокУЦ.ТипыЦен.Добавить();
СтрТЧВидЦены.ТипЦен = ТипБазовая;

СтрТЧВидЦены = ДокУЦ.ТипыЦен.Добавить();
СтрТЧВидЦены.ТипЦен = ТипДилерская;

// Шаг 3. Заполняем товары с вложенным циклом по типам цен
ИндексТовара = 0;

Для Каждого СтрД Из ТаблицаДанные1 Цикл
    
    // Получаем ссылку на номенклатуру (предполагается, что в таблице уже лежит ссылка)
    СпрНоменклатура = СтрД.Номенклатура;
    
    Для Каждого СтрТип Из ДокУЦ.ТипыЦен Цикл
        
        НоваяСтрока = ДокУЦ.Товары.Добавить();
        
        // Связываем строки одной позиции номенклатуры общим индексом
        НоваяСтрока.ИндексСтрокиТаблицыЦен = ИндексТовара;
        
        // Заполняем номенклатуру и тип цены
        НоваяСтрока.Номенклатура = СпрНоменклатура;
        НоваяСтрока.ТипЦен = СтрТип.ТипЦен;
        
        // Заполняем сопутствующие реквизиты
        НоваяСтрока.Валюта = СтрТип.ТипЦен.ВалютаЦены;
        НоваяСтрока.ЕдиницаИзмерения = СпрНоменклатура.ЕдиницаХраненияОстатков;
        НоваяСтрока.СпособРасчетаЦены = Перечисления.СпособыРасчетаЦены.ПоВходнойЦене;
        НоваяСтрока.ХарактеристикаНоменклатуры = Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка();
        
        // Определяем цену в зависимости от текущего типа цены
        Если СтрТип.ТипЦен = ТипБазовая Тогда
            НоваяСтрока.Цена = СтрД.БазоваяЦенаПостав;
        ИначеЕсли СтрТип.ТипЦен = ТипДилерская Тогда
            НоваяСтрока.Цена = СтрД.ДилерскаяЦенаПрихода;
        КонецЕсли;
        
    КонецЦикла;
    
    // Увеличиваем индекс для следующей номенклатурной позиции
    ИндексТовара = ИндексТовара + 1;
    
КонецЦикла;

// Шаг 4. Записываем и проводим документ
ДокУЦ.Записать(РежимЗаписиДокумента.Проведение);

Альтернативный способ: пакетная загрузка строк

Если требуется обработать большой массив номенклатуры (десятки тысяч строк), поэлементный вызов метода Добавить() может работать медленно. В таких сценариях рекомендуется предварительно сформировать отдельную ТаблицуЗначений с колонками, полностью повторяющими структуру табличной части Товары, и применить метод Загрузить():


// Предварительно заполняем ТЗДанных всеми необходимыми колонками
// (Номенклатура, ТипЦен, Цена, Валюта, ЕдиницаИзмерения, ИндексСтрокиТаблицыЦен)
ДокУЦ.Товары.Загрузить(ТЗДанных);
ДокУЦ.Записать(РежимЗаписиДокумента.Проведение);

Такой подход обеспечивает максимальное быстродействие при массовом формировании прайс-листов и загрузке номенклатурных цен из внешних источников.

← На главную