При автоматизации ценообразования в конфигурациях на обычных формах (таких как «1С:Комплексная автоматизация 1.1», «1С:Управление торговлей 10.3» или «1С:Управление производственным предприятием 1.3») разработчики часто сталкиваются с неожиданным поведением формы документа УстановкаЦенНоменклатуры. После программного добавления строк и записи документа при его интерактивном открытии одна и та же номенклатурная позиция отображается в нескольких строках: в первой строке заполнена колонка одного типа цен, а во второй — другого.
Разберем архитектуру хранения цен в документе, выясним причину такого поведения формы и рассмотрим пошаговый алгоритм корректного программного заполнения.
Для понимания сути проблемы проанализируем, как устроен документ УстановкаЦенНоменклатуры изнутри:
Товары всегда хранится в «плоском» виде. Каждая отдельная комбинация номенклатуры, характеристики и конкретного типа цены представляет собой самостоятельную физическую строку.ТаблицаЦен), где каждому выбранному в документе типу цен соответствует отдельная колонка.ИндексСтрокиТаблицыЦен: Чтобы форма документа поняла, какие физические строки табличной части Товары относятся к одной позиции номенклатуры и должны отображаться в одной строке на экране, используется реквизит ИндексСтрокиТаблицыЦен. Если это поле не заполнено или заполнено разными значениями, форма отрисует каждую запись отдельной строкой.Рассмотрим, как правильно организовать программное заполнение табличных частей ТипыЦен и Товары. Главное правило — присваивать одинаковое значение ИндексСтрокиТаблицыЦен всем физическим строкам одной номенклатурной позиции.
Посмотрим на пример кода:
// Получаем объект документа по ссылке
ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
// 1. Очищаем или заполняем список типов цен
ТипЦеныОтпускная = Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Отпускная");
ТипЦеныСебестоимость = Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Себестоимость");
// Добавляем типы цен в ТЧ ТипыЦен, если их там еще нет
Если ДокОбъект.ТипыЦен.Найти(ТипЦеныОтпускная, "ТипЦен") = Неопределено Тогда
НовТип = ДокОбъект.ТипыЦен.Добавить();
НовТип.ТипЦен = ТипЦеныОтпускная;
КонецЕсли;
Если ДокОбъект.ТипыЦен.Найти(ТипЦеныСебестоимость, "ТипЦен") = Неопределено Тогда
НовТип = ДокОбъект.ТипыЦен.Добавить();
НовТип.ТипЦен = ТипЦеныСебестоимость;
КонецЕсли;
// 2. Определяем сквозной индекс для номенклатурной позиции
// Если документ уже был частично заполнен, берем следующий максимальный индекс
ТекущийИндекс = 0;
Для Каждого Стр Из ДокОбъект.Товары Цикл
Если Стр.ИндексСтрокиТаблицыЦен > ТекущийИндекс Тогда
ТекущийИндекс = Стр.ИндексСтрокиТаблицыЦен;
КонецЕсли;
КонецЦикла;
ТекущийИндекс = ТекущийИндекс + 1;
// 3. Добавляем строки для номенклатуры по каждому типу цен с единым индексом
// Строка 1: Себестоимость
СтрТовар1 = ДокОбъект.Товары.Добавить();
СтрТовар1.Номенклатура = ВыбраннаяНоменклатура;
СтрТовар1.ТипЦен = ТипЦеныСебестоимость;
СтрТовар1.Цена = 100;
СтрТовар1.Валюта = ТипЦеныСебестоимость.ВалютаЦены;
СтрТовар1.ЕдиницаИзмерения = ВыбраннаяНоменклатура.ЕдиницаХраненияОстатков;
СтрТовар1.СпособРасчетаЦены = Перечисления.СпособыРасчетаЦены.ЗадаетсяВручную;
СтрТовар1.ИндексСтрокиТаблицыЦен = ТекущийИндекс;
// Строка 2: Отпускная цена для той же позиции
СтрТовар2 = ДокОбъект.Товары.Добавить();
СтрТовар2.Номенклатура = ВыбраннаяНоменклатура;
СтрТовар2.ТипЦен = ТипЦеныОтпускная;
СтрТовар2.Цена = 150;
СтрТовар2.Валюта = ТипЦеныОтпускная.ВалютаЦены;
СтрТовар2.ЕдиницаИзмерения = ВыбраннаяНоменклатура.ЕдиницаХраненияОстатков;
СтрТовар2.СпособРасчетаЦены = Перечисления.СпособыРасчетаЦены.ЗадаетсяВручную;
СтрТовар2.ИндексСтрокиТаблицыЦен = ТекущийИндекс;
// Записываем документ
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
При программном создании строк в документе УстановкаЦенНоменклатуры важно не ограничиваться только полями Номенклатура и Цена. Проанализируем сопутствующие реквизиты, незаполнение которых может привести к ошибкам проведения или некорректным движениям в регистре сведений ЦеныНоменклатуры:
Валюта — должна соответствовать валюте, указанной в справочнике ТипыЦенНоменклатуры для данного типа цены (ТипЦен.ВалютаЦены).ЕдиницаИзмерения — как правило, заполняется базовой единицей или единицей хранения остатков (Номенклатура.ЕдиницаХраненияОстатков). Если единица не заполнена, при подборе цен в документах реализации расчет сумм будет выполняться неверно.СпособРасчетаЦены — значение перечисления СпособыРасчетаЦены (например, ЗадаетсяВручную или ПоДинамическомуПроценту).ПроцентСкидкиНаценки — заполняется, если тип цены рассчитывается динамически относительно базового типа.Когда требуется загрузить большой массив номенклатуры с разными типами цен, удобно предварительно сгруппировать исходные данные. Разберем шаблон алгоритма групповой загрузки:
// Предположим, у нас есть ТаблицаДанных с колонками: Номенклатура, ТипЦен, Цена
// Сворачиваем исходную таблицу для получения уникального списка позиций
ТаблицаПозиций = ТаблицаДанных.Скопировать();
ТаблицаПозиций.Свернуть("Номенклатура");
Индекс = 1;
Для Каждого СтрПозиция Из ТаблицаПозиций Цикл
// Находим все цены для текущей номенклатуры
Отбор = Новый Структура("Номенклатура", СтрПозиция.Номенклатура);
СтрокиЦен = ТаблицаДанных.НайтиСтроки(Отбор);
Для Каждого СтрЦена Из СтрокиЦен Цикл
НоваяСтрока = ДокОбъект.Товары.Добавить();
НоваяСтрока.Номенклатура = СтрЦена.Номенклатура;
НоваяСтрока.ТипЦен = СтрЦена.ТипЦен;
НоваяСтрока.Цена = СтрЦена.Цена;
НоваяСтрока.Валюта = СтрЦена.ТипЦен.ВалютаЦены;
НоваяСтрока.ЕдиницаИзмерения = СтрЦена.Номенклатура.ЕдиницаХраненияОстатков;
НоваяСтрока.СпособРасчетаЦены = Перечисления.СпособыРасчетаЦены.ЗадаетсяВручную;
// Присваиваем один и тот же индекс для всех цен этой номенклатуры
НоваяСтрока.ИндексСтрокиТаблицыЦен = Индекс;
КонецЦикла;
Индекс = Индекс + 1;
КонецЦикла;
Соблюдение правил заполнения реквизита ИндексСтрокиТаблицыЦен гарантирует, что визуальное представление документа в интерфейсе пользователя будет полностью соответствовать типовой логике работы конфигурации.