# Работа со справочниками

В системе используются 3 базы данных.

1. Основание системы - SQL база, которая хранит в себе значения ячеек и их историю. Работа с базой данных происходит через интерфейс хранимых процедур, а не на прямую.
2. Mongo - хранит в себе описание всех моделей и их взаимосвязей. На Mongo реализованы механизмы работы в режиме SandBox - когда пользовательские изменения изолируются друг от друга. В Mongo нет информации о данных из таблицы \[Core].\[Cells]
3. Redis - способ кэширования данных - используется для оптимизации

Для калькулятора реализовано несколько вспомогательных классов, для получения информации из справочников. Все эти классы понаследованы от базового Base.js и расположены в папке ./classes/calculator/helpers. Классы работают с Mongo базой и кэшируют результаты своей деятельности в Redis

Функционал базового класса - получение из кэша и сохранение в кэш плюс своя функция запросов к Mongo - query, которая является оберткой над обычным запросом find, но в зависимости от контекста документа подключает или нет режим работы в песочнице.

```javascript
module.exports = function(Context){

    var self = this;

    self.Context = _.clone(Context);

    self.query = function(modelName,query,fields){}

    self.cacheKey = function(){}

    self.loadFromCache = function(done){}

    self.saveToCache = function(Result,done){}
}
```

## Документ (Doc.js) <a href="#dokument-docjs" id="dokument-docjs"></a>

Класс предназначен для получения следующей информации:

1. Основные поля документа
2. Если документ рассчитан на работу с дочерними объектами учета - мы получим информацию о возможных объектах учета для данного документа
3. Информация о том кто этот документ подписывает

Подробнее о некоторых полях:

* IsShowRoots - Отображать прикрепленные строки полностью или только их дочерние строки
* IsActiveCondition - Доступна или нет панель настроек в режиме ввода
* IsOlap IsChart IsPresent IsInput - Доступны или нет режимы OLAP, График, Презентация, Форма ввода
* Для режима HasChildObjs = true - (документ работает на дочерних объектах учета) доступны дополнительные настройки:
* Link\_docobjtype - фильтрация дочерних объектов учета для документа
* IsDivObj - отображать дочерние объекты отдельным выбором или склеить ряды для всех объектов учета
* IsObjToRow - режим похожий на OLAP отчет - когда вместо рядов отображаются дочерние объекты учета
* IsShowParentObj - дополнительная настройка для IsObjToRow - когда дочерние объекты отображаются вместе с родительскими в структуре "дерево"

```javascript
{
   CodeDoc: "calc_chernmed",
   NameDoc: "Калькуляция черновой меди",
   PrintNameDoc: "Калькуляция черновой меди",
   PrintNumDoc: "",
   HasChildObjs: true,
   IsShowRoots: false,
   IsActiveCondition: false,
   IsPrimary: false,
   IsAnalytic: false,
   IsOlap: false,
   IsInput: true,
   IsChart: false,
   IsPresent: false,
   IsDivObj: true,
   IsObjToRow: false,
   IsShowParentObj: false,
   CodeModel: "TPFP",
   CodeGrp: "CALC_CU",
   CodeRole: "COST",
   CodeDocType: "MONTHDOC",
   Labels: [{
      IsSignature: true,
      IsApproval: false,
      CodePeriodGrp: "PLAN",
      CodeLabel: "gdir",
      NameLabel: "Генеральный директор",
      Idx: 1,
      Users: {
         473: {
            421: {
               NameUser: "######## ########### ###########",
               CodeUser: "421",
               JobTitle: "Генеральный директор",
               CodeOrg: "473"
            }
         }
      }
   }],
   ChildObjs: ["3272","5341","5342"]
}
```

## Ряд (Row\.js) <a href="#ryad-rowjs" id="ryad-rowjs"></a>

Получение рядов, отображаемых в таблицах.\
Для получения структуры берется информация из ссылок RowDoc (привязанные к документу корневые узлы) с учетом IsExpandTree (показывать только 1 строку или все дочерние узлы)\
При работе с OLAP документом IsExpandTree не учитывается и всегда считается false;\
В зависимости от параметра документа IsShowRoots корневые узлы будут отображаться или будут отображаться только их дочерние ряды

В зависимости от года передаваемого в запрос могут произойти следующие изменения:\
Ряд может быть исключен на основании значений полей FromObsolete и FromYear\
Ряд может изменить свой флажок IsFormula в зависимости от полей FormulaFromObsolete и FormulaFromYear

При тонкой фильтрации рядов используются следующие параметры:\
NoFiltered - строка (включая всю цепочку родительских узлов) всегда показывается\
HasFilteredChild - для всех дочерних строк применяется фильтрация на основании модели ObjGrp - в которой может быть установлена связь между рядом и объектом учета (или группой объектов или типом объектов)

Конструкции, когда по дереву происходит установка флага фильтрации HasFilteredChild->NoFiltered->HasFilteredChild приводят к ошибке.

Кроме этого для каждого ряда заполняется информация о его суммовых группах (если установленн флаг UseProdSumGrps, то суммовые метки применяются к списку продукции), тэгах и информация о форматировании значений.

