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

Программист 1С v8.3 (Управляемые формы)
← На главную

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

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

Понимание проблемы: почему "видимость" не работает так, как ожидается

Наш пользователь изначально пытался использовать следующий код:


если Строка(Шапка.Параметры.Количество)="0" тогда 
    ОбластьСтрока=ТабДок.Область("R4"); 
    ОбластьСтрока.Видимость=ложь; 
конецесли;

Используя ОбластьСтрока.Видимость=ложь, мы пытаемся управлять видимостью *области макета*, а не уже *выведенной области* в табличном документе. Когда мы вызываем ТабДок.Вывести(Область), мы добавляем копию этой области макета в формируемый табличный документ. После вывода, управление видимостью исходной области макета уже не влияет на то, что уже попало в табличный документ. Если же такое решение "работало" на пустом макете, это скорее совпадение или некая особенность его структуры, которая маскировала проблему.

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

Также обратим внимание на условие Строка(Шапка.Параметры.Количество)="0". Сравнение чисел как строк может привести к ошибкам. Например, если количество будет "0.00" или будет иметь другой строковый формат, это условие не сработает. Всегда сравнивайте числа с числами: Шапка.Параметры.Количество = 0.

Решение 1: Условный вывод областей

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

Рассмотрим по шагам, как адаптировать этот подход к ситуации, когда данные представлены в виде отдельных реквизитов документа (например, Количество1, Количество2, ..., Количество20), а не в виде табличной части.

  1. Подготовка данных и областей макета:

    Начнем с получения данных. В нашем случае, пользователь получает их запросом:

    
    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    | Документ1.Количество1,
    | Документ1.Количество2,
    | Документ1.Количество3 // ... и т.д. до Количество20
    | ИЗ
    | Документ.Документ1 как Документ1
    |ГДЕ
    |Документ1.Ссылка В (&Ссылка)";
    Запрос.Параметры.Вставить("Ссылка", Ссылка);
    Выборка = Запрос.Выполнить().Выбрать();
            
    // Переходим к первой записи выборки, чтобы заполнить параметры
    Если Выборка.Следующий() Тогда
        // Теперь Шапка.Параметры можно заполнить
    КонецЕсли;
    

    Предположим, что в макете у нас есть отдельные области для каждой детали, например, ОбластьДеталь1, ОбластьДеталь2 и так далее, каждая со своими параметрами (НазваниеДетали, КоличествоДетали).

  2. Условный вывод для каждого реквизита:

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

    Давайте адаптируем подход. Если все детали находятся в одной области Шапка, и каждый реквизит КоличествоX соответствует параметру КоличествоX в этой области, то такой подход становится сложнее. Проще, если каждая деталь имеет свою *отдельную область макета*. Предположим, что у нас есть области: ОбластьСтрокаДетали1, ОбластьСтрокаДетали2 и т.д.

    
    // ... Получаем Выборку, переходим к первой записи Выборка.Следующий() ...
            
    // Получаем общую шапку документа, если она есть и всегда выводится
    ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
    ТабДок.Вывести(ОбластьЗаголовок); // Выводим всегда
    
    // Если есть общая Шапка с датой/номером, которая всегда выводится
    ОбластьОбщаяШапка = Макет.ПолучитьОбласть("ОбщаяШапка");
    ОбластьОбщаяШапка.Параметры.Заполнить(Выборка); // Заполняем общие параметры
    ТабДок.Вывести(ОбластьОбщаяШапка); // Выводим всегда
    
    // Теперь обрабатываем детали
    // Для каждой детали создаем отдельную область в макете
    // и заполняем ее параметры перед выводом.
    
    // Деталь 1
    Если Выборка.Количество1 <> 0 Тогда
        ОбластьДеталь1 = Макет.ПолучитьОбласть("ОбластьДеталь1");
        ОбластьДеталь1.Параметры.НазваниеДетали = Выборка.НазваниеДетали1; // Предполагается, что есть такой реквизит
        ОбластьДеталь1.Параметры.Количество = Выборка.Количество1;
        ТабДок.Вывести(ОбластьДеталь1);
    КонецЕсли;
    
    // Деталь 2
    Если Выборка.Количество2 <> 0 Тогда
        ОбластьДеталь2 = Макет.ПолучитьОбласть("ОбластьДеталь2");
        ОбластьДеталь2.Параметры.НазваниеДетали = Выборка.НазваниеДетали2;
        ОбластьДеталь2.Параметры.Количество = Выборка.Количество2;
        ТабДок.Вывести(ОбластьДеталь2);
    КонецЕсли;
            
    // ... и так далее для всех 20 деталей ...
    
    // Деталь 20
    Если Выборка.Количество20 <> 0 Тогда
        ОбластьДеталь20 = Макет.ПолучитьОбласть("ОбластьДеталь20");
        ОбластьДеталь20.Параметры.НазваниеДетали = Выборка.НазваниеДетали20;
        ОбластьДеталь20.Параметры.Количество = Выборка.Количество20;
        ТабДок.Вывести(ОбластьДеталь20);
    КонецЕсли;
    

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

  3. Пример из форума, адаптированный к вашему случаю:

    Пользователь (9) предложил общую структуру:

    
    Пока Выборка.Следующий() Цикл
        Если Выборка.Количество <> 0 Тогда
            ТабДок.Вывести(Область);
        КонецЕсли;
    КонецЦикла;
    

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

