ЦИТК-979 - Поддержка обязательности связи в запросе

This commit is contained in:
Mikhail Chechnev 2025-10-10 13:33:22 +03:00
parent 6a6e58e88e
commit a46cd28656
7 changed files with 138 additions and 32 deletions

View File

@ -35,8 +35,20 @@ const useQueryRelations = query => {
[query, executeStored]
);
//Установка признака обязательности отношения сущности в запросе
const setRlMandatory = useCallback(
async (rl, mandatory) => {
await executeStored({
stored: "PKG_P8PANELS_QE.QUERY_RL_MANDATORY_SET",
args: { NRN: query, SID: rl, NMANDATORY: mandatory },
loader: false
});
},
[query, executeStored]
);
//Возвращаем интерфейс хука
return { addRl, removeRl };
return { addRl, removeRl, setRlMandatory };
};
//----------------

View File

@ -9,12 +9,26 @@
import React, { useContext } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Icon, Button } from "@mui/material"; //Интерфейсные элементы
import { Icon, Button, FormControlLabel, Checkbox } from "@mui/material"; //Интерфейсные элементы
import { MessagingСtx } from "../../../../context/messaging"; //Контекст сообщений приложения
import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы
import { RELATION_SHAPE } from "../relation/relation"; //Описание связи
import { useQueryRelations } from "./hooks"; //Хуки для работы со связями
//---------
//Константы
//---------
//Стили
const STYLES = {
MANDATORY_FORMCONTROLLABEL: {
width: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center"
}
};
//-----------
//Тело модуля
//-----------
@ -25,7 +39,7 @@ const InspectorQueryRelations = ({ query, relation, onOptionsChanged }) => {
const { showMsgWarn } = useContext(MessagingСtx);
//Работа со связями на сервере
const { removeRl } = useQueryRelations(query);
const { removeRl, setRlMandatory } = useQueryRelations(query);
//Уведомление родителя об изменении свойств
const notifyOptionsChanged = () => onOptionsChanged && onOptionsChanged();
@ -39,11 +53,24 @@ const InspectorQueryRelations = ({ query, relation, onOptionsChanged }) => {
}
});
//При изменении состояния обязательности связи в запросе
const handleRelationMandatoryChange = async e => {
await setRlMandatory(relation.id, e.target.checked === true ? 1 : 0);
notifyOptionsChanged();
};
//Формирование представления
return (
<Button startIcon={<Icon>delete</Icon>} onClick={handleRelationRemoveClick}>
{BUTTONS.DELETE}
</Button>
<>
<FormControlLabel
sx={STYLES.MANDATORY_FORMCONTROLLABEL}
control={<Checkbox checked={relation.mandatory === 1 ? true : false} onChange={handleRelationMandatoryChange} />}
label={"Обязательна"}
/>
<Button startIcon={<Icon>delete</Icon>} onClick={handleRelationRemoveClick}>
{BUTTONS.DELETE}
</Button>
</>
);
};

View File

@ -194,10 +194,7 @@ const QueryDiagram = ({
onEdgeClick={handleEdgeClick}
onEdgesChange={handleEdgesChange}
onPaneClick={handlePaneClick}
defaultEdgeOptions={{
animated: true,
style: STYLES.EDGE
}}
defaultEdgeOptions={{ style: STYLES.EDGE }}
connectionLineStyle={STYLES.CONNECTION_LINE}
onConnect={handleConnect}
isValidConnection={validateConnection}

View File

@ -17,7 +17,8 @@ import PropTypes from "prop-types"; //Контроль свойств компо
const RELATION_SHAPE = PropTypes.shape({
id: PropTypes.string.isRequired,
source: PropTypes.string.isRequired,
target: PropTypes.string.isRequired
target: PropTypes.string.isRequired,
mandatory: PropTypes.number.isRequired
});
//----------------

View File

@ -89,7 +89,7 @@ const serverQueryData2QueryDiagram = (entities, relations) => {
result.nodes = [...result.nodes, ...nodes];
});
//Грани для диаграммы
result.edges = relations.map(r => ({ ...r }));
result.edges = relations.map(r => ({ ...r, animated: r.mandatory === 1 ? false : true }));
//Вернем итоговый результат
return result;
};

View File

