Перейти к основному содержанию
`Ctrl`+`K`

# Руководство по Ревью Кода Scala/SQL (GlobalFramework)

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

## 1. Метаописания и Структура Классов

### Именование классов

Имя класса должно быть уникальным, на латинице в формате `<Модуль>_<Имя>` (единственное число, именительный падеж, например, `Lbr_Book`). Имена сущностей регистрозависимы и должны точно совпадать с ожиданиями фреймворка (CamelCase).

Цель: Обеспечить ясность структуры, избежать конфликтов, гарантируя корректную работу фреймворка и улучшить читаемость кода.

### Именование атрибутов

Атрибуты должны иметь осознанные имена, без транслита, соответствующие содержанию поля (camelCase).

Цель: Улучшить читаемость и документировать структуру данных, соответствовать стилю Scala и GSF.

### Типы атрибутов

Для каждого поля должно быть указано имя, тип данных и связь. Простые поля используют типы `NLong`, `NNumber`, `NString`, `NDate` и т.д.; ссылочные поля – `Reference`, `VariableReference` или `GID`. Проверьте, что все `Reference` указывают на существующий класс и тип правильный (например, `GID` для переменной ссылочности). Типы полей должны быть корректно указаны и соответствовать БД (например, `NLong`, `NNumber`, `NBigDecimal`, `NString`, `NGid`, `NDate` и т.п.).

Цель: Обеспечить корректную работу с `null`-значениями, безопасность типов, интеграцию с фреймворком и согласованность схемы БД и метаданных.

### Соответствие БД и метаданных

Названия и типы полей в метаданных должны совпадать с колонками в БД (`NLong` → `bigint`, `NString` → `varchar/text`, `NDate` → `date`). При джоинах убедитесь в совместимости типов (избегайте неявных преобразований идентификаторов и используйте корректные приведения).

Цель: Обеспечить корректную работу ORM, избежать ошибок типов и обеспечить производительность запросов.

### Связи

Проверить корректность внешних ключей/ссылок, отраженных в метаданных. Использовать `GID` для переменной ссылочности в одном поле. Убедиться, что все связи отражены в метаданных (поля помечены как `Reference` или `VariableReference`).

Цель: Обеспечить целостность данных, корректную навигацию по объектной модели и работоспособность связанных функций GSF.

### JSON/Jsonb-поля

Если используется динамическое расширение (JSONB), проверьте настройку контейнера (тип `Json`) и работающую схему ключ–значение. Также проверьте необходимость индексов.

Цель: Обеспечить эффективное хранение и извлечение гибких структур данных, при необходимости — повысить производительность поиска.

### Отсутствие дубликатов

Убедиться, что в метаописаниях нет дублирующих или неиспользуемых полей и неактуальных связей.

Цель: Улучшить читаемость метаданных, упростить поддержку, снизить вероятность логических ошибок и избыточной сложности модели.

## 2. Конфигурация Модулей GSF

### `project.yaml`

Проверьте секции конфигурации проекта (версии Scala/Java, параметры сборки, список модулей). Например: `scalaFeatureRelease`, `sbtPlugin`, `modules` с описанием каждого модуля – имя, `source` (репозиторий), `branch`, флаг `isPublish`. Убедитесь, что указаны все нужные модули (например, `calculation`, `source`, `target`, `custom` при наличии) и их настройки верны.

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

### Структура каталогов

Поддерживайте корректную структуру каталогов (как сгенерировано Configurator), включая папки `src`, `resources`, тесты и пр.

Цель: Облегчить навигацию по коду, упростить настройку IDE и сборочных скриптов, повысить согласованность между проектами.

### Зависимости

Зависимости между модулями должны быть описаны явно в `build.sbt`, а так же продублированы в `module-info.xml` как метаданные

Цель: Обеспечить корректную сборку, изоляцию модулей, избежать проблем при сборке и развертывании.

### Конфигурации сборки/публикации

Конфигурации сборки (например, sbt) и плагины должны быть корректны. Названия модулей и подпроектов должны соответствовать соглашениям.

Цель: Обеспечить стабильность и воспроизводимость процесса сборки и публикации, улучшить читаемость и поддержку проекта.

## 3. Использование стандартных компонентов GSF

### Использование сервисов GSF

