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

Программист 1С v8.3 (Управляемые формы) Управленческий учет Промышленность, строительство и АПК
← На главную

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

Представим исходную ситуацию. У нас есть документ, который фиксирует выполнение заданий. В нем строка задания содержит несколько полей для ответственных и их результатов, например:

  1. Задание 1: Номенклатура, Материал, Кол-во продукции, Ответственный1, КоличествоОбработанного1, Ответственный2, КоличествоОбработанного2, Ответственный3, КоличествоОбработанного3.

Если на основании такого документа мы создаем движения в регистре накопления, сохраняя все эти поля как отдельные реквизиты, то в отчете мы столкнемся с тем, что информация об Ответственном1, Ответственном2 и Ответственном3 будет выводиться в разных колонках. Желаемый же результат — каждый ответственный за задание должен быть представлен отдельной строкой в отчете. Например:

  1. Задание 1, Номенклатура, Материал, Ответственный1, КоличествоОбработанного1
  2. Задание 1, Номенклатура, Материал, Ответственный2, КоличествоОбработанного2
  3. Задание 1, Номенклатура, Материал, Ответственный3, КоличествоОбработанного3

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

Нормализация данных: Ключ к решению проблемы

Первым и самым важным шагом является правильное проектирование структуры регистра накопления. Чтобы каждый ответственный отображался отдельной строкой, данные в регистре должны быть нормализованы. Это означает, что каждая запись (движение) в регистре должна относиться только к одному ответственному.

Для нашей задачи регистр накопления должен иметь следующую структуру:

  1. Измерения:
    • Задание (ссылка на элемент справочника или строковое поле, описывающее задание)
    • Номенклатура (ссылка на элемент справочника Номенклатура)
    • Ответственный (ссылка на элемент справочника ФизическиеЛица или Пользователи — одно поле для ответственного!)
    • Дополнительные измерения, если они необходимы для детализации (например, Материал, Подразделение).
  2. Ресурсы:
    • КоличествоОбработанного (числовое поле, хранящее объем работы, выполненной конкретным ответственным)
    • Дополнительные ресурсы, если они необходимы (например, КоличествоБрака).

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

Формирование движений регистра в обработке проведения документа

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

Рассмотрим подробнее по шагам, как это сделать:

  1. Получаем объект движений регистра: В начале процедуры ОбработкаПроведения нам необходимо обратиться к коллекции движений нашего регистра. Допустим, наш регистр накопления называется "КонтролерыОТК".
  2. Перебираем строки табличной части документа: Мы должны пройтись по каждой строке табличной части документа, которая содержит информацию о задании и возможных ответственных.
  3. Проверяем заполненность полей ответственных: Для каждой строки табличной части проверяем, заполнено ли поле Ответственный1. Если да, это означает, что первый ответственный выполнял работу. Затем аналогично проверяем Ответственный2 и Ответственный3.
  4. Создаем отдельные движения: Для каждого найденного (заполненного) ответственного мы создаем новое движение в регистре накопления. Это ключевой момент. Метод Добавить() коллекции движений создает новую запись, которую мы затем заполняем.
  5. Заполняем измерения и ресурсы движения:
    • Общие измерения, такие как Задание, Номенклатура, Материал, копируем из текущей строки табличной части документа.
    • Измерение Ответственный заполняем значением из соответствующего поля документа (СтрокаТЧ.Ответственный1, СтрокаТЧ.Ответственный2 и т.д.).
    • Ресурс КоличествоОбработанного заполняем значением из соответствующего поля документа (СтрокаТЧ.КоличествоОбработанного1, СтрокаТЧ.КоличествоОбработанного2 и т.д.).
    • Поле Регистратор, которое автоматически связывает движение с документом, заполнится само при записи движений.

Давайте посмотрим на пример кода для процедуры ОбработкаПроведения:


// В процедуре ОбработкаПроведения документа

// Устанавливаем режим записи движений регистра.
// Важно использовать РежимЗаписиДвижений.Записывать,
// чтобы движения создавались явно, а не перезаписывались все.
Движения.КонтролерыОТК.Записывать = Истина;

// Очищаем существующие движения по этому регистратору, чтобы избежать дублирования
// при повторном проведении документа.
Движения.КонтролерыОТК.Очистить();