Решение 2: Рефакторинг данных в табличную часть (Рекомендуемый подход)

На форуме справедливо заметили, что хранение однотипных данных (20 деталей с количествами) в виде отдельных реквизитов Количество1, Количество2 и т.д. является антипаттерном. Это значительно усложняет разработку, поддержку и формирование отчетов. Наиболее оптимальным и гибким решением является перевод этих данных в табличную часть документа.

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

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

    Это требование легко реализуется и с табличной частью. При выборе модели на форме документа, мы можем программно заполнять табличную часть нужными деталями. Например, в обработчике события поля "Модель":

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

    Таким образом, оператор увидит уже заполненную таблицу, а не пустую.

  2. Не давать оператору писать что угодно в количество, а ограничить выбор, но у разных деталей разное максимальное количество (от 2 до 4).

    Это также реализуемо. Для колонки "Количество" в табличной части можно использовать обработчики событий или динамические свойства:

    • Обработчик ПриИзменении:

      Вы можете добавить обработчик ПриИзменении для поля "Количество" в табличной части формы. В этом обработчике вы можете получить текущую деталь в строке и проверить введенное количество на соответствие максимальному значению для этой детали:

      
      &НаКлиенте
      Процедура ТабличнаяЧастьДеталиКоличествоПриИзменении(Элемент)
          ТекущиеДанные = Элементы.ТабличнаяЧастьДетали.ТекущиеДанные;
          Если ТекущиеДанные = Неопределено Тогда
              Возврат;
          КонецЕсли;
      
          // Получаем максимальное количество для текущей детали (можно хранить в ТЧ 
          // или получать из справочника/регистра)
          МаксимальноеКоличество = ПолучитьМаксимальноеКоличествоДляДетали(ТекущиеДанные.Деталь);
      
          Если ТекущиеДанные.Количество > МаксимальноеКоличество Тогда
              Сообщение = Новый СообщениеПользователю();
              Сообщение.Текст = "Количество не может превышать " + МаксимальноеКоличество + " для детали " + ТекущиеДанные.Деталь;
              Сообщение.Поле = "Количество";
              Сообщение.УстановитьОбъект(Элемент);
              Сообщение.Вывести();
              ТекущиеДанные.Количество = МаксимальноеКоличество; // Можно сбросить до максимума
          КонецЕсли;
      КонецПроцедуры
      
      &НаСервереБезКонтекста
      Функция ПолучитьМаксимальноеКоличествоДляДетали(Деталь)
          // Пример: получаем из регистра сведений
          Запрос = Новый Запрос;
          Запрос.Текст = "ВЫБРАТЬ 
                       |  МаксимальноеКоличество 
                       |ИЗ 
                       |  РегистрСведений.СоставМоделейКакДетали 
                       |ГДЕ 
                       |  Деталь = &Деталь";
          Запрос.УстановитьПараметр("Деталь", Деталь);
          Результат = Запрос.Выполнить().Выбрать();
          Если Результат.Следующий() Тогда
              Возврат Результат.МаксимальноеКоличество;
          Иначе
              Возврат 999; // Значение по умолчанию или ошибка
          КонецЕсли;
      КонецФункции;
      
    • Динамические свойства элемента формы:

      Можно использовать свойство ОграничениеЗначения для элемента формы, привязанного к колонке табличной части. Это свойство может быть установлено динамически для каждой строки таблицы. Для этого можно использовать обработчик ПриПолученииДанных элемента формы "Таблица" или установить его в ПриСозданииНаСервере для каждого элемента коллекции элементов формы, представляющих строки табличной части.

      Более простой способ — использовать поле ввода с типом "Число" и свойством "МинимальноеЗначение" и "МаксимальноеЗначение". Вы можете хранить максимальное количество как дополнительный реквизит табличной части (который не будет выводиться на печать), и при изменении строки или детали обновлять свойства поля "Количество".

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

