# Сервис LegacyXml2Select

**LegacyXml2Select** – это сервис, который предоставляет возможность парсить XML-запросы, сформированные в стиле «формальных выборок», и генерировать соответствующие SQL SELECT-запросы к документам в БД. Основное назначение – обеспечить обратную совместимость со старыми механизмами поиска.

Сервис состоит из нескольких компонентов:
- **LegacyXml2Select** – фасад, предоставляющий внешние методы.
- **LegacyXml2SelectDb** – сервис‑помощник для доступа к базе данных (получение метаданных, выполнение функций).
- **LegacyXml2SelectInnerPg** – ядро парсинга XML и построения SQL.
- **PropertyBuilder** – построитель условий для свойств документов.
- **MacroParser** – парсер макросов (функции, литералы).
- **SqlQuery / WhereCondition** – утилиты для программного конструирования SQL-выражений и оптимизации WHERE-условий.

Все компоненты располагаются в одном модуле и экспортируются как единое целое.

## Сервис LegacyXml2Select

**Назначение:** Принимает XML‑строку, параметры режима запроса и прав доступа, преобразует её в объект `SqlQuery` (или сразу в SQL‑строку) и возвращает результат.

### Зависимости
- `QueriesHelper` – обязательный (указан в `requirements`), предоставляет доступ к выполнению запросов к PostgreSQL.
- `LegacyXml2SelectDb` – обязательный (добавляется как вложенный сервис в конструкторе).

### Методы (запросы)

#### `parseToObj({ session, xml, queryMode=0, permissions=1 })`
Преобразует XML в объект `SqlQuery` (без построения финального SQL‑текста).

**Параметры:**
- `session` (объект, опционально) – должен содержать `userId`, используется для фильтрации прав.
- `xml` (string) – XML‑строка запроса.
- `queryMode` (number, по умолчанию `0`) – режим запроса:
  - `0` – полный запрос (выбирает поля документа),
  - `1` – подсчёт количества (`COUNT`),
  - `2` – только идентификаторы документов,
  - `3‑6` – специальные режимы (логика меняется).
- `permissions` (number, по умолчанию `1`) – уровень проверки прав доступа (`-1` – без проверки, `1` – стандартная).

**Возвращает:** экземпляр `SqlQuery`.

#### `parseToSql({ session, xml, queryMode=0, permissions=1 })`
Выполняет `parseToObj`, а затем вызывает `sql.build()`, возвращая готовый SQL‑запрос.

**Параметры:** те же, что у `parseToObj`.

**Возвращает:** строку SQL.

#### `macroParse({ macro })`
Парсит строку‑макрос в абстрактное синтаксическое дерево.

**Параметры:**
- `macro` (string) – строка, содержащая вызов функции, литерал или идентификатор.

**Возвращает:** объект с полем `tree` – AST макроса (например, `{ type: 'function', value: 'NOW', args: [] }`).

## Формат XML‑запроса для LegacyXml2Select

Сервис `LegacyXml2Select` преобразует XML‑описание выборки документов в SQL‑запрос `SELECT`.  
Корневой элемент — `<query>`, внутри которого обязательно присутствует элемент `<formal>`, задающий условия фильтрации.

---

### 1. Корневая структура

```xml
<?xml version="1.0"?>
<query>
  <formal dockind_id="ID_ВИДА_ДОКУМЕНТА" [name="and|or"]>
     <!-- набор условий -->
  </formal>
</query>
```

**Атрибуты `<formal>`:**

| Атрибут      | Обязательный | Тип       | Описание                                                      |
|--------------|--------------|-----------|---------------------------------------------------------------|
| `dockind_id` | да           | integer   | Идентификатор вида документа (`dockind_id`) целевой таблицы   |
| `name`       | нет          | `and`/`or`| Логическая операция, объединяющая все условия верхнего уровня (по умолчанию `and`) |

---

