---
name: ssl-patterns
description: "MUST use WHEN используешь или расширяешь функциональность БСП (Библиотека стандартных подсистем). Provides каталог готовых функций ОбщегоНазначения и правила вызова подсистем без дублирования."
alwaysApply: false
---

# Паттерны работы с БСП (Библиотека стандартных подсистем)

Код БСП проверен на миллионах установок, обновляется централизованно, знаком другим разработчикам. Дублирование БСП — антипаттерн.

---

## Правило 1: Модуль ОбщегоНазначения — основной «швейцарский нож»

Прежде чем писать свою реализацию, проверьте — возможно, в БСП уже есть готовая функция.

| Функция | Когда использовать |
|---------|-------------------|
| `ЗначениеРеквизитаОбъекта()` | Вместо `Ссылка.Реквизит` (избегаем точечную нотацию) |
| `ЗначенияРеквизитовОбъекта()` | Несколько реквизитов одним вызовом |
| `СообщитьПользователю()` | Сообщение с привязкой к полю (вместо `Сообщить()`) |
| `МенеджерОбъектаПоСсылке()` | Вместо `Выполнить("Справочники." + Имя)` |
| `ПодсистемаСуществует()` | Условный вызов модулей |
| `ОбщийМодуль()` | Динамический вызов модуля БСП |
| `ЭтоСсылка()` | Валидация параметров |
| `СсылкаСуществует()` | Проверка перед обращением |

```bsl
// ПЛОХО: три обращения к БД через точку
Наименование = КонтрагентСсылка.Наименование;
ИНН = КонтрагентСсылка.ИНН;
Ответственный = КонтрагентСсылка.ОсновнойМенеджер;

// ПРАВИЛЬНО: одно обращение через БСП
РеквизитыКонтрагента = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
    КонтрагентСсылка,
    "Наименование, ИНН, ОсновнойМенеджер");
```

---

## Правило 2: СтроковыеФункцииКлиентСервер — работа со строками

Модуль содержит оптимизированные функции, корректно обрабатывающие edge cases.

| Функция | Когда использовать |
|---------|-------------------|
| `ПодставитьПараметрыВСтроку()` | Аналог `СтрШаблон()`, с дополнительными проверками |
| `СтрокаСЧисломПредметов()` | Склонение: «5 документов», «1 документ» |
| `ЕстьНедопустимыеСимволы()` | Валидация ввода |
| `ТолькоЦифрыВСтроке()` | Валидация ИНН, КПП |
| `РазложитьСтрокуВМассивПодстрок()` | Парсинг по разделителю |

```bsl
// Склонение: «1 документ», «2 документа», «5 документов»
ТекстОповещения = СтроковыеФункцииКлиентСервер.СтрокаСЧисломПредметов(
    КоличествоДокументов,
    НСтр("ru = 'документ, документа, документов'"));
```

---

## Правило 3: ОбщегоНазначенияКлиентСервер — утилиты для обеих сред

Директива `&НаКлиентеНаСервереБезКонтекста` — доступен и на клиенте, и на сервере.

| Функция | Описание |
|---------|----------|
| `ДополнитьМассив()` | Объединение двух массивов |
| `ДополнитьСтруктуру()` | Объединение двух структур |
| `СвойствоСтруктуры()` | Безопасное чтение свойства (значение по умолчанию если нет) |
| `ПроверитьПараметр()` | Валидация типа с информативной ошибкой |

```bsl
// Безопасный доступ с значением по умолчанию
ДатаНачала = ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(
    ПараметрыОтчёта, "ДатаНачала", НачалоГода(ТекущаяДатаСеанса()));
```

---

## Правило 4: Стратегия поиска функций БСП

### Алгоритм: LSP -> grep -> AI

1. **LSP** (если доступен): `navigate_symbol("ЗначенияРеквизитовОбъекта")`
2. **Поиск по тексту**: `grep -r "Функция.*КурсВалюты" src/CommonModules/`
3. **AI-ассистент**: «Есть ли в БСП функция для получения курса валюты на дату?»

### Когда писать своё vs использовать БСП

| Ситуация | Решение |
|----------|---------|
| В БСП есть подходящая функция | **Используй БСП** |
| В БСП есть похожая, но с лишним функционалом | **Используй БСП** — лишнее не мешает |
| Нужной функции нет в БСП | Пиши своё в стиле БСП |
| Конфигурация без БСП | Пиши своё |

---

## Правило 5: Работа с журналом регистрации через БСП

См. `error-handling`, правило 7.

---

## Правило 6: РаботаСФайлами — вместо прямого ФайловаяСистема

Прямая работа с файлами не учитывает: права доступа, временные файлы, кросс-платформенность.

