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