diff --git a/README.md b/README.md index b24a5cc..3b402a8 100644 --- a/README.md +++ b/README.md @@ -111,9 +111,10 @@ git clone https://git.citpb.ru/CITKParus/P8-Panels.git При компиляции учитывайте следующее: -- объекты `PKG_P8PANELS`, `PKG_P8PANELS_BASE`, `PKG_P8PANELS_VISUAL` и `P8PNL_SELECTLIST` - обязательны к компиляции. **Это ядро серверной части расширения. Без этих объектов его работа невозможна. Все остальные, перечисленные ниже объекты серверной части - прикладные, не обязательны к компиляции и на функционирование непосредственно фреймворка влияния не оказывают.** +- объекты `PKG_P8PANELS`, `PKG_P8PANELS_BASE`, `PKG_P8PANELS_VISUAL`, `PKG_P8PANELS_SETTINGS`, `P8PNL_SETTINGS` и `P8PNL_SELECTLIST` и - обязательны к компиляции. **Это ядро серверной части расширения. Без этих объектов его работа невозможна. Все остальные, перечисленные ниже объекты серверной части - прикладные, не обязательны к компиляции и на функционирование непосредственно фреймворка влияния не оказывают.** - объекты `PKG_P8PANELS_SAMPLES`, `P8PNL_SMPL_CYCLOGRAM` и `P8PNL_SMPL_GANTT` - обеспечивают работу панели "Samples" ("Примеры для разработчиков"). Это абстрактный набор примеров применения фреймворка, не привязанный жестко к комплектации Системы. Должен откомпилироваться практически в любой комплектации Системы (единственное требование - наличие словаря "Контрагенты"). - объекты `PKG_P8PANELS_PROJECTS`, `P8PNL_JB_JOBS`, `P8PNL_JB_JOBSPREV`, `P8PNL_JB_PERIODS`, `P8PNL_JB_PRJCTS` и `P8PNL_JB_PRMS` - могут быть откомпилированы в Системе, имеющей в комлектации приложения "Планирование и учёт в проектах" и "Управление закупками, складом и реализацией". Эти объекты отвечают за работу панелей группы "Планирование и учёт в проектах" ("PrjFin" - "Экономика проектов", "PrjJobs" - "Работы проектов", "PrjGraph" - "Графики проектов", "PrjInfo" - "Информация о проектах"). Компилируйте только если комлектация Системы включает указанные приложения. +- объекты `I_P8PNL_*` - содержат индексы для загружаемых таблиц, **перед созданием индексов необходимо заменить в тексте файла шаблон `<ИМЯ_ИНДЕКСНОГО_ТАБЛИЧНОГО_ПРОСТРАНСТВА>` на имя индексного табличного пространства экземпляра БД**. - объект `PKG_P8PANELS_CLNTTSKBRD` - может быть откомпилирован в Системе, имеющей в комлектации приложение "Управление деловыми процессами". Этот объект отвечает за работу панелей группы "Управление деловыми процессами" ("ClntTaskBoard" - "Доски задач"). Компилируйте только если комлектация Системы включает указанное приложение. - объект `PKG_P8PANELS_MECHREC` - может быть откомпилирован в Системе, имеющей в комлектации приложение "Планирование и учёт в дискретном производстве". Этот объект отвечает за работу панелей группы "Планирование и учёт в дискретном производстве" ("MechRecCostProdPlans" - "Производственная программа", "MechRecDeptCostProdPlans" - "Производственный план цеха", "MechRecCostJobsManage" - "Выдача сменного задания", "MechRecCostJobsManageMP" - "Выдача сменного задания на участок", "MechRecDeptCostJobs" - "Загрузка цеха", "MechRecAssemblyMon" - "Мониторинг сборки изделий"). Компилируйте только если комлектация Системы включает указанное приложение. - объект `PKG_P8PANELS_EQUIPSRV` - может быть откомпилирован в Системе, имеющей в комлектации приложение "Управление техническим обслуживанием и ремонтами". Этот объект отвечает за работу панелей группы "Управление техническим обслуживанием и ремонтами" ("EqsPrfrm" - "Выполнение работ по ТОиР"). Компилируйте только если комлектация Системы включает указанное приложение. @@ -986,6 +987,397 @@ const P8Online = ({ title }) => { ``` +### API для взаимодействия с параметрами фреймворка + +Для работы с параметрами панелей в составе расширения предусмотрен специальный API. Его подключение к компоненте панели осуществляется через контекст `SettingsCtx` ("app/context/settings.js"). Данный контекст обеспечивает инициализацию, считывание и обновление параметров различных видов (системные, панельные, пользовательские). + +В состав API входят: + +- `SETTINGS_KINDS` - объект с константами видов параметров +- `initSettings` - функция, инициализация параметров панелей +- `getSettings` - функция, считывание параметров +- `putSettings` - функция, добавление/обновление параметров +- `showSettingsDialog` - функция, отображение диалога настройки параметров + +#### `SETTINGS_KINDS` + +Объект, содержащий константы видов параметров: + +``` +{ + SYSTEM: 0, + PANEL: 1, + USER: 2 +} +``` + +- `SYSTEM` - Системные параметры, общие для всех панелей. Не требуют указания панели при работе. +- `PANEL` - Параметры конкретной панели. Требуют указания панели (`panel`). +- `USER` - Пользовательские параметры. Требуют указания панели (`panel`). + +#### `async Object initSettings(Object)` + +Инициализирует параметры панелей в БД Системы. Если параметры с указанными ключами уже существуют - будут перезаписаны. + +**Входные параметры:** + +``` +{ + kind, + settings, + panel = null, + full = false, + loader = true +} +``` + +`kind` - обязательный, число, вид параметров (`SETTINGS_KINDS.SYSTEM`, `SETTINGS_KINDS.PANEL` или `SETTINGS_KINDS.USER`)\ +`settings` - обязательный, объект, параметры для инициализации вида `{ "МНЕМОКОД_ПАРАМЕТРА": { name: <НАИМЕНОВАНИЕ>, dataType: <ТИП_ДАННЫХ>, value: <ЗНАЧЕНИЕ>, desc: <ОПИСАНИЕ>, options: {...} }, ... }`\ +`panel` - необязательный, строка, имя панели (требуется для `PANEL` и `USER`, игнорируется для `SYSTEM`)\ +`full` - необязательный, логический, признак возврата полного ответа сервера (по умолчанию `false`)\ +`loader` - необязательный, логический, признак отображения типового индикатора процесса + +**Результат:** объект с инициализированными параметрами (если `full = false`) или полный типовой ответ сервера (если `full = true`). + +**Особенности:** + +- Для вида `SYSTEM` параметр `panel` не требуется и игнорируется +- Для видов `PANEL` и `USER` параметр `panel` обязателен, иначе будет выведена ошибка +- Параметр `options` является необязательным, но если указан, то должен быть объект вида `{<КЛЮЧ>:<ЗНАЧЕНИЕ>, ...}`. Параметры поддерживают привязку к разделам системы, для этого требуется указать следующие `options` `{unitcode: <МНЕМОКОД_РАЗДЕЛА>, showMethod: <МЕТОД_РАЗДЕЛА>, inParameter: <ИМЯ_ВХОДНОГО_ПАРАМЕТРА>, outParameter: <ИМЯ_ВЫХОДНОГО_ПАРАМЕТРА>}`, все параметры являются обязательными +- Функция выполняет обновление существующих параметров, обновляемые значения: `options`, `name`, `desc` и анализирует необходимость обновления значения, если `dataType` не соответствует инициализируемому - обновляет значение `value`, в ином случае значение не обновляется + +**Пример:** + +``` +import React, { useContext } from "react"; +import { SettingsCtx } from "../../context/settings"; +import { P8P_DATA_TYPES } from "../../core/data_types"; + +//Инициализируемые параметры +const INIT_SETTINGS = { + INFO: { + name: "Дополнительная информация", + dataType: P8P_DATA_TYPES.STR, + value: "Необходима проверка документов.", + desc: "Дополнительная информация для контрагента" + }, + COUNT_DOCS: { + name: "Количество отображаемых документов", + dataType: P8P_DATA_TYPES.NUMB, + value: 10, + desc: "" + }, + START_DATE: { + name: "Дата начала", + dataType: P8P_DATA_TYPES.DATE, + value: "2026-02-15", + desc: "Дата начала по умолчанию" + }, + AGENT: { + name: "Выбранный контрагент", + dataType: P8P_DATA_TYPES.STR, + value: "", + desc: "Мнемокод выбранного контрагента", + options: { + unitcode: "AGNLIST", + showMethod: "main", + inParameter: "in_AGNABBR", + outParameter: "out_AGNABBR" + } + } +}; + +const MyPanel = () => { + //Собственное состояние - параметры + const [state, setState] = useState({}); + //Подключение к контексту параметров + const { initSettings, SETTINGS_KINDS } = useContext(SettingsCtx); + + //Инициализация параметров + const initializeSettings = async () => { + // Инициализация пользовательских параметров + const res = await initSettings({ + kind: SETTINGS_KINDS.USER, + panel: "PanelName", + settings: INIT_SETTINGS + }); + //Устанавливаем параметры + setState({ ...res }); + }; + + return
...
; +}; +``` + +#### `async Object getSettings(Object)` + +Считывает параметры панелей из БД Системы. + +**Входные параметры:** + +``` +{ + kind, + code = null, + panel = null, + full = false, + loader = true +} +``` + +`kind` - обязательный, число, вид параметров (`SETTINGS_KINDS.SYSTEM`, `SETTINGS_KINDS.PANEL` или `SETTINGS_KINDS.USER`)\ +`code` - необязательный, строка, мнемокод параметра для отбора, при пустом значении считывает все доступные параметры\ +`panel` - необязательный, строка, имя панели (требуется для `PANEL` и `USER`, игнорируется для `SYSTEM`)\ +`full` - необязательный, логический, признак возврата полного ответа сервера с информацией о параметре (по умолчанию `false`)\ +`loader` - необязательный, логический, признак отображения типового индикатора процесса + +**Результат:** объект с считанными параметрами вида `{ <МНЕМОКОД_ПАРАМЕТРА>: <ЗНАЧЕНИЕ>, ... }` (если `full = false`) или полный ответ сервера (если `full = true`). + +**Особенности:** + +- Для вида `SYSTEM` параметр `panel` не требуется и игнорируется +- Для видов `PANEL` и `USER` параметр `panel` рекомендуется указать для получения параметров конкретной панели +- Возвращает только значения параметров, без метаданных (если `full = false`) + +**Пример:** + +``` +import React, { useContext, useState, useEffect } from "react"; +import { SettingsCtx } from "../../context/settings"; + +const MyPanel = () => { + //Собственное состояние - параметры + const [state, setState] = useState({}); + //Подключение к контексту параметров + const { getSettings, SETTINGS_KINDS } = useContext(SettingsCtx); + + //Загрузка параметров пользователя + const handleLoadSettings = async () => { + // Считывание параметров панели + const settings = await getSettings({ + kind: SETTINGS_KINDS.USER, + panel: "PanelName" + }); + //Устанавливаем параметры + setState({ ...settings }); + }; + + return
...
; +}; +``` + +#### `async Object putSettings(Object)` + +Добавляет или обновляет параметры панелей в БД Системы. + +**Входные параметры:** + +``` +{ + kind, + settings, + panel = null, + loader = true +} +``` + +`kind` - обязательный, число, вид параметров (`SETTINGS_KINDS.SYSTEM`, `SETTINGS_KINDS.PANEL` или `SETTINGS_KINDS.USER`)\ +`settings` - обязательный, объект, параметры для добавления/обновления вида `{ <МНЕМОКОД_ПАРАМЕТРА>: { name: <НАИМЕНОВАНИЕ>, dataType: <ТИП_ДАННЫХ>, value: <ЗНАЧЕНИЕ>, desc: <ОПИСАНИЕ>, options: {...} }, ... }`\ +`panel` - необязательный, строка, имя панели (требуется для `PANEL` и `USER`, игнорируется для `SYSTEM`)\ +`loader` - необязательный, логический, признак отображения типового индикатора процесса + +**Результат:** функция не возвращает значимого результата. + +**Особенности:** + +- Для вида `SYSTEM` параметр `panel` не требуется и игнорируется +- Для видов `PANEL` и `USER` параметр `panel` обязателен, иначе будет выведена ошибка и функция вернётся без выполнения +- При обновлении параметра обновляются следующие значения: `options`, `name`, `desc`, `dataType`, `value` + +**Пример:** + +``` +import React, { useContext } from "react"; +import { SettingsCtx } from "../../context/settings"; +import { formatDateJSONDateOnly } from "../../core/utils"; + +//Инициализируемые параметры +const PUT_SETTINGS = { + LAST_UPDATE: { + name: "Дата последнего обновления", + dataType: "DATE", + value: formatDateJSONDateOnly(new Date()), + desc: "" + }, + COUNT_DOCS: { + name: "Количество отображаемых документов", + dataType: P8P_DATA_TYPES.NUMB, + value: 15, + desc: "" + } +}; + +const MyPanel = () => { + //Собственное состояние - параметры + const [state, setState] = useState({}); + //Подключение к контексту параметров + const { putSettings, SETTINGS_KINDS } = useContext(SettingsCtx); + + //Добавление/обновление параметров пользователя + const handlePutSettings = async () => { + //Обновление системных параметров + await putSettings({ + kind: SETTINGS_KINDS.USER, + panel: "PanelName", + settings: PUT_SETTINGS + }); + }; + + return
...
; +}; +``` + +#### `async undefined showSettingsDialog(Object)` + +Отображает диалог настройки параметров пользователя. Диалог позволяет пользователю изменить значения параметров. + +**Входные параметры:** + +``` +{ + panel = null, + onOk = null, + onCancel = null +} +``` + +`panel` - необязательный, строка, имя панели (если не указан - открываются все параметры с разделением по панелям)\ +`onOk` - необязательный, функция вида `f({ settings })`, будет вызвана при нажатии "ОК" в диалоге. В параметр `settings` передаются изменённые параметры\ +`onCancel` - необязательный, функция вида `f()`, будет вызвана при нажатии "Отмена" или закрытии диалога + +**Результат:** функция возвращает следующие объекты:\ + +- `settings` - объект, содержащий информацию о всех параметрах с учетом изменений +- `isChanged` - признак изменений данных (`true` - параметры были изменены, `false` - изменений не было) + +**Особенности:** + +- Диалог реализован компонентом `P8PSettingsDialog` ("app/components/p8p_settings_dialog.js") +- При нажатии "ОК" параметры автоматически сохраняются через `putSettings` +- При нажатии "Отмена" параметры не сохраняются +- Диалог закрывается автоматически после обработки нажатий + +**Пример:** + +``` +import React, { useContext } from "react"; +import { SettingsCtx } from "../../context/settings"; +import { Button } from "@mui/material"; + +const MyPanel = () => { + //Подключение к контексту параметров + const { showSettingsDialog, SETTINGS_KINDS } = useContext(SettingsCtx); + + //При отображении параметров панели + const handleSettingsDialog = async () => { + //Отображаем параметры панели + showSettingsDialog({ + panel: "PanelName", + onOk: ({ settings }) => { + console.log("Параметры сохранены:", { settings, isChanged }); + }, + onCancel: () => { + console.log("Изменения отменены"); + } + }); + }; + + return ( +
+ +
+ ); +}; +``` + +#### Формат параметров + +Параметры имеют следующую структуру: + +``` +{ + "МНЕМОКОД_ПАРАМЕТРА": { + name: <НАИМЕНОВАНИЕ>, + dataType: <ТИП_ДАННЫХ>, + value: <ЗНАЧЕНИЕ>, + desc: <ОПИСАНИЕ>, + options: { ... } // необязательный + } +} +``` + +**Поля параметра:** + +- `name` - обязательный, строка, наименование параметра +- `dataType` - обязательный, строка, тип данных параметра (`STR` - строка, `NUMB` - число, `DATE` - дата) +- `value` - необязательный, значение параметра (тип должен соответствовать `dataType`) +- `desc` - необязательный, строка, описание параметра (может использоваться в диалоге настроек как подсказка) +- `options` - необязательный, объект, дополнительные настройки параметра + +**Настройки привязки к разделу (`options`):** + +Параметр может быть привязан к разделу Системы для автоматического заполнения значения через словарь. Для этого в `options` указываются: + +- `unitcode` - строка, код раздела Системы (например, `"AGNLIST"` для словаря "Контрагенты") +- `showMethod` - строка, метод вызова раздела +- `inParameter` - строка, имя входного параметра метода вызова для отбора/позиционирования (например, `"in_AGNABBR"`) +- `outParameter` - строка, имя выходного параметра метода вызова для получения значения (например, `"out_AGNABBR"`) + +**Пример параметра с привязкой к словарю:** + +```javascript +{ + AGENT: { + name: "Выбранный контрагент", + dataType: "STR", + value: "", + desc: "Мнемокод контрагента для отчёта", + options: { + unitcode: "AGNLIST", + showMethod: "main", + inParameter: "in_AGNABBR", + outParameter: "out_AGNABBR" + } + } +} +``` + +При использовании такого параметра в диалоге настроек (`showSettingsDialog`) будет автоматически предложен выбор из словаря "Контрагенты", и значение параметра будет заполнено после выбора записи. + +**Пример:** + +Полный актуальный исходный код примера можно увидеть в "app/panels/samples/settings.js" данного репозитория. + +#### Примечания + +1. **Наименование панели:** при использовании параметра `panel` в него необходимо передавать соответствующее наименование панели из `p8panels.config`. + +2. **Формат параметров:** каждый параметр должен быть объектом со структурой `{ name, dataType, value, desc, options }`. + +3. **Типы данных (`dataType`):** поддерживаются следующие типы: + - `STR` - строка + - `NUMB` - число + - `DATE` - дата (в формате ISO 8601, например `"2024-01-15"`) + - `BOOL` - логическое (`true`/`false`) + +4. **Имена параметров:** мнемокоды параметров должны быть уникальными в рамках одной панели/системы. + +5. **Привязка к разделу:** для параметров, которые должны заполняться через выбор из словаря/раздела Системы, обязательно заполняется `options` с полями `unitcode`, `showMethod`, `inParameter`, `outParameter`. + +6. **Метаданные:** параметр `full = true` возвращает не только значения параметров, но и все метаданные (`name`, `dataType`, `desc`, `options`), что полезно для построения динамических форм редактирования. + ### Компоненты пользовательского интерфейса #### Типовые интерфейсные примитивы diff --git a/app.text.js b/app.text.js index fe03322..26f114f 100644 --- a/app.text.js +++ b/app.text.js @@ -33,6 +33,7 @@ export const TEXTS = { export const BUTTONS = { NAVIGATE_HOME: "Домой", //Переход к домашней странице NAVIGATE_BACK: "Назад", //Возврат назад по навигации + NAVIGATE_SETTINGS: "Параметры", //Переход к параметрам панелей NAVIGATE: "Перейти", //Переход к разделу/панели/адресу OK: "ОК", //Ок CANCEL: "Отмена", //Отмена diff --git a/app/app.js b/app/app.js index bccb4e2..ba3b31f 100644 --- a/app/app.js +++ b/app/app.js @@ -11,6 +11,7 @@ import React, { useState, useContext, useEffect } from "react"; //Классы R import PropTypes from "prop-types"; //Контроль свойств компонента import { createHashRouter, RouterProvider, useRouteError } from "react-router-dom"; //Роутер import { ApplicationCtx } from "./context/application"; //Контекст приложения +import { SettingsCtx } from "./context/settings"; //Контекст параметров import { NavigationContext, NavigationCtx, getRootLocation } from "./context/navigation"; //Контекст навигации import { P8PAppErrorPage } from "./components/p8p_app_error_page"; //Страница с ошибкой import { P8PAppWorkspace } from "./components/p8p_app_workspace"; //Рабочее пространство панели @@ -89,6 +90,12 @@ const Workspace = ({ panels = [], selectedPanel, children } = {}) => { //Подключение к контексту приложения const { appState } = useContext(ApplicationCtx); + //Подключение к контексту параметров + const { showSettingsDialog } = useContext(SettingsCtx); + + //При открытии диалога параметров панели + const handleSettingsDialog = panel => showSettingsDialog({ panel }); + //Отработка действия навигации домой const handleHomeNavigate = () => navigateRoot(); @@ -103,8 +110,10 @@ const Workspace = ({ panels = [], selectedPanel, children } = {}) => { selectedPanel={selectedPanel} caption={appState.appBarTitle} showAppBar={appState.appBarShow} + showAppBarSettings={appState.appBarSettingsShow} onHomeNavigate={handleHomeNavigate} onItemNavigate={handleItemNavigate} + onSettingsDialog={handleSettingsDialog} > {children} diff --git a/app/components/p8p_app_workspace.js b/app/components/p8p_app_workspace.js index 2d42859..082e9ae 100644 --- a/app/components/p8p_app_workspace.js +++ b/app/components/p8p_app_workspace.js @@ -21,7 +21,8 @@ import { List, ListItemButton, ListItemIcon, - ListItemText + ListItemText, + Divider } from "@mui/material"; //Интерфейсные компоненты import { P8PPanelsMenuDrawer, P8P_PANELS_MENU_PANEL_SHAPE } from "./p8p_panels_menu"; //Меню import { APP_STYLES } from "../../app.styles"; //Типовые стили @@ -38,6 +39,9 @@ const STYLES = { DRAWER: { [`& .MuiDrawer-paper`]: { ...APP_STYLES.SCROLL } }, ROOT_BOX: { display: "flex" }, APP_BAR: { position: "fixed" }, + APP_BAR_MAIN_BOX: { display: "flex", width: "100vw", alignItems: "center", justifyContent: "space-between" }, + APP_BAR_LEFT_SIDE: { display: "flex", alignItems: "center", justifyContent: "flex-start" }, + APP_BAR_RIGHT_SIDE: { display: "flex", alignItems: "center", justifyContent: "flex-end" }, APP_BAR_BUTTON: { mr: 2 }, MAIN: { flexGrow: 1 } }; @@ -53,10 +57,13 @@ const P8PAppWorkspace = ({ selectedPanel, caption, showAppBar = true, + showAppBarSettings = true, closeCaption, homeCaption, + settingsCaption, onHomeNavigate, - onItemNavigate + onItemNavigate, + onSettingsDialog } = {}) => { //Собственное состояния const [open, setOpen] = useState(false); @@ -80,6 +87,9 @@ const P8PAppWorkspace = ({ onItemNavigate ? onItemNavigate(panel) : null; }; + //Отработка нажатия на элемент открытия параметров + const handleSettingsDialog = (panel = null) => onSettingsDialog(panel); + //Генерация содержимого return ( @@ -88,18 +98,35 @@ const P8PAppWorkspace = ({ - - {open ? "chevron_left" : "menu"} - - - {caption || selectedPanel?.caption} - + + + + {open ? "chevron_left" : "menu"} + + + {caption || selectedPanel?.caption} + + + + {showAppBarSettings && selectedPanel.showUserSettings ? ( + handleSettingsDialog(selectedPanel.name)} + edge="end" + sx={STYLES.APP_BAR_BUTTON} + > + settings + + ) : null} + + @@ -116,6 +143,13 @@ const P8PAppWorkspace = ({ + + handleSettingsDialog()}> + + settings + + + @@ -136,10 +170,13 @@ P8PAppWorkspace.propTypes = { selectedPanel: P8P_PANELS_MENU_PANEL_SHAPE, caption: PropTypes.string, showAppBar: PropTypes.bool, + showAppBarSettings: PropTypes.bool, closeCaption: PropTypes.string.isRequired, homeCaption: PropTypes.string.isRequired, + settingsCaption: PropTypes.string.isRequired, onHomeNavigate: PropTypes.func, - onItemNavigate: PropTypes.func + onItemNavigate: PropTypes.func, + onSettingsDialog: PropTypes.func }; //---------------- diff --git a/app/components/p8p_settings_dialog.js b/app/components/p8p_settings_dialog.js new file mode 100644 index 0000000..fc657ac --- /dev/null +++ b/app/components/p8p_settings_dialog.js @@ -0,0 +1,153 @@ +/* + Парус 8 - Панели мониторинга + Компонент: Диалог отображения параметров +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useContext, useMemo } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Stack, List, ListItem, ListItemButton, ListItemText, Typography, Box, Divider } from "@mui/material"; //Интерфейсные элементы +import { P8PDialog, P8P_DIALOG_WIDTH } from "./p8p_dialog"; //Типовой диалог +import { APP_STYLES } from "../../app.styles"; //Типовые стили +import { P8PSettingsList } from "./p8p_settings_list"; //Список параметров +import { ApplicationCtx } from "../context/application"; //Контекст приложения +import { deepCopyObject, hasValue } from "../core/utils"; //Вспомогательные функции + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + CONTAINER: { display: "flex", flexDirection: "row", alignItems: "flex-start" }, + BOX_PANELS: { width: "300px", height: "500px", overflow: "auto", ...APP_STYLES.SCROLL }, + BOX_SETTINGS: { width: "520px", height: "500px", overflow: "auto", ...APP_STYLES.SCROLL } +}; + +//----------- +//Тело модуля +//----------- + +//Диалог отображения параметров +const P8PSettingsDialog = ({ settings, panel = null, onOk, onClose }) => { + //Собственное состояние - параметры панелей + const [panelSettings, setPanelSettings] = useState(settings); + + //Собственное состояние - мнемокод выбранной панели + const [selectedPanel, setSelectedPanel] = useState(panel); + + //Собственное состояние - наличие отображаемых данных + const isSettingsExists = useMemo(() => { + return selectedPanel + ? panelSettings && Object.keys(panelSettings).length > 0 && Object.keys(panelSettings[selectedPanel]).length > 0 + : Object.keys(panelSettings).length > 0; + }, [selectedPanel, panelSettings]); + + //Подключение к контексту приложения + const { appState } = useContext(ApplicationCtx); + + //При нажатии на "ОК" + const handleOk = () => onOk && onOk(panelSettings); + + //При нажатии на "Отменить" + const handleCancel = () => onClose && onClose(); + + //При изменении параметра + const handleSettingChange = (settingCode, newSettings) => { + //Определяем панели + const newPanelSettings = deepCopyObject(panelSettings); + //Изменяем настройку у панели + newPanelSettings[selectedPanel][settingCode] = deepCopyObject(newSettings); + //Обновляем панели + setPanelSettings(newPanelSettings); + }; + + //При нажатии на панель + const handlePanelSelect = panelName => setSelectedPanel(selectedPanel === panelName ? null : panelName); + + //Генерация области выбора панели + const panelsListRender = () => { + //Генерация содержимого + return ( + <> + + + {Object.keys(panelSettings).map((panel, i) => { + //Считываем информацию о панели + const panelInfo = appState.panels.find(appPanel => appPanel.name == panel); + //Элемент панели + return ( + + handlePanelSelect(panel)} selected={panel === selectedPanel}> + + {`${panelInfo.desc ? panelInfo.desc : "Описание отсутствует"}`} + + } + /> + + + ); + })} + + + + + ); + }; + + //Формирование представления + return ( + appPanel.name == panel).caption}"` : "панелей"}`} + width={P8P_DIALOG_WIDTH.LG} + onCancel={handleCancel} + onOk={handleOk} + scrollContent={false} + okDisabled={Object.keys(panelSettings).length === 0} + > + {isSettingsExists ? ( + + {!hasValue(panel) ? panelsListRender() : null} + + {hasValue(selectedPanel) ? ( + + ) : ( + + Выберите панель для отображения параметров + + )} + + + ) : ( + + Отсутствуют доступные параметры + + )} + + ); +}; + +//Контроль свойств компонента - Диалог отображения параметров +P8PSettingsDialog.propTypes = { + settings: PropTypes.object.isRequired, + panel: PropTypes.string, + onOk: PropTypes.func, + onClose: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PSettingsDialog }; diff --git a/app/components/p8p_settings_list.js b/app/components/p8p_settings_list.js new file mode 100644 index 0000000..67fde11 --- /dev/null +++ b/app/components/p8p_settings_list.js @@ -0,0 +1,178 @@ +/* + Парус 8 - Панели мониторинга + Компонент: Список параметров +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useContext, useMemo } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Stack, List, ListItem, ListItemButton, ListItemText, Typography } from "@mui/material"; //Интерфейсные элементы +import { P8PDialog } from "./p8p_dialog"; //Типовой диалог +import { deepCopyObject } from "../core/utils"; //Вспомогательные функции +import { ApplicationCtx } from "../context/application"; //Контекст приложения +import { P8P_DATA_TYPES } from "../core/data_types"; //Типы данных + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + LIST: { width: "510px", bgcolor: "background.paper", overflowY: "auto" }, + TYPOGRAPHY_VALUE: { maxWidth: "200px" }, + TEXT_FIELD_STR_VALUE: { minWidth: "400px" } +}; + +//-------------------------------- +//Вспомогательные классы и функции +//-------------------------------- + +//Диалог изменения параметра +const P8PSettingChangeDialog = ({ initSetting, onSave, onClose }) => { + //Собственное состояние - параметр + const [setting, setSetting] = useState(deepCopyObject(initSetting)); + + //Собственное состояние - доступность выбора из словаря + const isDictionary = useMemo(() => { + //Проверяем наличие обязательных настроек + return ( + setting.options && + setting.options?.unitcode && + setting.options?.showMethod && + setting.options?.inParameter && + setting.options?.outParameter + ); + }, [setting.options]); + + //Подключение к контексту приложения + const { pOnlineShowDictionary } = useContext(ApplicationCtx); + + //При закрытии диалога + const handleClose = () => onClose && onClose(); + + //При нажатии "ОК" + const handleOk = () => onSave && onSave(setting); + + //При изменении значения параметра + const handleSettingChange = (name, value) => { + setSetting(pv => ({ ...pv, [name]: value })); + }; + + //При выборе значения из справочника + const handleDictionarySelect = (currentFormValues, setFormValues) => { + //Открываем справочник с учетом параметров + pOnlineShowDictionary({ + unitCode: initSetting.options.unitcode, + showMethod: initSetting.options.showMethod, + inputParameters: [{ name: initSetting.options.inParameter, value: currentFormValues.value }], + callBack: res => { + res.success === true ? setFormValues([{ name: "value", value: res.outParameters[initSetting.options.outParameter] }]) : null; + } + }); + }; + + //Формирование представления + return ( + + ); +}; + +//Контроль свойств компонента - Диалог изменения параметра +P8PSettingChangeDialog.propTypes = { + initSetting: PropTypes.object, + onSave: PropTypes.func, + onClose: PropTypes.func +}; + +//----------- +//Тело модуля +//----------- + +//Список параметров +const P8PSettingsList = ({ settings, onSettingChange }) => { + //Собственное состояние - изменяемый параметр + const [changingSetting, setChangingSetting] = useState(null); + + //При нажатии на параметр + const handleSettingClick = settingCode => setChangingSetting(settingCode); + + //При очистки изменяемого параметра + const handleChangingSettingClear = () => setChangingSetting(null); + + //При изменении значения параметра + const handleSettingChange = newSetting => { + //Вызываем изменение параметра + onSettingChange && onSettingChange(changingSetting, newSetting); + //Очищаем изменяемый параметр + handleChangingSettingClear(); + }; + + //Формирование представления + return ( + <> + + {Object.keys(settings).map((setting, i) => { + return ( + + handleSettingClick(setting)}> + + {`${settings[setting].desc ? settings[setting].desc : "Описание отсутствует"}`} + {`${settings[setting].value}`} + + } + /> + + + ); + })} + + {changingSetting ? ( + + ) : null} + + ); +}; + +//Контроль свойств компонента - Список параметров +P8PSettingsList.propTypes = { + settings: PropTypes.object, + onSettingChange: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PSettingsList }; diff --git a/app/components/p8p_table.js b/app/components/p8p_table.js index 363d51c..c9f57b0 100644 --- a/app/components/p8p_table.js +++ b/app/components/p8p_table.js @@ -37,6 +37,7 @@ import { import { useTheme } from "@mui/material/styles"; //Взаимодействие со стилями MUI import { P8PAppInlineError, P8PHintDialog } from "./p8p_app_message"; //Встраиваемое сообщение об ошибке import { P8P_TABLE_AT, HEADER_INITIAL_STATE, hasValue, p8pTableReducer } from "./p8p_table_reducer"; //Редьюсер состояния +import { P8P_DATA_TYPES } from "../core/data_types"; //Типы данных //--------- //Константы @@ -50,9 +51,9 @@ const P8P_TABLE_SIZE = { //Типы данных const P8P_TABLE_DATA_TYPE = { - STR: "STR", - NUMB: "NUMB", - DATE: "DATE" + STR: P8P_DATA_TYPES.STR, + NUMB: P8P_DATA_TYPES.NUMB, + DATE: P8P_DATA_TYPES.DATE }; //Направления сортировки diff --git a/app/config_wrapper.js b/app/config_wrapper.js index 86de65d..6dea7fd 100644 --- a/app/config_wrapper.js +++ b/app/config_wrapper.js @@ -30,7 +30,8 @@ const P8P_PANELS_MENU_GRID_CONFIG_PROPS = { //Конфигурируемые свойства "Рабочего пространства" (P8PAppWorkspace) const P8P_APP_WORKSPACE_CONFIG_PROPS = { closeCaption: BUTTONS.CLOSE, - homeCaption: BUTTONS.NAVIGATE_HOME + homeCaption: BUTTONS.NAVIGATE_HOME, + settingsCaption: BUTTONS.NAVIGATE_SETTINGS }; //Конфигурируемые свойства "Таблицы" (P8PTable) diff --git a/app/context/application.js b/app/context/application.js index fd9214e..9254f1d 100644 --- a/app/context/application.js +++ b/app/context/application.js @@ -39,7 +39,7 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c const [state, dispatch] = useReducer(applicationReducer, INITIAL_STATE(displaySizeGetter)); //Подключение к контексту взаимодействия с сервером - const { getConfig, getRespPayload } = useContext(BackEndCtx); + const { getConfig, getRespPayload, executeStored } = useContext(BackEndCtx); //Подключение к контексту отображения сообщений const { showMsgErr } = useContext(MessagingCtx); @@ -54,7 +54,7 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c const setUrlBase = urlBase => dispatch({ type: APP_AT.SET_URL_BASE, payload: urlBase }); //Установка списка панелей - const setPanels = panels => dispatch({ type: APP_AT.LOAD_PANELS, payload: panels }); + const setPanels = (panels, panelsSettings) => dispatch({ type: APP_AT.LOAD_PANELS, payload: { panels, panelsSettings } }); //Установка заголовка в шапке приложения const setAppBarTitle = useCallback(appBarTitle => dispatch({ type: APP_AT.SET_APP_BAR_TITLE, payload: appBarTitle }), []); @@ -62,6 +62,12 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c //Установка флага отображения шапки приложения const setAppBarShow = useCallback(appBarShow => dispatch({ type: APP_AT.SET_APP_BAR_SHOW, payload: appBarShow }), []); + //Установка флага отображения настройки параметров в шапке приложения + const setAppBarSettingsShow = useCallback( + appBarSettingsShow => dispatch({ type: APP_AT.SET_APP_BAR_SETTINGS_SHOW, payload: appBarSettingsShow }), + [] + ); + //Поиск раздела по имени const findPanelByName = name => state.panels.find(panel => panel.name == name); @@ -147,17 +153,36 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c //Получение базового URL приложения const configUrlBase = useMemo(() => state.urlBase, [state.urlBase]); + //Инициализация информации о параметрах панелей + const initPanelsParams = useCallback(async () => { + //Инициализируем параметры панелей + try { + const res = await executeStored({ + stored: "PKG_P8PANELS_SETTINGS.PANELS_INIT", + respArg: "COUT", + loader: false + }); + return res; + } catch (e) { + showMsgErr(`Ошибка инициализации параметров панелей.`, null, e.message); + } + }, [executeStored, showMsgErr]); + //Инициализация приложения const initApp = useCallback(async () => { //Читаем конфигурацию с сервера let res = await getConfig(); //Сохраняем базовый URL приложения setUrlBase(getRespPayload(res)?.Panels?.urlBase); + //Инициализируем панели + const panels = getRespPayload(res)?.Panels?.Panel; + //Инициализируем информацию о параметрах панелей + const panelsSettings = await initPanelsParams(); //Сохраняем список панелей - setPanels(getRespPayload(res)?.Panels?.Panel); + setPanels(panels, panelsSettings); //Установим флаг завершения инициализации setInitialized(); - }, [getConfig, getRespPayload]); + }, [getConfig, getRespPayload, initPanelsParams]); //Обработка подключения контекста к странице useEffect(() => { @@ -177,6 +202,7 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c value={{ setAppBarTitle, setAppBarShow, + setAppBarSettingsShow, findPanelByName, pOnlineShowTab, pOnlineShowUnit, diff --git a/app/context/application_reducer.js b/app/context/application_reducer.js index 8f124de..e71ac1f 100644 --- a/app/context/application_reducer.js +++ b/app/context/application_reducer.js @@ -14,7 +14,8 @@ const APP_AT = { SET_INITIALIZED: "SET_INITIALIZED", //Установка флага инициализированности приложения SET_DISPLAY_SIZE: "SET_DISPLAY_SIZE", //Установка текущего типового размера экрана SET_APP_BAR_TITLE: "SET_APP_BAR_TITLE", //Установка заголовка в шапке приложения - SET_APP_BAR_SHOW: "SET_APP_BAR_SHOW" //Установка флага отображения шапки приложения + SET_APP_BAR_SHOW: "SET_APP_BAR_SHOW", //Установка флага отображения шапки приложения + SET_APP_BAR_SETTINGS_SHOW: "SET_APP_BAR_SETTINGS_SHOW" //Установка флага отображения настройки параметров в шапке приложения }; //Состояние приложения по умолчанию @@ -22,6 +23,7 @@ const INITIAL_STATE = displaySizeGetter => ({ displaySize: displaySizeGetter(), appBarTitle: "", appBarShow: true, + appBarSettingsShow: true, urlBase: "", panels: [], panelsLoaded: false, @@ -39,7 +41,8 @@ const handlers = { //Загрузка списка панелей [APP_AT.LOAD_PANELS]: (state, { payload }) => { let panels = []; - if (payload && Array.isArray(payload)) for (let p of payload) panels.push({ ...p }); + if (payload && Array.isArray(payload.panels)) + for (let p of payload.panels) panels.push({ ...p, showUserSettings: payload?.panelsSettings?.[p.name]?.userSettings === 1 ? true : false }); return { ...state, panels, @@ -54,6 +57,8 @@ const handlers = { [APP_AT.SET_APP_BAR_TITLE]: (state, { payload }) => ({ ...state, appBarTitle: payload }), //Установка флага отображения шапки приложения [APP_AT.SET_APP_BAR_SHOW]: (state, { payload }) => ({ ...state, appBarShow: payload }), + //Установка флага отображения настройки параметров в шапке приложения + [APP_AT.SET_APP_BAR_SETTINGS_SHOW]: (state, { payload }) => ({ ...state, appBarSettingsShow: payload }), //Обработчик по умолчанию DEFAULT: state => state }; diff --git a/app/context/settings.js b/app/context/settings.js new file mode 100644 index 0000000..eea755b --- /dev/null +++ b/app/context/settings.js @@ -0,0 +1,172 @@ +/* + Парус 8 - Панели мониторинга + Контекст: Взаимодействие с API параметров +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { createContext, useContext, useCallback, useState, useRef } from "react"; //ReactJS +import PropTypes from "prop-types"; //Контроль свойств компонента +import { MessagingCtx } from "./messaging"; //Контекст сообщений +import { P8PSettingsDialog } from "../components/p8p_settings_dialog"; //Диалог отображения параметров +import { useSettingsContext } from "./settings_hooks"; //Вспомогательные хуки параметров + +//--------- +//Константы +//--------- + +//Доступные виды параметров +const SETTINGS_KINDS = { + SYSTEM: 0, + PANEL: 1, + USER: 2 +}; + +//Доступные действия параметров +const SETTINGS_ACTIONS = { + INIT: "INIT", + GET: "GET", + PUT: "PUT" +}; + +//Псевдокод панели для системных параметров +const SETTINGS_SYSTEM_PSEUDO = "__P8_SYSTEM"; + +//---------------- +//Интерфейс модуля +//---------------- + +//Контекст взаимодействия с API параметров +export const SettingsCtx = createContext(); + +//Провайдер контекста взаимодействия с API параметров +export const SettingsContext = ({ children }) => { + //Подключение к контексту сообщений + const { showMsgErr } = useContext(MessagingCtx); + + //Собственное состояние - действия параметров + const { putPanelSettings, getPanelSettings, initPanelSettings } = useSettingsContext(showMsgErr); + //Собственное состояние - отображаемые параметры + const [settings, setSettings] = useState({ isOpen: false, data: {}, panel: null }); + //Собственное состояние - колбэки для обработки событий + const settingsCallbacks = useRef({ onOk: null, onCancel: null }); + + //Формирование структуры параметров панели + const buildPanelSettings = (kind, panel, settings) => + kind === SETTINGS_KINDS.SYSTEM ? { [SETTINGS_SYSTEM_PSEUDO]: { ...settings } } : { [panel]: { ...settings } }; + + //Извлечение настроек из ответа сервера + const extractSettings = (res, kind, panel) => + kind === SETTINGS_KINDS.SYSTEM ? { ...(res[SETTINGS_SYSTEM_PSEUDO] || {}) } : { ...(panel ? res[panel] : res) }; + + //Валидация параметров настройки + const validateSettingsParams = useCallback( + ({ action, kind, panel }) => { + if ([SETTINGS_KINDS.PANEL, SETTINGS_KINDS.USER].includes(kind) && !panel) { + //Формируем текст ошибки + const errMsg = `Ошибка ${ + action === SETTINGS_ACTIONS.INIT ? "инициализации" : action === SETTINGS_ACTIONS.PUT ? "добавления" : "действия" + } параметров. Для панельных/пользовательских параметров необходимо указать панель.`; + //Отображаем ошибку + showMsgErr(errMsg); + throw new Error(errMsg); + } + }, + [showMsgErr] + ); + + //Добавление параметров панелей + const putSettings = useCallback( + async ({ kind, settings, panel = null, loader = true }) => { + //Проверка корректности параметров + validateSettingsParams({ action: SETTINGS_ACTIONS.PUT, kind, panel }); + //Оборачиваем параметры в панель + const panelSettings = buildPanelSettings(kind, panel, settings); + //Добавляем параметры панели + const res = await putPanelSettings({ kind, panelSettings, loader }); + //Возвращаем результат + return res; + }, + [putPanelSettings, validateSettingsParams] + ); + + //Считывание параметров + const getSettings = useCallback( + async ({ kind, code = null, panel = null, full = false, loader = true }) => { + //Считываем параметры панели + const res = await getPanelSettings({ kind, code, panel, full, loader }); + //Возвращаем результат + return extractSettings(res, kind, panel); + }, + [getPanelSettings] + ); + + //Инициализация параметров + const initSettings = useCallback( + async ({ kind, settings, panel = null, full = false, loader = true }) => { + //Проверка корректности параметров + validateSettingsParams({ action: SETTINGS_ACTIONS.INIT, kind, panel }); + //Оборачиваем параметры в панель + const panelSettings = buildPanelSettings(kind, panel, settings); + //Добавляем параметры панели + const res = await initPanelSettings({ kind, panelSettings, full, loader }); + //Возвращаем результат + return extractSettings(res, kind, panel); + }, + [initPanelSettings, validateSettingsParams] + ); + + //При нажатии "ОК" диалога параметров + const handleOk = async newSettings => { + //Признак наличия изменений параметров + const isChanged = JSON.stringify(newSettings) !== JSON.stringify(settings.data); + //Если параметры изменились - обновляем на сервере + if (isChanged) await putPanelSettings({ kind: SETTINGS_KINDS.USER, panelSettings: newSettings }); + //Если есть колбэк на нажатие "ОК" - вызываем его + if (settingsCallbacks.current.onOk) + settingsCallbacks.current.onOk({ settings: settings.panel ? newSettings[settings.panel] : newSettings, isChanged }); + //Сбрасываем отображаемые данные + setSettings({ isOpen: false, data: {}, panel: null }); + }; + + //При нажатии "Отменить" диалога параметров + const handleClose = () => { + //Если есть колбэк на нажатие "Отменить" - вызываем его + if (settingsCallbacks.current.onCancel) settingsCallbacks.current.onCancel(); + //Сбрасываем отображаемые данные + setSettings({ isOpen: false, data: {}, panel: null }); + }; + + //Открытие диалога параметров + const showSettingsDialog = async ({ panel = null, onOk = null, onCancel = null } = {}) => { + //Загружаем информацию о параметрах + const panelSettings = await getPanelSettings({ kind: SETTINGS_KINDS.USER, panel, full: true }); + //Устанавливаем отображаемые данные + setSettings({ isOpen: true, data: panelSettings, panel }); + //Устанавливаем колбэки + settingsCallbacks.current = { onOk, onCancel }; + }; + + //Вернём компонент провайдера + return ( + + {settings.isOpen ? : null} + {children} + + ); +}; + +//Контроль свойств - Провайдер контекста взаимодействия с API параметров +SettingsContext.propTypes = { + children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]) +}; diff --git a/app/context/settings_hooks.js b/app/context/settings_hooks.js new file mode 100644 index 0000000..87a7ad8 --- /dev/null +++ b/app/context/settings_hooks.js @@ -0,0 +1,120 @@ +/* + Парус 8 - Панели мониторинга + Контекст: Параметры - вспомогательные хуки +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { useCallback, useContext } from "react"; //Классы React +import { BackEndCtx } from "./backend"; //Контекст взаимодействия с сервером +import { object2Base64XML, formatErrorMessage } from "../core/utils"; //Вспомогательные функции + +//----------- +//Тело модуля +//----------- + +//Хук для SettingsContext +const useSettingsContext = showMsgErr => { + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndCtx); + + //Добавление параметров панели + const putPanelSettings = useCallback( + async ({ kind, panelSettings, loader = true }) => { + //Добавляем параметры + try { + await executeStored({ + stored: "PKG_P8PANELS_SETTINGS.PUT", + args: { + NKIND: kind, + CPANELS: { + VALUE: object2Base64XML({ + XPANELS: panelSettings + }), + SDATA_TYPE: SERV_DATA_TYPE_CLOB + } + }, + loader + }); + } catch (e) { + //Разбираем текст ошибки + let errMsg = formatErrorMessage(e.message); + //Отображаем ошибку + showMsgErr(errMsg.text, null, errMsg.fullErrorText); + throw e; + } + }, + [SERV_DATA_TYPE_CLOB, executeStored, showMsgErr] + ); + + //Считывание параметров + const getPanelSettings = useCallback( + async ({ kind, code = null, panel = null, full = false, loader = true }) => { + //Считываем параметры + try { + const res = await executeStored({ + stored: "PKG_P8PANELS_SETTINGS.GET", + args: { + NKIND: kind, + SCODE: code, + SPANEL: panel, + NFULL: full ? 1 : 0 + }, + loader, + respArg: "COUT" + }); + return res || {}; + } catch (e) { + //Разбираем текст ошибки + let errMsg = formatErrorMessage(e.message); + //Отображаем ошибку + showMsgErr(errMsg.text, null, errMsg.fullErrorText); + throw e; + } + }, + [executeStored, showMsgErr] + ); + + //Инициализация параметров панелей + const initPanelSettings = useCallback( + async ({ kind, panelSettings, full = false, loader = true }) => { + //Инициализируем параметры + try { + const res = await executeStored({ + stored: "PKG_P8PANELS_SETTINGS.INIT", + args: { + NKIND: kind, + CPANELS: { + VALUE: object2Base64XML({ + XPANELS: panelSettings + }), + SDATA_TYPE: SERV_DATA_TYPE_CLOB + }, + NFULL: full ? 1 : 0 + }, + loader, + respArg: "COUT" + }); + return res; + } catch (e) { + //Разбираем текст ошибки + let errMsg = formatErrorMessage(e.message); + //Отображаем ошибку + showMsgErr(errMsg.text, null, errMsg.fullErrorText); + throw e; + } + }, + [SERV_DATA_TYPE_CLOB, executeStored, showMsgErr] + ); + + //Возвращаем результат + return { putPanelSettings, getPanelSettings, initPanelSettings }; +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { useSettingsContext }; diff --git a/app/core/data_types.js b/app/core/data_types.js new file mode 100644 index 0000000..60fdfbb --- /dev/null +++ b/app/core/data_types.js @@ -0,0 +1,15 @@ +/* + Парус 8 - Панели мониторинга + Ядро: Типы данных +*/ + +//--------- +//Константы +//--------- + +//Типы данных +export const P8P_DATA_TYPES = { + STR: "STR", + NUMB: "NUMB", + DATE: "DATE" +}; diff --git a/app/panels/samples/samples.js b/app/panels/samples/samples.js index 3837fed..b57e84a 100644 --- a/app/panels/samples/samples.js +++ b/app/panels/samples/samples.js @@ -7,7 +7,7 @@ //Подключение библиотек //--------------------- -import React, { useState } from "react"; //Классы React +import React, { useState, useEffect, useContext } from "react"; //Классы React import { Button, Fab, Icon } from "@mui/material"; //Интерфейсные элементы import { BUTTONS } from "../../../app.text"; //Текстовые ресурсы и константы import { P8Online } from "./p8online"; //Пример: API для взаимодействия с "ПАРУС 8 Онлайн" @@ -20,6 +20,8 @@ import { Gantt } from "./gantt"; //Пример: Диаграмма Ганта " import { Svg } from "./svg"; //Пример: Интерактивные изображения "P8PSVG" import { Cyclogram } from "./cyclogram"; //Пример: Циклограмма "P8PCyclogram" import { Indicator } from "./indicator"; //Пример: Индикатор "P8PIndicator" +import { Settings } from "./settings"; //Пример: Параметры панелей +import { ApplicationCtx } from "../../context/application"; //Контекст приложения //--------- //Константы @@ -36,7 +38,8 @@ const MODES = { GANTT: { name: "GANTT", caption: 'Диаграмма Ганта "P8PGantt"', component: Gantt }, SVG: { name: "SVG", caption: 'Интерактивные изображения "P8PSVG"', component: Svg }, CYCLOGRAM: { name: "CYCLOGRAM", caption: 'Циклограмма "P8PCyclogram"', component: Cyclogram }, - INDICATOR: { name: "INDICATOR", caption: 'Индикатор "P8PIndicator"', component: Indicator } + INDICATOR: { name: "INDICATOR", caption: 'Индикатор "P8PIndicator"', component: Indicator }, + SETTINGS: { name: "SETTINGS", caption: "Параметры панелей", component: Settings } }; //Стили @@ -55,6 +58,15 @@ const Samples = () => { //Собственное состояние const [mode, setMode] = useState(""); + //Подключение к контексту приложения + const { setAppBarSettingsShow } = useContext(ApplicationCtx); + + //При загрузке/открытии режима + useEffect(() => { + if (mode === MODES.SETTINGS.name) setAppBarSettingsShow(true); + else setAppBarSettingsShow(false); + }, [mode, setAppBarSettingsShow]); + //Генерация содержимого return (
diff --git a/app/panels/samples/settings.js b/app/panels/samples/settings.js new file mode 100644 index 0000000..78cc090 --- /dev/null +++ b/app/panels/samples/settings.js @@ -0,0 +1,235 @@ +/* + Парус 8 - Панели мониторинга - Примеры для разработчиков + Пример: Сообщения +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useContext, useState, useEffect } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Typography, Divider, Button, Box } from "@mui/material"; //Интерфейсные элементы +import { SettingsCtx } from "../../context/settings"; //Контекст параметров +import { P8P_DATA_TYPES } from "../../core/data_types"; //Типы данных +import { formatDateJSONDateOnly } from "../../core/utils"; //Вспомогательные функции +import { ApplicationCtx } from "../../context/application"; //Контекст приложения + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + CONTAINER: { textAlign: "center", paddingTop: "20px" }, + TITLE: { paddingBottom: "15px" }, + DIVIDER: { margin: "15px" }, + BOX_SETTINGS_VALUES: { display: "flex", justifyContent: "space-around" }, + SETTING_CARD: { + display: "flex", + justifyContent: "space-between", + alignItems: "center", + padding: "12px 16px", + borderRadius: 1, + border: "1px solid", + borderColor: "divider" + }, + SETTING_NAME: { + fontWeight: 500, + color: "text.primary" + }, + SETTING_VALUE: { + fontWeight: 600, + color: "primary.main", + marginLeft: 2 + } +}; + +//Наименование панели +const PANEL_NAME = "Samples"; + +//Параметры панели +const PANEL_PARAMS = { + STR_PARAM: { + name: "Строковый параметр", + dataType: P8P_DATA_TYPES.STR, + //value: "Строковое значение", + desc: "Описание строкового параметра" + }, + NUM_PARAM: { + name: "Числовой параметр", + dataType: P8P_DATA_TYPES.NUMB, + value: 10, + desc: "Описание числового параметра" + }, + DATE_PARAM: { + name: "Параметр дата", + dataType: P8P_DATA_TYPES.DATE, + value: "2026-02-15", + desc: "" + }, + AGENT: { + name: "Привязка к контрагенту", + dataType: P8P_DATA_TYPES.STR, + value: "", + desc: "Мнемокод выбранного контрагента", + options: { + unitcode: "AGNLIST", + showMethod: "main", + inParameter: "in_AGNABBR", + outParameter: "out_AGNABBR" + } + } +}; + +//Параметр "Дата последнего обновления" +const LAST_UPDATE_PARAM = { + LAST_UPDATE: { + name: "Дата последнего обновления", + dataType: "DATE", + value: formatDateJSONDateOnly(new Date()), + desc: "" + } +}; + +//----------- +//Тело модуля +//----------- + +//Пример: Параметры +const Settings = ({ title }) => { + //Собственное состояние - параметры + const [state, setState] = useState({}); + //Собственное состояние - отображение кнопки параметров панели + const [showPanelSettings, setShowPanelSettings] = useState(true); + + //Подключение к контексту параметров + const { getSettings, initSettings, putSettings, showSettingsDialog, SETTINGS_KINDS } = useContext(SettingsCtx); + //Подключение к контексту приложения + const { setAppBarSettingsShow } = useContext(ApplicationCtx); + + //Инициализация параметров пользователя + const handleInitSettings = async () => { + // Инициализация пользовательских параметров + const settings = await initSettings({ + kind: SETTINGS_KINDS.USER, + panel: PANEL_NAME, + settings: PANEL_PARAMS + }); + //Устанавливаем параметры + setState({ ...settings }); + }; + + //Загрузка параметров пользователя + const handleLoadSettings = async () => { + // Считывание параметров панели + const settings = await getSettings({ + kind: SETTINGS_KINDS.USER, + panel: PANEL_NAME + }); + //Устанавливаем параметры + setState({ ...settings }); + }; + + //Добавление/обновление параметров пользователя + const handlePutSettings = async () => { + //Добавляем параметр + await putSettings({ + kind: SETTINGS_KINDS.USER, + panel: PANEL_NAME, + settings: LAST_UPDATE_PARAM + }); + //Перечитываем параметры + handleLoadSettings(); + }; + + //При отображении параметров панели + const handleSettingsDialog = async () => { + //Отображаем параметры панели + showSettingsDialog({ + panel: PANEL_NAME, + onOk: ({ settings, isChanged }) => { + console.log("Параметры сохранены:", { settings, isChanged }); + }, + onCancel: () => { + console.log("Изменения отменены"); + } + }); + }; + + //Отображение/сокрытие кнопки открытия параметров панели + const handleShowPanelSettingsToggle = () => { + //Переключаем состояние + setShowPanelSettings(!showPanelSettings); + //Скрываем/отображаем кнопку параметров + setAppBarSettingsShow(!showPanelSettings); + }; + + //При открытии панели + useEffect(() => { + //Инициализируем параметры + handleInitSettings(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + //Генерация содержимого + return ( +
+ + {title} + + + + + + + + + + + + + {Object.keys(state).length > 0 ? ( + <> + + + Значения параметров + + + {Object.keys(state).map((item, index) => ( + + + {`${Object.keys(PANEL_PARAMS).includes(item) ? PANEL_PARAMS[item].name : LAST_UPDATE_PARAM[item].name}:`} + + + {state[item]} + + + ))} + + + ) : null} +
+ ); +}; + +//Контроль свойств - Пример: параметры +Settings.propTypes = { + title: PropTypes.string.isRequired +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { Settings }; diff --git a/db/P8PNL_SETTINGS.sql b/db/P8PNL_SETTINGS.sql new file mode 100644 index 0000000..31891c1 --- /dev/null +++ b/db/P8PNL_SETTINGS.sql @@ -0,0 +1,30 @@ +/* + Парус 8 - Панели мониторинга + Параметры панелей +*/ +create table P8PNL_SETTINGS +( + RN number(17) not null, -- Рег. номер записи + CODE varchar2(40) not null, -- Мнемокод параметра + NAME varchar2(240) not null, -- Наименование параметра + KIND number(1) not null -- Вид параметра + constraint C_P8PNL_SETTINGS_KIND_VAL check ( KIND in (0,1,2) ), + PANEL varchar2(100) default null, -- Код панели + COMPANY number(17) default null, -- Рег. номер организации + AUTHID varchar2(30) default null, -- Пользователь + OPTIONS clob, -- Дополнительные параметры + DATA_TYPE number(1) default 0 -- Тип данных + constraint C_P8PNL_SETTINGS_DT_VAL check ( DATA_TYPE in (0,1,2) ), + VALUE_STR varchar2(4000) default null, -- Значение (строка) + VALUE_NUM number default null, -- Значение (число) + VALUE_DATE date default null, -- Значение (дата) + DESCRIPTION varchar2(240) default null, -- Примечание + constraint C_P8PNL_SETTINGS_PK primary key (RN), + constraint C_P8PNL_SETTINGS_UK unique (CODE, PANEL, COMPANY, AUTHID), + constraint C_P8PNL_SETTINGS_DATA_TYPE_CH check (((DATA_TYPE = 0) and ((VALUE_NUM is null) and (VALUE_DATE is null))) + or ((DATA_TYPE = 1) and ((VALUE_STR is null) and (VALUE_DATE is null))) + or ((DATA_TYPE = 2) and ((VALUE_STR is null) and (VALUE_NUM is null)))), + constraint C_P8PNL_SETTINGS_KIND_CH check (((KIND = 0) and ((PANEL is null) and (COMPANY is null) and (AUTHID is null))) + or ((KIND = 1) and ((PANEL is not null) and (COMPANY is null) and (AUTHID is not null))) + or ((KIND = 2) and ((PANEL is not null) and (COMPANY is not null) and (AUTHID is not null)))) +); \ No newline at end of file diff --git a/db/PKG_P8PANELS_SETTINGS.pck b/db/PKG_P8PANELS_SETTINGS.pck new file mode 100644 index 0000000..4f4b1a0 --- /dev/null +++ b/db/PKG_P8PANELS_SETTINGS.pck @@ -0,0 +1,1524 @@ +create or replace package PKG_P8PANELS_SETTINGS as + + /* Типы данных - Настройка параметра */ + type TOPTION is record + ( + SCODE PKG_STD.TSTRING, -- Код настройки параметра + SVALUE PKG_STD.TSTRING -- Значение + ); + + /* Типы данных - коллекция настроек параметра */ + type TOPTIONS is table of TOPTION; + + /* Типы данных - Параметр */ + type TSETTING is record + ( + SCODE PKG_STD.TSTRING, -- Мнемокод + SNAME PKG_STD.TSTRING, -- Наименование + NKIND PKG_STD.TNUMBER, -- Вид параметра + SPANEL PKG_STD.TSTRING, -- Панель + NCOMPANY PKG_STD.TREF, -- Рег. номер организации + SAUTHID PKG_STD.TSTRING, -- Пользователь + ROPTIONS TOPTIONS, -- Настройка параметра + SDATA_TYPE PKG_STD.TSTRING, -- Тип данных + SVALUE PKG_STD.TSTRING, -- Значение + SDESC PKG_STD.TSTRING -- Описание + ); + + /* Типы данных - Коллекция параметров */ + type TSETTINGS is table of TSETTING; + + /* Типы данных - Панели */ + type TPANEL is record + ( + SNAME PKG_STD.TSTRING, -- Наименование панели + RSETTINGS TSETTINGS -- Настройки панели + ); + + /* Типы данных - коллекция панелей */ + type TPANELS is table of TPANEL; + + /* Считывание параметров */ + procedure GET + ( + NKIND in number, -- Вид параметра + SCODE in varchar2 := null, -- Мнемокод параметра (null - все) + SPANEL in varchar2 := null, -- Панель + NFULL in number := 0, -- Флаг включения атрибутов (0 - нет, 1 - да) + COUT out clob -- Информация о параметрах + ); + + /* Добавление параметра */ + procedure PUT + ( + NKIND in number, -- Вид параметра + CPANELS in clob -- XML-описание панелей + ); + + /* Инициализация параметров */ + procedure INIT + ( + NKIND in number, -- Вид параметра + CPANELS in clob, -- XML-описание панелей + NFULL in number := 0, -- Флаг включения в ответ атрибутов параметров (0 - нет, 1 - да) + COUT out clob -- Информация о параметрах + ); + + /* Инициализация информации о настройках панелей */ + procedure PANELS_INIT + ( + COUT out clob -- Информация о панелях + ); + +end PKG_P8PANELS_SETTINGS; +/ +create or replace package body PKG_P8PANELS_SETTINGS as + + /* Константы - Виды параметров */ + NKIND_SYSTEM constant PKG_STD.TNUMBER := 0; -- Системный + NKIND_PANEL constant PKG_STD.TNUMBER := 1; -- Панельный + NKIND_USER constant PKG_STD.TNUMBER := 2; -- Пользовательский + + /* Константы - Типы данных */ + NDATA_TYPE_STR constant PKG_STD.TNUMBER := 0; -- Строковый + NDATA_TYPE_NUM constant PKG_STD.TNUMBER := 1; -- Числовой + NDATA_TYPE_DATE constant PKG_STD.TNUMBER := 2; -- Дата + + /* Константы - Теги для сериализации */ + STAG_DATA constant PKG_STD.TSTRING := 'XDATA'; -- Данные + STAG_PANELS constant PKG_STD.TSTRING := 'XPANELS'; -- Панели + + /* Константы - Атрибуты для сериализации */ + SATTR_OPTIONS constant PKG_STD.TSTRING := 'options'; -- Настройки параметров + SATTR_NAME constant PKG_STD.TSTRING := 'name'; -- Наименование + SATTR_KIND constant PKG_STD.TSTRING := 'kind'; -- Вид параметра + SATTR_PANEL constant PKG_STD.TSTRING := 'panel'; -- Панель + SATTR_COMPANY constant PKG_STD.TSTRING := 'company'; -- Организация + SATTR_AUTHID constant PKG_STD.TSTRING := 'authid'; -- Пользователь + SATTR_DATA_TYPE constant PKG_STD.TSTRING := 'dataType'; -- Тип данных + SATTR_VALUE constant PKG_STD.TSTRING := 'value'; -- Значение + SATTR_DESC constant PKG_STD.TSTRING := 'desc'; -- Описание + SATTR_USER_SETTINGS constant PKG_STD.TSTRING := 'userSettings'; -- Наличие пользовательских параметров + + /* Константы - Вспомогательные */ + SPANEL_SYSTEM_NAME constant PKG_STD.TSTRING := '__P8_SYSTEM'; -- Наименование панели для системных параметров + + /* Конвертация типа данных в числовой формат */ + function UTL_SDATA_TYPE_CONVERT + ( + SDATA_TYPE in varchar2 -- Тип данных (см. константы PKG_P8PANELS_VISUAL.SDATA_TYPE_*) + ) return number -- Тип данных в числовом формате (см. константы NDATA_TYPE_*) + is + NRESULT PKG_STD.TNUMBER; -- Тип данных в числовом формате + begin + /* Исходим от типа данных */ + case SDATA_TYPE + /* Строка */ + when PKG_P8PANELS_VISUAL.SDATA_TYPE_STR then + NRESULT := NDATA_TYPE_STR; + /* Число */ + when PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB then + NRESULT := NDATA_TYPE_NUM; + /* Дата */ + when PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE then + NRESULT := NDATA_TYPE_DATE; + else + P_EXCEPTION(0, 'Не определен тип данных параметра.'); + end case; + /* Возвращаем результат */ + return NRESULT; + end UTL_SDATA_TYPE_CONVERT; + + /* Конвертация типа данных в строковый формат */ + function UTL_NDATA_TYPE_CONVERT + ( + NDATA_TYPE in number -- Тип данных в числовом формате (см. константы NDATA_TYPE_*) + ) return varchar2 -- Тип данных в строковом формате (см. константы PKG_P8PANELS_VISUAL.SDATA_TYPE_*) + is + SRESULT PKG_STD.TSTRING; -- Тип данных в строковом формате + begin + /* Исходим от типа данных */ + case NDATA_TYPE + /* Строка */ + when NDATA_TYPE_STR then + SRESULT := PKG_P8PANELS_VISUAL.SDATA_TYPE_STR; + /* Число */ + when NDATA_TYPE_NUM then + SRESULT := PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB; + /* Дата */ + when NDATA_TYPE_DATE then + SRESULT := PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE; + else + P_EXCEPTION(0, 'Не определен тип данных параметра.'); + end case; + /* Возвращаем результат */ + return SRESULT; + end UTL_NDATA_TYPE_CONVERT; + + /* Добавление информации о настройке во временную таблицу */ + procedure UTL_SELECTLIST_INSERT + ( + NIDENT in number, -- Идентификатор процесса + SAUTHID in varchar2, -- Пользователь + SCONNECT_EXT in varchar2, -- Внешний идентификатор сеанса + NDOCUMENT in number -- Рег. номер записи настройки + ) + is + begin + /* Добавляем настройку во временную таблицу */ + insert into P8PNL_SELECTLIST + (RN, IDENT, authid, SESSION_ID, CONNECT_EXT, DOCUMENT) + values + (GEN_ID(), NIDENT, SAUTHID, 'NULL', SCONNECT_EXT, NDOCUMENT); + end UTL_SELECTLIST_INSERT; + + /* Очистка временной таблицы */ + procedure UTL_SELECTLIST_CLEAR + ( + NIDENT in number -- Идентификатор процесса + ) + is + begin + /* Очищаем данные по идентификатору */ + delete from P8PNL_SELECTLIST T where T.IDENT = NIDENT; + end UTL_SELECTLIST_CLEAR; + + /* Сериализация настройки параметра */ + procedure TOPTION_TO_XML + ( + ROPTION in TOPTION -- Настройка параметра + ) + is + begin + /* Настройка параметра */ + PKG_XFAST.HERB(SNAME => ROPTION.SCODE, SVALUE => ROPTION.SVALUE); + end TOPTION_TO_XML; + + /* Десериализация настройки параметра */ + function TOPTION_FROM_XML + ( + CXML in clob -- XML-описание настройки параметра + ) return TOPTION -- Настройка параметра + is + RRES TOPTION; -- Буфер для результата + XDOC PKG_XPATH.TDOCUMENT; -- Документ XML + XROOT PKG_XPATH.TNODE; -- Корень документа XML + XNODE PKG_XPATH.TNODE; -- Буфер узла документа + begin + /* Если данные есть */ + if (CXML is not null) then + begin + /* Разбираем XML */ + XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML); + /* Считываем корневой узел */ + XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC); + /* Считываем узел параметра */ + XNODE := PKG_XPATH.FIRST_NODE(RPARENT_NODE => XROOT); + /* Получаем наименование параметра */ + RRES.SCODE := PKG_XPATH.NODE_NAME(RNODE => XNODE); + /* Получаем значение настройки параметра */ + RRES.SVALUE := PKG_XPATH.VALUE(RNODE => XNODE); + /* Освободим документ */ + PKG_XPATH.FREE(RDOCUMENT => XDOC); + exception + when others then + /* Освободим документ */ + PKG_XPATH.FREE(RDOCUMENT => XDOC); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end; + end if; + /* Вернём результат */ + return RRES; + end TOPTION_FROM_XML; + + /* Сериализация списка настроек параметра */ + procedure TOPTIONS_TO_XML + ( + ROPTIONS in TOPTIONS -- Список настроек параметра + ) + is + begin + /* Открываем корень */ + PKG_XFAST.DOWN_NODE(SNAME => SATTR_OPTIONS); + /* Обходим параметры из коллекции */ + if ((ROPTIONS is not null) and (ROPTIONS.COUNT > 0)) then + for I in ROPTIONS.FIRST .. ROPTIONS.LAST + loop + begin + /* Добавляем описание параметра */ + TOPTION_TO_XML(ROPTION => ROPTIONS(I)); + exception + when NO_DATA_FOUND then + null; + end; + end loop; + end if; + /* Закрываем корень */ + PKG_XFAST.UP(); + end TOPTIONS_TO_XML; + + /* Десериализация списка настроек параметра */ + function TOPTIONS_FROM_XML + ( + CXML in clob -- XML-описание списка настроек параметра + ) return TOPTIONS -- Коллекция настроек параметров + is + RRES TOPTIONS; -- Буфер для результата + XDOC PKG_XPATH.TDOCUMENT; -- Документ XML + XROOT PKG_XPATH.TNODE; -- Корень документа XML + XOPTION_NODE PKG_XPATH.TNODE; -- Узел настройки параметра + XOPTIONS_NODE PKG_XPATH.TNODE; -- Узел параметра + begin + /* Инициализируем результат */ + RRES := TOPTIONS(); + /* Если данные есть */ + if (CXML is not null) then + begin + /* Разбираем XML */ + XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML); + /* Считываем корневой узел */ + XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC); + /* Считываем узел списка настроек параметра */ + XOPTIONS_NODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => SATTR_OPTIONS); + /* Считываем узел настройки параметра */ + XOPTION_NODE := PKG_XPATH.FIRST_NODE(RPARENT_NODE => XOPTIONS_NODE); + /* Цикл по списку настройки параметров */ + while not (PKG_XPATH.IS_NULL(RNODE => XOPTION_NODE)) + loop + /* Сериализуем и добавим его в коллекцию */ + RRES.EXTEND(); + RRES(RRES.LAST) := TOPTION_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => XOPTION_NODE)); + /* Считываем следующий параметр */ + XOPTION_NODE := PKG_XPATH.NEXT_NODE(RCURRENT_NODE => XOPTION_NODE); + end loop; + /* Освободим документ */ + PKG_XPATH.FREE(RDOCUMENT => XDOC); + exception + when others then + /* Освободим документ */ + PKG_XPATH.FREE(RDOCUMENT => XDOC); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end; + end if; + /* Вернём результат */ + return RRES; + end TOPTIONS_FROM_XML; + + /* Формирование аргумента */ + function TSETTING_MAKE + ( + SCODE in varchar2, -- Мнемокод + SNAME in varchar2, -- Наименование + NKIND in number, -- Вид параметра + SPANEL in varchar2, -- Панель + NCOMPANY in number, -- Рег. номер организации + SAUTHID in varchar2, -- Пользователь + COPTIONS in clob, -- Настройка параметра + SDATA_TYPE in varchar2, -- Тип данных + SVALUE in varchar2, -- Значение + SDESC in varchar2 -- Описание + ) return TSETTING -- Описание параметра + is + RSETTING TSETTING; -- Буфер для результата + begin + /* Если мнемокод не указан */ + if (SCODE is null) then + P_EXCEPTION(0, 'Мнемокод параметра не указан.'); + end if; + /* Если наименование не указано */ + if (SNAME is null) then + P_EXCEPTION(0, 'Наименование параметра не указано.'); + end if; + /* Если вид параметра не указан */ + if (NKIND is null) then + P_EXCEPTION(0, 'Вид параметра не указан.'); + end if; + /* Если тип данные не указан */ + if (SDATA_TYPE is null) then + P_EXCEPTION(0, 'Тип данных параметра не указан.'); + end if; + /* Если тип данных не поддерживается */ + if (SDATA_TYPE not in + (PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE)) then + P_EXCEPTION(0, + 'Параметры типа "%s" не поддерживаются.', + COALESCE(TO_CHAR(SDATA_TYPE), '<НЕ УКАЗАН>')); + end if; + /* Собираем аргумент */ + RSETTING.SCODE := SCODE; + RSETTING.SNAME := SNAME; + RSETTING.NKIND := NKIND; + RSETTING.SPANEL := SPANEL; + RSETTING.NCOMPANY := NCOMPANY; + RSETTING.SAUTHID := SAUTHID; + /* Если есть настройки параметра */ + if (COPTIONS is not null) then + RSETTING.ROPTIONS := TOPTIONS_FROM_XML(CXML => COPTIONS); + end if; + RSETTING.SDATA_TYPE := SDATA_TYPE; + RSETTING.SVALUE := SVALUE; + RSETTING.SDESC := SDESC; + /* Вернем полученное */ + return RSETTING; + end TSETTING_MAKE; + + /* Сериализация списка настроек параметра */ + function TSETTING_OPTIONS_TO_XML + ( + RSETTING in TSETTING -- Параметр + ) return clob -- XML-описание + is + CRES clob; -- Буфер для результата + begin + /* Если есть настройки параметра */ + if ((RSETTING.ROPTIONS is not null) and (RSETTING.ROPTIONS.COUNT > 0)) then + /* Начинаем формирование XML */ + PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_); + /* Добавляем настройки */ + TOPTIONS_TO_XML(ROPTIONS => RSETTING.ROPTIONS); + /* Сериализуем */ + CRES := PKG_XFAST.SERIALIZE_TO_CLOB(); + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + else + CRES := null; + end if; + /* Возвращаем полученное */ + return CRES; + exception + when others then + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end TSETTING_OPTIONS_TO_XML; + + /* Сериализация параметра */ + procedure TSETTING_TO_XML + ( + RSETTING in TSETTING, -- Параметр + NFULL in number := 0 -- Флаг включения в ответ атрибутов параметров (0 - нет, 1 - да) + ) + is + begin + /* Открываем описание сущности */ + PKG_XFAST.DOWN_NODE(SNAME => RSETTING.SCODE); + /* Если не требуется включать атрибуты */ + if (NFULL = 0) then + /* Добавляем значение напрямую */ + PKG_XFAST.VALUE(SVALUE => RSETTING.SVALUE); + else + /* Описываем атрибуты параметра */ + PKG_XFAST.ATTR(SNAME => SATTR_NAME, SVALUE => RSETTING.SNAME); + PKG_XFAST.ATTR(SNAME => SATTR_KIND, NVALUE => RSETTING.NKIND); + PKG_XFAST.ATTR(SNAME => SATTR_PANEL, SVALUE => RSETTING.SPANEL); + PKG_XFAST.ATTR(SNAME => SATTR_COMPANY, NVALUE => RSETTING.NCOMPANY); + PKG_XFAST.ATTR(SNAME => SATTR_AUTHID, SVALUE => RSETTING.SAUTHID); + PKG_XFAST.ATTR(SNAME => SATTR_DATA_TYPE, SVALUE => RSETTING.SDATA_TYPE); + PKG_XFAST.ATTR(SNAME => SATTR_VALUE, SVALUE => RSETTING.SVALUE); + PKG_XFAST.ATTR(SNAME => SATTR_DESC, SVALUE => RSETTING.SDESC); + /* Если есть данные по настройке */ + if ((RSETTING.ROPTIONS is not null) and (RSETTING.ROPTIONS.COUNT > 0)) then + TOPTIONS_TO_XML(ROPTIONS => RSETTING.ROPTIONS); + end if; + end if; + PKG_XFAST.UP(); + end TSETTING_TO_XML; + + /* Десериализация параметра */ + function TSETTING_FROM_XML + ( + CXML in clob -- XML-описание параметра + ) return TSETTING -- Связь + is + RRES TSETTING; -- Буфер для результата + XDOC PKG_XPATH.TDOCUMENT; -- Документ XML + XROOT PKG_XPATH.TNODE; -- Корень документа XML + XNODE PKG_XPATH.TNODE; -- Буфер узла документа + begin + /* Если данные есть */ + if (CXML is not null) then + begin + /* Разбираем XML */ + XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML); + /* Считываем корневой узел */ + XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC); + /* Считываем узел параметра */ + XNODE := PKG_XPATH.FIRST_NODE(RPARENT_NODE => XROOT); + /* Получаем значения */ + RRES.SCODE := PKG_XPATH.NODE_NAME(RNODE => XNODE); + RRES.SNAME := PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => SATTR_NAME); + RRES.ROPTIONS := TOPTIONS_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XNODE, + SPATTERN => SATTR_OPTIONS))); + RRES.SDATA_TYPE := PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => SATTR_DATA_TYPE); + RRES.SVALUE := PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => SATTR_VALUE); + RRES.SDESC := PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => SATTR_DESC); + /* Освободим документ */ + PKG_XPATH.FREE(RDOCUMENT => XDOC); + exception + when others then + /* Освободим документ */ + PKG_XPATH.FREE(RDOCUMENT => XDOC); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end; + end if; + /* Вернём результат */ + return RRES; + end TSETTING_FROM_XML; + + /* Сериализация коллекции параметров */ + function TSETTINGS_TO_XML + ( + RSETTINGS in TSETTINGS, -- Коллекция параметров + NFULL in number := 0 -- Флаг включения в ответ атрибутов параметров (0 - нет, 1 - да) + ) return clob -- Список параметров + is + CRES clob; -- Буфер для сериализации + begin + /* Начинаем формирование XML */ + PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_, BALINE => true, BINDENT => true); + /* Открываем корень */ + PKG_XFAST.DOWN_NODE(SNAME => STAG_DATA); + /* Обходим параметры из коллекции */ + if ((RSETTINGS is not null) and (RSETTINGS.COUNT > 0)) then + for I in RSETTINGS.FIRST .. RSETTINGS.LAST + loop + begin + /* Добавляем описание параметра */ + TSETTING_TO_XML(RSETTING => RSETTINGS(I), NFULL => NFULL); + exception + when NO_DATA_FOUND then + null; + end; + end loop; + end if; + /* Закрываем корень */ + PKG_XFAST.UP(); + /* Сериализуем */ + CRES := PKG_XFAST.SERIALIZE_TO_CLOB(); + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + /* Возвращаем результат */ + return CRES; + exception + when others then + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end TSETTINGS_TO_XML; + + /* Десериализация параметров */ + function TSETTINGS_FROM_XML + ( + CXML in clob -- XML-описание параметров + ) return TSETTINGS -- Параметры + is + RRES TSETTINGS; -- Буфер для результата + XDOC PKG_XPATH.TDOCUMENT; -- Документ XML + XROOT PKG_XPATH.TNODE; -- Корень документа XML + XSETTINGS_NODE PKG_XPATH.TNODE; -- Узел параметров + XSETTING_NODE PKG_XPATH.TNODE; -- Узел параметра + begin + /* Инициализируем результат */ + RRES := TSETTINGS(); + /* Если данные есть */ + if (CXML is not null) then + begin + /* Разбираем XML */ + XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML); + /* Считываем корневой узел */ + XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC); + /* Считываем узел параметров */ + XSETTINGS_NODE := PKG_XPATH.FIRST_NODE(RPARENT_NODE => XROOT); + /* Считываем узел параметра */ + XSETTING_NODE := PKG_XPATH.FIRST_NODE(RPARENT_NODE => XSETTINGS_NODE); + /* Цикл по списку параметров */ + while not (PKG_XPATH.IS_NULL(RNODE => XSETTING_NODE)) + loop + /* Сериализуем и добавим его в коллекцию */ + RRES.EXTEND(); + RRES(RRES.LAST) := TSETTING_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => XSETTING_NODE)); + /* Считываем следующий параметр */ + XSETTING_NODE := PKG_XPATH.NEXT_NODE(RCURRENT_NODE => XSETTING_NODE); + end loop; + /* Освободим документ */ + PKG_XPATH.FREE(RDOCUMENT => XDOC); + exception + when others then + /* Освободим документ */ + PKG_XPATH.FREE(RDOCUMENT => XDOC); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end; + end if; + /* Вернём результат */ + return RRES; + end TSETTINGS_FROM_XML; + + /* Сериализация панели */ + procedure TPANEL_TO_XML + ( + RPANEL in TPANEL, -- Панель + NFULL in number := 0 -- Флаг включения в ответ атрибутов параметров (0 - нет, 1 - да) + ) + is + begin + /* Открываем описание панели */ + PKG_XFAST.DOWN_NODE(SNAME => RPANEL.SNAME); + /* Если у панели есть параметры */ + if ((RPANEL.RSETTINGS is not null) and (RPANEL.RSETTINGS.COUNT > 0)) then + /* Добавляем параметры */ + for I in RPANEL.RSETTINGS.FIRST .. RPANEL.RSETTINGS.LAST + loop + begin + /* Добавляем описание параметра */ + TSETTING_TO_XML(RSETTING => RPANEL.RSETTINGS(I), NFULL => NFULL); + exception + when NO_DATA_FOUND then + null; + end; + end loop; + end if; + PKG_XFAST.UP(); + end TPANEL_TO_XML; + + /* Десериализация панели */ + function TPANEL_FROM_XML + ( + CXML in clob -- XML-описание панели + ) return TPANEL -- Панель + is + RRES TPANEL; -- Буфер для результата + XDOC PKG_XPATH.TDOCUMENT; -- Документ XML + XROOT PKG_XPATH.TNODE; -- Корень документа XML + XNODE PKG_XPATH.TNODE; -- Буфер узла документа + begin + /* Если данные есть */ + if (CXML is not null) then + begin + /* Разбираем XML */ + XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML); + /* Считываем корневой узел */ + XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC); + /* Считываем узел параметра */ + XNODE := PKG_XPATH.FIRST_NODE(RPARENT_NODE => XROOT); + /* Получаем значения */ + RRES.SNAME := PKG_XPATH.NODE_NAME(RNODE => XNODE); + RRES.RSETTINGS := TSETTINGS_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => XNODE)); + /* Освободим документ */ + PKG_XPATH.FREE(RDOCUMENT => XDOC); + exception + when others then + /* Освободим документ */ + PKG_XPATH.FREE(RDOCUMENT => XDOC); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end; + end if; + /* Вернём результат */ + return RRES; + end TPANEL_FROM_XML; + + /* Сериализация коллекции панелей */ + function TPANELS_TO_XML + ( + RPANELS in TPANELS, -- Коллекция панелей + NFULL in number := 0 -- Флаг включения в ответ атрибутов параметров (0 - нет, 1 - да) + ) return clob -- Список параметров + is + CRES clob; -- Буфер для сериализации + begin + /* Начинаем формирование XML */ + PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_, BALINE => true, BINDENT => true); + /* Открываем корень */ + PKG_XFAST.DOWN_NODE(SNAME => STAG_DATA); + /* Обходим параметры из коллекции */ + if ((RPANELS is not null) and (RPANELS.COUNT > 0)) then + for I in RPANELS.FIRST .. RPANELS.LAST + loop + begin + /* Добавляем описание панели */ + TPANEL_TO_XML(RPANEL => RPANELS(I), NFULL => NFULL); + exception + when NO_DATA_FOUND then + null; + end; + end loop; + end if; + /* Закрываем корень */ + PKG_XFAST.UP(); + /* Сериализуем */ + CRES := PKG_XFAST.SERIALIZE_TO_CLOB(); + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + /* Возвращаем результат */ + return CRES; + exception + when others then + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end TPANELS_TO_XML; + + /* Десериализация панелей */ + function TPANELS_FROM_XML + ( + CXML in clob -- XML-описание панелей + ) return TPANELS -- Панели + is + RRES TPANELS; -- Буфер для результата + XDOC PKG_XPATH.TDOCUMENT; -- Документ XML + XROOT PKG_XPATH.TNODE; -- Корень документа XML + XPANELS_NODE PKG_XPATH.TNODE; -- Узел панелей + XPANEL_NODE PKG_XPATH.TNODE; -- Узел панели + begin + /* Инициализируем результат */ + RRES := TPANELS(); + /* Если данные есть */ + if (CXML is not null) then + begin + /* Разбираем XML */ + XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CXML); + /* Считываем корневой узел */ + XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC); + /* Считываем узел панелей */ + XPANELS_NODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => STAG_PANELS); + /* Считываем узел панели */ + XPANEL_NODE := PKG_XPATH.FIRST_NODE(RPARENT_NODE => XPANELS_NODE); + /* Цикл по списку панелей */ + while not (PKG_XPATH.IS_NULL(RNODE => XPANEL_NODE)) + loop + /* Сериализуем и добавим её в коллекцию */ + RRES.EXTEND(); + RRES(RRES.LAST) := TPANEL_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => XPANEL_NODE)); + /* Считываем следующую панель */ + XPANEL_NODE := PKG_XPATH.NEXT_NODE(RCURRENT_NODE => XPANEL_NODE); + end loop; + /* Освободим документ */ + PKG_XPATH.FREE(RDOCUMENT => XDOC); + exception + when others then + /* Освободим документ */ + PKG_XPATH.FREE(RDOCUMENT => XDOC); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end; + end if; + /* Вернём результат */ + return RRES; + end TPANELS_FROM_XML; + + /* Базовое добавления параметра */ + procedure SETTING_INSERT + ( + SCODE in varchar2, -- Мнемокод + SNAME in varchar2, -- Наименование + NKIND in number, -- Вид параметра + SPANEL in varchar2, -- Мнемокод панели + NCOMPANY in number, -- Рег. номер организации + SAUTHID in varchar2, -- Пользователь + COPTIONS in clob, -- Дополнительные параметры + NDATA_TYPE in number, -- Тип данных + SVALUE_STR in varchar2, -- Значение (строка) + NVALUE_NUM in number, -- Значение (число) + DVALUE_DATE in date, -- Значение (дата) + SDESCRIPTION in varchar2, -- Описание + NRN out number -- Рег. номер записи + ) + is + SPANEL_TMP PKG_STD.TSTRING; -- Панель (буфер) + NCOMPANY_TMP PKG_STD.TNUMBER; -- Рег. номер организации (буфер) + SAUTHID_TMP PKG_STD.TSTRING; -- Пользователь (буфер) + begin + /* Определяем, какие поля заполнять в зависимости от NKIND */ + case NKIND + /* Системный */ + when NKIND_SYSTEM then + SPANEL_TMP := null; + NCOMPANY_TMP := null; + SAUTHID_TMP := null; + /* Панельный */ + when NKIND_PANEL then + SPANEL_TMP := SPANEL; + NCOMPANY_TMP := null; + SAUTHID_TMP := null; + /* Пользовательский */ + when NKIND_USER then + SPANEL_TMP := SPANEL; + NCOMPANY_TMP := NCOMPANY; + SAUTHID_TMP := SAUTHID; + else + P_EXCEPTION('Неизвестный вид параметра "%s"', NKIND); + end case; + /* Генерируем рег. номер записи */ + NRN := GEN_ID(); + /* Добавление записи в таблицу */ + insert into P8PNL_SETTINGS + (RN, CODE, name, KIND, PANEL, COMPANY, authid, OPTIONS, DATA_TYPE, VALUE_STR, VALUE_NUM, VALUE_DATE, DESCRIPTION) + values + (NRN, + SCODE, + SNAME, + NKIND, + SPANEL_TMP, + NCOMPANY_TMP, + SAUTHID_TMP, + COPTIONS, + NDATA_TYPE, + SVALUE_STR, + NVALUE_NUM, + DVALUE_DATE, + SDESCRIPTION); + end SETTING_INSERT; + + /* Базовое обновление параметра */ + procedure SETTING_UPDATE + ( + NRN in number, -- Рег. номер записи + SCODE in varchar2, -- Мнемокод + SNAME in varchar2, -- Наименование + NKIND in number, -- Вид параметра + SPANEL in varchar2, -- Мнемокод панели + NCOMPANY in number, -- Рег. номер организации + SAUTHID in varchar2, -- Пользователь + COPTIONS in clob, -- Дополнительные параметры + NDATA_TYPE in number, -- Тип данных + SVALUE_STR in varchar2, -- Значение (строка) + NVALUE_NUM in number, -- Значение (число) + DVALUE_DATE in date, -- Значение (дата) + SDESCRIPTION in varchar2 -- Описание + ) + is + begin + /* Исправление записи в таблице */ + update P8PNL_SETTINGS + set CODE = SCODE, + name = SNAME, + KIND = NKIND, + PANEL = SPANEL, + COMPANY = NCOMPANY, + authid = SAUTHID, + OPTIONS = COPTIONS, + DATA_TYPE = NDATA_TYPE, + VALUE_STR = SVALUE_STR, + VALUE_NUM = NVALUE_NUM, + VALUE_DATE = DVALUE_DATE, + DESCRIPTION = SDESCRIPTION + where RN = NRN; + + if (sql%notfound) then + PKG_MSG.RECORD_NOT_FOUND(NDOCUMENT => NRN, SUNIT_TABLE => 'P8PNL_SETTINGS'); + end if; + end SETTING_UPDATE; + + /* Базовое удаление параметра */ + procedure SETTING_DELETE + ( + NRN in number -- Рег. номер записи + ) + is + begin + /* Удаление записи из таблицы */ + delete from P8PNL_SETTINGS where RN = NRN; + /* Контроль изменения данных */ + if (sql%notfound) then + PKG_MSG.RECORD_NOT_FOUND(NDOCUMENT => NRN, SUNIT_TABLE => 'P8PNL_SETTINGS'); + end if; + end SETTING_DELETE; + + /* Считывание системного параметра */ + function SETTINGS_SYSTEM_GET + ( + SCODE in varchar2 -- Мнемокод параметра + ) return P8PNL_SETTINGS%rowtype -- Запись системного параметра + is + RRES P8PNL_SETTINGS%rowtype; -- Запись системного параметра + begin + /* Считываем системный параметр */ + begin + select T.* + into RRES + from P8PNL_SETTINGS T + where T.CODE = SCODE + and T.KIND = NKIND_SYSTEM + and T.PANEL is null + and T.COMPANY is null + and T.AUTHID is null; + exception + when others then + RRES := null; + end; + /* Возвращаем результат */ + return RRES; + end SETTINGS_SYSTEM_GET; + + /* Считывание панельного параметра */ + function SETTINGS_PANEL_GET + ( + SCODE in varchar2, -- Мнемокод параметра + SPANEL in varchar2 -- Панель + ) return P8PNL_SETTINGS%rowtype -- Запись панельного параметра + is + RRES P8PNL_SETTINGS%rowtype; -- Запись панельного параметра + begin + /* Если панель не указана */ + if (SPANEL is null) then + P_EXCEPTION(0, + 'Ошибка считывания параметра панели. Панель не определена.'); + end if; + /* Считываем системный параметр */ + begin + select T.* + into RRES + from P8PNL_SETTINGS T + where T.CODE = SCODE + and T.KIND = NKIND_PANEL + and T.PANEL = SPANEL + and T.COMPANY is null + and T.AUTHID is null; + exception + when others then + RRES := null; + end; + /* Возвращаем результат */ + return RRES; + end SETTINGS_PANEL_GET; + + /* Считывание пользовательского параметра */ + function SETTINGS_USER_GET + ( + SCODE in varchar2, -- Мнемокод параметра + SPANEL in varchar2, -- Панель + NCOMPANY in number, -- Рег. номер организации + SAUTHID in varchar2 -- Пользователь + ) return P8PNL_SETTINGS%rowtype -- Запись пользовательского параметра + is + RRES P8PNL_SETTINGS%rowtype; -- Запись пользовательского параметра + begin + /* Если панель не указана */ + if (SPANEL is null) then + P_EXCEPTION(0, + 'Ошибка считывания пользовательского параметра. Панель не определена.'); + end if; + /* Если организация не указана */ + if (NCOMPANY is null) then + P_EXCEPTION(0, + 'Ошибка считывания пользовательского параметра. Организация не определена.'); + end if; + /* Если пользователь не указан */ + if (SAUTHID is null) then + P_EXCEPTION(0, + 'Ошибка считывания пользовательского параметра. Пользователь не определен.'); + end if; + /* Считываем системный параметр */ + begin + select T.* + into RRES + from P8PNL_SETTINGS T + where T.CODE = SCODE + and T.KIND = NKIND_USER + and T.PANEL = SPANEL + and T.COMPANY = NCOMPANY + and T.AUTHID = SAUTHID; + exception + when others then + RRES := null; + end; + /* Возвращаем результат */ + return RRES; + end SETTINGS_USER_GET; + + /* Считывание записи параметра по виду */ + function SETTINGS_GET_BY_KIND + ( + NKIND in number, -- Вид параметра + SCODE in varchar2, -- Мнемокод параметра + SPANEL in varchar2 := null, -- Панель + NCOMPANY in number := null, -- Рег. номер организации + SAUTHID in varchar2 := null -- Пользователь + ) return P8PNL_SETTINGS%rowtype -- Запись параметра + is + RRES P8PNL_SETTINGS%rowtype; -- Запись параметра + begin + /* Системный параметр */ + case NKIND + /* Системный */ + when NKIND_SYSTEM then + RRES := SETTINGS_SYSTEM_GET(SCODE => SCODE); + /* Панельный */ + when NKIND_PANEL then + RRES := SETTINGS_PANEL_GET(SCODE => SCODE, SPANEL => SPANEL); + /* Пользовательский */ + when NKIND_USER then + RRES := SETTINGS_USER_GET(SCODE => SCODE, SPANEL => SPANEL, NCOMPANY => NCOMPANY, SAUTHID => SAUTHID); + else + P_EXCEPTION(0, 'Не определен вид параметра.'); + end case; + /* Возвращаем результат */ + return RRES; + end SETTINGS_GET_BY_KIND; + + /* Очистка параметров панели по идентификатору отмеченных записей */ + procedure SETTINGS_CLEAR_BY_IDENT + ( + NIDENT in number, -- Идентификатор отмеченных записей + SPANEL in varchar2, -- Панель + NCOMPANY in number, -- Рег. номер организации + SAUTHID in varchar2 -- Пользователь + ) + is + begin + /* Если определены необходимые параметры */ + if ((SPANEL is not null) and (NCOMPANY is not null) and (SAUTHID is not null)) then + /* Обходим неотмеченные параметры пользователя */ + for REC in (select T.RN + from P8PNL_SETTINGS T + where T.KIND = NKIND_USER + and T.PANEL = SPANEL + and T.AUTHID = SAUTHID + and T.COMPANY = NCOMPANY + and not exists (select null + from P8PNL_SELECTLIST S + where S.IDENT = NIDENT + and S.DOCUMENT = T.RN)) + loop + /* Удаляем параметр */ + SETTING_DELETE(NRN => REC.RN); + end loop; + end if; + /* Очищаем временную таблицу по идентификатору */ + UTL_SELECTLIST_CLEAR(NIDENT => NIDENT); + end SETTINGS_CLEAR_BY_IDENT; + + /* Определение значений параметра по типу данных */ + procedure SETTINGS_GET_VALUES_BY_DT + ( + SDATA_TYPE in varchar2, -- Тип данных (см. константы PKG_P8PANELS_VISUAL.SDATA_TYPE_*) + SVALUE in varchar2, -- Исходное значение + SVALUE_STR out varchar2, -- Значение (строка) + NVALUE_NUM out number, -- Значение (число) + DVALUE_DATE out date -- Значение (дата) + ) + is + begin + /* Исходим от типа данных */ + case SDATA_TYPE + /* Строка */ + when PKG_P8PANELS_VISUAL.SDATA_TYPE_STR then + SVALUE_STR := SVALUE; + /* Число */ + when PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB then + /* Конвертируем в число */ + begin + NVALUE_NUM := TO_NUMBER(SVALUE); + exception + when others then + P_EXCEPTION(0, + 'Ошибка преобразования значения "' || SVALUE || '" в число.'); + end; + /* Дата */ + when PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE then + begin + DVALUE_DATE := TO_DATE(SVALUE, 'yyyy-mm-dd'); + exception + when others then + P_EXCEPTION(0, + 'Ошибка преобразования значения "' || SVALUE || '" в дату.'); + end; + else + P_EXCEPTION(0, 'Не определен тип данных параметра.'); + end case; + end SETTINGS_GET_VALUES_BY_DT; + + /* Определение значения по типу данных */ + function SETTINGS_GET_VALUE_BY_DT + ( + SDATA_TYPE in varchar2, -- Тип данных (см. константы PKG_P8PANELS_VISUAL.SDATA_TYPE_*) + SVALUE_STR in varchar2, -- Значение (строка) + NVALUE_NUM in number, -- Значение (число) + DVALUE_DATE in date -- Значение (дата) + ) return varchar2 -- Итоговое значение + is + SRESULT PKG_STD.TSTRING; -- Итоговое значение + begin + /* Исходим от типа данных */ + case SDATA_TYPE + /* Строка */ + when PKG_P8PANELS_VISUAL.SDATA_TYPE_STR then + SRESULT := SVALUE_STR; + /* Число */ + when PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB then + SRESULT := TO_CHAR(NVALUE_NUM); + /* Дата */ + when PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE then + SRESULT := TO_CHAR(DVALUE_DATE, 'yyyy-mm-dd'); + else + P_EXCEPTION(0, 'Не определен тип данных параметра.'); + end case; + /* Возвращаем результат */ + return SRESULT; + end SETTINGS_GET_VALUE_BY_DT; + + /* Добавление/обновление значений параметров */ + procedure SETTINGS_PUT + ( + RSETTINGS in TSETTINGS, -- Коллекция параметров + NKIND in number, -- Вид параметра + SPANEL in varchar2 -- Панель + ) + is + NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации + SAUTHID PKG_STD.TSTRING := PKG_SESSION.GET_UTILIZER(); -- Пользователь + RREC P8PNL_SETTINGS%rowtype; -- Запись параметра + SVALUE_STR P8PNL_SETTINGS.VALUE_STR%type; -- Значение (строка) + NVALUE_NUM P8PNL_SETTINGS.VALUE_NUM%type; -- Значение (число) + DVALUE_DATE P8PNL_SETTINGS.VALUE_DATE%type; -- Значение (дата) + NTMP PKG_STD.TREF; -- Заглушка + begin + /* Обходим параметры */ + if ((RSETTINGS is not null) and (RSETTINGS.COUNT > 0)) then + for I in RSETTINGS.FIRST .. RSETTINGS.LAST + loop + /* Считываем параметр */ + RREC := SETTINGS_GET_BY_KIND(NKIND => NKIND, + SCODE => RSETTINGS(I).SCODE, + SPANEL => SPANEL, + NCOMPANY => NCOMPANY, + SAUTHID => SAUTHID); + /* Собираем поля, которые обновляются независимо */ + RREC.OPTIONS := TSETTING_OPTIONS_TO_XML(RSETTING => RSETTINGS(I)); + RREC.NAME := RSETTINGS(I).SNAME; + RREC.DESCRIPTION := RSETTINGS(I).SDESC; + /* Определяем значения параметра */ + SETTINGS_GET_VALUES_BY_DT(SDATA_TYPE => RSETTINGS(I).SDATA_TYPE, + SVALUE => RSETTINGS(I).SVALUE, + SVALUE_STR => SVALUE_STR, + NVALUE_NUM => NVALUE_NUM, + DVALUE_DATE => DVALUE_DATE); + /* Если параметр не существует */ + if (RREC.RN is null) then + /* Добавляем параметр */ + SETTING_INSERT(SCODE => RSETTINGS(I).SCODE, + SNAME => RREC.NAME, + NKIND => NKIND, + SPANEL => SPANEL, + NCOMPANY => NCOMPANY, + SAUTHID => SAUTHID, + COPTIONS => RREC.OPTIONS, + NDATA_TYPE => UTL_SDATA_TYPE_CONVERT(SDATA_TYPE => RSETTINGS(I).SDATA_TYPE), + SVALUE_STR => SVALUE_STR, + NVALUE_NUM => NVALUE_NUM, + DVALUE_DATE => DVALUE_DATE, + SDESCRIPTION => RREC.DESCRIPTION, + NRN => NTMP); + else + /* Обновляем параметр */ + SETTING_UPDATE(NRN => RREC.RN, + SCODE => RREC.CODE, + SNAME => RREC.NAME, + NKIND => RREC.KIND, + SPANEL => RREC.PANEL, + NCOMPANY => RREC.COMPANY, + SAUTHID => RREC.AUTHID, + COPTIONS => RREC.OPTIONS, + NDATA_TYPE => UTL_SDATA_TYPE_CONVERT(SDATA_TYPE => RSETTINGS(I).SDATA_TYPE), + SVALUE_STR => SVALUE_STR, + NVALUE_NUM => NVALUE_NUM, + DVALUE_DATE => DVALUE_DATE, + SDESCRIPTION => RREC.DESCRIPTION); + end if; + end loop; + end if; + end SETTINGS_PUT; + + /* Считывание параметров */ + procedure SETTINGS_GET + ( + NKIND in number, -- Вид параметра + SCODE in varchar2 := null, -- Мнемокод параметра (null - все) + SPANEL in varchar2 := null, -- Панель + RSETTINGS out TSETTINGS -- Информация о параметрах + ) + is + RSETTING TSETTING; -- Параметр + RREC P8PNL_SETTINGS%rowtype; -- Запись параметра + NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации + SAUTHID PKG_STD.TSTRING := PKG_SESSION.GET_UTILIZER(); -- Пользователь + SDATA_TYPE PKG_STD.TSTRING; -- Тип данных в строковом формате + begin + /* Если коллекция не проинициализирована */ + if (RSETTINGS is null) then + RSETTINGS := TSETTINGS(); + end if; + /* Если параметр указан - читаем только его */ + if (SCODE is not null) then + /* Считыаем запись параметра */ + RREC := SETTINGS_GET_BY_KIND(NKIND => NKIND, + SCODE => SCODE, + SPANEL => SPANEL, + NCOMPANY => NCOMPANY, + SAUTHID => SAUTHID); + /* Если запись существует */ + if (RREC.RN is not null) then + /* Определяем тип данных в строковом формате */ + SDATA_TYPE := UTL_NDATA_TYPE_CONVERT(NDATA_TYPE => RREC.DATA_TYPE); + /* Добавляем новый элемент */ + RSETTINGS.EXTEND(); + /* Формируем параметр */ + RSETTING := TSETTING_MAKE(SCODE => RREC.CODE, + SNAME => RREC.NAME, + NKIND => RREC.KIND, + SPANEL => RREC.PANEL, + NCOMPANY => RREC.COMPANY, + SAUTHID => RREC.AUTHID, + COPTIONS => RREC.OPTIONS, + SDATA_TYPE => SDATA_TYPE, + SVALUE => SETTINGS_GET_VALUE_BY_DT(SDATA_TYPE => SDATA_TYPE, + SVALUE_STR => RREC.VALUE_STR, + NVALUE_NUM => RREC.VALUE_NUM, + DVALUE_DATE => RREC.VALUE_DATE), + SDESC => RREC.DESCRIPTION); + /* Добавляем в итоговый результат */ + RSETTINGS(RSETTINGS.LAST) := RSETTING; + end if; + else + /* Обходим все подходящие параметры */ + for C in (select T.* + from P8PNL_SETTINGS T + where T.KIND = NKIND + and ((NKIND = NKIND_SYSTEM) or ((NKIND in (NKIND_PANEL, NKIND_USER)) and (T.PANEL = SPANEL))) + and ((NKIND in (NKIND_SYSTEM, NKIND_PANEL)) or ((NKIND = NKIND_USER) and (T.COMPANY = NCOMPANY))) + and ((NKIND in (NKIND_SYSTEM, NKIND_PANEL)) or ((NKIND_USER = NKIND_USER) and (T.AUTHID = SAUTHID)))) + loop + /* Определяем тип данных в строковом формате */ + SDATA_TYPE := UTL_NDATA_TYPE_CONVERT(NDATA_TYPE => C.DATA_TYPE); + /* Добавляем новый элемент */ + RSETTINGS.EXTEND(); + /* Формируем параметр */ + RSETTING := TSETTING_MAKE(SCODE => C.CODE, + SNAME => C.NAME, + NKIND => C.KIND, + SPANEL => C.PANEL, + NCOMPANY => C.COMPANY, + SAUTHID => C.AUTHID, + COPTIONS => C.OPTIONS, + SDATA_TYPE => SDATA_TYPE, + SVALUE => SETTINGS_GET_VALUE_BY_DT(SDATA_TYPE => SDATA_TYPE, + SVALUE_STR => C.VALUE_STR, + NVALUE_NUM => C.VALUE_NUM, + DVALUE_DATE => C.VALUE_DATE), + SDESC => C.DESCRIPTION); + /* Добавляем в итоговый результат */ + RSETTINGS(RSETTINGS.LAST) := RSETTING; + end loop; + end if; + end SETTINGS_GET; + + /* Инициализация параметров */ + procedure SETTINGS_INIT + ( + SPANEL in varchar2, -- Панель + NKIND in number, -- Вид параметра + RSETTINGS in out TSETTINGS -- Коллекция параметров + ) + is + RREC P8PNL_SETTINGS%rowtype; -- Запись параметра + NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации + SAUTHID PKG_STD.TSTRING := PKG_SESSION.GET_UTILIZER(); -- Пользователь + NDATA_TYPE PKG_STD.TNUMBER; -- Тип данных в числовом формате + SVALUE_STR P8PNL_SETTINGS.VALUE_STR%type; -- Значение (строка) + NVALUE_NUM P8PNL_SETTINGS.VALUE_NUM%type; -- Значение (число) + DVALUE_DATE P8PNL_SETTINGS.VALUE_DATE%type; -- Значение (дата) + SCONNECT_EXT PKG_STD.TSTRING := PKG_SESSION.GET_CONNECT_EXT(); -- Внешний идентификатор сеанса + NIDENT PKG_STD.TREF; -- Идентификатор отмеченных записей + begin + /* Обходим коллекцию */ + if ((RSETTINGS is not null) and (RSETTINGS.COUNT > 0)) then + /* Генерируем идентификатор отмеченных записей */ + NIDENT := GEN_IDENT(); + /* Обходим коллекцию параметров */ + for I in RSETTINGS.FIRST .. RSETTINGS.LAST + loop + /* Считываем параметр */ + RREC := SETTINGS_GET_BY_KIND(NKIND => NKIND, + SCODE => RSETTINGS(I).SCODE, + SPANEL => SPANEL, + NCOMPANY => NCOMPANY, + SAUTHID => SAUTHID); + /* Определяем тип данных в числовом формате */ + NDATA_TYPE := UTL_SDATA_TYPE_CONVERT(SDATA_TYPE => RSETTINGS(I).SDATA_TYPE); + /* Собираем поля, которые обновляются независимо */ + RREC.OPTIONS := TSETTING_OPTIONS_TO_XML(RSETTING => RSETTINGS(I)); + RREC.NAME := RSETTINGS(I).SNAME; + RREC.DESCRIPTION := RSETTINGS(I).SDESC; + /* Определяем значения параметра */ + SETTINGS_GET_VALUES_BY_DT(SDATA_TYPE => RSETTINGS(I).SDATA_TYPE, + SVALUE => RSETTINGS(I).SVALUE, + SVALUE_STR => SVALUE_STR, + NVALUE_NUM => NVALUE_NUM, + DVALUE_DATE => DVALUE_DATE); + /* Если параметр не существует */ + if (RREC.RN is null) then + /* Добавляем параметр */ + SETTING_INSERT(SCODE => RSETTINGS(I).SCODE, + SNAME => RREC.NAME, + NKIND => NKIND, + SPANEL => SPANEL, + NCOMPANY => NCOMPANY, + SAUTHID => SAUTHID, + COPTIONS => RREC.OPTIONS, + NDATA_TYPE => NDATA_TYPE, + SVALUE_STR => SVALUE_STR, + NVALUE_NUM => NVALUE_NUM, + DVALUE_DATE => DVALUE_DATE, + SDESCRIPTION => RREC.DESCRIPTION, + NRN => RREC.RN); + /* Перечитываем запись */ + RREC := SETTINGS_GET_BY_KIND(NKIND => NKIND, + SCODE => RSETTINGS(I).SCODE, + SPANEL => SPANEL, + NCOMPANY => NCOMPANY, + SAUTHID => SAUTHID); + else + /* Если тип данных не совпадает */ + if (NDATA_TYPE <> RREC.DATA_TYPE) then + /* Указываем новые значения параметра */ + RREC.DATA_TYPE := NDATA_TYPE; + RREC.VALUE_STR := SVALUE_STR; + RREC.VALUE_NUM := NVALUE_NUM; + RREC.VALUE_DATE := DVALUE_DATE; + end if; + /* Обновляем параметр */ + SETTING_UPDATE(NRN => RREC.RN, + SCODE => RREC.CODE, + SNAME => RREC.NAME, + NKIND => RREC.KIND, + SPANEL => RREC.PANEL, + NCOMPANY => RREC.COMPANY, + SAUTHID => RREC.AUTHID, + COPTIONS => RREC.OPTIONS, + NDATA_TYPE => RREC.DATA_TYPE, + SVALUE_STR => RREC.VALUE_STR, + NVALUE_NUM => RREC.VALUE_NUM, + DVALUE_DATE => RREC.VALUE_DATE, + SDESCRIPTION => RREC.DESCRIPTION); + end if; + /* Заполняем недостающие данные для последующего наличия */ + RSETTINGS(I).NKIND := RREC.KIND; + RSETTINGS(I).SPANEL := RREC.PANEL; + RSETTINGS(I).NCOMPANY := RREC.COMPANY; + RSETTINGS(I).SAUTHID := RREC.AUTHID; + /* Определяем итоговое значение параметра */ + RSETTINGS(I).SVALUE := SETTINGS_GET_VALUE_BY_DT(SDATA_TYPE => RSETTINGS(I).SDATA_TYPE, + SVALUE_STR => RREC.VALUE_STR, + NVALUE_NUM => RREC.VALUE_NUM, + DVALUE_DATE => RREC.VALUE_DATE); + /* Если происходит инициализация пользовательских параметров */ + if (NKIND = NKIND_USER) then + /* Добавляем информацию во временную таблицу */ + UTL_SELECTLIST_INSERT(NIDENT => NIDENT, + SAUTHID => SAUTHID, + SCONNECT_EXT => SCONNECT_EXT, + NDOCUMENT => RREC.RN); + end if; + end loop; + /* Если происходит инициализация пользовательских параметров */ + if (NKIND = NKIND_USER) then + /* Очищаем от лишних параметров */ + SETTINGS_CLEAR_BY_IDENT(NIDENT => NIDENT, SPANEL => SPANEL, NCOMPANY => NCOMPANY, SAUTHID => SAUTHID); + end if; + end if; + exception + when others then + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end SETTINGS_INIT; + + /* Считывание параметров */ + procedure GET + ( + NKIND in number, -- Вид параметра + SCODE in varchar2 := null, -- Мнемокод параметра (null - все) + SPANEL in varchar2 := null, -- Панель + NFULL in number := 0, -- Флаг включения атрибутов (0 - нет, 1 - да) + COUT out clob -- Информация о параметрах + ) + is + RPANELS TPANELS; -- Панели + RSETTINGS TSETTINGS; -- Параметры + NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации + SAUTHID PKG_STD.TSTRING := PKG_SESSION.GET_UTILIZER(); -- Пользователь + begin + /* Инициализируем результат */ + RPANELS := TPANELS(); + /* Если отбор в рамках панелей */ + if (NKIND in (NKIND_PANEL, NKIND_USER)) then + /* Обходим все доступные панели */ + for REC in (select T.PANEL + from P8PNL_SETTINGS T + where T.KIND = NKIND + and ((SPANEL is null) or ((SPANEL is not null) and (T.PANEL = SPANEL))) + and ((NKIND = NKIND_PANEL) or ((NKIND = NKIND_USER) and (T.COMPANY = NCOMPANY))) + and ((NKIND = NKIND_PANEL) or ((NKIND_USER = NKIND_USER) and (T.AUTHID = SAUTHID))) + and ((SCODE is null) or ((SCODE is not null) and (T.CODE = SCODE))) + group by T.PANEL) + loop + /* Добавляем элемент панели */ + RPANELS.EXTEND(); + /* Инициализируем параметры */ + RSETTINGS := TSETTINGS(); + /* Считываем параметры панели */ + SETTINGS_GET(NKIND => NKIND, SCODE => SCODE, SPANEL => REC.PANEL, RSETTINGS => RSETTINGS); + /* Добавляем информацию о панели */ + RPANELS(RPANELS.LAST).SNAME := REC.PANEL; + RPANELS(RPANELS.LAST).RSETTINGS := RSETTINGS; + end loop; + else + /* Добавляем элемент панели */ + RPANELS.EXTEND(); + /* Инициализируем параметры */ + RSETTINGS := TSETTINGS(); + /* Считываем параметры панели */ + SETTINGS_GET(NKIND => NKIND, SCODE => SCODE, RSETTINGS => RSETTINGS); + /* Добавляем информацию о системных параметрах */ + RPANELS(RPANELS.LAST).SNAME := SPANEL_SYSTEM_NAME; + RPANELS(RPANELS.LAST).RSETTINGS := RSETTINGS; + end if; + /* Считываем итоговый список панелей с параметрами */ + COUT := TPANELS_TO_XML(RPANELS => RPANELS, NFULL => NFULL); + exception + when others then + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end GET; + + /* Добавление параметра */ + procedure PUT + ( + NKIND in number, -- Вид параметра + CPANELS in clob -- XML-описание панелей + ) + is + RPANELS TPANELS; -- Панели + begin + /* Десериализуем панели */ + RPANELS := TPANELS_FROM_XML(CXML => BLOB2CLOB(LBDATA => BASE64_DECODE(LCSRCE => CPANELS), + SCHARSET => PKG_CHARSET.CHARSET_UTF_())); + /* Обходим панели */ + if ((RPANELS is not null) and (RPANELS.COUNT > 0)) then + for I in RPANELS.FIRST .. RPANELS.LAST + loop + /* Обходим параметры панели */ + if ((RPANELS(I).RSETTINGS is not null) and (RPANELS(I).RSETTINGS.COUNT > 0)) then + /* Добавляем/обновляем значения параметров панели */ + SETTINGS_PUT(RSETTINGS => RPANELS(I).RSETTINGS, NKIND => NKIND, SPANEL => RPANELS(I).SNAME); + end if; + end loop; + end if; + exception + when others then + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end PUT; + + /* Инициализация параметров */ + procedure INIT + ( + NKIND in number, -- Вид параметра + CPANELS in clob, -- XML-описание панелей + NFULL in number := 0, -- Флаг включения в ответ атрибутов параметров (0 - нет, 1 - да) + COUT out clob -- Информация о параметрах + ) + is + RPANELS TPANELS; -- Панели + begin + /* Десериализуем панели */ + RPANELS := TPANELS_FROM_XML(CXML => BLOB2CLOB(LBDATA => BASE64_DECODE(LCSRCE => CPANELS), + SCHARSET => PKG_CHARSET.CHARSET_UTF_())); + /* Обходим панели */ + if ((RPANELS is not null) and (RPANELS.COUNT > 0)) then + for I in RPANELS.FIRST .. RPANELS.LAST + loop + /* Проверяем наличие названия панели, если соответствующий вид параметра */ + if ((NKIND in (NKIND_PANEL, NKIND_USER)) and (RPANELS(I).SNAME is null)) then + P_EXCEPTION(0, + 'Ошибка инициализации параметров. Для вида параметра "%s" панель не может быть пустой.', + NKIND); + end if; + /* Обходим параметры панели */ + if ((RPANELS(I).RSETTINGS is not null) and (RPANELS(I).RSETTINGS.COUNT > 0)) then + /* Инициализируем параметры */ + SETTINGS_INIT(SPANEL => RPANELS(I).SNAME, NKIND => NKIND, RSETTINGS => RPANELS(I).RSETTINGS); + end if; + end loop; + end if; + /* Считываем итоговый список панелей с параметрами */ + COUT := TPANELS_TO_XML(RPANELS => RPANELS, NFULL => NFULL); + exception + when others then + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end INIT; + + /* Инициализация информации о настройках панелей */ + procedure PANELS_INIT + ( + COUT out clob -- Информация о панелях + ) + is + NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации + SAUTHID PKG_STD.TSTRING := PKG_SESSION.GET_UTILIZER(); -- Пользователь + begin + /* Начинаем формирование XML */ + PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_); + /* Открываем корень */ + PKG_XFAST.DOWN_NODE(SNAME => STAG_DATA); + /* Обходим все панели, имеющие параметры */ + for REC in (select T.PANEL + from P8PNL_SETTINGS T + where T.KIND = NKIND_USER + and T.COMPANY = NCOMPANY + and T.AUTHID = SAUTHID + group by T.PANEL) + loop + /* Открываем информацию о панели */ + PKG_XFAST.DOWN_NODE(SNAME => REC.PANEL); + /* Добавляем атрибут наличия пользовательских параметров панели */ + PKG_XFAST.ATTR(SNAME => SATTR_USER_SETTINGS, NVALUE => 1); + /* Закрываем информацию о панели */ + PKG_XFAST.UP(); + end loop; + /* Закрываем корень */ + PKG_XFAST.UP(); + /* Сериализуем */ + COUT := PKG_XFAST.SERIALIZE_TO_CLOB(); + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + exception + when others then + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end PANELS_INIT; + +end PKG_P8PANELS_SETTINGS; +/ \ No newline at end of file