УТ 11.4 Получить всех пользователей, у которых есть Роль
Подскажите, какие ниже в коде есть ошибки (или можно быстрее получить, при помощи БСП список пользователей?)
Мне нужно им письма отправить.

```1c
    Функция ПолучитьВсехПользователей(ИмяРоли)

    Таб = Новый ТаблицаЗначений;
    Таб.Колонки.Добавить("Пользователь");
    Таб.Колонки.Добавить("Почта");

    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ
    | Пользователи.Ссылка КАК Ссылка,
    | Пользователи.Представление КАК Почта
    |ИЗ
    | Справочник.Пользователи.КонтактнаяИнформация КАК Пользователи
    |ГДЕ
    | Пользователи.Ссылка.ПометкаУдаления = ЛОЖЬ
    | И Пользователи.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты)";

    Результат = Запрос.Выполнить();
    Выборка = Результат.Выбрать();

    Пока Выборка.Следующий() Цикл
    ПользовательИмя = Выборка.Ссылка.Наименование;
    ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоИмени(ПользовательИмя);

    Если НЕ ПользовательИБ = Неопределено И ПользовательИБ.Роли.Содержит(Метаданные.Роли[ИмяРоли]) Тогда
    стр = Таб.Добавить();
    стр.Пользователь = Выборка.Ссылка;
    стр.Почта= Выборка.Почта;
    КонецЕсли;
    КонецЦикла;

    Возврат Таб;
    КонецФункции
```
---
Сейчас код выглядит так:

```1c
    Функция ПолучитьВсехПользователей(ИмяРоли, Знач СПочтой = Ложь)

    // Пример: ПолучитьВсехПользователей("ПолныеПрава", Истина);

    РольВМетаданных = Метаданные.Роли[ИмяРоли];

    Таб = Новый ТаблицаЗначений;
    Таб.Колонки.Добавить("Пользователь");

    Если СПочтой Тогда
    Таб.Колонки.Добавить("Почта");
    Таб.Колонки.Добавить("ЕстьПочта");
    КонецЕсли;

    Запрос = Новый Запрос;

    Если НЕ СПочтой Тогда // Это условие чаще выполняется, поэтому оно указано в начале Если.

    Запрос.Текст = "ВЫБРАТЬ
    | Пользователи.Ссылка КАК Ссылка,
    | Пользователи.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ
    |ИЗ
    | Справочник.Пользователи КАК Пользователи
    |ГДЕ
    | Пользователи.ПометкаУдаления = ЛОЖЬ";
    Иначе

    Запрос.Текст = "ВЫБРАТЬ
    | Пользователи.Ссылка КАК Ссылка,
    | Пользователи.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ,
    | КИ.Представление КАК Почта
    |ИЗ
    | Справочник.Пользователи КАК Пользователи
    | ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи.КонтактнаяИнформация КАК КИ
    | ПО Пользователи.Ссылка = КИ.Ссылка
    | И (КИ.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты))
    |ГДЕ
    | Пользователи.ПометкаУдаления = ЛОЖЬ";

    КонецЕсли;

    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл

    ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(Выборка.ИдентификаторПользователяИБ);

    Если ПользовательИБ <> Неопределено И ПользовательИБ.Роли.Содержит(РольВМетаданных) Тогда
    стр = Таб.Добавить();
    стр.Пользователь = Выборка.Ссылка;

    Если СПочтой Тогда
    стр.Почта = Выборка.Почта;
    Если ЗначениеЗаполнено(Выборка.Почта) Тогда
    стр.ЕстьПочта = Истина;
    Иначе
    стр.ЕстьПочта = Ложь;
    КонецЕсли;
    КонецЕсли;

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

    Возврат Таб;
    КонецФункции
```
---
В справочнике Пользователи есть реквизит ИдентификаторПользователяИБ?
---
(4)
(3)
Получилось так:

