Как добавить колонку с розничной ценой в документ «Заказ поставщику» в 1С:Управление торговлей 11?

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

При оформлении заказов поставщикам менеджерам по закупкам часто необходимо сравнивать закупочные цены с текущими розничными ценами предприятия. Это позволяет оценить планируемую маржинальность и вовремя выявить удорожание товаров. Однако в типовом документе ЗаказПоставщику конфигурации «1С:Управление торговлей 11» колонка с розничной ценой отсутствуют по умолчанию.

В этой статье мы подробно разберем, как реализовать отображение и расчет розничной цены в табличной части «Товары», проанализируем архитектурные подходы и рассмотрим готовый программный код заполнения данных.

Постановка задачи и архитектурные особенности

Сформулируем основные требования к доработке:

  1. В табличную часть Товары документа ЗаказПоставщику необходимо добавить новую колонку Розничная цена.
  2. Значения должны получаться из регистра сведений ЦеныНоменклатуры по выбранному виду цен (например, «Розничная»).
  3. При расчете должны корректно учитываться характеристики номенклатуры, выбранные упаковки (коэффициенты пересчета) и валюта документа.

При проектировании важно учитывать особенности платформы «1С:Предприятие 8.3». Если колонка создается как реквизит формы, а не как реквизит объекта метаданных, ее значение существует только во время открытого сеанса работы с формой. При повторном открытии документа данные потребуется пересчитывать заново.

Способ 1. Заполнение колонки через команду формы с вызовом механизмов УТ 11

Рассмотрим вариант, при котором менеджер может выделить строки в документе и по кнопке запросить расчет розничной цены по требуемому виду цен. Для этого в форму документа добавляется команда и реквизит табличной части (или реквизит формы).

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

Шаг 1. Клиентская процедура обработки команды

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


&НаКлиенте
Процедура ЗаполнитьРозничныеЦеныВыделенныхСтрокПоВидуЦен(Команда)
    Если ПродажиКлиент.НеобходимоЗаполнениеЦенПоВидуЦен(Объект, "Товары", НСтр("ru='Товары'")) Тогда
        
        ДополнительныеПараметры = Новый Структура;
        ПродажиКлиент.ВыбратьВидЦен(
            Новый ОписаниеОповещения("ЗаполнитьРозничныеЦеныВыделенныхСтрокПоВидуЦенЗавершение", ЭтотОбъект, ДополнительныеПараметры),
            Объект.ЦенаВключаетНДС,
            Истина);
        
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ЗаполнитьРозничныеЦеныВыделенныхСтрокПоВидуЦенЗавершение(ВидЦен, ДополнительныеПараметры) Экспорт
    
    Если ЗначениеЗаполнено(ВидЦен) Тогда
        
        МассивСтрок = Новый Массив;
        Для Каждого ТекСтрока Из Элементы.Товары.ВыделенныеСтроки Цикл
            НайденнаяСтрока = Объект.Товары.НайтиПоИдентификатору(ТекСтрока);
            Если НайденнаяСтрока.КодСтроки = 0 Тогда
                МассивСтрок.Добавить(НайденнаяСтрока);
            КонецЕсли;
        КонецЦикла;
        
        ЦеныРассчитаны = ЗаполнитьРозничныеЦеныВыделенныхСтрокПоВидуЦенСервер(ВидЦен);
        ПродажиКлиент.ОповеститьОбОкончанииЗаполненияЦенПоВидуЦен(ЦеныРассчитаны, ВидЦен);
        
    КонецЕсли;
    
КонецПроцедуры

Шаг 2. Серверная логика получения и расчета цен

На сервере формируются параметры заполнения, а запрос к временным таблицам соединяет позиции документа с последними срезами регистра ЦеныНоменклатуры с учетом коэффициентов упаковок и курсов валют:


&НаСервере
Функция ЗаполнитьРозничныеЦеныВыделенныхСтрокПоВидуЦенСервер(ВидЦен) Экспорт
    
    МассивСтрок = Новый Массив;
    Для Каждого ТекСтрока Из Элементы.Товары.ВыделенныеСтроки Цикл
        НайденнаяСтрока = Объект.Товары.НайтиПоИдентификатору(ТекСтрока);
        МассивСтрок.Добавить(НайденнаяСтрока);
    КонецЦикла;
    
    НаборыВызовСервера.ДополнитьДоПолногоНабора(Объект.Товары, МассивСтрок, Истина);
    
    ПараметрыЗаполнения = Новый Структура();
    ПараметрыЗаполнения.Вставить("Дата", Объект.Дата);
    ПараметрыЗаполнения.Вставить("Валюта", Объект.Валюта);
    ПараметрыЗаполнения.Вставить("ВидЦены", ВидЦен);
    ПараметрыЗаполнения.Вставить("РасчитыватьНаборы", Истина);
    ПараметрыЗаполнения.Вставить("ЦенаВключаетНДС", Объект.ЦенаВключаетНДС);
    ПараметрыЗаполнения.Вставить("ПоляЗаполнения", "ЦенаРозница");
    
    ЦеныРассчитаны = ЗаполнитьРозничныеЦены(
        Объект.Товары, 
        МассивСтрок, 
        ПараметрыЗаполнения,
        Неопределено);
    
    ОбщегоНазначенияУТ.ЗаполнитьДубликатыЗависимыхРеквизитовВКоллекции(Объект.Товары, ЗависимыеРеквизиты());
    
    Возврат ЦеныРассчитаны;
    
КонецФункции 

Функция ЗаполнитьРозничныеЦены(ТабличнаяЧасть, ВыделенныеСтроки = Неопределено, ПараметрыЗаполнения, СтруктураДействий = Неопределено) Экспорт
    
    ЦеныЗаполнены = Ложь;
    Если ПараметрыЗаполнения = Неопределено Тогда
        Возврат ЦеныЗаполнены;
    КонецЕсли;
    
    ПараметрыЦ = Новый Структура("ПоляЗаполнения, КолонкиПоЗначению, ДругиеИменаКолонок", "ЦенаРозница", Новый Структура, Новый Структура);
    ОбщегоНазначенияУТКлиентСервер.ДополнитьСтруктуру(ПараметрыЦ, ПараметрыЗаполнения, Истина);
    
    Таблица = ОбщегоНазначенияУТ.ВыгрузитьТаблицуЗначений(
        ТабличнаяЧасть,
        ВыделенныеСтроки,
        "НомерСтроки, Номенклатура, Характеристика, Упаковка",
        ПараметрыЦ.КолонкиПоЗначению,
        ПараметрыЦ.ДругиеИменаКолонок);
        
    Запрос = Новый Запрос;    
    МВТ = Новый МенеджерВременныхТаблиц;
    Запрос.МенеджерВременныхТаблиц = МВТ;
    Запрос.УстановитьПараметр("Дата", ПараметрыЦ.Дата);
    Запрос.УстановитьПараметр("Валюта", ПараметрыЦ.Валюта);
    Запрос.УстановитьПараметр("Таблица", Таблица);
    Запрос.Текст = Запрос.Текст + ПолучитьТекстЗапросаВременнойТаблицыТоваров("втТаблицаТовары");
    
    Если ПараметрыЦ.Свойство("ВидЦены") Тогда
        Запрос.УстановитьПараметр("ВидЦены", ПараметрыЦ.ВидЦены);
        Запрос.Текст = Запрос.Текст + ПолучитьТекстЗапросаВременнойТаблицыЦен("втТаблицаЦены", "втТаблицаТовары", Истина);
        Запрос.Текст = Запрос.Текст + "
        |ВЫБРАТЬ
        |    втТаблицаТовары.НомерСтроки КАК НомерСтроки,
        |    втТаблицаЦены.ВидЦены КАК ВидЦены,
        |    втТаблицаЦены.Цена КАК ЦенаРозница
        |ИЗ
        |    втТаблицаТовары КАК втТаблицаТовары
        |        ЛЕВОЕ СОЕДИНЕНИЕ втТаблицаЦены КАК втТаблицаЦены
        |        ПО (втТаблицаЦены.НомерСтроки = втТаблицаТовары.НомерСтроки)";
    КонецЕсли;
    
    РезультатЗапроса = Запрос.Выполнить();
    Если РезультатЗапроса.Пустой() Тогда
        Возврат ЦеныЗаполнены;
    КонецЕсли;
    
    СтруктураЗаполнения = Новый Структура(ПараметрыЦ.ПоляЗаполнения);
    Выборка = РезультатЗапроса.Выбрать();
    Пока Выборка.Следующий() Цикл
        ЗаполнитьЗначенияСвойств(СтруктураЗаполнения, Выборка);
        СтрокаТЧ = ТабличнаяЧасть[Выборка.НомерСтроки - 1];
        ЗаполнитьЗначенияСвойств(СтрокаТЧ, СтруктураЗаполнения);
    КонецЦикла;
    
    Возврат Истина;
    