```bsl
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
Попытка
    ТабличныйДокумент.Записать(ИмяВременногоФайла, ТипФайлаТабличногоДокумента.XLSX);
    // ... работа с файлом ...
Исключение
    // Обработка ошибки
КонецПопытки;

// Явное удаление
УдалитьФайлы(ИмяВременногоФайла);
```

---

## Правило 7: Типовые паттерны БСП

### Проверка заполнения (ОбработкаПроверкиЗаполнения)

```bsl
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)

    Если НЕ ЗначениеЗаполнено(Контрагент) Тогда
        ОбщегоНазначения.СообщитьПользователю(
            НСтр("ru = 'Не заполнен контрагент.'"),
            ЭтотОбъект, "Контрагент",, Отказ);
    КонецЕсли;

    // Условное исключение реквизитов из проверки
    Если ВидОперации = Перечисления.ВидыОпераций.Услуга Тогда
        ОбщегоНазначенияКлиентСервер.УдалитьЗначениеИзМассива(
            ПроверяемыеРеквизиты, "Склад");
    КонецЕсли;

КонецПроцедуры
```

### Получение данных для печати

```bsl
Процедура Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм,
        ОбъектыПечати, ПараметрыВывода) Экспорт

    Если УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "Счёт") Тогда

        ТабличныйДокумент = Новый ТабличныйДокумент;
        ТабличныйДокумент.КлючПараметровПечати = "Документ.РеализацияТоваровУслуг.Счёт";

        УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(
            КоллекцияПечатныхФорм, "Счёт", НСтр("ru = 'Счёт на оплату'"),
            ТабличныйДокумент);
    КонецЕсли;

КонецПроцедуры
```

---

## Правило 8: Не дублируйте функционал БСП

| Что часто пишут сами | Что есть в БСП |
|----------------------|----------------|
| Получение реквизита по ссылке | `ОбщегоНазначения.ЗначениеРеквизитаОбъекта()` |
| Подстановка в строку | `СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку()` |
| Склонение слов | `СтроковыеФункцииКлиентСервер.СтрокаСЧисломПредметов()` |
| Отправка почты | `РаботаСПочтовымиСообщениями` |
| Курс валюты | `РаботаСКурсамиВалют.ПолучитьКурсВалюты()` |
| Длительная операция в фоне | `ДлительныеОперации.ВыполнитьФункцию()` |
| Хранение секретов / паролей | `БезопасноеХранилище.ПрочитатьДанные()` |
| Профили прав доступа | `ГруппыДоступаПользователей` / `ПрофилиГруппДоступа` |
| Регистрация внешней обработки | `СведенияОВнешнейОбработке()` |

---

## Правило 9: Модули «КлиентСервер» — разделение ответственности

| Суффикс модуля | Среда | Пример |
|----------------|-------|--------|
| (без суффикса) | Сервер | `ОбщегоНазначения` |
| `Клиент` | Клиент | `ОбщегоНазначенияКлиент` |
| `КлиентСервер` | Обе среды | `ОбщегоНазначенияКлиентСервер` |
| `ПовтИсп` | Сервер, с кэшированием | `ОбщегоНазначенияПовтИсп` |

Для клиентского кода формы — ищите сначала в `*КлиентСервер`, потом в `*Клиент`. Для серверного — в основном модуле (без суффикса). `*ПовтИсп` — для часто запрашиваемых справочных данных.

---

## Правило 10: Длительные операции (ДлительныеОперации)

Используй подсистему `ДлительныеОперации` для любой серверной работы дольше ~3 сек. Не блокируй UI самодельным циклом ожидания.

```bsl
// Запуск фоновой задачи
&НаСервере
Функция ЗапуститьОперацию(Параметры)
    ПараметрыФона = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
    ПараметрыФона.НаименованиеФоновогоЗадания = НСтр("ru = 'Обработка данных'");
    Возврат ДлительныеОперации.ВыполнитьФункцию("ОбщийМодуль.ФункцияДляФона",
        ПараметрыФона, Параметры);
КонецФункции

// Подключение ожидания на клиенте
&НаКлиенте
Процедура ЗапуститьОперациюНаКлиенте()
    Операция = ЗапуститьОперацию(ПараметрыРасчёта);
    ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
    ПараметрыОжидания.ВыводитьПрогресс = Истина;
    ДлительныеОперацииКлиент.ОжидатьЗавершение(Операция,
        Новый ОписаниеОповещения("ОперацияЗавершена", ЭтотОбъект), ПараметрыОжидания);
КонецПроцедуры

// Обработка результата
&НаКлиенте
Процедура ОперацияЗавершена(Операция, ДополнительныеПараметры) Экспорт
    Если Операция = Неопределено Тогда
        Возврат; // Отменена пользователем
    КонецЕсли;
    Если Операция.Статус = "Ошибка" Тогда
        СтандартныеПодсистемыКлиент.ОбработатьОшибкуФоновогоЗадания(Операция);
        Возврат;
    КонецЕсли;
    // Получить результат
    РезультатОперации = ПолучитьРезультатСервер(Операция.АдресРезультата);
КонецПроцедуры
```