// Перебираем каждую строку табличной части документа, например, "ТабличнаяЧастьЗадания"
Для Каждого СтрокаТЧ Из ДокументОбъект.ТабличнаяЧастьЗадания Цикл

    // Проверяем, заполнен ли первый ответственный
    Если ЗначениеЗаполнено(СтрокаТЧ.Ответственный1) Тогда
        // Создаем новое движение для первого ответственного
        Движение = Движения.КонтролерыОТК.Добавить();

        // Заполняем общие измерения из строки табличной части
        // Метод ЗаполнитьЗначенияСвойств копирует свойства, если их имена совпадают.
        // Здесь мы явно указываем, какие свойства копировать, чтобы не скопировать лишнее,
        // а также чтобы обеспечить копирование полей, которые могут отличаться от имен измерений
        // регистра (например, если в ТЧ поле "ЗаданиеНомер", а в регистре "Задание").
        // Для простоты примера, мы предполагаем, что поля документа названы так же, как измерения регистра.
        Движение.Задание = СтрокаТЧ.Задание;
        Движение.Номенклатура = СтрокаТЧ.Номенклатура;
        // ... другие общие измерения из строки ТЧ ...

        // Устанавливаем специфичные для данного ответственного измерения и ресурсы
        Движение.Ответственный = СтрокаТЧ.Ответственный1;
        Движение.КоличествоОбработанного = СтрокаТЧ.КоличествоОбработанного1;

        // Если есть дополнительные ресурсы (например, брак), добавляем их
        // Движение.КоличествоБрака = СтрокаТЧ.КоличествоБрака1;
    КонецЕсли;

    // Проверяем, заполнен ли второй ответственный
    Если ЗначениеЗаполнено(СтрокаТЧ.Ответственный2) Тогда
        // Создаем новое движение для второго ответственного
        Движение = Движения.КонтролерыОТК.Добавить();

        // Заполняем общие измерения
        Движение.Задание = СтрокаТЧ.Задание;
        Движение.Номенклатура = СтрокаТЧ.Номенклатура;
        // ... другие общие измерения из строки ТЧ ...

        // Устанавливаем специфичные для второго ответственного
        Движение.Ответственный = СтрокаТЧ.Ответственный2;
        Движение.КоличествоОбработанного = СтрокаТЧ.КоличествоОбработанного2;

        // Движение.КоличествоБрака = СтрокаТЧ.КоличествоБрака2;
    КонецЕсли;

    // Проверяем, заполнен ли третий ответственный (если он есть)
    Если ЗначениеЗаполнено(СтрокаТЧ.Ответственный3) Тогда
        // Создаем новое движение для третьего ответственного
        Движение = Движения.КонтролерыОТК.Добавить();

        // Заполняем общие измерения
        Движение.Задание = СтрокаТЧ.Задание;
        Движение.Номенклатура = СтрокаТЧ.Номенклатура;
        // ... другие общие измерения из строки ТЧ ...

        // Устанавливаем специфичные для третьего ответственного
        Движение.Ответственный = СтрокаТЧ.Ответственный3;
        Движение.КоличествоОбработанного = СтрокаТЧ.КоличествоОбработанного3;

        // Движение.КоличествоБрака = СтрокаТЧ.КоличествоБрака3;
    КонецЕсли;

КонецЦикла;

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

Теперь, после проведения документа, одна строка табличной части может породить столько движений, сколько ответственных было заполнено в этой строке. Например, если в строке Задания 1 были заполнены Ответственный1 и Ответственный2, то в регистре будет создано две отдельные записи для одного и того же Задания 1, но с разными ответственными.

Улучшение структуры документа: Дополнительная табличная часть

Хотя текущая структура документа с колонками Ответственный1, Ответственный2, Ответственный3 может быть рабочей, она не является самой гибкой. Что, если в будущем появится необходимость добавить четвертого или пятого ответственного? Придется изменять структуру документа и код проведения. Более элегантным и гибким решением, как было предложено в обсуждении, является использование дополнительной табличной части в документе.

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

  1. Основная табличная часть "Задания":
    • НомерСтроки (системное поле, уникальный идентификатор строки)
    • Задание
    • Номенклатура
    • КоличествоПродукции
    • ...другие общие поля задания...
  2. Вложенная табличная часть "ОтветственныеПоСтрокеЗадания":
    • НомерСтрокиЗадания (ссылка на НомерСтроки основной табличной части)
    • Ответственный
    • КоличествоОбработанного
    • ...другие специфичные для ответственного поля...

Такая структура позволяет связать любое количество ответственных с каждой строкой основного задания. Механизм КлючСтроки в 1С часто используется для установления таких связей между строками табличных частей.

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

  1. Перебираем строки основной табличной части "Задания".
  2. Для каждой строки основной табличной части, находим соответствующие строки вложенной табличной части "ОтветственныеПоСтрокеЗадания" (по полю НомерСтрокиЗадания).
  3. Для каждой строки из "ОтветственныеПоСтрокеЗадания" создаем новое движение в регистре, используя данные из обеих табличных частей.

Примерная логика кода для проведения документа с дополнительной табличной частью:


// В процедуре ОбработкаПроведения документа

Движения.КонтролерыОТК.Записывать = Истина;
Движения.КонтролерыОТК.Очистить();