По возможности используйте готовые сервисы фреймворка: аудит изменений, автонумерацию, группировку, поиск по шаблону, прикреплённые файлы и т.д. Например, если требуется фильтрация классов, включите «Универсальный фильтр» – он позволит фильтровать по полям класса и его коллекций на уровне БД. Группировка объектов должна работать через механизмы группировки GSF.

Цель: Снизить вероятность ошибок, ускорить разработку, обеспечить единообразие функциональности, упростить поддержку и обновление.

### Кэширование и запросы

Если используется кэширование GSF (Shared-cache), убедитесь, что для часто запрашиваемых сущностей настроены `cache-index` в ORM и при запросах используется `.unique()`. Используйте объектные запросы (`OQuery`) с `tryCacheQueryResults()` там, где это целесообразно. Проверьте стратегии инвалидирования кэша.

Цель: Повысить производительность за счёт снижения обращений к БД, обеспечить актуальность данных в кэше.

### Логирование

Для записи логов в базу задействуйте встроенный `LogTransaction` (выделяет отдельную лог-сессию). При большом количестве записей выполняйте `commitByInterval()` (например, каждые 1000 записей). Такой подход изолирует логи от основной транзакции и снижает нагрузку на БД.

Цель: Изолировать логи от основной транзакции, избежать потери логов при откате, снизить вероятность конфликтов и нагрузку на БД.

## 4. Обработка Ошибок и Зависимости

### Прикладные исключения

В коде обрабатывайте только «прикладные» исключения — наследники `AppException`. Системные исключения не перехватывайте, а выбрасывайте дальше, пропускайте (так фреймворк сможет корректно прервать сессию).

Цель: Чётко разделить бизнес-логику ошибок и системные сбои, предотвратить скрытие серьёзных проблем и нарушение целостности данных.

### Объявление исключений

Создавайте свои исключения как подклассы `AppException` и заводите для них фабрику (например, `object ExceptionName extends ExceptionFactory(new ExceptionName(_))`). Выбрасывайте исключения через `throw AppException(…)` или `e.raise(…)` (сохранит стек вызовов). При необходимости логирования ошибки используйте отдельную лог-транзакцию.

Цель: Обеспечить единообразие, упростить обработку, легко идентифицировать тип ошибки, гарантировать запись лога при откате основной транзакции.

### Логирование ошибок

При необходимости логирования ошибки используйте отдельную лог-транзакцию. В `try/catch` используйте минимальное информативное логирование; причина исключения не скрывается.

Цель: Изолировать запись лога от основного процесса, избежать засорения логов, сохранить ключевую информацию для диагностики, сохранить причину исключения.

### Транзакции и зависимые задачи

Избегайте слишком длинных транзакций (несколько минут и более) – PostgreSQL плохо их переносит. Разбивайте большие операции на логические блоки и выполняйте `session.commitWorkAuto()` после каждой партии изменений. Например, при массовой загрузке в цикле вызывайте `commitWorkAuto()` через заданный интервал, чтобы регулярно сбрасывать пакет в БД. При этом используйте `flush()` / `commit()` осмотрительно: не делайте неожиданных коммитов внутри server-side методов, чтобы не прерывать ожидания отката.

Цель: Снизить вероятность конфликтов, рост WAL, улучшить общую производительность БД, уменьшить вероятность сбоев, облегчить откат, сохранить согласованность данных.

## 5. Особенности SQL в GSF (Валидации и Ограничения)

### Оптимизация запросов

Всегда проверяйте план выполнения (`EXPLAIN`) для поиска «узких мест». Сложные запросы по возможности разбивайте на подзапросы, упрощая логику.

Цель: Выявить проблемы с производительностью, улучшить читаемость, потенциально повысить производительность.

### Индексы и параметры

При фильтрации используйте индексируемые поля (например, не фильтруйтесь по служебным столбцам типа `gid` без индекса). Всегда передавайте значения в запрос через параметры (`?` или именованные), чтобы избежать SQL-инъекций и позволить СУБД кэшировать планы.

Цель: Обеспечить быстрое выполнение запросов, защитить от SQL-инъекций, ускорить выполнение за счёт переиспользования планов.

### Соединения и типы

При `JOIN` таблиц по ключам используйте одинаковые типы полей (например, связывайте `bigint` с `NLong`); избегайте неявных преобразований (например, конвертации `varchar` и `text`). При необходимости приводите типы явно.