### 2. Элементы внутри `<formal>` и `<reference>`

Допускаются следующие элементы (в любом порядке и в любых сочетаниях):

| Элемент      | Назначение                                                     |
|--------------|----------------------------------------------------------------|
| `<reference>`| Ссылка на другой документ / тип документа (JOIN / EXISTS)      |
| `<property>` | Условие на значение свойства документа                         |
| `<status>`   | Фильтр по статусу (виду события) документа                     |
| `<operation>`| Логическая группа условий (`AND` / `OR`)                       |
| `<select>`   | (Зарезервирован; влияет на логику outer join, но вывод не меняет)|

Все они могут вкладываться внутрь `<reference>` и `<operation>`.

---

### 3. Элемент `<reference>`

Описывает переход к связанному документу через свойство‑ссылку. Реализуется либо как `EXISTS`/`NOT EXISTS`, либо как `JOIN` (в зависимости от атрибута `referencing` и режима запроса).

```xml
<reference
  docprop_id="ID_СВОЙСТВА_ССЫЛКИ"
  referencing="join|is null"
  dockind_id="ID_ЦЕЛЕВОГО_ВИДА"
  [condition="l|nl|g|ng|e|ne"]
  [count="целое_число"]
  [valuevar="строка"]
  [leftp="строка"]
  [rightp="строка"]
>
  <!-- вложенные reference, property, status, operation -->
</reference>
```

| Атрибут       | Обязат. | Тип               | Значения / Описание                                                                                     |
|---------------|---------|-------------------|---------------------------------------------------------------------------------------------------------|
| `docprop_id`  | да      | integer           | Идентификатор свойства‑ссылки (из `model_documents.doc_kind_properties`)                                 |
| `referencing` | да      | строка            | `join` – обычная связь, проверяется существование связанного документа;<br/>`is null` – анти‑связь, условие `NOT EXISTS` (связанного документа нет) |
| `dockind_id`  | да      | integer           | Идентификатор вида документа, на который ссылаемся                                                      |
| `condition`   | нет     | `l,nl,g,ng,e,ne`  | Условие сравнения **количества** связанных документов (используется вместе с `count`; **реализовано частично** – вызывает ошибку) |
| `count`       | нет     | integer           | Ожидаемое количество документов для сравнения (см. `condition`)                                         |
| `valuevar`    | нет     | строка            | Зарезервировано; в текущей реализации не используется                                                   |
| `leftp`       | нет     | строка            | Зарезервировано; не используется                                                                        |
| `rightp`      | нет     | строка            | Зарезервировано; не используется                                                                        |

**Логика работы:**
- Если внутри `<reference>` нет ни вложенных `<reference>`, `<property>`, `<status>`, `<operation>`, ни `<select>`, и режим запроса = 4–6, то такой элемент при `referencing="join"` или внешнем соединении превращается в условие `1=1`.
- В остальных случаях для `<reference>` генерируется подзапрос `EXISTS` (или `NOT EXISTS` при `referencing="is null"`), в котором проверяется наличие связанных документов и дополнительно накладываются вложенные условия (свойства, статусы, другие ссылки).
- При `referencing="join"` и режимах 4–6 (специальные режимы отчётов) возможен сценарий с `OUTER JOIN`, но детали определяются вложенными условиями.

---

### 4. Элемент `<property>`

Задаёт фильтр по значению конкретного свойства документа (или по служебному идентификатору).

```xml
<property
  docprop_id="ID_СВОЙСТВА"
  condition="код_операции"
  [doc_prop_value="значение"]
  [valuevar="макрос"]
  [value="значение"]
  [type="s|t|n|d|v"]
  [kindtype="число"]
/>
```