Формирование печатной формы с табличной частью

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

  1. Получаем область шапки и выводим ее:

    
    ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
    ОбластьШапка.Параметры.Заполнить(ОбъектДокумента); // Заполняем параметры из реквизитов документа
    ТабДок.Вывести(ОбластьШапка);
    
  2. Получаем область строки табличной части:

    ОбластьСтрокаТЧ = Макет.ПолучитьОбласть("СтрокаТабличнойЧасти");
  3. Перебираем строки табличной части документа, применяя условие:

    
    Для Каждого СтрокаТЧ Из ОбъектДокумента.ТабличнаяЧастьДетали Цикл
        Если СтрокаТЧ.Количество <> 0 Тогда // Проверяем условие
            ОбластьСтрокаТЧ.Параметры.Заполнить(СтрокаТЧ); // Заполняем параметры из текущей строки ТЧ
            ТабДок.Вывести(ОбластьСтрокаТЧ); // Выводим строку, если условие выполнено
        КонецЕсли;
    КонецЦикла;
    

    Этот код является наглядным примером из сообщения (9) и идеально подходит для работы с табличными частями. Он элегантен, легко читаем и масштабируем.

Резюмируем и анализируем ситуацию

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

Ключевые моменты для успешного условного вывода:

  1. Выводите только то, что нужно: Используйте условные операторы Если ... Тогда перед вызовом ТабДок.Вывести(). Если условие не выполняется, метод Вывести() просто не вызывается, и область не попадает в табличный документ.

  2. Правильное сравнение типов: Всегда сравнивайте числовые значения с числовыми (например, Количество = 0), а не строковые представления чисел. Это предотвращает ошибки, связанные с форматированием.

  3. Структурируйте данные: Для однотипных повторяющихся данных (как детали с количествами) всегда предпочтительнее использовать табличные части документа. Это значительно упрощает как логику работы с данными на форме, так и формирование печатных форм и отчетов. Требования по предзаполнению и динамическим ограничениям для полей легко реализуются для табличных частей через обработчики событий формы.

  4. Именование областей: В макете давайте осмысленные и уникальные имена областям, особенно если у вас много однотипных строк, которые выводятся по условию. Это поможет избежать путаницы и ошибок.

Применяя эти подходы, мы сможем создавать гибкие и корректно работающие печатные формы, которые будут выводить информацию именно так, как нам необходимо, без "фокусов" и неожиданного поведения.

← На главную