КонецФункции

Функция ПолучитьТекстЗапросаВременнойТаблицыТоваров(ИмяТаблицы)
    Возврат "
    |ВЫБРАТЬ
    |    Таблица.НомерСтроки КАК НомерСтроки,
    |    Таблица.Номенклатура КАК Номенклатура,
    |    Таблица.Характеристика КАК Характеристика,
    |    Таблица.Упаковка КАК Упаковка
    |ПОМЕСТИТЬ " + ИмяТаблицы + "
    |ИЗ
    |    &Таблица КАК Таблица
    |;
    |";
КонецФункции

Функция ПолучитьТекстЗапросаВременнойТаблицыЦен(ИмяТаблицы, ИмяВременнойТаблицыТоваров, ВидЦеныКакПараметр = Истина)
    ТекстЗапроса =
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |    ВременнаяТаблицаТовары.НомерСтроки КАК НомерСтроки,
    |    " + ?(ВидЦеныКакПараметр, "ЕСТЬNULL(ЦеныНоменклатурыСрезПоследних.ВидЦены, &ВидЦены)", "ЦеныНоменклатурыСрезПоследних.ВидЦены") + " КАК ВидЦены,
    |    ВЫБОР
    |        КОГДА ВременнаяТаблицаТовары.Упаковка <> ЗНАЧЕНИЕ(Справочник.УпаковкиЕдиницыИзмерения.ПустаяСсылка)
    |        ТОГДА &ТекстЗапросаКоэффициентУпаковки1
    |        ИНАЧЕ 1
    |    КОНЕЦ
    |    * ЕстьNULL(ЦеныНоменклатурыСрезПоследних.Цена, 0) / ЕстьNULL(&ТекстЗапросаКоэффициентУпаковки2, 1)
    |    * ВЫБОР
    |        КОГДА &Валюта <> ЦеныНоменклатурыСрезПоследних.Валюта
    |        ТОГДА ВЫБОР
    |                КОГДА ЕСТЬNULL(КурсыВалютыЦены.Кратность, 0) > 0 И ЕСТЬNULL(КурсыВалютыЦены.Курс, 0) > 0
    |                    И ЕСТЬNULL(КурсыВалюты.Кратность, 0) > 0 И ЕСТЬNULL(КурсыВалюты.Курс, 0) > 0
    |                ТОГДА (КурсыВалютыЦены.Курс * КурсыВалюты.Кратность) / (КурсыВалюты.Курс * КурсыВалютыЦены.Кратность)
    |                ИНАЧЕ 0
    |            КОНЕЦ
    |        ИНАЧЕ 1
    |    КОНЕЦ КАК Цена
    |ПОМЕСТИТЬ " + ИмяТаблицы + "
    |ИЗ
    | " + ИмяВременнойТаблицыТоваров + " КАК ВременнаяТаблицаТовары
    |ЛЕВОЕ СОЕДИНЕНИЕ
    |    РегистрСведений.ЦеныНоменклатуры.СрезПоследних(КОНЕЦПЕРИОДА(&Дата, ДЕНЬ), 
    |        ВидЦены = &ВидЦены И (Номенклатура, Характеристика) В (
    |            ВЫБРАТЬ ВременнаяТаблицаТовары.Номенклатура, ВременнаяТаблицаТовары.Характеристика
    |            ИЗ " + ИмяВременнойТаблицыТоваров + " КАК ВременнаяТаблицаТовары)
    |    ) КАК ЦеныНоменклатурыСрезПоследних
    |ПО ВременнаяТаблицаТовары.Номенклатура = ЦеныНоменклатурыСрезПоследних.Номенклатура
    |    И ВременнаяТаблицаТовары.Характеристика = ЦеныНоменклатурыСрезПоследних.Характеристика
    |ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют.СрезПоследних(&Дата, ) КАК КурсыВалютыЦены
    |ПО ЦеныНоменклатурыСрезПоследних.Валюта = КурсыВалютыЦены.Валюта
    |ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют.СрезПоследних(&Дата, Валюта = &Валюта) КАК КурсыВалюты
    |ПО Истина
    |;
    |";
    
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекстЗапросаКоэффициентУпаковки1",
        Справочники.УпаковкиЕдиницыИзмерения.ТекстЗапросаКоэффициентаУпаковки("ВременнаяТаблицаТовары.Упаковка", "ВременнаяТаблицаТовары.Номенклатура"));
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекстЗапросаКоэффициентУпаковки2",
        Справочники.УпаковкиЕдиницыИзмерения.ТекстЗапросаКоэффициентаУпаковки("ЦеныНоменклатурыСрезПоследних.Упаковка", "ЦеныНоменклатурыСрезПоследних.Номенклатура"));
            
    Возврат ТекстЗапроса;