@ -111,6 +111,14 @@ create or replace package PKG_P8PANELS_QE as
SID in varchar2 -- Идентификатор связи
);
/* Установка признака обязательности связи */
procedure QUERY_RL_MANDATORY_SET
(
NRN in number, -- Рег. номер запроса
SID in varchar2, -- Идентификатор связи
NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
);
/* Добавление аргумента запроса */
procedure QUERY_OPT_ARG_ADD
(
@ -469,7 +477,7 @@ create or replace package body PKG_P8PANELS_QE as
/* Читаем существующие связи */
RRLS := PKG_P8PANELS_QE_BASE.QUERY_RLS_GET(CRLS => RQ.RLS);
/* Формируем описание новой связи и добавляем её в коллекцию */
PKG_P8PANELS_QE_BASE.TRLS_ADD(RRLS => RRLS, SSOURCE => SSOURCE, STARGET => STARGET);
PKG_P8PANELS_QE_BASE.TRLS_ADD(RRLS => RRLS, SSOURCE => SSOURCE, STARGET => STARGET, NMANDATORY => 0);
/* Сохраняем обновленный набор связей */
PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
/* Переформируем SQL-выражение */
@ -504,6 +512,32 @@ create or replace package body PKG_P8PANELS_QE as
PKG_P8PANELS_QE_BASE.QUERY_QRY_SET(NRN => RQ.RN, CQRY => SQRY, CQRY_MSG => SQRY_MSG);
end QUERY_RL_REMOVE;
/* Установка признака обязательности связи */
procedure QUERY_RL_MANDATORY_SET
(
NRN in number, -- Рег. номер запроса
SID in varchar2, -- Идентификатор связи
NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
)
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);
/* Читаем существующие связи */
RRLS := PKG_P8PANELS_QE_BASE.QUERY_RLS_GET(CRLS => RQ.RLS);
/* Устанавливаем признакобязательности */
PKG_P8PANELS_QE_BASE.TRLS_MANDATORY_SET(RRLS => RRLS, SID => SID, NMANDATORY => NMANDATORY);
/* Сохраняем обновленный набор связей */
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);
end QUERY_RL_MANDATORY_SET;
/* Добавление аргумента запроса */
procedure QUERY_OPT_ARG_ADD
(

View File

@ -57,7 +57,8 @@ create or replace package PKG_P8PANELS_QE_BASE as
(
SID PKG_STD.TSTRING, -- Уникальный идентификатор в запросе
SSOURCE PKG_STD.TSTRING, -- Идентификатор атрибута-источника
STARGET PKG_STD.TSTRING -- Идентификатор атрибута-приёмника
STARGET PKG_STD.TSTRING, -- Идентификатор атрибута-приёмника
NMANDATORY PKG_STD.TNUMBER -- Флаг обязательности (1 - да, 0 - нет)
);
/* Типы данных - Коллекция отношений */
@ -165,7 +166,8 @@ create or replace package PKG_P8PANELS_QE_BASE as
(
RRLS in out nocopy TRLS, -- Изменяемая коллекция
SSOURCE in varchar2, -- Источник
STARGET in varchar2 -- Приёмник
STARGET in varchar2, -- Приёмник
NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
);
/* Удаление связи из коллекции */
@ -175,6 +177,14 @@ create or replace package PKG_P8PANELS_QE_BASE as
SID in varchar2 -- Идентификатор удялемой связи
);
/* Установка признака обязательности связи в коллекции */
procedure TRLS_MANDATORY_SET
(
RRLS in out nocopy TRLS, -- Изменяемая коллекция
SID in varchar2, -- Идентификатор связи
NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
);
/* Подчистка коллекции связей по атрибуту */
procedure TRLS_CLEANUP_BY_ATTR
(
@ -1649,15 +1659,17 @@ create or replace package body PKG_P8PANELS_QE_BASE as
(
SID in varchar2 := null, -- Идентификатор (null - автоформирование)
SSOURCE in varchar2, -- Источник
STARGET in varchar2 -- Приёмник
STARGET in varchar2, -- Приёмник
NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
) return TRL -- Описание связи
is
RRL TRL; -- Буфер для результата
begin
/* Собираем описание связи */
RRL.SID := COALESCE(SID, TRL_ID_MAKE(SSOURCE => SSOURCE, STARGET => STARGET));
RRL.SSOURCE := SSOURCE;
RRL.STARGET := STARGET;
RRL.SID := COALESCE(SID, TRL_ID_MAKE(SSOURCE => SSOURCE, STARGET => STARGET));
RRL.SSOURCE := SSOURCE;
RRL.STARGET := STARGET;
RRL.NMANDATORY := NMANDATORY;
/* Вернем полученное */
return RRL;
end TRL_MAKE;
@ -1675,6 +1687,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
PKG_XFAST.ATTR(SNAME => SATTR_ID, SVALUE => RRL.SID);
PKG_XFAST.ATTR(SNAME => SATTR_SOURCE, SVALUE => RRL.SSOURCE);
PKG_XFAST.ATTR(SNAME => SATTR_TARGET, SVALUE => RRL.STARGET);
PKG_XFAST.ATTR(SNAME => SATTR_MANDATORY, NVALUE => RRL.NMANDATORY);
/* Закрываем описание связи */
PKG_XFAST.UP();
end TRL_TO_XML;
@ -1700,9 +1713,10 @@ create or replace package body PKG_P8PANELS_QE_BASE as
/* Считаваем узел связи */
XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_RL);
/* Получаем значения */
RRES.SID := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_ID);
RRES.SSOURCE := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_SOURCE);
RRES.STARGET := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_TARGET);
RRES.SID := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_ID);
RRES.SSOURCE := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_SOURCE);
RRES.STARGET := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_TARGET);
RRES.NMANDATORY := PKG_XPATH.ATTRIBUTE_NUM(RNODE => XNODE, SNAME => SATTR_MANDATORY);
/* Освободим документ */
PKG_XPATH.FREE(RDOCUMENT => XDOC);
exception
@ -1766,7 +1780,10 @@ create or replace package body PKG_P8PANELS_QE_BASE as
if (((NLIST_TYPE = 0) and (RRLS(I).SSOURCE = SSOURCE_TARGET)) or
((NLIST_TYPE = 1) and (RRLS(I).STARGET = SSOURCE_TARGET))) then
RRES.EXTEND();
RRES(RRES.LAST) := TRL_MAKE(SID => RRLS(I).SID, SSOURCE => RRLS(I).SSOURCE, STARGET => RRLS(I).STARGET);
RRES(RRES.LAST) := TRL_MAKE(SID => RRLS(I).SID,
SSOURCE => RRLS(I).SSOURCE,
STARGET => RRLS(I).STARGET,
NMANDATORY => RRLS(I).NMANDATORY);
end if;
exception
when NO_DATA_FOUND then
@ -1783,7 +1800,8 @@ create or replace package body PKG_P8PANELS_QE_BASE as
(
RRLS in out nocopy TRLS, -- Изменяемая коллекция
SSOURCE in varchar2, -- Источник
STARGET in varchar2 -- Приёмник
STARGET in varchar2, -- Приёмник
NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
)
is
RRL TRL; -- Добавляемая связь
@ -1793,7 +1811,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
RRLS := TRLS();
end if;
/* Формируем пописание связи */
RRL := TRL_MAKE(SSOURCE => SSOURCE, STARGET => STARGET);
RRL := TRL_MAKE(SSOURCE => SSOURCE, STARGET => STARGET, NMANDATORY => NMANDATORY);
/* Добавляем её в коллекцию */
RRLS.EXTEND();
RRLS(RRLS.LAST) := RRL;
@ -1816,6 +1834,22 @@ create or replace package body PKG_P8PANELS_QE_BASE as
end if;
end TRLS_REMOVE;
/* Установка признака обязательности связи в коллекции */
procedure TRLS_MANDATORY_SET
(
RRLS in out nocopy TRLS, -- Изменяемая коллекция
SID in varchar2, -- Идентификатор связи
NMANDATORY in number -- Флаг обязательности (1 - да, 0 - нет)
)
is
NIND PKG_STD.TNUMBER; -- Индекс связи в коллекции
begin
NIND := TRLS_INDEX_BY_ID(RRLS => RRLS, SID => SID);
if (NIND is not null) then
RRLS(NIND).NMANDATORY := NMANDATORY;
end if;
end TRLS_MANDATORY_SET;
/* Подчистка коллекции связей по атрибуту */
procedure TRLS_CLEANUP_BY_ATTR
(
@ -2806,6 +2840,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
)
is
NENT_SRC PKG_STD.TNUMBER; -- Индекс сущности-источника связи
SJOIN PKG_STD.TSTRING; -- Буфер для операции объединения
STABLE PKG_STD.TSTRING; -- Буфер для таблицы запроса
begin
/* Обходим связи */
@ -2823,8 +2858,14 @@ create or replace package body PKG_P8PANELS_QE_BASE as
RRLS (I)
.STARGET);
end if;
/* Определим тип объединения */
if (RRLS(I).NMANDATORY = 1) then
SJOIN := 'join';
else
SJOIN := 'left join';
end if;
/* Соберем выражение и добавим его в запрос */
STABLE := 'left join ' || RENTS(NENT_SRC).SNAME || ' ' || RENTS(NENT_SRC).SID || ' on ' || RRLS(I).STARGET ||
STABLE := SJOIN || ' ' || RENTS(NENT_SRC).SNAME || ' ' || RENTS(NENT_SRC).SID || ' on ' || RRLS(I).STARGET ||
' = ' || RRLS(I).SSOURCE;
PKG_SQL_BUILD.APPEND(SSQL => SQRY, SELEMENT1 => STABLE);
/* Добавим связи сущности-источника */
@ -2874,12 +2915,6 @@ create or replace package body PKG_P8PANELS_QE_BASE as
end if;
end BUILD_WHERE;
begin
/*
TODO: owner="root" created="10.09.2025"
text="Предусмотреть связям признак ""обязательность""
с ним - join
без него - left join"
*/
/*
TODO: owner="root" created="10.09.2025"
text="Предусмотреть отладочные значения для аргументов запроса"