# Устройство класса

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

Класс определяет правила хранения и обработки данных в таблице базы данных.

## Общие сведения о классах

Класс сущности (далее — класс) определяет хранилище совокупности объектов (строк), которые имеют одинаковые характеристики, подчиняются общим настройкам и операциям и функционируют в рамках единой логики.

Класс содержит набор атрибутов. Атрибут может быть:

- простым значением — используется для ввода и хранения значения определённого типа данных;
- ссылочным — используется для выбора значения из справочника или другого множества;
- переменной ссылкой — используется для ссылки на любую таблицу в системе;
- ссылкой на класс — таблица классов является служебной таблицей метаданных, в которой хранится весь перечень классов решения.

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

Правила именования класса:

- Системное имя задаётся на латинице.
- Имя задаётся в формате `{Module}_{Name}`, где:
  - `Module` — имя модуля;
  - `Name` — имя класса в единственном числе и именительном падеже.

Пример: `Lbr_Book`.

### Типы данных (`attribute-type`)

| Системное имя | Наименование | Хранение (PostgreSQL) | Особенности |
| --- | --- | --- | --- |
| `Varchar` | Строка | `varchar(n)` | Рекомендуемый тип для строк. Ограничение длины — обычно до 4000 символов |
| `Text` | Текст | `text` | Тип строк. Запрещён к использованию, так как является неограниченным типом данных |
| `Clob` | Большой текст | `text` | Используется для хранения больших текстов |
| `Number` | Число | `numeric / decimal` | Поддерживает целые и дробные значения (`BigDecimal`) |
| `Long` | Целое число | `bigint` | Используется для `id` и ссылок |
| `Date` | Дата | `timestamp` / `date` | Поддерживает дату и время |
| `Blob` | Бинарные данные | `bytea` | Хранит файл без метаданных |
| `Jsonb` | JSON | `jsonb` | Данные без фиксированной схемы |

### Типы атрибутов (`type`)

| Тип | Описание | Примечание |
| --- | --- | --- |
| `basic` | Значимый атрибут | Используется для хранения простых значений (`Varchar`, `Number`, `Date` и др.) |
| `refObject` | Ссылка на объект | Хранит `id` типа `Long`. Основной тип ссылок |
| `gRefObject` | Глобальная ссылка | Хранит GID в виде строки. Используется для интеграций |
| `refAnyObject` | Переменная ссылка | Хранит GID. Позволяет ссылаться на любой класс |
| `refClassByRoot` | Ссылка на класс | Применяется для ссылок на класс `Btk_Class` с ограничением по иерархии |
| `refClassByUnion` | Ссылка на группу классов | Позволяет ограничить ссылку набором классов |
| `refState` | Ссылка на состояние | Используется для ссылок на состояние |
| `refGroup` | Ссылка на группу | Предназначен для ссылок на группы (`Btk_Group`) |
| `autoNum` | Автонумерация | Значение генерируется системой |
| `calc` | Вычисляемый атрибут | Не хранится в базе данных. Вычисляемые значения реализуются на уровне выборок без объявления атрибута в ODM |

### Примеры использования

**1. Простой атрибут строкового типа**

```xml
<attr name="sCaption"
      attribute-type="Varchar"
      type="basic"
      caption="Наименование"/>
```

**2. Ссылка на объект**

```xml
<attr name="idState"
      attribute-type="Long"
      caption="Состояние"
      type="refObject"
      ref.class="Btk_ClassState"/>
```

**3. Ссылка на класс (`refClassByRoot`)**

```xml
<attr name="idContactClass"
      attribute-type="Long"
      caption="Класс контактной информации"
      type="refClassByRoot"
      ref.class="Btk_Class"/>
```

**4. Ссылка на группу классов (`refClassByUnion`)**

```xml
<attr name="idWorkDirClass"
      attribute-type="Long"
      caption="Область применения"
      type="refClassByUnion"
      ref.class="Bs_WorkDirClassArray"/>
```

