diff --git a/app/panels/query_editor/components/inspector_query_rls/hooks.js b/app/panels/query_editor/components/inspector_query_rls/hooks.js index 53af628..a212705 100644 --- a/app/panels/query_editor/components/inspector_query_rls/hooks.js +++ b/app/panels/query_editor/components/inspector_query_rls/hooks.js @@ -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 }; }; //---------------- diff --git a/app/panels/query_editor/components/inspector_query_rls/inspector_query_rls.js b/app/panels/query_editor/components/inspector_query_rls/inspector_query_rls.js index 31cb9c3..8422385 100644 --- a/app/panels/query_editor/components/inspector_query_rls/inspector_query_rls.js +++ b/app/panels/query_editor/components/inspector_query_rls/inspector_query_rls.js @@ -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 ( - + <> + } + label={"Обязательна"} + /> + + ); }; diff --git a/app/panels/query_editor/components/query_diagram/query_diagram.js b/app/panels/query_editor/components/query_diagram/query_diagram.js index a269c9d..88c51ff 100644 --- a/app/panels/query_editor/components/query_diagram/query_diagram.js +++ b/app/panels/query_editor/components/query_diagram/query_diagram.js @@ -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} diff --git a/app/panels/query_editor/components/relation/relation.js b/app/panels/query_editor/components/relation/relation.js index a7b62a5..782663c 100644 --- a/app/panels/query_editor/components/relation/relation.js +++ b/app/panels/query_editor/components/relation/relation.js @@ -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 }); //---------------- diff --git a/app/panels/query_editor/hooks.js b/app/panels/query_editor/hooks.js index b62396c..4d89f21 100644 --- a/app/panels/query_editor/hooks.js +++ b/app/panels/query_editor/hooks.js @@ -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; }; diff --git a/db/PKG_P8PANELS_QE.pck b/db/PKG_P8PANELS_QE.pck index 322f741..c0929b8 100644 --- a/db/PKG_P8PANELS_QE.pck +++ b/db/PKG_P8PANELS_QE.pck @@ -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 ( diff --git a/db/PKG_P8PANELS_QE_BASE.pck b/db/PKG_P8PANELS_QE_BASE.pck index 56ba0f4..577e9b8 100644 --- a/db/PKG_P8PANELS_QE_BASE.pck +++ b/db/PKG_P8PANELS_QE_BASE.pck @@ -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="Предусмотреть отладочные значения для аргументов запроса"