Compare commits

...

12 Commits

Author SHA1 Message Date
Mikhail Chechnev
18dac12396 ЦИТК-979 - Редактор запросов - переименовал процедуру формирования SQL-выражения 2025-10-15 15:18:17 +03:00
Mikhail Chechnev
d446fd96ab ЦИТК-979 - Редактор условия запроса - раздельное хранение канонического SQL-выражения и выражения с подстановками 2025-10-14 19:21:18 +03:00
Mikhail Chechnev
6efbb8508c ЦИТК-979 - Редактор условия запроса - реализовано добавление элемента условия по месту курсора/выделения 2025-10-14 14:45:53 +03:00
Mikhail Chechnev
3df01a36c6 ЦИТК-979 - Редактор запросов - исправлена ошибка сброса состояния диалога редактирования аргумента запроса при возникновении серверной ошибки 2025-10-14 14:13:03 +03:00
Mikhail Chechnev
1530bfa3bf ЦИТК-979 - Редактор запросов - Поддержка подстановки отладочных значений аргументов в SQL-выражение 2025-10-14 00:09:52 +03:00
Mikhail Chechnev
64145d7ac0 ЦИТК-979 - Редактор запросов - Контроль имени аргументов: только латинские буквы, цифры и подчеркивание, начало только с буквы, принудительный APPERCASE, контроль уникальности 2025-10-13 17:03:32 +03:00
Mikhail Chechnev
63e3d3833e ЦИТК-979 - Редактор запросов - контроль соответствия условий отбора запроса набору его аргументов, инкапсуляция обновления SQL-выражения запроса в базовом пакете 2025-10-13 16:27:51 +03:00
Mikhail Chechnev
db5bf1f72c WEBAPP: Свежая сборка 2025-10-10 22:35:10 +03:00
Mikhail Chechnev
0388d5630b ЦИТК-878 - "Доски задач" - включение панели в поставку 2025-10-10 22:27:16 +03:00
Mikhail Chechnev
c9e4894a40 ЦИТК-979 - Редактор запросов - сокрытие области текста запроса при отображении редактора 2025-10-10 22:22:42 +03:00
Mikhail Chechnev
9cbc52cff2 ЦИТК-878 - "Доски задач" - доработана под логику смены статуса события с релиза "июнь 2025", исправлена ошибка инициализации клиента с пустым хранилищем, купирована ошибка с отсутствием профиля настроек 2025-10-10 21:58:03 +03:00
Mikhail Chechnev
fe1e3ba04f ЦИТК-878 - Гранты для панели "Доски задач" 2025-10-10 21:09:00 +03:00
17 changed files with 1519 additions and 227 deletions

View File

@ -95,7 +95,7 @@ const useColorRules = () => {
const [colorRules, setColorRules] = useState({
loaded: false,
rules: [],
selectedColorRule: JSON.parse(getLocalStorageValue("settingsColorRule") || {})
selectedColorRule: JSON.parse(getLocalStorageValue("settingsColorRule")) || {}
});
//Подключение к контексту взаимодействия с сервером

View File

@ -160,7 +160,7 @@ const useTasksFunctions = () => {
callBack: sendPrms => {
const mainArgs = {
NIDENT: pointInfo.NIDENT,
NSTEP: 3,
NSTEP: 4,
NEVENT: nEvent,
SEVENT_STAT: pointInfo.SEVENT_STAT,
SSEND_CLIENT: sendPrms.outParameters.out_CLIENT_CODE,
@ -182,7 +182,7 @@ const useTasksFunctions = () => {
//Общие аргументы
const mainArgs = {
NIDENT: pointInfo.NIDENT,
NSTEP: 3,
NSTEP: 4,
NEVENT: nEvent,
SEVENT_STAT: pointInfo.SEVENT_STAT
};
@ -193,46 +193,71 @@ const useTasksFunctions = () => {
[handleEventRoutesPointExecutersOpen, handleStateChange]
);
//При выполнении третьего шага
const handleMakeThirdStep = useCallback(
async ({ nEvent, pointInfo, onReload = null, onNoteOpen = null }) => {
//Выполняем переход на следующий шаг
await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
args: {
NIDENT: pointInfo.NIDENT,
NSTEP: 3,
NPASS: pointInfo.NPASS
}
});
//Выполняем выбор исполнителя
handleExecuterSelect({
nEvent,
pointInfo,
onReload,
onNoteOpen
});
},
[executeStored, handleExecuterSelect]
);
//При выполнении второго шага
const handleMakeSecondStep = useCallback(
async ({ nIdent, nPass }) => {
async ({ nEvent, pointInfo, onReload = null, onNoteOpen = null }) => {
//Состояние параметров текущего действия
let currentPointInfo = { ...pointInfo };
//Выполняем переход на следующий шаг
const secondStep = await executeStored({
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
args: {
NIDENT: nIdent,
NIDENT: currentPointInfo.NIDENT,
NSTEP: 2,
NPASS: nPass
NPASS: currentPointInfo.NPASS
}
});
//Возвращаем параметры выполнения
return secondStep;
//Устанавливаем признак необходимости выбора исполнителя
currentPointInfo.NSELECT_EXEC = secondStep.NSELECT_EXEC;
//Выполняем третий шаг
handleMakeThirdStep({ nEvent, pointInfo: currentPointInfo, onReload, onNoteOpen });
},
[executeStored]
[executeStored, handleMakeThirdStep]
);
//При выборе следующей точки события
const handleNextPointSelect = useCallback(
({ nEvent, pointInfo, onReload = null, onNoteOpen = null }) => {
//Состояние параметров текущего действия
let currentPointInfo = { ...pointInfo };
//Открываем раздел "Маршруты событий (точки перехода)" для выбора следующей точки
handleEventRoutesPointsPassessOpen({
sEventType: pointInfo.SEVENT_TYPE,
sEventStatus: pointInfo.SEVENT_STAT,
nPoint: pointInfo.NPOINT,
sEventType: currentPointInfo.SEVENT_TYPE,
sEventStatus: currentPointInfo.SEVENT_STAT,
nPoint: currentPointInfo.NPOINT,
callBack: async point => {
//Устанавливаем полученную точку перехода
currentPointInfo.NPASS = point.outParameters.out_RN;
currentPointInfo.SEVENT_STAT = point.outParameters.out_NEXT_POINT;
//Выполняем второй шаг
let secondStep = await handleMakeSecondStep({ nIdent: pointInfo.NIDENT, nPass: point.outParameters.out_RN });
//Выполняем выбор исполнителя
handleExecuterSelect({
nEvent,
pointInfo: { ...pointInfo, SEVENT_STAT: point.outParameters.out_NEXT_POINT, NSELECT_EXEC: secondStep.NSELECT_EXEC },
onReload,
onNoteOpen
});
handleMakeSecondStep({ nEvent, pointInfo: currentPointInfo, onReload, onNoteOpen });
}
});
},
[handleEventRoutesPointsPassessOpen, handleMakeSecondStep, handleExecuterSelect]
[handleEventRoutesPointsPassessOpen, handleMakeSecondStep]
);
//По нажатию действия "Перейти"
@ -259,17 +284,12 @@ const useTasksFunctions = () => {
onNoteOpen
});
} else {
//Выполняем перехода без выбора точки
handleExecuterSelect({
nEvent,
pointInfo: eventInfo,
onReload,
onNoteOpen
});
//Выполняем второй шаг
handleMakeSecondStep({ nEvent, pointInfo: eventInfo, onReload, onNoteOpen });
}
}
},
[executeStored, handleExecuterSelect, handleNextPointSelect]
[executeStored, handleMakeSecondStep, handleNextPointSelect]
);
return { handleTaskStateChange, handleTaskSend };