Цель: Обеспечить эффективное использование индексов, избежать дорогостоящих преобразований данных, улучшить производительность.

### Рекурсивные запросы

Если пишете рекурсивные CTE, обязательно добавьте условие выхода (обычно это `WHERE id NOT IN (…)` с ограничением глубины) и защиту от зацикливания. И используйте `NOT EXISTS` вместо `NOT IN`, чтобы учесть `NULL` и не терять результаты.

Цель: Предотвратить бесконечные циклы в запросе, обеспечить надёжность при наличии `NULL`-значений.

## 6. Требования к Структуре Scala-Кода

### Соглашения по стилю

Соблюдайте стандарты именования Scala (классы – `CamelCase`, методы/переменные – `camelCase`) по руководству Scala. Global3-FrameWork регистрозависим, поэтому имена сущностей (классов, полей, методов) должны точно совпадать с тем, как они объявлены. Не используйте подчёркивания в именах (за исключением разделителя модуля и имени класса).

Цель: Обеспечить корректную работу фреймворка, повысить читаемость кода, облегчить его понимание и поддержку.

### D/A-паттерн кода

При генерации классов учтите, что фреймворк генерирует две иерархии: `Domain`- и `Application`-части. Доменные классы (`ClassDpi`, `ClassDvi`) перезаписываются при генерации и содержат «скелет» логики, а прикладные классы (`ClassApi`, `ClassAvi`) – расширяют их и используются для ручной бизнес-логики. Логика должна писаться только в прикладных классах (Api/Avi), доменные файлы не редактируйте.

Цель: Обеспечить стабильность при регенерации кода, изолировать бизнес-логику от автоматически генерируемого кода.

### Null-типы GSF

Для работы со значениями из БД используйте расширенные типы GSF (`NLong`, `NNumber`, `NGid`, `NDate`, `NString`, `NBigDecimal` и т.д.), которые безопасно обрабатывают `null`. Например, идентификаторы – `NLong` (фабрика `nl`), числовые поля – `NNumber` или `NBigDecimal` (фабрика `nr`). Важное правило: для финансовых расчётов используйте `NBigDecimal` (BigDecimal), чтобы избежать ошибок двоичной арифметики. При сравнении nullable-значений в скриптах GSF применяйте оператор `===` (предотвращает NPE) вместо стандартного `==`.

Цель: Защитить от `NullPointerException`, обеспечить корректную обработку `null`, гарантировать точность вычислений для финансовых данных, обеспечить безопасность при сравнениях.

## 7. Общие Принципы и Стиль Кода

### Комментарии

Код должен сопровождаться понятными комментариями.

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

### Именование

Переменным и атрибутам необходимо давать осознанные имена, транслит необходимо избегать. Следуйте scala-конвенциям наименования сущностей.

Цель: Улучшить читаемость, документировать структуру данных, соответствовать стилю Scala и GSF.

### Форматирование

`sql` и `scala` код должны быть отформатированы и не превращаться в «кашу».

Цель: Улучшить читаемость, облегчить понимание и сопровождение кода.

### Scaladoc

Писать scaladoc к методам, классам и переменным.

Цель: Предоставить официальную документацию к API, облегчить поддержку и использование кода другими разработчиками.

## 8. Безопасность и Надёжность

### `null`

Использование `null` должно быть полностью исключено для всех ссылочных типов.

Цель: Предотвратить `NullPointerException`, использовать безопасные GSF-типы.

### `null` для AnyVal

Для значимых типов (AnyVal), таких как `JObject`, запрещено использовать `null` или его приведение (`null.asInstanceOf[JObject]`). Вместо этого необходимо использовать пустые экземпляры (например, `JObject()`), а проверку на наличие данных выполнять через `.nonEmpty`.

Цель: Предотвратить `NullPointerException` при работе с такими типами.

### `head` / `get`

Необходимо избегать использования `head` или `get`; вместо них следует использовать `headOption` или `getOption`, чтобы избежать `NPE`.

Цель: Обеспечить безопасную обработку отсутствия значения, сделать код более надёжным.

### `if-else` / `pattern-matching`

При ветвлении кода `if-else` необходимо указывать ветку `else`; для `pattern-matching` обязателен случай `case _ =>`.