```javascript
{
   CodeRow: "m200090",
   NameRow: "Рентабельность продаж",
   IndexRow: 0,
   NumRow: "1.3.1",
   IsFormula: true,
   Formula: "$m200080? / $m200060? * 100",
   IsSum: false,
   NoSum: false,
   IsMinus: false,
   IsControlPoint: false,
   IsCalcSum: false,
   IsAgFormula: false,
   AgFormula: "",
   AsAgFormula: true,
   NoDoSum: false,
   UseProdSumGrps: false,
   CodeValuta: "NONE",
   CodeParentRow: "m200080",
   rgt: 4286,
   lft: 4285,
   level: 3,
   Sums: [ ],
   Filter: [ ],
   Tags: [
      "olap_osnpok:1",
      "numberformat:0.##",
      "AsAgFomula:1",
      "fstr:1"
   ],
   IsLeaf: true,
   Format: "0.##"
}
```

Для рядов подключен механизм работы с деревьями Nested Sets подробнее можно почитать[здесь](https://github.com/groupdock/mongoose-nested-set)

## Корневые узлы (DocRow\.js) <a href="#kornevye-uzly-docrowjs" id="kornevye-uzly-docrowjs"></a>

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

Сейчас используется следующий алгоритм поиска документа:

1. Предпочтение отдается документам без IsOlap IsChart IsPresent
2. IsExpandTree у ссылки true - предпочтительней
3. Для документов не на корневых объектах учета core.Docs.HasChildObjs=0
   1. Количество корневых узлов - выбирается документ с наименьшим числом корневых узлов в таблице link.DocRows
   2. Длина кода документа - выбирается документ с наименьшей длиной кода в поле core.Docs.CodeDoc
4. Для документов на дочерних объектах учета core.Docs.HasChildObjs=1
   1. Определяется тип или класс объекта учета из контекста
   2. В таблице link.DocObjs фильтруется перечень документов, из которых нужный определяется по типу или классу.
   3. Если все параметры сошлись, но осталось несколько документов, то выбирается документ с более коротким кодом core.Docs.CodeDoc

При обращении к классу вы получите хэш таблицу, для вычисления кодов документов

```javascript
{
    WithChildObjs: {
    z100: {
        Empty: {
            Empty: "calc_balproiz"
        },
        CALCED: {
            CE_STL: "calc_chermet",
            CE_STPK: "calc_chermet",
            CE_STKL: "calc_chermet",
            ...
            CE_ZN: "calc_zn"
        }
    },
    z112: {
        Empty: {
            Empty: "calc_ruda"
        },
        CALCEDOLD: {
            CE_SCR: "calc_ruda_pered",
    ...
    },
    NoChildObjs: {
        a111: "balans",
        r214: "finres",
        ...
        b265: "zp_zatr_conto69"
    }
}
```

## Объект учета (Div.js) <a href="#obekt-ucheta-divjs" id="obekt-ucheta-divjs"></a>

Получение информации по объектам учета. Полная информация распределена по следующим таблицам:

| Модель  | Поля                                |
| ------- | ----------------------------------- |
| obj     | CodeObjType, CodeOrg, CodeParentObj |
| org     | CodeDiv, CodeOtrasl, CodeCity       |
| city    | CodeRegion                          |
| objtype | CodeObjClass                        |
| objtag  | информация о Тэгах                  |

Обращаясь к этому классу вы получаете собранную полную информацию обо всех объектах учета плюс информацию по иерархии RootObj, CodeParentObj, Children, AllChildren :

```javascript
{
    CodeObj: "473",
    IsFormula: false,
    Formula: "",
    CodeValuta: "RUB",
    CodeObjType: "OWNORG",
    CodeOrg: "473",
    CodeParentObj: "353",
    CodeDiv: "MET",
    CodeObjClass: "ORGS",
    Groups: [
        "OPR",
        "G1",
        "G5",
        "DIV_MET",
        "G2"
    ],
    CodeCity: "35379",
    CodeRegion: "56",
    CodeOtrasl: "ЦМ",
    Tags: [
        "carow:CA_STRUCT2000"
    ],
    RootObj: "353",
    Children: [
        "1873",
        "5829"    
    ],
    AllChildren: [
        "1873",
        "5829"
    ]
}
```

## Период (Period.js) <a href="#period-periodjs" id="period-periodjs"></a>

В системе есть следующие типы периодов

1. Обычные (расчетные) все остальные типы так или иначе приводятся к ним

```javascript
19: {
      CodePeriod: "19",
      Name: "сен.",
      SName: "сен.",
      MCount: 1,
      MonthStart: 9,
      Conditions: {
         ismonth: true,
         issumperiod: false,
         iskorrperiod: false,
         isplanperiod: false,
         isozhidperiod: false
      }
}
```

1. Формульные периоды (начинаются со знака "-"). Содержат хэш таблицу преобразования периода из контекста в расчетный период. Если в качестве значения используются массивы, то расчетчик суммирует входящие в массив периоды.

```javascript
-201: {
   1: "1",
   2: "2",
   3: "3",
   4: "4",
   12: "22",
   13: "1",
   14: "24",
   15: "25",
  ...
   619: "629",
   6110: "6210",
   6111: "6211",
   6112: "6212"
},
```

Кроме информации о периодах класс отдает информацию о том, какое название периода использовать после формульных преобразований (позднее эта информация используется в получении колонок документа). Эта информация хранится в хэш таблице DisplayNames.

```javascript
DisplayNames: {
   -201: {
      403: "403"
   },
   -406: {
      406: "406"
   },
   -311: {
      31: "311",
      32: "312",
      33: "313",
      34: "314"
   },
   ...
```