```1c
    Функция ПолучитьВсехПользователей(ИмяРоли)

    Таб = Новый ТаблицаЗначений;
    Таб.Колонки.Добавить("Пользователь");
    Таб.Колонки.Добавить("Почта");
    Таб.Колонки.Добавить("ЕстьПочта");

    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ
    | Пользователи.Ссылка КАК Ссылка,
    | КИ.Представление КАК Почта
    |ИЗ
    | Справочник.Пользователи КАК Пользователи
    | ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи.КонтактнаяИнформация КАК КИ
    | ПО Пользователи.Ссылка = КИ.Ссылка
    | И (КИ.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты))
    |ГДЕ
    | Пользователи.ПометкаУдаления = ЛОЖЬ";

    Выборка = Запрос.Выполнить().Выбрать();

    Пока Выборка.Следующий() Цикл
    ПользовательИмя = Выборка.Ссылка.Наименование;
    ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоИмени(ПользовательИмя);

    Если НЕ ПользовательИБ = Неопределено И ПользовательИБ.Роли.Содержит(Метаданные.Роли[ИмяРоли]) Тогда
    стр = Таб.Добавить();
    стр.Пользователь = Выборка.Ссылка;
    стр.Почта = Выборка.Почта;
    Если ЗначениеЗаполнено(Выборка.Почта) Тогда
    стр.ЕстьПочта = Истина;
    Иначе
    стр.ЕстьПочта = Ложь;
    КонецЕсли;
    КонецЕсли;
    КонецЦикла;

    Возврат Таб;
    КонецФункции
```

Если почта не нужна, а только список пользователей, ввел параметр ЕстьПочта.
Может ещё как-то можно улучшить код?
---
Не понял как его использовать.
---
```1c
    ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(Выборка.ИдентификаторПользователяИБ);
```

Сопоставление по имени это устаревший механизм.
---
ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(Выборка.ИдентификаторПользователяИБ);
Спасибо за совет.
Поправил так:

```1c
    Функция ПолучитьВсехПользователей(ИмяРоли)

    Таб = Новый ТаблицаЗначений;
    Таб.Колонки.Добавить("Пользователь");
    Таб.Колонки.Добавить("Почта");
    Таб.Колонки.Добавить("ЕстьПочта");

    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ
    | Пользователи.Ссылка КАК Ссылка,
    | Пользователи.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ,
    | КИ.Представление КАК Почта
    |ИЗ
    | Справочник.Пользователи КАК Пользователи
    | ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи.КонтактнаяИнформация КАК КИ
    | ПО Пользователи.Ссылка = КИ.Ссылка
    | И (КИ.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты))
    |ГДЕ
    | Пользователи.ПометкаУдаления = ЛОЖЬ";

    Выборка = Запрос.Выполнить().Выбрать();

    Пока Выборка.Следующий() Цикл

    ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(Выборка.ИдентификаторПользователяИБ);

    Если НЕ ПользовательИБ = Неопределено И ПользовательИБ.Роли.Содержит(Метаданные.Роли[ИмяРоли]) Тогда
    стр = Таб.Добавить();
    стр.Пользователь = Выборка.Ссылка;
    стр.Почта = Выборка.Почта;
    Если ЗначениеЗаполнено(Выборка.Почта) Тогда
    стр.ЕстьПочта = Истина;
    Иначе
    стр.ЕстьПочта = Ложь;
    КонецЕсли;
    КонецЕсли;
    КонецЦикла;

    Возврат Таб;
    КонецФункции
```
---
Метаданные.Роли[ИмяРоли]
Чтобы постоянно к метаданным не обращаться, можно присвоить это переменной ДО цикла.
---
А теперь есть, что ещё можно оптимизировать? :)

```1c
    Функция ПолучитьВсехПользователей(ИмяРоли, Знач СПочтой = Ложь)

    // Пример: ПолучитьВсехПользователей("ПолныеПрава", Истина);

    РольВМетаданных = Метаданные.Роли[ИмяРоли];

    Таб = Новый ТаблицаЗначений;
    Таб.Колонки.Добавить("Пользователь");

    Если СПочтой Тогда
    Таб.Колонки.Добавить("Почта");
    Таб.Колонки.Добавить("ЕстьПочта");
    КонецЕсли;

    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ
    | Пользователи.Ссылка КАК Ссылка,
    | Пользователи.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ,
    | КИ.Представление КАК Почта
    |ИЗ
    | Справочник.Пользователи КАК Пользователи
    | ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи.КонтактнаяИнформация КАК КИ
    | ПО Пользователи.Ссылка = КИ.Ссылка
    | И (КИ.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты))
    |ГДЕ
    | Пользователи.ПометкаУдаления = ЛОЖЬ";

    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл

    ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(Выборка.ИдентификаторПользователяИБ);

    Если НЕ ПользовательИБ = Неопределено И ПользовательИБ.Роли.Содержит(РольВМетаданных) Тогда
    стр = Таб.Добавить();
    стр.Пользователь = Выборка.Ссылка;

    Если СПочтой Тогда
    стр.Почта = Выборка.Почта;
    Если ЗначениеЗаполнено(Выборка.Почта) Тогда
    стр.ЕстьПочта = Истина;
    Иначе
    стр.ЕстьПочта = Ложь;
    КонецЕсли;
    КонецЕсли;

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

    Возврат Таб;
    КонецФункции
```
---
Как вариант, заменить это
НЕ ПользовательИБ = Неопределено
на это