| Атрибут          | Обязат. | Тип    | Описание / Возможные значения                                                                                                                                       |
|------------------|---------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `docprop_id`     | да      | integer| Идентификатор свойства документа. **Особые значения:**<br/>`-2` – ID документа (прямая выборка по document_id);<br/>`-4` – дата события (`event_date`);<br/>`-5`, `-6` – пользователь, породивший событие (`event_user`);<br/>`-7` – описание документа (`doc_descr`). Остальные числа – обычные свойства из `doc_kind_properties` |
| `condition`      | да      | строка | Код операции сравнения (см. таблицу ниже)                                                                                                                           |
| `doc_prop_value` | нет*    | строка | Непосредственное значение для сравнения. Допускается использование макросов (см. раздел 6). *Обязательно, если только не используется `IS NULL`/`IS NOT NULL`.       |
| `valuevar`       | нет     | строка | Ссылка на переменную или макрос; если задана и `condition != 'DIRECT'`, подменяет собой `doc_prop_value`.                                                           |
| `value`          | нет     | строка | Альтернативное значение (например, для работы со словарями; приоритет ниже `doc_prop_value`).                                                                       |
| `type`           | нет     | символ | Тип свойства (берётся из БД, если не указан):<br/>`s` – строка, `t` – текст, `n` – число, `d` – дата, `v` – ссылка на словарь и т.п.                               |
| `kindtype`       | нет     | integer| Подтип свойства (берётся из БД). Влияет на построение условия (например, `4` – иерархический словарь, `10` – мультизначное свойство).                               |

**Коды операций (`condition`) и их SQL‑эквиваленты:**

| Код         | SQL оператор                | Примечание                                                                                      |
|-------------|-----------------------------|--------------------------------------------------------------------------------------------------|
| `e`         | `=` или `IN`                | `IN` используется для свойств типа `v`, `t` и `kindtype=4` (словарь)                             |
| `ne`        | `<>` или `NOT IN`           | аналогично                                                                                       |
| `l`         | `<`                         |                                                                                                  |
| `nl`        | `>=`                        |                                                                                                  |
| `g`         | `>`                         |                                                                                                  |
| `ng`        | `<=`                        |                                                                                                  |
| `like`      | `LIKE`                      |                                                                                                  |
| `not like`  | `NOT LIKE`                  |                                                                                                  |
| `in`        | `IN`                        | Только для строковых типов (`s`, `t`), если `kindtype` != 4                                      |
| `d`         | `DIRECT`                    | Специальный режим: прямое присоединение таблицы свойств без `EXISTS` (используется для `docprop_id=-2`) |
| `is null`   | `IS NULL`                   |                                                                                                  |
| `is not null`| `IS NOT NULL`              |                                                                                                  |
| `min`       | `MIN`                       | (зарезервировано, используется в агрегациях)                                                      |
| `max`       | `MAX`                       | (зарезервировано)                                                                                |

**Особые `docprop_id`:**

- `-2` (`DIRECT`): фильтр по идентификаторам документов.  
  * `doc_prop_value` – список ID через запятую, например `"10,20,30"`.  
  * `valuevar` – имя функции, возвращающей массив ID (вызывается через `Xml2SelectDb.execFunc`).
- `-4`: фильтр по дате события (`event_date`). Для `condition='<=...'` значение автоматически корректируется на конец дня.
- `-5`, `-6`: фильтр по пользователю события (`event_user`).
- `-7`: фильтр по текстовому описанию (`doc_descr`). Для операторов `LIKE`/`NOT LIKE` – сравнение без учёта регистра.

---

### 5. Элемент `<status>`

Фильтрация по статусу (виду события) документа.

```xml
<status doceventkind_id="ID_СОБЫТИЯ" />
```

Может встречаться многократно. Каждый экземпляр задаёт одно значение `doceventkind_id`.  
В SQL формируется условие:  
`dt_{level}.doceventkind_id IN (0, список_ID_событий)`

Если список событий в XML совпадает с полным набором событий для данного вида документа (или полный набор пуст), условие **не добавляется** (чтобы не перегружать запрос избыточным перечислением).

---