Цель: Гарантировать обработку всех возможных сценариев, предотвратить `MatchError`, сделать код более надёжным.

## 9. Производительность

### `.view`

При использовании нескольких комбинаторов подряд (например, `arr.map.flatMap.filter`) необходимо добавлять `.view` в начало, чтобы не создавались временные коллекции.

Цель: Улучшить производительность за счёт снижения расхода памяти.

### Pattern matching в комбинаторах

При использовании комбинаторов по карте необходимо применять pattern matching для деструктуризации кортежей. Плохо: `any_map.foreach { tpe => tpe._1 + tpe._2 }`. Хорошо: `any_map.foreach { case (key, value) => key + value }`.

Цель: Сделать код более выразительным, типобезопасным и читаемым.

### Изменяемые коллекции

В параметрах методов и возвращаемых значениях не должны использоваться изменяемые коллекции `collection.mutable`.

Цель: Повысить безопасность и предсказуемость кода, уменьшить вероятность ошибок.

### `ArrayBuffer`

Необходимо использовать `ArrayBuffer` вместо `ListBuffer`.

Цель: Обеспечить лучшую производительность для операций вставки и доступа по индексу.

### Выбор коллекции

При выборе коллекции (`List`, `Array`, `Vector`, `ArrayBuffer`) необходимо учитывать асимптотическую сложность операций (вставка, доступ по индексу), чтобы обеспечить оптимальную производительность.

Цель: Использовать подходящие структуры данных для ускорения выполнения операций.

### Большие реестры

Получение данных из больших реестров должно выполняться реляционными запросами в БД по максимальному количеству условий с предварительным `session.flush()`, чтобы не перегружать ОЗУ сервера.

Цель: Снизить нагрузку на память сервера приложений, повысить производительность за счёт фильтрации на уровне БД.

## 10. Работа с API и ROP

### HTTP-пакеты

Прямое обращение к http пакетам запрещено; необходимо использовать `btk_httppkg`.

Цель: Обеспечить централизованное управление HTTP-вызовами, соблюдение стандартов безопасности и логирования.

### Файлы

При работе с файлами необходимо использовать стандартное api — `btk_fileapi`.

Цель: Гарантировать корректную обработку файлов, безопасность и совместимость с инфраструктурой.

### `copyAro`

Метод `copyAro` не должен использоваться, так как все данные провайдера строки копируются в оперативную память.

Цель: Предотвратить чрезмерный расход памяти, особенно при работе с большими объектами или в циклах.

### Загрузка ROP по `gid`

При загрузке ропов по `gid` необходимо использовать `Btk_Pkg.loadByGid` (обёрнутый в `TryApp`). Полученный `rop` должен матчиться с нужным API, например: `case Btk_ClassApi(rop) =>`.

Цель: Обеспечить безопасную загрузку объектов и правильную их типизацию.

### Поиск по коду

Для поиска по коду необходимо использовать методы `____Api().findByMnemoCode("")`, а не прямые запросы. Объекты API должны быть объявлены через `lazy val`.

Цель: Использовать встроенные механизмы GSF, повысить читаемость и поддерживаемость кода.

### `getVar`

Вместо конструкции `api().load(getVar("idAnyClass").asNLong)` необходимо использовать метод `get`, так как нет гарантии, что `getVar` вернёт значение вместо `null`.

Цель: Обеспечить более безопасное и надёжное получение данных.

### Использование source-generated конструкций

Для доступа к атрибутам необходимо использовать конструкции, сгенерированные генератором источников (например, `rop.get(_.attr)`), а не методы `getByAttrName` и `getAttrByName`, так как последние используют рефлексию, ошибки которой могут быть обнаружены только во время выполнения.

Цель: Обеспечить безопасность на этапе компиляции, предотвратить ошибки, связанные с рефлексией.

## 11. Работа с Базой Данных и SQL (Дополнения)

### `checkworkability`

Обращение к базе данных в `checkworkability` должно быть исключено; нужные данные необходимо выносить в `onRefresh` и делать доступными через `getVar`.

Цель: Критично для производительности UI, предотвратить неотзывчивость интерфейса.

### Вложенные циклы

Запросы внутри вложенных циклов использовать нельзя.

Цель: Избежать экспоненциального роста числа запросов к БД, сохранить производительность.

### `onRefresh`

В `onRefresh` необходимо использовать `refreshByKey(parent)` вместо `byKey(parent)`.