КонецФункции

Способ 2. Программирование интерфейса без снятия формы с поддержки (БСП и расширения)

Если требуется сохранить типовую конфигурацию на поддержке без внесения изменений напрямую в форму документа, вы можете использовать механизмы БСП или Расширения конфигурации.

Вариант через переопределяемые модули БСП:

В конфигурации УТ 11 предусмотрены специальные общие модули для внедрения пользовательского кода при создании форм. Проанализируем пример добавления кнопки динамически:


// В общем модуле МодификацияКонфигурацииПереопределяемый
Процедура ПриСозданииНаСервере(Форма, Отказ, СтандартнаяОбработка) Экспорт
    Если Форма.ИмяФормы = "Документ.ЗаказПоставщику.Форма.ФормаДокумента" Тогда
        НоваяКоманда = Форма.Команды.Добавить("СообщитьРозничнуюЦену");
        НоваяКоманда.Действие = "ПослеЗаписи"; // Перенаправление на стандартный клик-обработчик
        
        НовыйЭлемент = Форма.Элементы.Добавить("КомандаЗаполнитьРозничнуюЦену", Тип("КнопкаФормы"), Форма.Элементы.ГруппаКомандТЧ);
        НовыйЭлемент.ИмяКоманды = "СообщитьРозничнуюЦену";
        НовыйЭлемент.Заголовок = "Заполнить розничные цены";
    КонецЕсли;
КонецПроцедуры

// В общем модуле МодификацияКонфигурацииКлиентПереопределяемый
Процедура ПослеЗаписи(Форма, ПараметрыЗаписи) Экспорт
    Если Форма.ИмяФормы = "Документ.ЗаказПоставщику.Форма.ФормаДокумента" Тогда
        Если ТипЗнч(ПараметрыЗаписи) = Тип("КомандаФормы") И ПараметрыЗаписи.Имя = "СообщитьРозничнуюЦену" Тогда
            // Вызов клиентской процедуры обработки розничных цен
            Форма.ЗаполнитьРозничныеЦеныВыделенныхСтрокПоВидуЦен(ПараметрыЗаписи);
        КонецЕсли;
    КонецЕсли;    
КонецПроцедуры

Вариант через Расширение конфигурации:

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

Способ 3. Подключаемая внешняя обработка заполнения табличных частей

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

  1. Обработка регистрируется в справочнике ДополнительныеОтчетыИОбработки с типом Заполнение табличных частей для документа ЗаказПоставщику.
  2. В методе СведенияОВнешнейОбработке() задается назначение обработки и вызываемая команда.
  3. В модуле объекта внешней обработки в процедуре ВыполнитьКоманду() вычисляются розничные цены для текущего объекта документа и результат возвращается в форму.

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

Резюме и рекомендации

Мы проанализировали три рабочих метода добавления колонки розничной цены в документ «Заказ поставщику». При выборе оптимального пути руководствуйтесь следующими правилами:

← На главную