В файле `columns.tsx` создается универсальное отображение всех моделей бэкенда в виде таблицы.
Например, данные по ссылке /contracts
:
[
{
"id": 1,
"name": "Договор №1",
"date": "20.08.2021",
"jurperson": {
"id": 1,
"name": "ЗАО ОМЕГА, Татарстан",
"address": "адрес: город, улица, дом, офис",
"bankDetails": "Банковские реквизиты"
},
"counterpartyJurperson": {
"id": 1,
"name": "ООО Один",
"address": "адрес: город, улица, дом, офис",
"bankDetails": "Банковские реквизиты",
"counterpartyCompany": {
"id": 1,
"name": "Магнит "
}
}
},
]
Будут отображены в таком виде:
Сложность создаёт наличие разных типов данных внутри ячеек:
0й уровень вложенности
{id, name}
1й уровень вложенности
2й и 3й уровни вложенности
name
= shortName
+ fullName
)Чтобы избежать этой сложности мы могли бы предварительно перед выводом модели сделать данные примитивным (строкой с названием). Но вместо этого мы помещаем в ячейку объект {id, name}
и используем компонент отрисовки renderCell
, чтобы представить там модель должным образом со всеми её параметрами. Это позволит нам открывать любую модель прямо из таблицы (например, для исправления). То есть любая ячейка таблицы связана с первоисточником.
Пример вложенных данных (3): Выплата/Должность/Точка/Контрагент
. (3й уровень вложенности, пока самый глубокий)
Пример комбинированных данных (4), это Мера работы, она составляется из данных первого уровня (без использования вложенностей) name = shortName + fullName
Пример частичной вложенности (5) Выход/Должность/Профессия
. Если вывести просто Должность
, то будет услуга, типа "Разгрузочные работы", а нам надо указать "Грузчик", но чтобы при это сохранить ссылку на Должность
по договору. Поэтому {id}
попадает из Должности
, а название из Профессии
.
Пример многих моделей – опциональные параметры работника или должности, каждый из которых является отдельной моделью.
Почему выбран подход писать универсальный компонент? Потому что очень много моделей и рутинного кода, чтобы отображать каждый из них. Если бы мы писали для каждой модели своё описание столбцов со всеми событиями, это было бы неподъемный объем рутины. В универсальном компоненте хоть и есть много отклонений от "одинаковости" в моделях, большинство кода получилось использовать однотипным.
Если читать данные удобно в "расширенных" страницах, типа "покажи всех юрлиц" и мы видим общий список и столбец "Контрагент", чтобы фильтровать их и сортировать… То писать данные логично из "суженного" формата. Например, добавлять работников находясь на странице "Филиала". Потому что в универсальной странице, если мы вбиваем 10 работников, пришлось бы 10 раз указывать Филиал (каждому одинаковый), а на странице "Филиала" он и так понятен. То же самое касается Выходов – им не надо выбирать каждому дату из календаря. Наоборот, мы смотрим на календарь с заранее проставленным периодом, и вписываем туда числа отработанных часов. То есть большинство информации уже подставлена (точка, должность, работник, число) и нам остаётся её немного уточнить/дополнить.
На базовом уровне запись можно разделить на 3 пункта.
Для записи данных мы будем использовать те же таблицы, что для чтения но с доработками:
(1) Суть отсечения столбцов в том, что когда мы находимся на странице Контрагента и добавляем туда новые Юрлица, нам не нужен столбец Контрагент:
(2) Когда мы заполняем документы, например табель и нам нужно подставлять работников – необходимо предварительно загрузить их. Пока данных не сильно много, это можно сделать за раз при первом открытии программы. Далее будем регулировать загружаемые данные по мере необходимости.
(3) Когда данные загружены, в конкретных документах надо фильтровать их, например, чтоб не приходилось выбирать из 100 человек, а из 10-20, по разным критериям. То же самое касается любой модели (которую приходится выбирать).
Заглянем в файлsrc/localization/ru.js
, там компактно описаны все наши модели:
{
// Организация и её настройки
companies: 'Компания',
jurpersons: 'Юрлица',
units: 'Филиалы',
professions: 'Профессии',
'work-params': 'Доп.параметры (должности)',
'work-measures': 'Меры работы',
'redemption-types': 'Типы выплат/удержаний',
// Контрагенты и договоры
'counterparty-companies': 'Контрагенты',
'counterparty-jurpersons': 'Юрлица контрагентов',
'counterparty-units': 'Точки контрагентов',
contracts: 'Договоры',
positions: 'Должности (по договору)',
// Работники и события
workers: 'Работники',
redemptions: 'Выплаты/Удержания',
attendances: 'Выходы (на работу)',
}
Видно, что почти все модели малочисленные (ограниченное, медленно растущее число записей). Их можно загружать при первом открытии программы и точечно обновлять при правках. Только последний блок (Работники и события) является "текучим, бесконечным", для него есть смысл создать динамическую загрузку - загружать порциями по разным сценариям (смотря какой документ открыт, какой период выставлен и т.д.).
Запись данных происходит не в общей таблице модели, а в рабочем пространстве, где можно создавать, редактировать и выбирать много связанных моделей (с заранее проставленными данными).
Эти данные создаются, грубо говоря, 1 раз, при регистрации в системе, и почти не меняются.
Содержит всю связанную с контрагентом информацию
Содержит все позиции (должности) со ставками. Открывается как вспомогательное окно из страницы Контрагента при нажатии на Договор.
Страница где по горизонтали выставляются работники, а по вертикали дни заданного периода. В заголовке выставляется контрагент или точка. Каждая ячейка, если заполнена, создаёт "Выход на работу" с заданным числом часов (или другой меры работы).
По удержаниям можно сделать подобный табель, либо в одном совмещать обе Модели.