View File

@ -183,9 +183,7 @@ export const formatSqlDate = timeStamp => {
};
//Считывание значений из локального хранилища
export const getLocalStorageValue = (sName, defaultValue = null) => {
return localStorage.getItem(sName) ? localStorage.getItem(sName) : defaultValue;
};
export const getLocalStorageValue = (sName, defaultValue = null) => localStorage.getItem(sName) || defaultValue;
//Форматирование фильтра в массив для отбора
export const convertFilterValuesToArray = filterValues => {

View File

@ -18,14 +18,26 @@ import { InspectorQueryArguments } from "../inspector_query_args/inspector_query
import { InspectorQueryConditions } from "../inspector_query_cond/inspector_query_cond"; //Управление условиями отбора запроса
import { InspectorQueryEntities } from "../inspector_query_ents/inspector_query_ents"; //Управление сущностями запроса
import { InspectorQueryRelations } from "../inspector_query_rls/inspector_query_rls"; //Управление связями запроса
import { QueryArea } from "./query_area"; //Область запроса
import { InspectorQueryArea } from "../inspector_query_area/inspector_query_area"; //Область SQL-выражения
//-----------
//Тело модуля
//-----------
//Инспектор свойств
const Inspector = ({ query, entity, relation, entities = [], args = [], cond = null, qry = "", qryMsg = "", onOptionsChanged = null }) => {
const Inspector = ({
query,
entity,
relation,
entities = [],
args = [],
cond = null,
substArgsVals = 0,
qry = "",
qryBnd = "",
qryMsg = "",
onOptionsChanged = null
}) => {
//При изменении настроек запроса
const handleOptionsChanged = () => onOptionsChanged && onOptionsChanged();
@ -44,7 +56,7 @@ const Inspector = ({ query, entity, relation, entities = [], args = [], cond = n
<InspectorQueryRelations query={query} relation={relation} onOptionsChanged={handleOptionsChanged} />
</>
)}
<QueryArea qry={qry} qryMsg={qryMsg} />
<InspectorQueryArea query={query} substArgsVals={substArgsVals} qry={qry} qryBnd={qryBnd} qryMsg={qryMsg} />
</P8PEditorBox>
);
};
@ -57,7 +69,9 @@ Inspector.propTypes = {
entities: PropTypes.arrayOf(ENTITY_SHAPE),
args: PropTypes.arrayOf(ARGUMENT_SHAPE),
cond: PropTypes.string,
substArgsVals: PropTypes.number,
qry: PropTypes.string,
qryBnd: PropTypes.string,
qryMsg: PropTypes.string,
onOptionsChanged: PropTypes.func
};

View File

@ -0,0 +1,35 @@
/*
Парус 8 - Панели мониторинга - Редактор запросов
Пользовательские хуки для работы с областью SQL-выражения
*/
//---------------------
//Подключение библиотек
//---------------------
import { useContext, useCallback } from "react"; //Классы React
import { BackEndСtx } from "../../../../context/backend"; //Контекст взаимодействия с сервером
//-----------
//Тело модуля
//-----------
//Работа с областью SQL-выражения
const useQuerySQLExpr = query => {
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
//Установка флага сокрытия/отображения значений аргументов в SQL-выражении запроса
const toggleSubstArgsVals = useCallback(async () => {
await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_OPT_SUBST_ARGS_VALS_TGL", args: { NRN: query }, loader: false });
}, [query, executeStored]);
//Возвращаем интерфейс хука
return { toggleSubstArgsVals };
};
//----------------
//Интерфейс модуля
//----------------
export { useQuerySQLExpr };

View File

@ -1,17 +1,18 @@
/*
Парус 8 - Панели мониторинга - Редактор запросов
Область запроса
Область SQL-выражения
*/
//---------------------
//Подключение библиотек
//---------------------
import React, { useState } from "react"; //Классы React
import React, { useState, useEffect } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Fab, Icon, Drawer, IconButton, TextField, Stack, Box, Snackbar, Alert } from "@mui/material"; //Компоненты MUI
import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
import { APP_STYLES } from "../../../../../app.styles"; //Общие стили приложения
import { useQuerySQLExpr } from "./hooks"; //Пользовательские хуки для работы с SQL-выражением
//---------
//Константы
@ -38,17 +39,29 @@ const SNACK_BAR_MESSAGE_INIT = { text: null, type: null };
//Тело модуля
//-----------
//Область запроса
const QueryArea = ({ qry = "", qryMsg = "" }) => {
//Область SQL-выражения
const InspectorQueryArea = ({ query, substArgsVals = 0, qry = "", qryBnd = "", qryMsg = "" }) => {
//Собственное состояние - отображение запроса с подстановками
const [showQryBnd, setShowQryBnd] = useState(substArgsVals);
//Собственное состояние - текст всплывающего сообщения
const [snackBarMessage, setSnackBarMessage] = useState(SNACK_BAR_MESSAGE_INIT);
//Собственное состояние - отображение области SQL запроса
const [displaySQL, setDisplaySQL] = useState(true);
const [displaySQL, setDisplaySQL] = useState(false);
//Собственное состояние - развёрнутость
const [expanded, setExpanded] = useState(false);
//Работа с SQL-выражением
const { toggleSubstArgsVals } = useQuerySQLExpr(query);
//При нажатии на кнопку отображения/сокрытия значений аргументов в SQL-выражении запроса
const handleToggleSubstArgsValsClick = async () => {
await toggleSubstArgsVals();
setShowQryBnd(showQryBnd === 1 ? 0 : 1);
};
//При нажатии на кнопку копирования текста запроса
const handleCopyClick = async () => {
try {
@ -74,6 +87,13 @@ const QueryArea = ({ qry = "", qryMsg = "" }) => {
//Расчет размеров тектовых полей
const [qryRows, qryMsgRows] = expanded ? [15, 6] : [5, 3];
//Расчет параметров отображения запроса
const [dispQry, qryViewTitle, qryViewIcon] =
showQryBnd === 0 ? [qry, "Отобразить значения аргументов", "code"] : [qryBnd, "Скрыть значения аргументов", "code_off"];
//При изменении состояние отображения подстановок в запросе
useEffect(() => setShowQryBnd(substArgsVals), [substArgsVals]);
//Генерация содержимого
return (
<>
@ -87,9 +107,14 @@ const QueryArea = ({ qry = "", qryMsg = "" }) => {
<Box p={2}>
<Stack direction={"row"} justifyContent={"right"} spacing={2}>
{qry && (
<IconButton onClick={handleCopyClick} title={"Скопировать текст запроса"}>
<Icon>content_copy</Icon>
</IconButton>
<>
<IconButton onClick={handleToggleSubstArgsValsClick} title={qryViewTitle}>
<Icon>{qryViewIcon}</Icon>
</IconButton>
<IconButton onClick={handleCopyClick} title={"Скопировать текст запроса"}>
<Icon>content_copy</Icon>
</IconButton>
</>
)}
<IconButton onClick={handleExpandClick} title={expanded ? "Свернуть" : "Развернуть"}>
<Icon>{expanded ? "expand_more" : "expand_less"}</Icon>
@ -99,12 +124,12 @@ const QueryArea = ({ qry = "", qryMsg = "" }) => {
</IconButton>
</Stack>
<Stack direction={"column"} spacing={2}>
{qry && (
{dispQry && (
<TextField
label={"Текст запроса"}
multiline
fullWidth
value={qry}
value={dispQry}
minRows={qryRows}
maxRows={qryRows}
variant={"standard"}
@ -139,9 +164,12 @@ const QueryArea = ({ qry = "", qryMsg = "" }) => {
);
};
//Контроль свойств компонента - Область запроса
QueryArea.propTypes = {
//Контроль свойств компонента - Область SQL-выражения
InspectorQueryArea.propTypes = {
query: PropTypes.number.isRequired,
substArgsVals: PropTypes.number,
qry: PropTypes.string,
qryBnd: PropTypes.string,
qryMsg: PropTypes.string
};
@ -149,4 +177,4 @@ QueryArea.propTypes = {
//Интерфейс модуля
//----------------
export { QueryArea };
export { InspectorQueryArea };

View File

@ -19,24 +19,21 @@ import { DATA_TYPE } from "../../common"; //Общие константы ред
//Диалог добавления/исправления аргумента запроса
const ArgIUDialog = ({ name = "", title = "", dataType = DATA_TYPE.NUMB, mandatory = 0, value = "", insert = true, onOk, onCancel }) => {
//Собственное состояние - текущее состояние аргумента
const [current, setCurrent] = useState({ value, dataType });
//Собственное состояние - текущие значения полей аргумента запроса
const [current, setCurrent] = useState({ name, title, dataType, mandatory, value });
//Нажатие на кнопку "Ok"
const handleOk = values => onOk && onOk({ ...values });
const handleOk = () => onOk && onOk(current);
//Нажатие на кнопку "Отмена"
const handleCancel = () => onCancel && onCancel();
//При изменении значений в поле ввода
const handleInputChange = (name, value) => {
//Если поменяли тип данных - надо пересмотреть поля для ввода
if (name === "dataType") {
//Сохраним в состоянии новый тип данных и сбросим "отладочное значение", ведь оно зависит от типа данных (от этого состояния зависит свойство inputs, передаваемое в P8PDialog ниже)
setCurrent({ dataType: value, value: "" });
//Мы сами пересчитали форму, туда придут новые настройки элементов ввода через свойство inputs, не надо делать пересчет состояния внутри диалога
return true;
}
//Сохраним в состоянии новое значение, если сменили тип данных - сбросим "отладочное значение"
setCurrent(pv => ({ ...pv, [name]: value, ...(name === "dataType" ? { value: "" } : {}) }));
//Мы сами пересчитали форму, туда придут новые настройки элементов ввода через свойство inputs, не надо делать пересчет состояния внутри диалога
return true;
};
//Генерация содержимого
@ -44,8 +41,8 @@ const ArgIUDialog = ({ name = "", title = "", dataType = DATA_TYPE.NUMB, mandato
<P8PDialog
title={`${insert === true ? TITLES.INSERT : TITLES.UPDATE} аргумента`}
inputs={[
{ name: "name", value: name, label: "Имя", disabled: insert != true },
{ name: "title", value: title, label: "Приглашение" },
{ name: "name", value: current.name, label: "Имя", disabled: insert != true },
{ name: "title", value: current.title, label: "Приглашение" },
{
name: "dataType",
value: current.dataType,
@ -58,7 +55,7 @@ const ArgIUDialog = ({ name = "", title = "", dataType = DATA_TYPE.NUMB, mandato
},
{
name: "mandatory",
value: mandatory,
value: current.mandatory,
label: "Обязательный",
list: [
{ name: "Нет", value: 0 },

View File

@ -50,6 +50,19 @@ const QueryCondDialog = ({ cond, entities, args, onOk, onCancel }) => {
}
};
//Добавление значения в редактор по месту курсора
const addCondValueToCursor = value => {
let newValue = conditions + value;
if (coditionInputRef.current) {
newValue = coditionInputRef.current.value;
newValue =
newValue.substring(0, coditionInputRef.current.selectionStart) + value + newValue.substring(coditionInputRef.current.selectionEnd);
coditionInputRef.current.value = newValue;
}
moveCondCursorToEnd();
setConditions(newValue);
};
//Нажатие на кнопку "ОК"
const handleOk = () => onOk && onOk(conditions);
@ -60,16 +73,10 @@ const QueryCondDialog = ({ cond, entities, args, onOk, onCancel }) => {
const handleChange = e => setConditions(e.target.value);
//При выборе компонента условия
const handleComponentSelected = value => {
setConditions(pv => pv + value);
moveCondCursorToEnd();
};
const handleComponentSelected = value => addCondValueToCursor(value);
//При нажатии на кнопку операции
const handleOperationButtonClick = value => {
setConditions(pv => pv + value);
moveCondCursorToEnd();
};
const handleOperationButtonClick = value => addCondValueToCursor(value);
//При нажатии на кнопку очистки условий
const handleClearClick = () => setConditions("");

View File

@ -139,8 +139,12 @@ const useQuery = query => {
loader: true
});
setQueryDiagram(serverQueryData2QueryDiagram(data?.XENTS?.XENT || [], data?.XRLS?.XRL || []));
setQueryOption({ args: data?.XOPT?.XARGS?.XARG || [], cond: data?.XOPT?.XCOND || null });
setQuerySQL({ qry: data?.XQRY, qryMsg: data?.XQRY_MSG });
setQueryOption({
args: data?.XOPT?.XARGS?.XARG || [],
cond: data?.XOPT?.XCOND || null,
substArgsVals: data?.XOPT?.XSUBST_ARGS_VALS || 0
});
setQuerySQL({ qry: data?.XQRY, qryBnd: data?.XQRY_BND, qryMsg: data?.XQRY_MSG });
setInit(true);
} finally {
setRefresh(false);

View File

@ -15,6 +15,7 @@ create table P8PNL_QE_QUERY
ENTS clob, -- Сущности запроса
RLS clob, -- Отношения сущностей запроса
QRY clob, -- Запрос (SQL выражение)
QRY_BND clob, -- Запрос (SQL выражение) с подставленными значениями аргументов
QRY_MSG clob, -- Сообщение при формировании запроса (предупреждения и ошибки формирования)
constraint C_P8PNL_QE_QUERY_RN_PK primary key (RN),
constraint C_P8PNL_QE_QUERY_CODE_NB check (rtrim(CODE) is not null),

View File

@ -94,7 +94,7 @@ create or replace package PKG_P8PANELS_CLNTTSKBRD as
SCLIENT_CLIENT in out varchar2, -- Клиент–организация, для отбора исполнителей
SCLIENT_PERSON in out varchar2, -- Клиент–сотрудник, для отбора исполнителей
NPOINT in out number, -- Текущая точка маршрута
NPASS in number, -- Точка перехода
NPASS in out number, -- Точка перехода
NSELECT_EXEC out number, -- Признак необходимости выбора исполнителя
SEXECUTEMETHOD out varchar2, -- Мнемокод метода
SSEND_CLIENT in varchar2, -- Направить организация
@ -1770,7 +1770,7 @@ create or replace package body PKG_P8PANELS_CLNTTSKBRD as
SCLIENT_CLIENT in out varchar2, -- Клиент–организация, для отбора исполнителей
SCLIENT_PERSON in out varchar2, -- Клиент–сотрудник, для отбора исполнителей
NPOINT in out number, -- Текущая точка маршрута
NPASS in number, -- Точка перехода
NPASS in out number, -- Точка перехода
NSELECT_EXEC out number, -- Признак необходимости выбора исполнителя
SEXECUTEMETHOD out varchar2, -- Мнемокод метода
SSEND_CLIENT in varchar2, -- Направить организация
@ -1882,43 +1882,17 @@ create or replace package body PKG_P8PANELS_CLNTTSKBRD as
NSEND_PREDEFINED_PROC => NSEND_PREDEFINED_PROC);
/* Если это первый шаг и уже известен следующий статус */
if ((NSTEP_CURRENT = 1) and (SNEXT_STAT is not null)) then
/* Сразу выполняем следующее действие */
P_CLNEVENTS_CHANGE_STATE_WEB(NCOMPANY => NCOMPANY,
NIDENT => NIDENT,
NSTEP => NSTEP,
NEVENT => NEVENT,
SEVENT => SEVENT,
SEVENT_TYPE => SEVENT_TYPE,
SEVENT_STAT => SEVENT_STAT,
SINIT_PERSON => SINIT_PERSON,
SINIT_AUTHNAME => SINIT_AUTHNAME,
SCLIENT_CLIENT => SCLIENT_CLIENT,
SCLIENT_PERSON => SCLIENT_PERSON,
NPOINT => NPOINT,
NPASS => EVRTPTPASS_NEXT_GET(NCOMPANY => NCOMPANY,
SEVENT_TYPE => SEVENT_TYPE,
NPOINT => NPOINT,
SEVENT_STAT => SEVENT_STAT,
SNEXT_STAT => SNEXT_STAT),
NSELECT_EXEC => NSELECT_EXEC,
NCONFIRM_REQUIRED => NCONFIRM_REQUIRED,
SCONFIRM_TEXT => SCONFIRM_TEXT,
SEXECUTEMETHOD => SEXECUTEMETHOD,
SSEND_CLIENT => SSEND_CLIENT,
SSEND_DIVISION => SSEND_DIVISION,
SSEND_POST => SSEND_POST,
SSEND_PERFORM => SSEND_PERFORM,
SSEND_PERSON => SSEND_PERSON,
SSEND_STAFFGRP => SSEND_STAFFGRP,
SSEND_USER_GROUP => SSEND_USER_GROUP,
SSEND_USER_NAME => SSEND_USER_NAME,
NSEND_PREDEFINED_EXEC => NSEND_PREDEFINED_EXEC,
NSEND_PREDEFINED_PROC => NSEND_PREDEFINED_PROC);
/* Определим точку перехода */
NPASS := EVRTPTPASS_NEXT_GET(NCOMPANY => NCOMPANY,
SEVENT_TYPE => SEVENT_TYPE,
NPOINT => NPOINT,
SEVENT_STAT => SEVENT_STAT,
SNEXT_STAT => SNEXT_STAT);
/* Указываем следующий статус */
SEVENT_STAT := SNEXT_STAT;
end if;
/* Если это последний шаг */
if (NSTEP_CURRENT = 3) then
if (NSTEP_CURRENT = 4) then
/* Если добавлено примечание */
if (SNOTE is not null) then
P_CLNEVNOTES_INSERT(NCOMPANY => NCOMPANY,
@ -2351,9 +2325,7 @@ create or replace package body PKG_P8PANELS_CLNTTSKBRD as
and U.UNITCODE = SUNIT_CLNEVENTS;
exception
when others then
P_EXCEPTION(0,
'Профиль пользователя "%s" для раздела "События" не найден.',
SAUTHID);
return null;
end;
/* Возвращаем результат */
return BRESULT;
@ -2361,6 +2333,11 @@ create or replace package body PKG_P8PANELS_CLNTTSKBRD as
begin
/* Считываем профиль пользователя для раздела "События" */
BXML := USERPROFILES_GET(NCOMPANY => NCOMPANY, SAUTHID => SAUTHID);
/* Проверим, что профиль не пустой */
if (BXML is null) then
return;
end if;
/* Данные в профиле есть - будем разбирать */
begin
/* Формируем XML данных */
PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);

View File

@ -155,6 +155,12 @@ create or replace package PKG_P8PANELS_QE as
SCOND in varchar2 -- Условия отбора
);
/* Переключение флага подстановки значений аргументов запроса в SQL-выражение */
procedure QUERY_OPT_SUBST_ARGS_VALS_TGL
(
NRN in number -- Рег. номер запроса
);
end PKG_P8PANELS_QE;
/
create or replace package body PKG_P8PANELS_QE as
@ -281,8 +287,6 @@ create or replace package body PKG_P8PANELS_QE as
is
RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
RENTS PKG_P8PANELS_QE_BASE.TENTS; -- Коллекция существующих сущностей
SQRY PKG_STD.TLSTRING; -- SQL-выражение запроса
SQRY_MSG PKG_STD.TLSTRING; -- Сообщение при формировании SQL-выражения
begin
/* Провим права доступа */
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
@ -295,8 +299,7 @@ create or replace package body PKG_P8PANELS_QE as
/* Сохраняем обновленный набор сущностей */
PKG_P8PANELS_QE_BASE.QUERY_ENTS_SET(NRN => RQ.RN, RENTS => RENTS);
/* Переформируем SQL-выражение */
PKG_P8PANELS_QE_BASE.QUERY_SQL_BUILD(NRN => RQ.RN, SQRY => SQRY, SQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_SET(NRN => RQ.RN, CQRY => SQRY, CQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN);
end QUERY_ENT_ADD;
/* Удаление сущности из запроса */
@ -310,8 +313,6 @@ create or replace package body PKG_P8PANELS_QE as
RENTS PKG_P8PANELS_QE_BASE.TENTS; -- Коллекция существующих сущностей
RENT PKG_P8PANELS_QE_BASE.TENT; -- Удаляемая сущность
RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
SQRY PKG_STD.TLSTRING; -- SQL-выражение запроса
SQRY_MSG PKG_STD.TLSTRING; -- Сообщение при формировании SQL-выражения
begin
/* Провим права доступа */
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
@ -346,8 +347,7 @@ create or replace package body PKG_P8PANELS_QE as
/* Сохраняем обновленный набор связей */
PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
/* Переформируем SQL-выражение */
PKG_P8PANELS_QE_BASE.QUERY_SQL_BUILD(NRN => RQ.RN, SQRY => SQRY, SQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_SET(NRN => RQ.RN, CQRY => SQRY, CQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN);
end QUERY_ENT_REMOVE;
/* Установка координат сущности в редакторе диаграммы запроса */
@ -404,8 +404,6 @@ create or replace package body PKG_P8PANELS_QE as
RATTRS PKG_P8PANELS_QE_BASE.TATTRS; -- Коллекция полученных атриубтов
RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
SCHECK_MSG PKG_STD.TLSTRING; -- Сообщение при проверке атрибутов
SQRY PKG_STD.TLSTRING; -- SQL-выражение запроса
SQRY_MSG PKG_STD.TLSTRING; -- Сообщение при формировании SQL-выражения
begin
/* Провим права доступа */
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
@ -453,8 +451,7 @@ create or replace package body PKG_P8PANELS_QE as
/* Сохраняем обновленный набор связей */
PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
/* Переформируем SQL-выражение */
PKG_P8PANELS_QE_BASE.QUERY_SQL_BUILD(NRN => RQ.RN, SQRY => SQRY, SQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_SET(NRN => RQ.RN, CQRY => SQRY, CQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN);
end QUERY_ENT_ATTRS_SET;
/* Добавление связи в запрос */
@ -467,8 +464,6 @@ create or replace package body PKG_P8PANELS_QE as
is
RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
SQRY PKG_STD.TLSTRING; -- SQL-выражение запроса
SQRY_MSG PKG_STD.TLSTRING; -- Сообщение при формировании SQL-выражения
begin
/* Провим права доступа */
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
@ -481,8 +476,7 @@ create or replace package body PKG_P8PANELS_QE as
/* Сохраняем обновленный набор связей */
PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
/* Переформируем SQL-выражение */
PKG_P8PANELS_QE_BASE.QUERY_SQL_BUILD(NRN => RQ.RN, SQRY => SQRY, SQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_SET(NRN => RQ.RN, CQRY => SQRY, CQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN);
end QUERY_RL_ADD;
/* Удаление связи из запроса */
@ -494,8 +488,6 @@ create or replace package body PKG_P8PANELS_QE as
is
RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
SQRY PKG_STD.TLSTRING; -- SQL-выражение запроса
SQRY_MSG PKG_STD.TLSTRING; -- Сообщение при формировании SQL-выражения
begin
/* Провим права доступа */
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
@ -508,8 +500,7 @@ create or replace package body PKG_P8PANELS_QE as
/* Сохраняем обновленный набор связей */
PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
/* Переформируем SQL-выражение */
PKG_P8PANELS_QE_BASE.QUERY_SQL_BUILD(NRN => RQ.RN, SQRY => SQRY, SQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_SET(NRN => RQ.RN, CQRY => SQRY, CQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN);
end QUERY_RL_REMOVE;
/* Установка признака обязательности связи */
@ -522,8 +513,6 @@ create or replace package body PKG_P8PANELS_QE as
is
RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
SQRY PKG_STD.TLSTRING; -- SQL-выражение запроса
SQRY_MSG PKG_STD.TLSTRING; -- Сообщение при формировании SQL-выражения
begin
/* Читаем запись запроса */
RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
@ -534,8 +523,7 @@ create or replace package body PKG_P8PANELS_QE as
/* Сохраняем обновленный набор связей */
PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
/* Переформируем SQL-выражение */
PKG_P8PANELS_QE_BASE.QUERY_SQL_BUILD(NRN => RQ.RN, SQRY => SQRY, SQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_SET(NRN => RQ.RN, CQRY => SQRY, CQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN);
end QUERY_RL_MANDATORY_SET;
/* Добавление аргумента запроса */
@ -567,6 +555,8 @@ create or replace package body PKG_P8PANELS_QE as
SVALUE => SVALUE);
/* Сохраняем обновленную настройку */
PKG_P8PANELS_QE_BASE.QUERY_OPT_SET(NRN => RQ.RN, ROPT => ROPT);
/* Переформируем SQL-выражение */
PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN);
end QUERY_OPT_ARG_ADD;
/* Изменение аргумента запроса */
@ -608,6 +598,8 @@ create or replace package body PKG_P8PANELS_QE as
SVALUE => SVALUE);
/* Сохраняем обновленную настройку */
PKG_P8PANELS_QE_BASE.QUERY_OPT_SET(NRN => RQ.RN, ROPT => ROPT);
/* Переформируем SQL-выражение */
PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN);
end QUERY_OPT_ARG_EDIT;
/* Удаление аргумента запроса */
@ -637,6 +629,8 @@ create or replace package body PKG_P8PANELS_QE as
PKG_P8PANELS_QE_BASE.TARGS_REMOVE(RARGS => ROPT.RARGS, SNAME => SNAME);
/* Сохраняем обновленную настройку */
PKG_P8PANELS_QE_BASE.QUERY_OPT_SET(NRN => RQ.RN, ROPT => ROPT);
/* Переформируем SQL-выражение */
PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN);
end QUERY_OPT_ARG_REMOVE;
/* Установка условий отбора запроса */
@ -648,8 +642,6 @@ create or replace package body PKG_P8PANELS_QE as
is
RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
ROPT PKG_P8PANELS_QE_BASE.TOPT; -- Настройка запроса
SQRY PKG_STD.TLSTRING; -- SQL-выражение запроса
SQRY_MSG PKG_STD.TLSTRING; -- Сообщение при формировании SQL-выражения
begin
/* Провим права доступа */
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
@ -662,9 +654,35 @@ create or replace package body PKG_P8PANELS_QE as
/* Сохраняем обновленную настройку */
PKG_P8PANELS_QE_BASE.QUERY_OPT_SET(NRN => RQ.RN, ROPT => ROPT);
/* Переформируем SQL-выражение */
PKG_P8PANELS_QE_BASE.QUERY_SQL_BUILD(NRN => RQ.RN, SQRY => SQRY, SQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_SET(NRN => RQ.RN, CQRY => SQRY, CQRY_MSG => SQRY_MSG);
PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN);
end QUERY_OPT_COND_SET;
/* Переключение флага подстановки значений аргументов запроса в SQL-выражение */
procedure QUERY_OPT_SUBST_ARGS_VALS_TGL
(
NRN in number -- Рег. номер запроса
)
is
RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
ROPT PKG_P8PANELS_QE_BASE.TOPT; -- Настройка запроса
begin
/* Провим права доступа */
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
/* Читаем запись запроса */
RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
/* Читаем текущую настройку */
ROPT := PKG_P8PANELS_QE_BASE.QUERY_OPT_GET(COPT => RQ.OPT);
/* Установим новый флаг подстановки значений аргументов в запрос */
if (ROPT.NSUBST_ARGS_VALS = 1) then
ROPT.NSUBST_ARGS_VALS := 0;
else
ROPT.NSUBST_ARGS_VALS := 1;
end if;
/* Сохраняем обновленную настройку */
PKG_P8PANELS_QE_BASE.QUERY_OPT_SET(NRN => RQ.RN, ROPT => ROPT);
/* Переформируем SQL-выражение */
PKG_P8PANELS_QE_BASE.QUERY_QRY_REFRESH(NRN => RQ.RN);
end QUERY_OPT_SUBST_ARGS_VALS_TGL;
end PKG_P8PANELS_QE;
/

View File

@ -16,8 +16,9 @@ create or replace package PKG_P8PANELS_QE_BASE as
/* Типы данных - Настройка запроса */
type TOPT is record
(
RARGS TARGS, -- Аргументы
SCOND PKG_STD.TSTRING -- Условия отбора
RARGS TARGS, -- Аргументы
SCOND PKG_STD.TSTRING, -- Условия отбора
NSUBST_ARGS_VALS PKG_STD.TNUMBER -- Флаг подстановки значений аргументов в запрос (1 - да, 0 - нет)
);
/* Типы данных - Атрибут сущности */
@ -329,19 +330,18 @@ create or replace package PKG_P8PANELS_QE_BASE as
ROPT in TOPT -- Настройка запроса
);
/* Запись SQL-выражения запроса */
procedure QUERY_QRY_SET
/* Обновление SQL-выражения запроса */
procedure QUERY_QRY_REFRESH
(
NRN in number, -- Рег. номер запроса
CQRY in clob, -- SQL-выражение
CQRY_MSG in clob -- Сообщение при формировании SQL-выражения (предупреждения и ошибки формирования)
NRN in number -- Рег. номер запроса
);
/* Формирование SQL запроса */
procedure QUERY_SQL_BUILD
/* Формирование SQL-выражения запроса */
procedure QUERY_QRY_BUILD
(
NRN in number, -- Рег. номер запроса
SQRY out varchar2, -- SQL-выражение
SQRY_BND out varchar2, -- SQL-выражение с подставленными значениями аргументов
SQRY_MSG out varchar2 -- Сообщение при формировании SQL-выражения (предупреждения и ошибки формирования)
);
@ -354,21 +354,23 @@ create or replace package body PKG_P8PANELS_QE_BASE as
SENT_TYPE_VIEW constant PKG_STD.TSTRING := 'VIEW'; -- Представление
/* Константы - Теги для сериализации */
STAG_DATA constant PKG_STD.TSTRING := 'XDATA'; -- Данные
STAG_QUERIES constant PKG_STD.TSTRING := 'XQUERIES'; -- Запросы
STAG_QUERY constant PKG_STD.TSTRING := 'XQUERY'; -- Запрос
STAG_ARGS constant PKG_STD.TSTRING := 'XARGS'; -- Аргументы запроса
STAG_ARG constant PKG_STD.TSTRING := 'XARG'; -- Аргумент запроса
STAG_ATTRS constant PKG_STD.TSTRING := 'XATTRS'; -- Атрибуты сущности
STAG_ATTR constant PKG_STD.TSTRING := 'XATTR'; -- Атрибут сущности
STAG_ENTS constant PKG_STD.TSTRING := 'XENTS'; -- Сущности
STAG_ENT constant PKG_STD.TSTRING := 'XENT'; -- Сущность
STAG_RLS constant PKG_STD.TSTRING := 'XRLS'; -- Связи
STAG_RL constant PKG_STD.TSTRING := 'XRL'; -- Связь
STAG_OPT constant PKG_STD.TSTRING := 'XOPT'; -- Настройка запроса
STAG_COND constant PKG_STD.TSTRING := 'XCOND'; -- Условия запроса
STAG_QRY constant PKG_STD.TSTRING := 'XQRY'; -- SQL-выражение запроса
STAG_QRY_MSG constant PKG_STD.TSTRING := 'XQRY_MSG'; -- Сообщение при формировании запроса
STAG_DATA constant PKG_STD.TSTRING := 'XDATA'; -- Данные
STAG_QUERIES constant PKG_STD.TSTRING := 'XQUERIES'; -- Запросы
STAG_QUERY constant PKG_STD.TSTRING := 'XQUERY'; -- Запрос
STAG_ARGS constant PKG_STD.TSTRING := 'XARGS'; -- Аргументы запроса
STAG_ARG constant PKG_STD.TSTRING := 'XARG'; -- Аргумент запроса
STAG_ATTRS constant PKG_STD.TSTRING := 'XATTRS'; -- Атрибуты сущности
STAG_ATTR constant PKG_STD.TSTRING := 'XATTR'; -- Атрибут сущности
STAG_ENTS constant PKG_STD.TSTRING := 'XENTS'; -- Сущности
STAG_ENT constant PKG_STD.TSTRING := 'XENT'; -- Сущность
STAG_RLS constant PKG_STD.TSTRING := 'XRLS'; -- Связи
STAG_RL constant PKG_STD.TSTRING := 'XRL'; -- Связь
STAG_OPT constant PKG_STD.TSTRING := 'XOPT'; -- Настройка запроса
STAG_COND constant PKG_STD.TSTRING := 'XCOND'; -- Условия запроса
STAG_SUBST_ARGS_VALS constant PKG_STD.TSTRING := 'XSUBST_ARGS_VALS'; -- Флаг подстановки значений аргументов в запрос
STAG_QRY constant PKG_STD.TSTRING := 'XQRY'; -- SQL-выражение запроса
STAG_QRY_BND constant PKG_STD.TSTRING := 'XQRY_BND'; -- SQL-выражение запроса с подстановками
STAG_QRY_MSG constant PKG_STD.TSTRING := 'XQRY_MSG'; -- Сообщение при формировании запроса
/* Константы - Атрибуты для сериализации */
SATTR_ID constant PKG_STD.TSTRING := 'id'; -- Идентификатор
@ -475,6 +477,10 @@ create or replace package body PKG_P8PANELS_QE_BASE as
P_EXCEPTION(0,
'Имя аргумента содержит недопустимые символы (разрешены латинские буквы, цифры и символ нижнего подчеркивания).');
end loop;
for C in (select null from DUAL where not REGEXP_LIKE(SUBSTR(SNAME, 1, 1), '^[A-z]+$'))
loop
P_EXCEPTION(0, 'Имя аргумента должно начинаться с буквы.');
end loop;
if (UPPER(SNAME) in (UPPER(SARG_NAME_PAGE), UPPER(SARG_NAME_PAGE_SIZE))) then
P_EXCEPTION(0, 'Это имя аргумента зарезервировано Системой.');
end if;
@ -582,7 +588,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
loop
begin
/* Возвращаем найденный индекс */
if (RARGS(I).SNAME = SNAME) then
if (UPPER(RARGS(I).SNAME) = UPPER(SNAME)) then
return I;
end if;
exception
@ -614,7 +620,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
end if;
/* Проверим уникальность имени */
if ((SNAME is not null) and (TARGS_INDEX_BY_NAME(RARGS => RARGS, SNAME => SNAME) is not null)) then
P_EXCEPTION(0, 'Аргумент с именем "%s" уже существует.', SNAME);
P_EXCEPTION(0, 'Аргумент с именем "%s" уже существует.', UPPER(SNAME));
end if;
/* Формируем аргумент */
RARG := TARG_MAKE(SNAME => SNAME,
@ -768,6 +774,8 @@ create or replace package body PKG_P8PANELS_QE_BASE as
TARGS_TO_XML(RARGS => ROPT.RARGS);
/* Условия отбора */
PKG_XFAST.HERB(SNAME => STAG_COND, SVALUE => ROPT.SCOND);
/* Флаг подстановки значений аргументов в запрос */
PKG_XFAST.HERB(SNAME => STAG_SUBST_ARGS_VALS, NVALUE => ROPT.NSUBST_ARGS_VALS);
/* Закрываем описание настройки */
PKG_XFAST.UP();
end TOPT_TO_XML;
@ -795,9 +803,10 @@ create or replace package body PKG_P8PANELS_QE_BASE as
/* Считаваем узел настройки */
XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_OPT);
/* Получаем значения */
RRES.SCOND := PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => STAG_COND);
RRES.RARGS := TARGS_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XNODE,
SPATTERN => STAG_ARGS)));
RRES.SCOND := PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => STAG_COND);
RRES.NSUBST_ARGS_VALS := PKG_XPATH.VALUE_NUM(RNODE => XNODE, SPATTERN => STAG_SUBST_ARGS_VALS);
RRES.RARGS := TARGS_FROM_XML(CXML => PKG_XPATH.SERIALIZE_TO_CLOB(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XNODE,
SPATTERN => STAG_ARGS)));
/* Освободим документ */
PKG_XPATH.FREE(RDOCUMENT => XDOC);
exception
@ -2334,6 +2343,8 @@ create or replace package body PKG_P8PANELS_QE_BASE as
end if;
/* SQL-выражение */
PKG_XFAST.HERB(SNAME => STAG_QRY, LCVALUE => RQ.QRY);
/* SQL-выражение с подстановками */
PKG_XFAST.HERB(SNAME => STAG_QRY_BND, LCVALUE => RQ.QRY_BND);
/* Сообщение при формировании SQL-выражения */
PKG_XFAST.HERB(SNAME => STAG_QRY_MSG, LCVALUE => RQ.QRY_MSG);
/* Закрываем корень */
@ -2689,6 +2700,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
(
NRN in number, -- Рег. номер запроса
CQRY in clob, -- SQL-выражение
CQRY_BND in clob, -- SQL-выражение с подставленными значениями аргументов
CQRY_MSG in clob -- Сообщение при формировании SQL-выражения (предупреждения и ошибки формирования)
)
is
@ -2696,6 +2708,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
/* Сохраним её */
update P8PNL_QE_QUERY T
set T.QRY = CQRY,
T.QRY_BND = CQRY_BND,
T.QRY_MSG = CQRY_MSG
where T.RN = NRN;
/* Контроль изменения данных */
@ -2706,11 +2719,28 @@ create or replace package body PKG_P8PANELS_QE_BASE as
QUERY_CH_DATE_SYNC(NRN => NRN);
end QUERY_QRY_SET;
/* Формирование SQL запроса */
procedure QUERY_SQL_BUILD
/* Обновление SQL-выражения запроса */
procedure QUERY_QRY_REFRESH
(
NRN in number -- Рег. номер запроса
)
is
SQRY PKG_STD.TLSTRING; -- SQL-выражение запроса
SQRY_BND PKG_STD.TLSTRING; -- SQL-выражение запроса с подстановками
SQRY_MSG PKG_STD.TLSTRING; -- Сообщение при формировании SQL-выражения
begin
/* Сформируем SQL-выражение */
QUERY_QRY_BUILD(NRN => NRN, SQRY => SQRY, SQRY_BND => SQRY_BND, SQRY_MSG => SQRY_MSG);
/* Обновим его в запросе */
QUERY_QRY_SET(NRN => NRN, CQRY => SQRY, CQRY_BND => SQRY_BND, CQRY_MSG => SQRY_MSG);
end QUERY_QRY_REFRESH;
/* Формирование SQL-выражения запроса */
procedure QUERY_QRY_BUILD
(
NRN in number, -- Рег. номер запроса
SQRY out varchar2, -- SQL-выражение
SQRY_BND out varchar2, -- SQL-выражение с подставленными значениями аргументов
SQRY_MSG out varchar2 -- Сообщение при формировании SQL-выражения (предупреждения и ошибки формирования)
)
is
@ -2720,7 +2750,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
ROPT TOPT; -- Настройка запроса
/* Кумулятивная установка сообщения формирования запроса */
procedure SET_MSG
procedure ADD_QRY_MSG
(
SMESSAGE in varchar2, -- Текст сообщения
BCLEAR_QRY in boolean := true -- Флаг очистки сформированного запроса
@ -2728,14 +2758,45 @@ create or replace package body PKG_P8PANELS_QE_BASE as
is
begin
if (BCLEAR_QRY) then
SQRY := null;
SQRY := null;
SQRY_BND := null;
end if;
if (SQRY_MSG is null) then
SQRY_MSG := SMESSAGE;
else
SQRY_MSG := SQRY_MSG || CHR(10) || CHR(13) || SMESSAGE;
end if;
end SET_MSG;
end ADD_QRY_MSG;
/* Проверка соответствия условий запроса набору его аргументов */
procedure CHECK_COND_ARGS
(
ROPT TOPT -- Настройка запроса
)
is
RCOND_VARS PKG_CONTVARLOC.TCONTAINER; -- Коллекция переменных условия запроса
SCOND_VAR PKG_STD.TSTRING; -- Текущая переменная условия запроса
begin
/* Извлечен переменные из условия запроса */
RCOND_VARS := PKG_SQL_BUILD.VAR_LIST(SSQL => ROPT.SCOND);
/* Если переменные в условии есть */
if (PKG_CONTVARLOC.COUNT_(RCONTAINER => RCOND_VARS) > 0) then
/* Обходим их */
SCOND_VAR := PKG_CONTVARLOC.FIRST_(RCONTAINER => RCOND_VARS);
while (SCOND_VAR is not null)
loop
/* Проверяем вхождение текущей переменной в состав аргументов запроса */
if (TARGS_INDEX_BY_NAME(RARGS => ROPT.RARGS, SNAME => SCOND_VAR) is null) then
/* В аргументах такой нет - добавляем предупреждение */
ADD_QRY_MSG(SMESSAGE => 'Переменная условия запроса "' || SCOND_VAR ||
'" отсутствует в составе его аргументов.',
BCLEAR_QRY => false);
end if;
/* Ищем следующу */
SCOND_VAR := PKG_CONTVARLOC.NEXT_(RCONTAINER => RCOND_VARS, SNAME => SCOND_VAR);
end loop;
end if;
end CHECK_COND_ARGS;
/* Поиск корневой (не имеет связей по выходу - т.е. нигде не является источником) сущности запроса */
procedure FIND_ROOT_ENT
@ -2898,7 +2959,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
end if;
else
/* Корневую сущность не нашли */
SET_MSG(SMESSAGE => SMESSAGE, BCLEAR_QRY => false);
ADD_QRY_MSG(SMESSAGE => SMESSAGE, BCLEAR_QRY => false);
end if;
end BUILD_FROM;
@ -2912,35 +2973,71 @@ create or replace package body PKG_P8PANELS_QE_BASE as
begin
if (ROPT.SCOND is not null) then
PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => 'where ' || ROPT.SCOND);
end if;
end if;
end BUILD_WHERE;
/* Подстановка отладочных значений в SQL-выражение */
procedure BOUND
(
ROPT TOPT, -- Настройка запроса
SQRY in varchar2, -- Сформированное SQL-выражение
SQRY_BND out varchar2 -- SQL-выражение с подстановками
)
is
begin
SQRY_BND := SQRY;
if ((ROPT.RARGS is not null) and (ROPT.RARGS.COUNT > 0)) then
for I in ROPT.RARGS.FIRST .. ROPT.RARGS.LAST
loop
begin
case
when ROPT.RARGS(I).NDATA_TYPE = PKG_STD.DATA_TYPE_STR then
SQRY_BND := PKG_SQL_BUILD.VAR_REPLACE_TO_STR(SSQL => SQRY_BND,
SNAME => ROPT.RARGS(I).SNAME,
SVALUE => ROPT.RARGS(I).SVALUE);
when ROPT.RARGS(I).NDATA_TYPE = PKG_STD.DATA_TYPE_NUM then
SQRY_BND := PKG_SQL_BUILD.VAR_REPLACE_TO_NUM(SSQL => SQRY_BND,
SNAME => ROPT.RARGS(I).SNAME,
NVALUE => TO_NUMBER(ROPT.RARGS(I).SVALUE));
when ROPT.RARGS(I).NDATA_TYPE = PKG_STD.DATA_TYPE_DATE then
SQRY_BND := PKG_SQL_BUILD.VAR_REPLACE_TO_DATE(SSQL => SQRY_BND,
SNAME => ROPT.RARGS(I).SNAME,
DVALUE => TO_DATE(ROPT.RARGS(I).SVALUE, 'yyyy-mm-dd'));
else
SQRY_BND := PKG_SQL_BUILD.VAR_REPLACE_TO_ANY(SSQL => SQRY_BND,
SNAME => ROPT.RARGS(I).SNAME,
SANY => ROPT.RARGS(I).SVALUE);
end case;
exception
when others then
ADD_QRY_MSG(SMESSAGE => 'Ошибка конвертации при подстановке значения "' || ROPT.RARGS(I).SVALUE ||
'" в аргумент запроса "' || ROPT.RARGS(I).SNAME || '".',
BCLEAR_QRY => false);
end;
end loop;
end if;
end BOUND;
begin
/*
TODO: owner="root" created="10.09.2025"
text="Предусмотреть отладочные значения для аргументов запроса"
*/
/*
TODO: owner="root" created="11.09.2025"
text="Проверять соответствие условий отбора запроса набору его аргументов"
*/
/* Читаем описание запроса */
RQ := QUERY_GET(NRN => NRN);
/* Читаем сущности запроса */
RENTS := QUERY_ENTS_GET(CENTS => RQ.ENTS);
/* Нет сущностей - нет запроса */
if ((RENTS is null) or (RENTS.COUNT = 0)) then
SET_MSG(SMESSAGE => 'В запросе нет сущностей.');
ADD_QRY_MSG(SMESSAGE => 'В запросе нет сущностей.');
return;
end if;
/* Читаем связи запроса */
RRLS := QUERY_RLS_GET(CRLS => RQ.RLS);
/* Нельзя построить запрос, если есть несвязанные сущности */
if ((RENTS.COUNT > 1) and ((RRLS is null) or (RENTS.COUNT - 1 > RRLS.COUNT))) then
SET_MSG(SMESSAGE => 'В запросе есть несвязанные сущности.');
ADD_QRY_MSG(SMESSAGE => 'В запросе есть несвязанные сущности.');
return;
end if;
/* Читаем настройку запроса */
ROPT := QUERY_OPT_GET(COPT => RQ.OPT);
/* Проверяем параметры запроса */
CHECK_COND_ARGS(ROPT => ROPT);
/* Добавляем подсказку совместимости */
SQRY := PKG_SQL_BUILD.COMPATIBLE(SSQL => SQRY);
/* Собираем запрос - поля */
@ -2949,11 +3046,13 @@ create or replace package body PKG_P8PANELS_QE_BASE as
BUILD_FROM(RENTS => RENTS, RRLS => RRLS, SQRY => SQRY);
/* Собираем запрос - условия */
BUILD_WHERE(ROPT => ROPT, SQRY => SQRY);
/* Сделаем подстановки */
BOUND(ROPT => ROPT, SQRY => SQRY, SQRY_BND => SQRY_BND);
exception
when others then
PKG_STATE.DIAGNOSTICS_STACKED();
SET_MSG(SMESSAGE => PKG_STATE.SQL_ERRM(), BCLEAR_QRY => false);
end QUERY_SQL_BUILD;
ADD_QRY_MSG(SMESSAGE => PKG_STATE.SQL_ERRM(), BCLEAR_QRY => false);
end QUERY_QRY_BUILD;
end PKG_P8PANELS_QE_BASE;
/

View File

@ -3,5 +3,6 @@ grant execute on PKG_P8PANELS_PROJECTS to public;
grant execute on PKG_P8PANELS_SAMPLES to public;
grant execute on PKG_P8PANELS_EQUIPSRV to public;
grant execute on PKG_P8PANELS_RRPCONFED to public;
grant execute on PKG_P8PANELS_CLNTTSKBRD to public;
grant execute on PKG_P8PANELS_PE to public;
grant execute on PKG_P8PANELS_QE to public;

1159
dist/p8-panels.js vendored

File diff suppressed because one or more lines are too long

BIN
img/clnt_task_board.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 KiB

View File

@ -28,6 +28,12 @@
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" name="ShowMechRecPanelAssemblyMon" caption="Мониторинг сборки изделий" panelName="MechRecAssemblyMon"/>
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" name="ShowMechRecPanelHelp" caption="Руководство &quot;Оперативное управление производством&quot;" panelName="MechRecHelp"/>
</App>
<App name="Client">
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" separator="true"/>
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" name="ShowClntPanelsRoot" caption="Панели мониторинга" url="Modules/P8-Panels/"/>
<MenuItem parent="{CA9A9853-AC90-412A-85B1-E39927147846}" separator="true"/>
<MenuItem parent="{CA9A9853-AC90-412A-85B1-E39927147846}" name="ShowClntTaskBoard" caption="Доски задач" panelName="ClntTaskBoard"/>
</App>
</MenuItems>
<Panels urlBase="Modules/P8-Panels/#/">
<Panel
@ -180,5 +186,15 @@
icon=""
showInPanelsList="false"
preview=""/>
<Panel
name="ClntTaskBoard"
group="Управление деловыми процессами"
caption="Доски задач"
desc="Мониторинг и управление назначенными задачами на Канбан-доске"
url="clnt_task_board"
path="clnt_task_board"
icon="dashboard_customize"
showInPanelsList="true"
preview="./img/clnt_task_board.jpg"/>
</Panels>
</CITK.P8Panels>