Как исправить некорректный вывод номенклатуры контрагента в печатной форме документа 1С:УТ 10.3?

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

При работе с типовыми конфигурациями 1С, такими как 1С:Управление торговлей 10.3, часто возникает необходимость доработки печатных форм для вывода дополнительной информации. Рассмотрим ситуацию, когда мы добавили в табличную часть документа пользовательский реквизит, например, НаименованиеНоменклатурыКонтрагента, и столкнулись с проблемой его некорректного отображения в печатной форме.

Типичные проявления этой проблемы:

  1. Поле НаименованиеНоменклатурыКонтрагента вообще не выводится.
  2. Поле выводится, но всегда показывает значение из первой строки документа, игнорируя данные из остальных строк.
  3. При попытке добавить поле в запрос возникает ошибка "недопустимое поле для группировки".

Давайте разберем по шагам, как можно решить эту проблему, используя пример из обсуждения.

Анализ исходного кода и причины некорректного вывода

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


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

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

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

Модификация запроса: добавление пользовательского реквизита

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


Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ТекущийДокумент", СсылкаНаОбъект);

Запрос.УстановитьПараметр("ВыводитьСкидки",Ложь);

Запрос.УстановитьПараметр("УчитыватьЦенуЛоготипа", (СсылкаНаОбъект.ВидОперации=Перечисления.ВидыОперацийСчетаЗаказыПокупателей.ТоварыСЛоготипами И НЕ СсылкаНаОбъект.ВыделениеЛоготиповОтдельнойУслугой));

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

В данном запросе используются вложенные запросы и объединение табличных частей Товары и Услуги. Проблема возникла из-за того, что реквизит НаименованиеНоменклатурыКонтрагента был добавлен в табличную часть Документ.СчетНаОплатуПокупателю.Товары, но отсутствовал в тексте запроса. При попытке добавить его через конструктор запросов, он мог быть не виден из-за вложенности запроса.

  1. Доступ к полям во вложенных запросах

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

    Нам необходимо добавить НаименованиеНоменклатурыКонтрагента в секцию ВЫБРАТЬ и СГРУППИРОВАТЬ ПО первого вложенного запроса, который работает с табличной частью Товары.

    После этого поле станет доступным в основном запросе как ВложенныйЗапрос.НаименованиеНоменклатурыКонтрагента.

  2. Условный выбор наименования номенклатуры с помощью ВЫБОР КОГДА

    Теперь, когда пользовательский реквизит доступен в запросе, мы можем использовать конструкцию ВЫБОР КОГДА ТОГДА ИНАЧЕ КОНЕЦ для динамического определения, какое наименование выводить — собственное или контрагента.

    Изменим секцию ВЫБРАТЬ основного запроса, где формируется поле Товар:

    
    ВЫБРАТЬ
        ...
        ВЫБОР
            КОГДА ВложенныйЗапрос.НаименованиеНоменклатурыКонтрагента ЕСТЬ НЕ NULL 
                 И ВложенныйЗапрос.НаименованиеНоменклатурыКонтрагента <> ""
                ТОГДА ВложенныйЗапрос.НаименованиеНоменклатурыКонтрагента
            ИНАЧЕ ВЫРАЗИТЬ(ВложенныйЗапрос.Номенклатура.НаименованиеПолное КАК СТРОКА(1000))
        КОНЕЦ КАК Товар,
        ...
    

    Эта конструкция означает: если поле НаименованиеНоменклатурыКонтрагента заполнено (не NULL и не пустая строка), то используем его значение. В противном случае, используем полное наименование номенклатуры из справочника. Обратите внимание, что мы используем ВЫРАЗИТЬ(... КАК СТРОКА(1000)) для приведения поля к строковому типу с указанной длиной. Это важно для совместимости типов и может предотвратить ошибки.

  3. Решение проблемы "недопустимое поле для группировки"

    Если вы добавили строковое поле в секцию ВЫБРАТЬ и пытаетесь сгруппировать по нему или по другому полю, которое в свою очередь требует его включения в группировку, может возникнуть ошибка "недопустимое поле для группировки". Это часто происходит со строковыми полями неограниченной длины или с полями, которые система не может однозначно сгруппировать без явного указания.

    Решение состоит в использовании функции ВЫРАЗИТЬ для явного приведения строкового поля к строке фиксированной или ограниченной длины непосредственно в секции СГРУППИРОВАТЬ ПО. Например, если НаименованиеНоменклатурыКонтрагента добавлено в первый вложенный запрос и участвует в группировке, его следует указать так:

    
    СГРУППИРОВАТЬ ПО
        ЗаказПокупателя.НомерСтроки,
        ЗаказПокупателя.Номенклатура,
        ЗаказПокупателя.ЕдиницаИзмерения,
        ЗаказПокупателя.ПроцентСкидкиНаценки,
        ЗаказПокупателя.ПроцентАвтоматическихСкидок,
        ЗаказПокупателя.Цена,
        ЗаказПокупателя.ЦенаЛоготипа,
        ЗаказПокупателя.ХарактеристикаНоменклатуры,
        ВЫРАЗИТЬ(ЗаказПокупателя.НаименованиеНоменклатурыКонтрагента КАК СТРОКА(1000)) // Добавляем здесь
    

    Указание явной длины (например, 1000 символов) помогает СУБД корректно обрабатывать это поле при группировке.

    Пользователь в обсуждении нашел способ обойти эту проблему в конструкторе, добавляя пустое поле и вручную вписывая выражение ВЫРАЗИТЬ. Это подтверждает, что именно такое форматирование требовалось для успешной группировки.

  4. Оптимизация структуры запроса и цикла вывода

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

    Обновим цикл вывода:

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

    Таким образом, мы итерируем непосредственно по результату запроса, что исключает рассинхронизацию данных и упрощает код. Если в запросе не требуется сложная агрегация данных по НомерСтроки (что часто бывает, если каждая строка документа должна выводиться как отдельная строка), то можно рассмотреть возможность исключения НомерСтроки из секции СГРУППИРОВАТЬ ПО для дальнейшей оптимизации, как было предложено в обсуждении.

Важные аспекты и рекомендации

При доработке печатных форм и работе с запросами в 1С всегда следует учитывать следующие моменты:

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

← На главную