// Итерируем по основной табличной части документа
Для Каждого СтрокаОсновнойТЧ Из ДокументОбъект.ТабличнаяЧастьЗадания Цикл

    // Теперь нам нужно найти всех ответственных, привязанных к этой конкретной строке задания.
    // Если табличные части связаны по полю "КлючСтроки" основной ТЧ и "КлючСтрокиЗадания" дополнительной ТЧ:
    Для Каждого СтрокаОтветственного Из ДокументОбъект.ОтветственныеПоСтрокеЗадания Цикл
        Если СтрокаОтветственного.КлючСтрокиЗадания = СтрокаОсновнойТЧ.КлючСтроки Тогда

            // Создаем новое движение для каждого ответственного
            Движение = Движения.КонтролерыОТК.Добавить();

            // Заполняем общие измерения из основной строки задания
            Движение.Задание = СтрокаОсновнойТЧ.Задание;
            Движение.Номенклатура = СтрокаОсновнойТЧ.Номенклатура;
            // ... другие общие измерения ...

            // Заполняем специфичные для ответственного измерения и ресурсы из строки ответственного
            Движение.Ответственный = СтрокаОтветственного.Ответственный;
            Движение.КоличествоОбработанного = СтрокаОтветственного.КоличествоОбработанного;

            // Если есть дополнительные ресурсы (например, брак), добавляем их
            // Движение.КоличествоБрака = СтрокаОтветственного.КоличествоБрака;

        КонецЕсли;
    КонецЦикла;

КонецЦикла;

Такой подход обеспечивает максимальную гибкость и упрощает поддержку решения в долгосрочной перспективе.

Разработка отчета с помощью Системы Компоновки Данных (СКД)

После того как данные корректно нормализованы и записаны в регистр накопления, построение отчета становится относительно простой задачей, которая выполняется с помощью Системы Компоновки Данных (СКД) в 1С.

Давайте разберем по шагам, как настроить отчет в СКД:

  1. Создаем новый отчет: В конфигураторе создаем новый объект "Отчет" и открываем его схему компоновки данных.
  2. Создаем набор данных-запрос: В схеме компоновки данных добавляем новый набор данных типа "Запрос". Запрос должен обращаться к виртуальной таблице нашего регистра накопления. В нашем случае это будет РегистрНакопления.КонтролерыОТК.Обороты, так как нам нужны обобщенные данные за период.
  3. Настраиваем параметры виртуальной таблицы: Критически важный момент! Для того чтобы в отчет можно было вывести ссылку на документ-регистратор (и обеспечить возможность клика по нему для открытия), необходимо правильно настроить параметры виртуальной таблицы оборотов. В запросе к виртуальной таблице оборотов необходимо указать параметр периодичности Авто или Регистратор.

    Пример запроса:

    
    ВЫБРАТЬ
        КонтролерыОТКОбороты.Задание,
        КонтролерыОТКОбороты.Номенклатура,
        КонтролерыОТКОбороты.Ответственный,
        КонтролерыОТКОбороты.КоличествоОбработанногоОборот КАК КоличествоОбработанного,
        КонтролерыОТКОбороты.Регистратор
    ИЗ
        РегистрНакопления.КонтролерыОТК.Обороты(&НачалоПериода, &КонецПериода, Авто, ) КАК КонтролерыОТКОбороты
    

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

  4. Выбираем поля для вывода: На закладке "Поля" набора данных выбираем все необходимые поля из запроса: Задание, Номенклатура, Ответственный, КоличествоОбработанного и Регистратор.
  5. Настраиваем группировки: Переходим на закладку "Настройки" схемы компоновки данных и добавляем группировки в желаемом порядке. Например, чтобы получить каждый ответственный на отдельной строке, мы можем сгруппировать данные так:
    1. Группировка по Задание
    2. Группировка по Номенклатура
    3. Группировка по Ответственный

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

  6. Настраиваем выбранные поля: На закладке "Выбранные поля" указываем, какие поля должны отображаться в отчете.
  7. Настраиваем параметры: Для полей &НачалоПериода и &КонецПериода, используемых в запросе, настраиваем параметры на закладке "Параметры", чтобы пользователь мог задавать период формирования отчета.
  8. Настраиваем отборы: При необходимости на закладке "Отборы" можно добавить стандартные отборы по полям отчета (например, по конкретному ответственному, заданию или номенклатуре).

После всех этих настроек отчет будет выводить данные именно так, как нам нужно: для каждого задания и номенклатуры будет список ответственных, и каждый ответственный будет представлен отдельной строкой со своим объемом выполненной работы. А благодаря полю Регистратор, пользователи смогут кликнуть по документу в отчете и перейти непосредственно в него для просмотра или редактирования.

Дополнительные рекомендации

  1. Обработка отсутствия ответственных: Если в документе возможно, что за задание никто не назначен, и вы не хотите, чтобы в регистре создавались пустые записи, убедитесь, что ваши проверки Если ЗначениеЗаполнено(СтрокаТЧ.ОтветственныйX) Тогда корректно работают. В крайнем случае, можно ввести специальное "физическое лицо" или "пользователя" типа "Без ответственного" и присваивать его в случае отсутствия реального ответственного, если требуется видеть все задания, даже те, у которых нет явных исполнителей.
  2. Производительность: Нормализованная структура регистра накопления и использование СКД — это стандартный и наиболее производительный подход в 1С для решения подобных задач, особенно при больших объемах данных. Платформа 1С оптимизирована для работы с такими структурами.
  3. Гибкость: Использование дополнительной табличной части для ответственных в документе делает вашу систему гораздо более гибкой к будущим изменениям. Если когда-либо потребуется добавить четвертого, пятого или даже неограниченное количество ответственных, вам не придется менять структуру документа или отчета – достаточно будет лишь изменить код проведения, который будет перебирать строки новой табличной части.

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

← На главную