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