Цель: Обновить данные в кэше, повысить эффективность.

### `@FlushBefore`

При необходимости использования `onRefresh` на основе `selectStatement` в детализации документа необходимо отключать `@FlushBefore`, чтобы избежать ошибок при создании документа с обязательными атрибутами.

Цель: Предотвратить конфликты сессии и ошибки при инициализации документа.

### `OQuery` и итерация

При прогрузке в кеш через `OQuery` необходимо использовать итерацию по списку или получение элемента в конце, что гарантирует выполнение запроса.

Цель: Обеспечить корректное поведение ленивых запросов.

### `OQuery` и условия

Условия в `OQuery`, зависящие от полей, которые могут измениться в кеше, должны обрабатываться одним из способов: предварительный `session.flush()`; вынос условия в `.filter` результата; использование транзакционного индекса.

Цель: Гарантировать, что условия в запросе будут учитывать актуальное состояние данных.

### Интерполяция строк

Вместо интерполяции строк необходимо использовать метод `on` в `sql` запросах.

Цель: Обеспечить безопасную параметризацию, защитить от SQL-инъекций.

### `Symbol` в `on`

При передаче параметров в запросы на основе Anorm через метод `on` необходимо использовать `Symbol("idGds")`, а не символьные литералы через апостроф (`'idGds`), так как последний устарел.

Цель: Соответствовать современным стандартам, обеспечить корректную работу.

## 12. Архитектура и Дизайн

### `lazy`

При объявлении переменных в классе необходимо использовать ключевое слово `lazy`, чтобы они инициализировались только при первом использовании.

Цель: Улучшить производительность, избежать ненужных вычислений.

### `dataInstall`

При регистрации типов, закладок, атрибутов, функциональных настроек, процедур в `dataInstall` необходимо добавлять `lazy val` с мнемокодами и `id` для зарегистрированных значений.

Цель: Улучшить читаемость и поддержку кода установки данных.

### Изменение параметров

При редактировании метода типы параметров не должны изменяться, это сохраняет обратную совместимость.

Цель: Критично для стабильности API, предотвратить ошибки в использующем коде.

### Новые параметры

Перед добавлением новых параметров в метод необходимо учитывать обратную совместимость. Новые необязательные параметры необходимо добавлять в конец сигнатуры метода.

Цель: Избежать необходимости изменять весь код, который вызывает этот метод.

### Сложная логика в AVI

Сложную логику не нужно расписывать в операции Avi, если она может быть вынесена в API/PKG. AVI-операция должна сводиться к вызову метода из API/PKG, это упрощает поддержку и минимизирует дублирование кода.

Цель: Улучшить поддерживаемость кода, разделить ответственности, тестировать бизнес-логику отдельно от UI-логики.

### Scala-классы с компаньоном

При создании структур Scala-классов с объектом-компаньоном необходимо использовать решение, позволяющее проектное переопределение: `def list(): List = { new List {...} }`.

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

## 13. Работа с Атрибутами и Полями (Дополнения)

### Source-generated доступ

Для доступа к атрибутам необходимо использовать конструкции, сгенерированные генератором источников (например, `rop.get(_.attr)`), а не методы `getByAttrName` и `getAttrByName`, так как последние используют рефлексию, ошибки которой могут быть обнаружены только во время выполнения.

Цель: Обеспечить безопасность на этапе компиляции, предотвратить ошибки, связанные с рефлексией.

### `getSelfVar` с `A.idPerson.name`

В выборках при обращении к стандартным атрибутам необходимо использовать `getSelfVar(A.idPerson.name)` или просто `A.idPerson.asNLong` вместо `getSelfVar("idPerson")`, это позволяет поймать ошибки во время компиляции при удалении атрибута.

Цель: Обеспечить безопасность типов на этапе компиляции.

### Хранимые поля в AVI

Хранимые поля класса в AVI должны получаться через объект `А`, сгенерированный генератором источников, чтобы избежать ошибок времени выполнения при переименовании атрибута в классе.

Цель: Сделать код устойчивым к переименованиям атрибутов.

### `var` в `case class` в `onRefresh`

В `case class`’ах, которые возвращаются в `onRefresh`, атрибуты должны быть мутабельными (`var`).

Цель: Обеспечить корректную работу с UI и обновление данных.

