ЦИТК-979 - Диалог настройки атрибута, ввод и контроль псевдонима атрибутов сущностей, короткие псевдонимы сущностей

This commit is contained in:
Mikhail Chechnev 2025-10-09 19:17:59 +03:00
parent 0767a12fa6
commit 6a6e58e88e
6 changed files with 297 additions and 38 deletions

View File

@ -15,7 +15,8 @@ export const TITLES = {
DEFAULT_PANELS_GROUP: "Без привязки к группе", //Заголовок группы панелей по умолчанию DEFAULT_PANELS_GROUP: "Без привязки к группе", //Заголовок группы панелей по умолчанию
DATA_SOURCE_CONFIG: "Настройка источника данных", //Заголовок для настройки источника данных DATA_SOURCE_CONFIG: "Настройка источника данных", //Заголовок для настройки источника данных
INSERT: "Добавление", //Заголовок для диалогов/форм добавления INSERT: "Добавление", //Заголовок для диалогов/форм добавления
UPDATE: "Исправление" //Заголовок для диалогов/форм исправления UPDATE: "Исправление", //Заголовок для диалогов/форм исправления
CONFIG: "Настройка" //Заголовок для диалога настройки
}; };
//Текст //Текст

View File

@ -0,0 +1,73 @@
/*
Парус 8 - Панели мониторинга - Редактор запросов
Компонент: Диалог настройки атрибута сущности
*/
//---------------------
//Подключение библиотек
//---------------------
import React from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { P8PDialog } from "../../../../components/p8p_dialog"; //Типовой диалог
import { TITLES } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
import { DATA_TYPE } from "../../common"; //Общие константы редактора
import { ATTRIBUTE_SHAPE } from "../attribute/attribute"; //Описание атрибута
//-----------
//Тело модуля
//-----------
//Диалог добавления/исправления аргумента запроса
const AttrSetupDialog = ({ attr, onOk, onCancel }) => {
//Достанем необходимое из атрибута
const { name, title, dataType, alias } = attr;
//Нажатие на кнопку "Ok"
const handleOk = values => onOk && onOk({ ...values });
//Нажатие на кнопку "Отмена"
const handleCancel = () => onCancel && onCancel();
//Генерация содержимого
return (
<P8PDialog
title={`${TITLES.CONFIG} атрибута "${title}"`}
inputs={[
{ name: "name", value: name, label: "Имя", disabled: true },
{ name: "title", value: title, label: "Наименование", disabled: true },
{
name: "dataType",
value: dataType,
label: "Тип данных",
list: [
{ name: "Строка", value: DATA_TYPE.STR },
{ name: "Число", value: DATA_TYPE.NUMB },
{ name: "Дата", value: DATA_TYPE.DATE }
],
disabled: true
},
{
name: "alias",
value: alias,
label: "Псевдоним"
}
]}
onOk={handleOk}
onCancel={handleCancel}
/>
);
};
//Контроль свойств - Диалог настройки атрибута сущности
AttrSetupDialog.propTypes = {
attr: ATTRIBUTE_SHAPE.isRequired,
onOk: PropTypes.func,
onCancel: PropTypes.func
};
//----------------
//Интерфейс модуля
//----------------
export { AttrSetupDialog };

View File