```1c
    ПользовательИБ <> Неопределено
```

Еще, если хочется пооптимизировать, можно сделать 2 разных запроса - если нужна почта и если не нужна.
Но в целом и так норм, имхо.
---
(12)
Выборка.ИдентификаторПользователяИБ
его можно в запросе проверить на заполненность, чтобы дальше не проверять?
у него тип УникальныйИдентификатор.
---
А зачем?
---
ЛЕВОЕ
на
```1c
    Внутреннее
```
---
Смешно=)
---
Аргументируйте Вашу реакцию!

(12) рекомендация по только тем у кого есть потча!
---
Собственно, вы сами написали, что не будет пользователей без почты.
Кажется, что смысл функции в другом - вернуть пользователей с ролью, а есть у них почта или нет, это вообще никак не связано.
---
Сейчас код выглядит так:

```1c
    Функция ПолучитьВсехПользователей(ИмяРоли, Знач СПочтой = Ложь)

    // Пример: ПолучитьВсехПользователей("ПолныеПрава", Истина);

    РольВМетаданных = Метаданные.Роли[ИмяРоли];

    Таб = Новый ТаблицаЗначений;
    Таб.Колонки.Добавить("Пользователь");

    Если СПочтой Тогда
    Таб.Колонки.Добавить("Почта");
    Таб.Колонки.Добавить("ЕстьПочта");
    КонецЕсли;

    Запрос = Новый Запрос;

    Если НЕ СПочтой Тогда // Это условие чаще выполняется, поэтому оно указано в начале Если.

    Запрос.Текст = "ВЫБРАТЬ
    | Пользователи.Ссылка КАК Ссылка,
    | Пользователи.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ
    |ИЗ
    | Справочник.Пользователи КАК Пользователи
    |ГДЕ
    | Пользователи.ПометкаУдаления = ЛОЖЬ";
    Иначе

    Запрос.Текст = "ВЫБРАТЬ
    | Пользователи.Ссылка КАК Ссылка,
    | Пользователи.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ,
    | КИ.Представление КАК Почта
    |ИЗ
    | Справочник.Пользователи КАК Пользователи
    | ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи.КонтактнаяИнформация КАК КИ
    | ПО Пользователи.Ссылка = КИ.Ссылка
    | И (КИ.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты))
    |ГДЕ
    | Пользователи.ПометкаУдаления = ЛОЖЬ";

    КонецЕсли;

    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл

    ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(Выборка.ИдентификаторПользователяИБ);

    Если ПользовательИБ <> Неопределено И ПользовательИБ.Роли.Содержит(РольВМетаданных) Тогда
    стр = Таб.Добавить();
    стр.Пользователь = Выборка.Ссылка;

    Если СПочтой Тогда
    стр.Почта = Выборка.Почта;
    Если ЗначениеЗаполнено(Выборка.Почта) Тогда
    стр.ЕстьПочта = Истина;
    Иначе
    стр.ЕстьПочта = Ложь;
    КонецЕсли;
    КонецЕсли;

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

    Возврат Таб;
    КонецФункции
```
---
маленькая поправочка, надо бы:

```1c
    ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(Новый УникальныйИдентификатор(Выборка.ИдентификаторПользователяИБ));
```
---
тогда мы потеряем пользователей, у которых почта не заполнена.
---
стр.ЕстьПочта
Не проще сделать так:
стр.ЕстьПочта = ЗначениеЗаполнено(Выборка.Почта) ;
---
И не у всех может быть заполнена КИ(КонтактнаяИнфа).
---
КАК Пользователи
КАК КИ

Пользователи.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты)
КИ.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты)

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