**Ключевые правила:**
- Передавай прогресс через `ДлительныеОперации.СообщитьПрогресс()` внутри фоновой процедуры.
- Не храни состояние между шагами в глобальных переменных — используй параметры задания.
- Реализуй идемпотентный перезапуск: повторный вызов с теми же параметрами должен давать тот же результат.

---

## Правило 11: Безопасное хранилище (БезопасноеХранилище)

Никогда не храни пароли, токены и секреты в:
- реквизитах объектов метаданных
- константах конфигурации
- журнале регистрации
- системе контроля версий (конфиги, xml)

```bsl
// Запись секрета
БезопасноеХранилище.Записать(ЭтотОбъект, Новый Структура("Пароль", ПарольПользователя));

// Чтение секрета
ДанныеХранилища = БезопасноеХранилище.ПрочитатьДанные(ЭтотОбъект);
Если ДанныеХранилища <> Неопределено Тогда
    Пароль = ДанныеХранилища.Пароль;
КонецЕсли;

// Удаление при удалении объекта
БезопасноеХранилище.Удалить(ЭтотОбъект);
```

В обработчике `ПередУдалением` объекта всегда вызывай `БезопасноеХранилище.Удалить()` — иначе «осиротевшие» записи накапливаются в хранилище.

---

## Правило 12: Профили групп доступа (ПрофилиГруппДоступа)

При разработке подсистем с ролевым доступом — используй механизм профилей БСП вместо прямого назначения ролей.

```bsl
// Пример описания профиля в ОписаниеПрофилейГруппДоступа()
Профиль = УправлениеДоступом.ОписаниеПрофиля();
Профиль.Идентификатор = "ИдентификаторПрофиля_UUID";
Профиль.Наименование   = НСтр("ru = 'Менеджер по продажам'");
Профиль.Роли.Добавить("РольМенеджерПродаж");
Профили.Добавить(Профиль);
```

**Ключевые правила:**
- Идентификатор профиля — фиксированный UUID, не меняется при переименовании.
- Для повышенных привилегий используй `ПривилегированныйРежим()` строго локально, сразу снимай после операции.
- Проверку прав выполняй через `УправлениеДоступом.ПроверитьДопустимостьДействия()`, не через `РольДоступна()` напрямую — последнее не учитывает RLS.

---

## Правило 13: Внешние обработки и расширения (СведенияОВнешнейОбработке)

Регистрация внешней обработки в БСП-базе требует функции `СведенияОВнешнейОбработке()` в основном модуле обработки.

```bsl
// В модуле обработки
Функция СведенияОВнешнейОбработке() Экспорт

    СведенияОВнешнейОбработке = ДополнительныеОтчётыИОбработки.СведенияОВнешнейОбработке();
    СведенияОВнешнейОбработке.Вид = ДополнительныеОтчётыИОбработкиКлиентСервер
        .ВидОбработки().ДополнительнаяОбработка;
    СведенияОВнешнейОбработке.Наименование = НСтр("ru = 'Моя обработка'");
    СведенияОВнешнейОбработке.Версия       = "1.0";
    СведенияОВнешнейОбработке.БезопасныйРежим = Истина;

    // Описание команды
    Команда = СведенияОВнешнейОбработке.Команды.Добавить();
    Команда.Представление = НСтр("ru = 'Выполнить'");
    Команда.Идентификатор = "Выполнить";
    Команда.ИспользованиеКонтекста = ДополнительныеОтчётыИОбработкиКлиентСервер
        .ИспользованиеКонтекстаКоманды().ВПроцедуреВыполнитьКоманду;

    Возврат СведенияОВнешнейОбработке;

КонецФункции

// Точка входа для команды
Процедура ВыполнитьКоманду(Идентификатор, ПараметрыКоманды, ОбъектыНазначения) Экспорт
    // ... реализация ...
КонецПроцедуры
```

---

## Поиск аналогов через Напарника

Если `search_ssl_functions` не дал результата — `ask_ai_assistant` (шаблон VALIDATE_BSL из `buddy-prompting`): передать фрагмент кода, получить рекомендации по замене на методы БСП. Также `SEARCH_DOCS` для документации по конкретному методу БСП.

---
depends_on: []
---