@ -30,7 +30,7 @@ const STYLES = {
//----------- //-----------
//Список атрибутов сущности //Список атрибутов сущности
const AttrsList = ({ attrs = [], onSelect = null, onShow = null } = {}) => { const AttrsList = ({ attrs = [], onSelect = null, onShow = null, onSetup = null } = {}) => {
//При выборе элемента списка //При выборе элемента списка
const handleSelectClick = attr => { const handleSelectClick = attr => {
onSelect && onSelect(attr); onSelect && onSelect(attr);
@ -42,6 +42,12 @@ const AttrsList = ({ attrs = [], onSelect = null, onShow = null } = {}) => {
onShow && onShow(attr); onShow && onShow(attr);
}; };
//При нажатии на кнопку настройки атрибута
const handleSetupClick = (e, attr) => {
e.stopPropagation();
onSetup && onSetup(attr);
};
//Формирование представления //Формирование представления
return ( return (
<List sx={STYLES.LIST}> <List sx={STYLES.LIST}>
@ -60,7 +66,7 @@ const AttrsList = ({ attrs = [], onSelect = null, onShow = null } = {}) => {
secondaryTypographyProps={{ component: "div" }} secondaryTypographyProps={{ component: "div" }}
secondary={ secondary={
<Stack direction={"column"}> <Stack direction={"column"}>
<Typography variant={"caption"}>{`${attr.alias || attr.name}`}</Typography> <Typography variant={"caption"}>{`${attr.name}${attr.alias ? ` (${attr.alias})` : ""}`}</Typography>
</Stack> </Stack>
} }
/> />
@ -68,6 +74,9 @@ const AttrsList = ({ attrs = [], onSelect = null, onShow = null } = {}) => {
<IconButton onClick={e => handleShowClick(e, attr)} title={showTitle}> <IconButton onClick={e => handleShowClick(e, attr)} title={showTitle}>
<Icon>{showIcon}</Icon> <Icon>{showIcon}</Icon>
</IconButton> </IconButton>
<IconButton onClick={e => handleSetupClick(e, attr)} title={"Настроить"}>
<Icon>settings</Icon>
</IconButton>
</Stack> </Stack>
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
@ -81,7 +90,8 @@ const AttrsList = ({ attrs = [], onSelect = null, onShow = null } = {}) => {
AttrsList.propTypes = { AttrsList.propTypes = {
attrs: PropTypes.arrayOf(ATTRIBUTE_SHAPE), attrs: PropTypes.arrayOf(ATTRIBUTE_SHAPE),
onSelect: PropTypes.func, onSelect: PropTypes.func,
onShow: PropTypes.func onShow: PropTypes.func,
onSetup: PropTypes.func
}; };
//---------------- //----------------

View File

@ -13,6 +13,7 @@ import { Box, TextField, InputAdornment, Icon, IconButton } from "@mui/material"
import { P8PDialog, P8P_DIALOG_WIDTH } from "../../../../components/p8p_dialog"; //Типовой диалог import { P8PDialog, P8P_DIALOG_WIDTH } from "../../../../components/p8p_dialog"; //Типовой диалог
import { P8PComponentInlineMessage } from "../../../../components/editors/p8p_component_inline_message"; //Типовое встраиваемое сообщение import { P8PComponentInlineMessage } from "../../../../components/editors/p8p_component_inline_message"; //Типовое встраиваемое сообщение
import { AttrsList } from "./attrs_list"; //Список атрибутов сущности import { AttrsList } from "./attrs_list"; //Список атрибутов сущности
import { AttrSetupDialog } from "./attr_setup_dialog"; //Диалог настройки атрибута
import { useEntityAttrs } from "./hooks"; //Хуки диалога настройки атрибутов сущности import { useEntityAttrs } from "./hooks"; //Хуки диалога настройки атрибутов сущности
//--------- //---------
@ -36,6 +37,9 @@ const EntityAttrsDialog = ({ query, id, title, onOk, onCancel }) => {
//Собственное состояние - список атрибутов //Собственное состояние - список атрибутов
const [attrs, setAttrs] = useState([]); const [attrs, setAttrs] = useState([]);
//Собственное состояние - настраиваемый атрибут
const [setupAttr, setSetupAttr] = useState(null);
//Хук для взаимодействия с сервером //Хук для взаимодействия с сервером
const [srvAttrs, saveAttrs] = useEntityAttrs(query, id); const [srvAttrs, saveAttrs] = useEntityAttrs(query, id);
@ -64,6 +68,21 @@ const EntityAttrsDialog = ({ query, id, title, onOk, onCancel }) => {
})) }))
); );
//Настройка атрибута
const handleAttrSetup = attr => setSetupAttr({ ...attr });
//При закрытии диалога настройки атрибута по "ОК"
const handleAttrSetupOk = values => {
setAttrs(
attrs.map(a => ({
...(a.id === setupAttr.id ? { ...a, use: 1, alias: values.alias } : a)
}))
);
setSetupAttr(null);
};
const handleAttrSetupCancel = () => setSetupAttr(null);
//При изменении значения фильтра //При изменении значения фильтра
const handleFilterChange = e => setFilter(e.target.value); const handleFilterChange = e => setFilter(e.target.value);
@ -87,6 +106,7 @@ const EntityAttrsDialog = ({ query, id, title, onOk, onCancel }) => {
//Генерация содержимого //Генерация содержимого
return ( return (
<P8PDialog title={`Атрибуты сущности "${title}"`} width={P8P_DIALOG_WIDTH.SM} fullWidth onOk={handleOk} onCancel={handleCancel}> <P8PDialog title={`Атрибуты сущности "${title}"`} width={P8P_DIALOG_WIDTH.SM} fullWidth onOk={handleOk} onCancel={handleCancel}>
{setupAttr && <AttrSetupDialog attr={setupAttr} onOk={handleAttrSetupOk} onCancel={handleAttrSetupCancel} />}
<TextField <TextField
margin={"normal"} margin={"normal"}
variant={"standard"} variant={"standard"}
@ -110,7 +130,9 @@ const EntityAttrsDialog = ({ query, id, title, onOk, onCancel }) => {
}} }}
/> />
<Box sx={STYLES.LIST_CONTAINER} justifyContent={"center"} alignItems={"center"} display={"flex"}> <Box sx={STYLES.LIST_CONTAINER} justifyContent={"center"} alignItems={"center"} display={"flex"}>
{displayAttrsList === true && <AttrsList attrs={filteredAttrs} filter={filter} onSelect={handleAttrSelect} onShow={handleAttrShow} />} {displayAttrsList === true && (
<AttrsList attrs={filteredAttrs} filter={filter} onSelect={handleAttrSelect} onShow={handleAttrShow} onSetup={handleAttrSetup} />
)}
{displayAttrsList === false && ( {displayAttrsList === false && (
<P8PComponentInlineMessage <P8PComponentInlineMessage
name={"Нет данных, соответствующих фильтру"} name={"Нет данных, соответствующих фильтру"}

View File

@ -395,6 +395,7 @@ create or replace package body PKG_P8PANELS_QE as
NENT_INDEX PKG_STD.TNUMBER; -- Индекс изменяемой сущности NENT_INDEX PKG_STD.TNUMBER; -- Индекс изменяемой сущности
RATTRS PKG_P8PANELS_QE_BASE.TATTRS; -- Коллекция полученных атриубтов RATTRS PKG_P8PANELS_QE_BASE.TATTRS; -- Коллекция полученных атриубтов
RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
SCHECK_MSG PKG_STD.TLSTRING; -- Сообщение при проверке атрибутов
SQRY PKG_STD.TLSTRING; -- SQL-выражение запроса SQRY PKG_STD.TLSTRING; -- SQL-выражение запроса
SQRY_MSG PKG_STD.TLSTRING; -- Сообщение при формировании SQL-выражения SQRY_MSG PKG_STD.TLSTRING; -- Сообщение при формировании SQL-выражения
begin begin
@ -434,6 +435,11 @@ create or replace package body PKG_P8PANELS_QE as
end if; end if;
end loop; end loop;
end if; end if;
/* Проверим, что новый набор атрибутов сущности корректен */
PKG_P8PANELS_QE_BASE.TATTRS_CHECK(RATTRS => RENTS(NENT_INDEX).RATTRS, SCHECK_MSG => SCHECK_MSG);
if (SCHECK_MSG is not null) then
P_EXCEPTION(0, SCHECK_MSG);
end if;
/* Сохраняем обновленный набор сущностей */ /* Сохраняем обновленный набор сущностей */
PKG_P8PANELS_QE_BASE.QUERY_ENTS_SET(NRN => RQ.RN, RENTS => RENTS); PKG_P8PANELS_QE_BASE.QUERY_ENTS_SET(NRN => RQ.RN, RENTS => RENTS);
/* Сохраняем обновленный набор связей */ /* Сохраняем обновленный набор связей */

View File

@ -69,6 +69,13 @@ create or replace package PKG_P8PANELS_QE_BASE as
SVIEW_NAME in varchar2 -- Имя представления SVIEW_NAME in varchar2 -- Имя представления
) return varchar2; -- Заголовок представления из метаданных ) return varchar2; -- Заголовок представления из метаданных
/* Поиск индекса атрибута по псевдониму */
function TATTRS_INDEX_BY_ALIAS
(
RATTRS in TATTRS, -- Коллекция атрибутов
SALIAS in varchar2 -- Искомый псевдоним
) return number; -- Индекс найденного атрибута (null - если не найдено)
/* Десериализация коллекции атрибутов сущности (из BASE64) */ /* Десериализация коллекции атрибутов сущности (из BASE64) */
function TATTRS_FROM_XML_BASE64 function TATTRS_FROM_XML_BASE64
( (
@ -77,6 +84,13 @@ create or replace package PKG_P8PANELS_QE_BASE as
BADD_ROOT in boolean := false -- Флаг необходимости добавления корневого тэга (false - нет, true - да) BADD_ROOT in boolean := false -- Флаг необходимости добавления корневого тэга (false - нет, true - да)
) return TATTRS; -- Коллекция атрибутов сущности ) return TATTRS; -- Коллекция атрибутов сущности
/* Проверка корректности атрибутов сущности */
procedure TATTRS_CHECK
(
RATTRS in TATTRS, -- Коллекция атрибутов сущности
SCHECK_MSG out varchar2 -- Сообщения проверки (null - ошибок нет)
);
/* Поиск индекса аргумента по имени */ /* Поиск индекса аргумента по имени */
function TARGS_INDEX_BY_NAME function TARGS_INDEX_BY_NAME
( (
@ -809,6 +823,36 @@ create or replace package body PKG_P8PANELS_QE_BASE as
return SENT_ID || '.' || SNAME; return SENT_ID || '.' || SNAME;
end TATTR_ID_MAKE; end TATTR_ID_MAKE;
/* Формирование псевдонима атрибута сущности */
function TATTR_ALIAS_MAKE
(
SENT_ID in varchar2, -- Уникальный идентификатор родительской сущности
RATTRS in TATTRS, -- Коллекция уже существующих атрибутов
NINDEX in number -- Индекс элемента, для которого формируется псевдоним
) return varchar2 -- Сформированный идентификатор
is
SRES PKG_STD.TSTRING; -- Буфер для результата
NCNT PKG_STD.TNUMBER := 0; -- Текущая попытка
NMAX_CNT PKG_STD.TNUMBER := 1000; -- Предельное количество попыток
NATTR_INDEX PKG_STD.TNUMBER := 0; -- Индекс поля в коллекции атрибутов сущности, при проверке уникальности псевдонима
begin
/* Подбираем псевдоним */
while ((NATTR_INDEX is not null) and (NCNT < NMAX_CNT))
loop
SRES := SENT_ID || TO_CHAR(NINDEX + NCNT);
NATTR_INDEX := TATTRS_INDEX_BY_ALIAS(RATTRS => RATTRS, SALIAS => SRES);
NCNT := NCNT + 1;
end loop;
/* Если так и не смогли сформировать уникальный псевдоним */
if ((NATTR_INDEX is not null) and (NCNT >= NMAX_CNT)) then
P_EXCEPTION(0,
'Не удалось сформировать уникальный псевдоним для атрибута сущности "%s".',
SENT_ID);
end if;
/* Вернём полученный псевдоним */
return SRES;
end TATTR_ALIAS_MAKE;
/* Сериализация атрибута сущности */ /* Сериализация атрибута сущности */
procedure TATTR_TO_XML procedure TATTR_TO_XML
( (
@ -930,6 +974,61 @@ create or replace package body PKG_P8PANELS_QE_BASE as
return null; return null;
end TATTRS_INDEX_BY_NAME; end TATTRS_INDEX_BY_NAME;
/* Поиск индекса атрибута по псевдониму */
function TATTRS_INDEX_BY_ALIAS
(
RATTRS in TATTRS, -- Коллекция атрибутов
SALIAS in varchar2 -- Искомый псевдоним
) return number -- Индекс найденного атрибута (null - если не найдено)
is
begin
/* Обходим коллекцию */
if ((RATTRS is not null) and (RATTRS.COUNT > 0)) then
for I in RATTRS.FIRST .. RATTRS.LAST
loop
begin
/* Возвращаем найденный индекс */
if (RATTRS(I).SALIAS = SALIAS) then
return I;
end if;
exception
when NO_DATA_FOUND then
null;
end;
end loop;
end if;
/* Ничего не нашли */
return null;
end TATTRS_INDEX_BY_ALIAS;
/* Подсчет количества атрибутов по псевдониму */
function TATTRS_COUNT_BY_ALIAS
(
RATTRS in TATTRS, -- Коллекция атрибутов
SALIAS in varchar2 -- Искомый псевдоним
) return number -- Количество найденных атрибутов
is
NRES PKG_STD.TNUMBER := 0; --Буфер для расчета
begin
/* Обходим коллекцию */
if ((RATTRS is not null) and (RATTRS.COUNT > 0)) then
for I in RATTRS.FIRST .. RATTRS.LAST
loop
begin
/* Увеличиваем счетчик, если наш клиент */
if (RATTRS(I).SALIAS = SALIAS) then
NRES := NRES + 1;
end if;
exception
when NO_DATA_FOUND then
null;
end;
end loop;
end if;
/* Вернем количество найденных */
return NRES;
end TATTRS_COUNT_BY_ALIAS;
/* Формирование списка атрибутов сущности */ /* Формирование списка атрибутов сущности */
function TATTRS_MAKE function TATTRS_MAKE
( (
@ -995,6 +1094,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
RRES(RRES.LAST).STITLE := DMSCLVIEWSATTRS_TITLE_GET(SVIEW_NAME => RENT.SNAME, RRES(RRES.LAST).STITLE := DMSCLVIEWSATTRS_TITLE_GET(SVIEW_NAME => RENT.SNAME,
SATTR_NAME => RRES(RRES.LAST).SNAME); SATTR_NAME => RRES(RRES.LAST).SNAME);
RRES(RRES.LAST).NDATA_TYPE := RVIEW_FIELD.DATA_TYPE; RRES(RRES.LAST).NDATA_TYPE := RVIEW_FIELD.DATA_TYPE;
RRES(RRES.LAST).SALIAS := TATTR_ALIAS_MAKE(SENT_ID => RENT.SID, RATTRS => RRES, NINDEX => RRES.LAST);
/* Если это инициализиция сущности - установим флаг применения в запросе (тогда атрибут будет отображен в диаграмме) */ /* Если это инициализиция сущности - установим флаг применения в запросе (тогда атрибут будет отображен в диаграмме) */
if (BINIT) then if (BINIT) then
RRES(RRES.LAST).NUSE := 1; RRES(RRES.LAST).NUSE := 1;
@ -1108,24 +1208,61 @@ create or replace package body PKG_P8PANELS_QE_BASE as
return TATTRS_FROM_XML(CXML => CTMP); return TATTRS_FROM_XML(CXML => CTMP);
end TATTRS_FROM_XML_BASE64; end TATTRS_FROM_XML_BASE64;
/* Проверка корректности атрибутов сущности */
procedure TATTRS_CHECK
(
RATTRS in TATTRS, -- Коллекция атрибутов сущности
SCHECK_MSG out varchar2 -- Сообщения проверки (null - ошибок нет)
)
is
begin
/* Обходим коллекцию */
if ((RATTRS is not null) and (RATTRS.COUNT > 0)) then
for I in RATTRS.FIRST .. RATTRS.LAST
loop
/* Псевдоним должен быть задан */
if (RATTRS(I).SALIAS is null) then
SCHECK_MSG := SCHECK_MSG || 'Для атрибута "' || RATTRS(I).SNAME || '" не задан псевдоним; ';
else
/* А если задан - должен соответствовать по длине */
if (LENGTH(RATTRS(I).SALIAS) > 30) then
SCHECK_MSG := SCHECK_MSG || 'Длина псевдонима атрибута "' || RATTRS(I).SNAME ||
'" превышает максимально допустимую (30 символов); ';
end if;
/* Набору символов */
if (INSTR(RATTRS(I).SALIAS, '"') <> 0) then
SCHECK_MSG := SCHECK_MSG || 'Псевдоним атрибута "' || RATTRS(I).SNAME ||
'" содержит недопустимые символы; ';
end if;
/* Быть уникальным */
if (TATTRS_COUNT_BY_ALIAS(RATTRS => RATTRS, SALIAS => RATTRS(I).SALIAS) > 1) then
SCHECK_MSG := SCHECK_MSG || 'Нарушение уникальности псевдонима атрибута "' || RATTRS(I).SNAME || '"; ';
end if;
end if;
end loop;
end if;
if (SCHECK_MSG is not null) then
SCHECK_MSG := RTRIM(SCHECK_MSG, '; ');
end if;
end TATTRS_CHECK;
/* Формирование идентификатора сущности */ /* Формирование идентификатора сущности */
function TENT_ID_MAKE function TENT_ID_MAKE
( (
SNAME in varchar2, -- Имя сущности STYPE in varchar2, -- Тип (см. константы SENT_TYPE_*)
NNUMB in number -- Номер сущности в запросе NNUMB in number -- Номер сущности в запросе
) return varchar2 -- Сформированный идентификатор ) return varchar2 -- Сформированный идентификатор
is is
begin begin
/* Проверим параметры */ /* Проверим параметры */
if (SNAME is null) then if (STYPE is null) then
P_EXCEPTION(0, 'Не указано имя сущности.'); P_EXCEPTION(0, 'Не указан тип сущности.');
end if;
if (NNUMB is null) then
P_EXCEPTION(0, 'Не указан номер сущности в запросе.');
end if; end if;
/* Соберем идентификатор */ /* Соберем идентификатор */
if (NNUMB is null) then return SUBSTR(STYPE, 1, 1) || TO_CHAR(NNUMB);
return SNAME;
else
return SNAME || TO_CHAR(NNUMB);
end if;
end TENT_ID_MAKE; end TENT_ID_MAKE;
/* Формирование описания сущности */ /* Формирование описания сущности */
@ -1154,7 +1291,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
/* Получим описание представления */ /* Получим описание представления */
RVIEW := PKG_OBJECT_DESC.DESC_VIEW(SVIEW_NAME => SNAME, BRAISE_ERROR => true); RVIEW := PKG_OBJECT_DESC.DESC_VIEW(SVIEW_NAME => SNAME, BRAISE_ERROR => true);
/* Собираем заголовок сущности */ /* Собираем заголовок сущности */
RENT.SID := TENT_ID_MAKE(SNAME => RVIEW.VIEW_NAME, NNUMB => NNUMB); RENT.SID := TENT_ID_MAKE(STYPE => STYPE, NNUMB => NNUMB);
RENT.SNAME := RVIEW.VIEW_NAME; RENT.SNAME := RVIEW.VIEW_NAME;
RENT.STITLE := DMSCLVIEWS_TITLE_GET(SVIEW_NAME => RENT.SNAME); RENT.STITLE := DMSCLVIEWS_TITLE_GET(SVIEW_NAME => RENT.SNAME);
RENT.STYPE := SENT_TYPE_VIEW; RENT.STYPE := SENT_TYPE_VIEW;
@ -1306,13 +1443,13 @@ create or replace package body PKG_P8PANELS_QE_BASE as
function TENTS_NEXT_NUMB function TENTS_NEXT_NUMB
( (
RENTS in TENTS, -- Коллекция сущностей RENTS in TENTS, -- Коллекция сущностей
SNAME in varchar2 -- Имя STYPE in varchar2 -- Тип (см. константы SENT_TYPE_*)
) return number -- Номер сущности в коллекции ) return number -- Номер сущности в коллекции
is is
NNUMB PKG_STD.TNUMBER := 0; -- Буфер для результата NNUMB PKG_STD.TNUMBER := 0; -- Буфер для результата
begin begin
/* Подбираем первый свободный номер */ /* Подбираем первый свободный номер */
while (TENTS_INDEX_BY_ID(RENTS => RENTS, SID => TENT_ID_MAKE(SNAME => SNAME, NNUMB => NNUMB)) is not null) while (TENTS_INDEX_BY_ID(RENTS => RENTS, SID => TENT_ID_MAKE(STYPE => STYPE, NNUMB => NNUMB)) is not null)
loop loop
NNUMB := NNUMB + 1; NNUMB := NNUMB + 1;
end loop; end loop;
@ -1338,7 +1475,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
/* Формируем пописание сущности */ /* Формируем пописание сущности */
RENT := TENT_MAKE(SNAME => SNAME, RENT := TENT_MAKE(SNAME => SNAME,
STYPE => STYPE, STYPE => STYPE,
NNUMB => TENTS_NEXT_NUMB(RENTS => RENTS, SNAME => SNAME), NNUMB => TENTS_NEXT_NUMB(RENTS => RENTS, STYPE => STYPE),
NINIT_ATTRS => NINIT_ATTRS); NINIT_ATTRS => NINIT_ATTRS);
/* Добавляем её в коллекцию */ /* Добавляем её в коллекцию */
RENTS.EXTEND(); RENTS.EXTEND();
@ -2580,6 +2717,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
/* Если сущность одна - то она и главная */ /* Если сущность одна - то она и главная */
if (RENTS.COUNT = 1) then if (RENTS.COUNT = 1) then
RENT := RENTS(RENTS.FIRST); RENT := RENTS(RENTS.FIRST);
return;
end if; end if;
/* Обходим все сущности */ /* Обходим все сущности */
for E in RENTS.FIRST .. RENTS.LAST for E in RENTS.FIRST .. RENTS.LAST
@ -2590,6 +2728,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
for A in RENTS(E).RATTRS.FIRST .. RENTS(E).RATTRS.LAST for A in RENTS(E).RATTRS.FIRST .. RENTS(E).RATTRS.LAST
loop loop
/* Обойдем связи и поищем атрибут среди источников */ /* Обойдем связи и поищем атрибут среди источников */
if ((RRLS is not null) and (RRLS.COUNT > 0)) then
for R in RRLS.FIRST .. RRLS.LAST for R in RRLS.FIRST .. RRLS.LAST
loop loop
if (RRLS(R).SSOURCE = RENTS(E).RATTRS(A).SID) then if (RRLS(R).SSOURCE = RENTS(E).RATTRS(A).SID) then
@ -2597,6 +2736,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
exit; exit;
end if; end if;
end loop; end loop;
end if;
/* Если хоть один атрибут сущности есть среди источников - дальше можно атрибуты этой сущности не рассматривать */ /* Если хоть один атрибут сущности есть среди источников - дальше можно атрибуты этой сущности не рассматривать */
exit when BFOUND = true; exit when BFOUND = true;
end loop; end loop;
@ -2626,7 +2766,9 @@ create or replace package body PKG_P8PANELS_QE_BASE as
) )
is is
SFIELD PKG_STD.TSTRING; -- Буфер для поля запроса SFIELD PKG_STD.TSTRING; -- Буфер для поля запроса
BFOUND boolean := false; -- Флаг наличия визуализируемых атрибутов
begin begin
/* Формируем */
PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => 'select '); PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => 'select ');
for I in RENTS.FIRST .. RENTS.LAST for I in RENTS.FIRST .. RENTS.LAST
loop loop
@ -2634,15 +2776,24 @@ create or replace package body PKG_P8PANELS_QE_BASE as
for J in RENTS(I).RATTRS.FIRST .. RENTS(I).RATTRS.LAST for J in RENTS(I).RATTRS.FIRST .. RENTS(I).RATTRS.LAST
loop loop
if (RENTS(I).RATTRS(J).NSHOW = 1) then if (RENTS(I).RATTRS(J).NSHOW = 1) then
SFIELD := RENTS(I).RATTRS(J).SID || ' ' || RENTS(I).RATTRS(J).SNAME || I || J; if (RENTS(I).RATTRS(J).SALIAS is null) then
if (not ((I = RENTS.LAST) and (J = RENTS(I).RATTRS.LAST))) then P_EXCEPTION(0,
SFIELD := SFIELD || ', '; 'Для атрибута ("%s") не задан севдоним.',
RENTS(I).RATTRS(J).SID);
end if; end if;
SFIELD := RENTS(I).RATTRS(J).SID || ' "' || RENTS(I).RATTRS(J).SALIAS || '", ';
PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => SFIELD); PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => SFIELD);
BFOUND := true;
end if; end if;
end loop; end loop;
end if; end if;
end loop; end loop;
SQRY := RTRIM(SQRY, ', ');
/* Если не нашли ни одного атрибута */
if (not BFOUND) then
P_EXCEPTION(0,
'Для запроса не определено ни одного визуализируемого атрибута.');
end if;
end BUILD_SELECT; end BUILD_SELECT;
/* Формирование иерархии связей в секции "from" */ /* Формирование иерархии связей в секции "from" */
@ -2701,7 +2852,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => 'from '); PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => 'from ');
STABLE := RENT_ROOT.SNAME || ' ' || RENT_ROOT.SID; STABLE := RENT_ROOT.SNAME || ' ' || RENT_ROOT.SID;
PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => STABLE); PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => STABLE);
if (RRLS.COUNT > 0) then if ((RRLS is not null) and (RRLS.COUNT > 0)) then
BUILD_FROM_HIER(RENT_START => RENT_ROOT, RENTS => RENTS, RRLS => RRLS, SQRY => SQRY); BUILD_FROM_HIER(RENT_START => RENT_ROOT, RENTS => RENTS, RRLS => RRLS, SQRY => SQRY);
end if; end if;
else else
@ -2723,10 +2874,6 @@ create or replace package body PKG_P8PANELS_QE_BASE as
end if; end if;
end BUILD_WHERE; end BUILD_WHERE;
begin begin
/*
TODO: owner="root" created="09.09.2025"
text="Контроль длины псевдонима таблицы и поля (максимум 30 символов)"
*/
/* /*
TODO: owner="root" created="10.09.2025" TODO: owner="root" created="10.09.2025"
text="Предусмотреть связям признак ""обязательность"" text="Предусмотреть связям признак ""обязательность""
@ -2746,14 +2893,14 @@ create or replace package body PKG_P8PANELS_QE_BASE as
/* Читаем сущности запроса */ /* Читаем сущности запроса */
RENTS := QUERY_ENTS_GET(CENTS => RQ.ENTS); RENTS := QUERY_ENTS_GET(CENTS => RQ.ENTS);
/* Нет сущностей - нет запроса */ /* Нет сущностей - нет запроса */
if ((RENTS is null) and (RENTS.COUNT = 0)) then if ((RENTS is null) or (RENTS.COUNT = 0)) then
SET_MSG(SMESSAGE => 'В запросе нет сущностей.'); SET_MSG(SMESSAGE => 'В запросе нет сущностей.');
return; return;
end if; end if;
/* Читаем связи запроса */ /* Читаем связи запроса */
RRLS := QUERY_RLS_GET(CRLS => RQ.RLS); RRLS := QUERY_RLS_GET(CRLS => RQ.RLS);
/* Нельзя построить запрос, если есть несвязанные сущности */ /* Нельзя построить запрос, если есть несвязанные сущности */
if ((RENTS.COUNT > 1) and (RENTS.COUNT - 1 > RRLS.COUNT)) then if ((RENTS.COUNT > 1) and ((RRLS is null) or (RENTS.COUNT - 1 > RRLS.COUNT))) then
SET_MSG(SMESSAGE => 'В запросе есть несвязанные сущности.'); SET_MSG(SMESSAGE => 'В запросе есть несвязанные сущности.');
return; return;
end if; end if;