## 14. Строки и JSON

### Интерполяция строк

При сборке строк из статических и динамических элементов необходимо использовать интерполяцию `s"text${value}otherText"` или многострочный вариант: `s"""1-st line |2-nd line |.... |last line""".stripMargin`. Интерполяция должна использоваться вместо конкатенации, так как она оптимальнее и более читаема.

Цель: Повысить читаемость, обеспечить оптимизацию компилятором.

### `.stripMargin`

Для многострочных строк необходимо использовать `|` и `.stripMargin`, чтобы сохранить форматирование и структуру файла.

Цель: Улучшить читаемость многострочных строк в коде.

### `StringBuilder`

При сборке очень больших строк необходимо использовать `StringBuilder` вместо `pos.map(s"...").mkString()`.

Цель: Обеспечить более эффективную работу с большими строками.

### `scala.util.parsing.json`

Использовать нельзя, предпочтительнее `JObject`.

Цель: Использовать интегрированный с GSF и более безопасный тип.

### `Btk_JsonPkg`

Использование `Btk_JsonPkg` необходимо избегать из-за зафиксированного разного поведения при его использовании с `JObject`.

Цель: Предотвратить неожиданное поведение и ошибки.

## 15. Специфика UI: AVI и Списочные Отображения

### `getVar` / `getSelfVar` / `A` в `checkWorkAbility`

В `Avi.checkWorkAbility` и других AVI-операциях списочных отображений значения полей необходимо получать через `getVar`, `getSelfVar` или `A`, а не через `thisRop()`, чтобы избежать ошибки `load id = null` в пустом списке.

Цель: Предотвратить ошибки при работе с пустыми списками.

### Поля с большим объёмом данных

В списочные отображения не должны выводиться поля с большим объёмом данных, чтобы не перегружать сервер. Например, поле `sResponse` журнала сообщений должно быть вынесено на закладку детализации или храниться в файле.

Цель: Повысить производительность UI и улучшить пользовательский опыт.

### `@FlushBefore` в LookUp

В LookUp-отображениях для `onRefresh` необходимо добавлять аннотацию `@FlushBefore(mode = FlushBeforeMode.Disabled)`.

Цель: Предотвратить нежелательные сбросы сессии при обновлении данных в LookUp.

### `DefaultRep#deactivateOper`

Вместо `oper.isActive = someCondition` необходимо использовать `DefaultRep#deactivateOper` для деактивации операций при невыполнении условий, это обеспечивает корректную работу при различных перекрытиях.

Цель: Использовать встроенный механизм фреймворка для корректной деактивации операций.

### `prepareSelectStatement`

Сортировка по умолчанию и передача макросов фильтрации должны быть указаны в `prepareSelectStatement`, а не в `selectStatement`.

Цель: Улучшить структуру и читаемость кода настройки выборки.

## 16. Специфика UI: Кэширование, Логика и Настройки

### Переходы состояний

Переходы состояний должны определяться сравнением `nOrder` и формированием условия из двух частей (из какого в какое), например: `nvStateFrom < 100.nn && nvStateTo >= 100.nn`, это обеспечивает работоспособность логики даже при удалении перехода.

Цель: Сделать логику переходов устойчивой к изменениям структуры состояний.

### `@Setter(refreshAfter = true)`

Аннотация `@Setter(refreshAfter = true)` должна выполнять `selection.refreshItem()` в конце сеттера.

Цель: Обеспечить обновление UI после изменения значения.

### Кастомные выборки

Редактируемые кастомные выборки должны быть реализованы объектным запросом, чтобы собрать пул изменений и отправить их в БД одной транзакцией.

Цель: Повысить эффективность, обеспечить целостность данных.

### `onRefreshExt` и `id`

При реализации кастомных выборок на объектном запросе результат выборки должен содержать поле `id` для работы `onRefreshExt`.

Цель: Обеспечить корректную работу механизма обновления расширенных данных.

### `AdditionalInfo`

Использование `AdditionalInfo` необходимо дважды обдумывать. Предпочтительнее получать данные через `onRefreshExt`.

Цель: Улучшить структуру и производительность, избегать избыточного кода.

### `AdditionalInfo2`