**5. Ссылка на группу (`refGroup`)**

```xml
<attr name="idRefGroup"
      attribute-type="Long"
      caption="Группа"
      type="refGroup"
      ref.class="Btk_Group"/>
```

### Приведение типов атрибутов

При работе с динамическими атрибутами значения часто поступают в строковом виде (`sDefaultValue`). Для корректной обработки требуется явное приведение типов.

Пример:

```scala
// sDefaultValue — строковое значение

// если атрибут ссылочный
if (attr.get(_.sType) === AttrTypes.RefObject.toString)
  sDefaultValue.nl

// если атрибут переменной ссылочности
else if (attr.get(_.sType) === AttrTypes.RefAnyObject.toString)
  sDefaultValue.ng

// если атрибут дата
else if (attr.get(_.sAttrType) === AttributeTypes.Date.toString.ns) {
  NDate.parse(sDefaultValue.get)
}

// если атрибут числового типа
else if (attr.get(_.sAttrType) === AttributeTypes.Number.toString.ns)
  sDefaultValue.nr

// если атрибут целочисленного типа
else if (attr.get(_.sAttrType) === AttributeTypes.Long.toString.ns)
  sDefaultValue.nl

// по умолчанию — строка
else {
  sDefaultValue
}
```

### JSON-контейнер

JSON-контейнер — это расширение объекта класса NoSQL-нотацией в реляционной СУБД. Контейнер не имеет жёсткой, заранее определённой схемы и основан на множестве пар «ключ — значение». Это позволяет использовать его как динамическое расширение объекта. Для добавления новых данных в контейнер не требуются перекомпиляция кода или изменение структуры СУБД.

## Кодогенерация и прикладные сервисы

Класс позволяет существенно ускорить разработку бизнес-логики, ориентированной на работу с данными. Разработчику достаточно объявить перечень атрибутов класса, чтобы за счет кодогенерации получить набор готовых сервисов.

При кодогенерации создаются следующие элементы:

- Доменная автономная бизнес-логика (`Dpi`) — содержит код автономной бизнес-логики.
- Каркас прикладной автономной логики (`Api`) — Scala-класс с окончанием `Api`, в котором задаётся автономная бизнес-логика для работы с классом. Наследуется от `Dpi`.
- Доменная интерактивная бизнес-логика (`Dvi`).
- Каркас прикладной интерактивной логики (`Avi`) — Scala-класс с окончанием `Avi`, в котором задаётся интерактивная бизнес-логика. Наследуется от `Dvi`.
- Доменная разметка выборки (`dvm.xml`) — содержит сгенерированную по умолчанию разметку выборки.
- Каркас прикладной декларации пользовательского интерфейса (`Avm`) — XML-файл с расширением `avm.xml`, в котором задаётся разметка выборки.
- Интеграция с ORM:
  - POJO-объект для хранения данных в кеше;
  - `Aro` — объект интеграции POJO во фреймворк.

```{note}
При кодогенерации обычно создаются элементы двух типов:

- `D` — Domain. Доменный элемент всегда перезаписывается при кодогенерации и содержит бизнес-логику для подключения сервисов.
- `A` — Application. Прикладной элемент не изменяется при кодогенерации и служит для хранения бизнес-логики, написанной программистом вручную. Прикладной элемент наследуется от доменного.
```

Доступные прикладные сервисы:

- Аудит — фиксирует события, возникающие при работе пользователей в системе.
- Администрирование — позволяет управлять доступом к автономной и интерактивной бизнес-логике за счёт выдачи прав на привилегии.
- Универсальная фильтрация — позволяет пользователю строить комплексную фильтрацию списка классов на уровне базы данных. В универсальном фильтре можно использовать поля самого класса и его коллекций, а также поля классов, на которые есть ссылки.
- Автонумерация — выдаёт номера объектам класса и позволяет переопределять алгоритм выдачи номеров на проекте.
- Копирование объектов — обеспечивает кодогерацию бизнес-логики для копирования объектов.
- Группировка — используется для систематизации хранения объектов и удобства их восприятия. Группировка также позволяет массово управлять характеристиками и настройками объектов класса.
- Сервис прикреплённых файлов — позволяет прикреплять к объектам класса произвольные файлы.
- Поиск по шаблону — позволяет искать объекты класса по частичному или полному совпадению введённого текста со значениями полей, заголовком или мнемокодом объекта.
- Объектные характеристики — позволяют добавлять к классу на проекте произвольные поля.
- Генерация штрихкодов объекта — генерирует штрихкоды для объектов класса при их создании.
- Подписи объектов для печати — позволяют формировать в печатной форме список лиц с местом для подписи.
- Полнотекстовый поиск — позволяет классу выполнять быстрый поиск по значениям его атрибутов.

## Схема окружения

Окружение класса создаётся в момент кодогенерации:

![Окружение класса](/img/class_concept.jpg)

## Глобальный идентификатор GID

GID является уникальным идентификатором в рамках системы.

Генерация GID необходима для решения следующих задач:

- Уникальная идентификация — обеспечивает уникальную идентификацию каждого объекта в рамках системы независимо от его типа.
- Переменная ссылочность — обеспечивает возможность хранить ссылку на объект любого класса в одном атрибуте.
- Системная интеграция — GID является частью системного миксина `btk_object`. Подробнее см. в разделе [«Системные миксины»](/030_class/060_класс.md#системные-миксины).

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

### Формат ссылки GID

Глобальный идентификатор имеет формат:

```xml
gid :== idClass \ id
```

где:

- `idClass` — идентификатор класса, ссылающийся на объект в таблице `btk_class`;
- `id` — уникальный идентификатор конкретного объекта внутри класса. Идентификатор не является уникальным в системе, так как генерируется для каждого класса из отдельной последовательности (`sequence`).

### Внешние ключи и их роль в GID

Внешний ключ — это столбец или набор столбцов одной таблицы, который ссылается на первичный ключ другой таблицы.

Внешний ключ гарантирует, что ссылка, хранящаяся через GID, указывает на существующую запись, и предотвращает появление «битых» связей.

Пример:

```xml
<attr name="gidSrc" attribute-type="Varchar" caption="Источник" order="60" type="refAnyObject" isVisible="false"/>
<attr name="idDoc" attribute-type="Long" type="refObject" ref.class="Wf_Doc"/>
<attr name="idAction" attribute-type="Long" type="refObject" ref.class="Pro_Action"/>
```

В этом примере параметр `type="refObject"` вместе с `ref.class="Wf_Doc"` означает, что атрибут является ссылкой на объект указанного класса. Атрибут хранит идентификатор связанной записи, обеспечивая логическую связь между объектами.

**Создание внешнего ключа**

Внешний ключ создаётся автоматически при указании ссылочности на класс.

Чтобы создать внешний ключ вручную:

1. Создайте расчётную колонку, выделяющую идентификатор объекта из GID:

   ```sql
   ALTER TABLE event_log ADD COLUMN id_doc bigint GENERATED ALWAYS AS (split_part(gid_src, '\\', 2)::bigint) STORED;
   ```

   Значение `id_doc` автоматически вычисляется из `gid_src`.

2. Добавьте внешний ключ:

   ```sql
   ALTER TABLE event_log ADD CONSTRAINT fk_event_log_doc FOREIGN KEY (id_doc) REFERENCES wf_doc(id);
   ```

```{note}
Таких колонок может быть несколько — по одной на каждую логическую связь.
```

**Выявление существующих нарушений**

Для анализа существующих нарушений ссылочности используются разные инструменты в зависимости от типа ссылки:

- Для обычных ссылок с внешними ключами используется [«Сервис проверки целостности ссылок»](https://help.globalerp.ru/books/SettingsGuide/SNAPSHOT/html/0030_%D0%A1%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B.html#id8).
- Для GID-ссылок используется [«Отчёт по нарушенной переменной ссылочности»](https://help.globalerp.ru/books/GlobalUserGuideSystemWide/SNAPSHOT/html/service/0120_%D0%BE%D1%82%D1%87%D0%B5%D1%82_%D0%BD%D0%B5%D1%80%D0%B0%D0%B1%D0%BE%D1%87%D0%B8%D1%85_%D0%B3%D0%B8%D0%B4.html#). Отчёт показывает некорректные GID-ссылки, которые указывают на несуществующий класс или объект класса либо не соответствуют формату GID.

### Реализация переменной ссылочности через GID

**Переменная ссылочность**

GID упрощает реализацию [переменной ссылочности](/030_class/060_класс.md#переменная-ссылочность), так как содержит всю необходимую информацию — класс и идентификатор. Для реализации переменной ссылочности создаётся атрибут, способный хранить GID:

```xml
<attr name="gidSrcXxxxxx"
      attribute-type="String"
      type="refAnyObject"/>
```

**Ссылочность на миксин**

Ссылка на миксин — это переменная ссылочность с дополнительным полем `ref.class`, указывающим класс миксина. Подробнее см. в разделе [«Ссылочность на миксин»](/030_class/060_класс.md#ссылочность-на-миксин-1).

### Генерация GID

**Классы с автоматической генерацией GID**

GID генерируется автоматически для:

- классов с подтипом сущности `reference` или `document`;
- классов с тегом `attachType` в ODM-файле, обеспечивающим поддержку прикреплённых файлов у значений:
  - `simple` — обычная;
  - `versioned` — версионная;
- классов с подключёнными `vCollections` — коллекциями с переменной ссылочностью.

В остальных случаях GID не генерируется автоматически.

**Ручная генерация GID**

Чтобы сгенерировать GID вручную:

1. Укажите атрибут GID в классе:

   ```xml
   <attr name="gid" attribute-type="Varchar" isVisible="false"/>
   ```

2. Чтобы заполнить пустые значения GID, выполните операцию «Заполнить пустые gid и обновить системные миксины для класса».

   Путь: Дополнительно → Заполнить пустые gid и обновить системные миксины для класса.

```{note}
Операция также обеспечивает попадание в миксин `btk_object`.
```

## Супертипы классов

Для проектирования доступно несколько разновидностей класса, с помощью которых можно реализовать модель с необходимой бизнес-логикой. Такие разновидности называются супертипами.

Супертип определяет базовое назначение класса и влияет на работу системы с объектами этого класса.

Для класса супертип задаёт:

- стандартное поведение объекта;
- набор базовых методов и сервисной обвязки;
- особенности хранения и обработки данных;
- допустимые сценарии использования класса в прикладной модели;
- ограничения на самостоятельное существование объектов, если они предусмотрены выбранным супертипом.

Супертип должен быть указан для каждого класса. Если супертип не задан явно, класс считается обычным справочником и обрабатывается как класс с супертипом `reference`.

Основные супертипы:

- `reference` — справочник. Справочник — это прикладной объект для хранения данных, имеющих одинаковую структуру и списочный характер. Примеры: справочник физических лиц, места хранения, справочник ТМЦ.
- `document` — документ. Документ — это прикладной объект, который хранит данные о событиях или операциях на предприятии. Примеры: заявка на отгрузку, приходная накладная, акт сверки. Документ обычно имеет атрибут состояния, который отражает его жизненный цикл.
- `collection` — коллекция. Объекты класса-коллекции не могут существовать самостоятельно и создаются только для объектов других классов. Коллекции применяются как табличные части документов или логические развязки между классами. Добавление коллекций в бизнес-объект позволяет массово загружать данные в объектный кеш, что минимизирует нагрузку на базу данных. Элементы коллекции также можно обходить по родителю без транзакционного индекса, что уменьшает нагрузку на процессор.
- `vcollection` — переменная коллекция. Переменная коллекция расширяет возможности обычных коллекций и может ссылаться на родителя переменной ссылкой. Это требуется, когда для нескольких классов используется одинаковая коллекция.
- `journal` — журнал. Журнал — это особый тип класса, приспособленный для хранения большого количества записей. Такие классы имеют ограниченную функциональную обвязку ядровыми методами фреймворка, что позволяет увеличить быстродействие при работе с журналом. Примеры: записи по потребности ТМЦ на заказ в разрезе документов, журнал трудоёмкости в разрезе операций.
- `trait` — трейт. Абстрактный класс-предок, не имеющий собственной структуры хранения. Такой класс содержит общую логику нескольких классов-потомков и является частью механизма повторного использования кода.
- `mixin` — миксин (класс-примесь). Миксин — это особый вид класса, который служит для хранения данных из разных классов. Миксины используются для построения общих списочных форм различных диалогов подбора в пользовательских интерфейсах, а также для обработки данных в прикладной бизнес-логике. Миксин позволяет объединить несколько таблиц, что даёт возможность использовать внешние ключи и индексы для этого объединения.
- `simpleMixin` — простой миксин. Простой миксин — это класс-примесь, не являющийся коллекцией по отношению к реализующему его классу. Используется для подключения дополнительного набора атрибутов или логики к различным классам без создания отдельной коллекционной структуры. Позволяет повторно использовать общие поля и поведение в нескольких объектах.
- `sharedReference` — разделяемый справочник. Разделяемый справочник оптимизирован для совместного использования и интенсивного чтения данных. Использует механизм общего кеширования и предназначен для хранения редко изменяемых нормативно-справочных данных, доступных многим пользователям и процессам. Класс не может иметь миксинов. Механизм позволяет снизить нагрузку на базу данных при частом обращении.
- `classArray` — массив классов. Служебный супертип для хранения или обработки набора ссылок на объекты различных классов в рамках единой структуры.
- `setting` — настройка. Класс для хранения конфигурационных данных и параметров настройки системы или её модулей. Используется для управления поведением приложения без изменения кода.
- `simpleExtension` — простое расширение. Класс с дополнительными атрибутами, расширяющий базовый класс без изменения его основной структуры. Применяется, когда к существующему классу требуется добавить специфичные поля, не затрагивая его исходную схему. Подробнее см. в разделе [«Классы-расширения. Simple Extensions»](https://help.globalerp.ru/books/GlobalServerAppGuide/SNAPSHOT/html/090_appendix/how_to/050_%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D1%8B_%D1%80%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%B8%D1%8F.html#id1).

## Пример разметки класса

```xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<class xmlns="http://www.global-system.ru/xsd/global3-class-1.0" name="Bs_GdsCostDeviationType"
       caption="Виды отклонений в стоимости ТМЦ"
       cardEditor.representation="Card" listEditor.representation="List"
       viewOptions.openCardType="mdi" supertype="reference">
    <attributes>
        <attr name="id" attribute-type="Long" caption="Идентификатор" order="-1" type="basic" isVisible="false"/>
        <attr name="idClass" attribute-type="Long" caption="idClass" order="-2" type="basic" isVisible="false"/>
        <attr name="gid" attribute-type="Varchar" isVisible="false"/>
        <attr name="sCode" attribute-type="Varchar" caption="Код" order="10" isMnemoCode="true" type="basic"
              isRequired="true" isVisible="true"/>
        <attr name="sCaption" attribute-type="Varchar" caption="Наименование" order="20" isHeadLine="true" type="basic"
              isRequired="true" isVisible="true"/>
        <attr name="sDescription" attribute-type="Varchar" caption="Описание" order="30" type="basic" editorType="memo"
              isVisible="true">
            <descriptionColumn/>
        </attr>
    </attributes>
</class>
```