### 6. Элемент `<operation>`

Группирует несколько условий с заданной логической связкой.

```xml
<operation name="and|or">
   <!-- reference, property, status, другие operation -->
</operation>
```

- `name="and"` – все вложенные условия объединяются через `AND`.
- `name="or"` – через `OR`.
- Атрибут `name` можно опустить; тогда элемент становится прозрачным контейнером (вложенные условия просто передаются на уровень выше без добавления собственной группы).

---

### 7. Элемент `<select>` (зарезервирован)

Присутствует в коде, но не влияет на итоговый SQL. Его наличие/отсутствие используется только во внутренней логике определения, нужно ли создавать `OUTER JOIN`.  
Пример:

```xml
<select />
```

В текущей реализации его содержимое игнорируется.

---

### 8. Подстановка макросов в значениях

В атрибутах `doc_prop_value`, `valuevar`, `value` (а также `@_valuevar` у `<reference>`) могут использоваться макросы.  
Поддерживаются следующие макросы (регистр важен):

| Макрос                | Подстановка                                                      |
|-----------------------|------------------------------------------------------------------|
| `@USERID`             | ID текущего пользователя (`documents.get_user_id`)               |
| `@USERNAME`           | Полное имя пользователя (Фамилия Имя Отчество)                   |
| `@ROLEID`             | ID текущей роли пользователя (`documents.get_role_id`)           |
| `@ROLENAME`           | Название роли                                                   |
| `@DEPARTID`           | Идентификатор подразделения пользователя                         |
| `@DATE`               | Текущая дата/время (`SYSDATE`); может комбинироваться с функциями: `@DATE+1`, `@DATE-7` и т.п. |
| `GET_USER_FILIALS`    | Вызов функции `GET_USER_FILIALS` (и другие подобные)             |
| `NOW`, `TODAY`, `WORKDAY`, `MONTHDAY`, `*YEAR*` | Вызов соответствующей функции БД, возвращающей дату |

Макросы распознаются по точному совпадению (`@USERID`) или по вхождению в строку (например, `TO_DATE('@DATE','DD.MM.YYYY')` приводит к подстановке `SYSDATE`).

Если значение начинается с буквы и содержит скобки (например, `my_func(1,2)`), то оно рассматривается как вызов функции БД и выполняется через `Xml2SelectDb.execFunc` или `execDate`.

---

### 9. Примеры

#### Простейший запрос (получить все документы вида 10)

```xml
<query>
  <formal dockind_id="10" />
</query>
```

#### Фильтр по свойствам и статусу

```xml
<query>
  <formal dockind_id="10" name="and">
    <property docprop_id="100" condition="e" doc_prop_value="Иванов"/>
    <property docprop_id="101" condition="g" doc_prop_value="1000"/>
    <status doceventkind_id="3"/>
    <status doceventkind_id="5"/>
  </formal>
</query>
```

#### Ссылка на связанный документ

```xml
<query>
  <formal dockind_id="10">
    <reference docprop_id="200" referencing="join" dockind_id="20">
      <property docprop_id="201" condition="like" doc_prop_value="%утверждён%"/>
    </reference>
  </formal>
</query>
```

#### Анти‑ссылка (NOT EXISTS)

```xml
<reference docprop_id="200" referencing="is null" dockind_id="20"/>
```

#### Использование макроса

```xml
<property docprop_id="102" condition="e" doc_prop_value="@USERID"/>
```

#### Группировка условий с OR

```xml
<operation name="or">
  <property docprop_id="100" condition="e" doc_prop_value="A"/>
  <property docprop_id="100" condition="e" doc_prop_value="B"/>
</operation>
```

#### Выборка по списку ID

```xml
<property docprop_id="-2" condition="d" doc_prop_value="101,205,330"/>
```

или через функцию:

```xml
<property docprop_id="-2" condition="d" valuevar="my_package.get_docs(55)"/>
```