При переопределении отображения, в котором уже используется `AdditionalInfo`, новые вычисляемые атрибуты не должны добавляться в новый `AdditionalInfo2`. В `OnRefresh` необходимо использовать несколько case-классов: `thisApi().byParent(getIdMaster).map { rop => (rop, getAdditionalInfo(rop), getAdditionalInfo2(rop)) }`.

Цель: Улучшить структуру и читаемость кода обновления данных.

## 17. Отражение (Reflection)

### Обоснование

Использование reflection должно быть оправдано; в общем случае оно запрещено.

Цель: Повысить безопасность типов, производительность, упростить сопровождение кода.

### `scala.reflect.io._`

Использовать нельзя, предпочтительнее Java-классы.

Цель: Обеспечить лучшую стабильность и совместимость.

## 18. Специфика Модулей и Проектов

### Модуль STK

Для получения остатков необходимо использовать `ru.bitec.app.stk.Stk_Pkg#getRemainsMulti`. Для получения цены необходимо использовать `ru.bitec.app.stk.Stk_Pkg#getnPrCostConsSum`.

Цель: Использовать стандартные, оптимизированные методы для получения данных из модуля STK.

### Модуль ACT

Запрос оборотов должен выполняться с учётом логовой таблицы изменений через `union all`, чтобы получить полную и актуальную информацию.

Цель: Гарантировать корректность и полноту данных об оборотах.

### Проектные модули

При открытии MR необходимо указывать соответствующую ветку с учётом проекта, на котором разрабатывается. Например, для проекта СНГ разработка ведётся на `sng-internal-dev` → ветка проектного модуля `gs`; `pdev` → `dev`.

Цель: Обеспечить правильную интеграцию изменений в проектную ветку.

## 19. Безопасность и Целостность Данных

### SQL-инъекции

Все входные данные параметризованы, нет риска SQL-инъекций.

Цель: Защитить от SQL-инъекций, ускорить выполнение за счёт переиспользования планов.

### Ограничения целостности

Ограничения целостности (foreign key, unique) соответствуют метаданным.

Цель: Обеспечить корректность и согласованность данных на уровне БД.

### Валидации

Валидации на уровне приложения дублируют, но не заменяют БД-ограничения.

Цель: Обеспечить дополнительный уровень защиты и пользовательскую обратную связь.

### Чувствительные данные

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

Цель: Защитить конфиденциальную информацию от утечки.

## 20. Соответствие Best Practices

### Документация

Убедитесь, что код и конфигурации соответствуют рекомендациям из официального руководства (например, настройка логирования, работа с транзакциями, параллельные вычисления и т.д.). Всегда проверяйте разделы «Практика разработки» в документации GlobalFramework и сверяйтесь с примерами.

Цель: Повысить качество, поддерживаемость и безопасность кода, облегчить его понимание новыми разработчиками.

### Чистота кода

Код должен быть читабельным и понятным (часто читают больше, чем пишут). Держите методы короткими, избегайте дублирования. Имена должны ясно отражать назначение сущности. Следуйте общим принципам GSF: не используйте служебные символы (например, «!», «?», «@») в идентификаторах, оформляйте документацию и комментарии по необходимости.

Цель: Улучшить читаемость, облегчить понимание и сопровождение кода.

## 21. Критические ошибки

### DML через ASQL

DML (INSERT/UPDATE/DELETE) выполняется через ASQL. — Блокирующее

Цель: Предотвратить нарушение целостности данных из-за автокоммита ASQL.

### Долгая транзакция

Долгая транзакция, захватывающая большое число строк без разбивки. — Блокирующее

Цель: Избежать проблем с производительностью и блокировками БД.

### Функции GSF в массовых выборках

Массовое использование `getattribute`/`getmnemocode`/`getheadline` в больших выборках/отчётах. — Блокирующее

Цель: Избежать чрезмерной нагрузки на БД.

### Арифметика с N-типами

Арифметика с N-типами без проверки nullable (`.isNotNull`). — Блокирующее

Цель: Предотвратить `NullPointerException` и ошибки времени выполнения.

### Конкатенация строк SQL

SQL-строки формируются конкатенацией с пользовательскими данными. — Блокирующее

Цель: Предотвратить уязвимость к SQL-инъекциям.

### Дублирование логики

Необъяснимое и несогласованное дублирование логики в разных модулях. — Блокирующее

Цель: Упростить сопровождение, снизить вероятность ошибок, упростить внесение изменений.