main #2

Merged
davay-popozhe merged 44 commits from main into EqsPrfrm 2024-09-20 16:11:33 +03:00
2 changed files with 122 additions and 58 deletions
Showing only changes of commit 79eea7f38f - Show all commits

View File

@ -40,7 +40,9 @@ import {
} from "@mui/material"; //Интерфейсные элементы } from "@mui/material"; //Интерфейсные элементы
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
import { NavigationCtx } from "../../context/navigation"; //Контекст навигации
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
import { P8PGantt, taskLegendDesc } from "../../components/p8p_gantt"; //Диаграмма Ганта import { P8PGantt, taskLegendDesc } from "../../components/p8p_gantt"; //Диаграмма Ганта
import { xml2JSON, formatDateJSONDateOnly, formatDateRF, hasValue } from "../../core/utils"; //Вспомогательные функции import { xml2JSON, formatDateJSONDateOnly, formatDateRF, hasValue } from "../../core/utils"; //Вспомогательные функции
import { useFilteredPlanCtlgs } from "./hooks"; //Вспомогательные хуки import { useFilteredPlanCtlgs } from "./hooks"; //Вспомогательные хуки
@ -58,12 +60,6 @@ const DECLINATIONS = ["план", "плана", "планов"];
const SORT_REP_DATE = "DREP_DATE"; const SORT_REP_DATE = "DREP_DATE";
const SORT_REP_DATE_TO = "DREP_DATE_TO"; const SORT_REP_DATE_TO = "DREP_DATE_TO";
//Высота диаграммы Ганта
const GANTT_HEIGHT = "75vh";
//Ширина диаграммы Ганта
const GANTT_WIDTH = "98vw";
//Стили //Стили
const STYLES = { const STYLES = {
PLANS_FINDER: { marginTop: "10px", marginLeft: "10px", width: "93%" }, PLANS_FINDER: { marginTop: "10px", marginLeft: "10px", width: "93%" },
@ -71,20 +67,23 @@ const STYLES = {
PLANS_LIST_ITEM_ZERODOCS: { backgroundColor: "#ebecec" }, PLANS_LIST_ITEM_ZERODOCS: { backgroundColor: "#ebecec" },
PLANS_LIST_ITEM_PRIMARY: { wordWrap: "break-word" }, PLANS_LIST_ITEM_PRIMARY: { wordWrap: "break-word" },
PLANS_LIST_ITEM_SECONDARY: { wordWrap: "break-word", fontSize: "0.6rem", textTransform: "uppercase" }, PLANS_LIST_ITEM_SECONDARY: { wordWrap: "break-word", fontSize: "0.6rem", textTransform: "uppercase" },
PLANS_BUTTON: { position: "absolute" }, PLANS_BUTTON: { position: "absolute", top: `calc(${APP_BAR_HEIGHT} + 16px)`, left: "16px" },
PLANS_DRAWER: { PLANS_DRAWER: {
width: "350px", width: "350px",
display: "inline-block", display: "inline-block",
flexShrink: 0, flexShrink: 0,
[`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box" } [`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box" }
}, },
GANTT_CONTAINER: { height: GANTT_HEIGHT, width: GANTT_WIDTH }, GANTT_CONTAINER: { height: `calc(100vh - ${APP_BAR_HEIGHT})`, width: "100vw", paddingTop: "24px" },
GANTT_TITLE: { paddingLeft: "100px", paddingRight: "120px" }, GANTT_TITLE: { paddingLeft: "250px", paddingRight: "250px" },
SECOND_TABLE: { paddingTop: "30px" }, SECOND_TABLE: { paddingTop: "30px" },
TASK_DIALOG_CARD_CONTAINER: { padding: "0px" }, TASK_DIALOG_CARD_CONTAINER: { padding: "0px" },
TASK_DIALOG_LIST_ITEM_ICON: { justifyContent: "center" }, TASK_DIALOG_LIST_ITEM_ICON: { justifyContent: "center" },
TASK_DIALOG_ICON: { fontSize: "2rem" }, TASK_DIALOG_ICON: { fontSize: "2rem" },
TASK_DIALOG_ACTION_CONTAINER: { border: 1, borderColor: "text.primary", borderRadius: "5px", width: "100%" } TASK_DIALOG_ACTION_CONTAINER: { border: 1, borderColor: "text.primary", borderRadius: "5px", width: "100%" },
FILTERS: { display: "table", float: "right" },
FILTERS_DATE: { display: "table-cell", verticalAlign: "middle" },
FILTERS_LEVEL: { display: "table-cell", verticalAlign: "middle", paddingLeft: "15px" }
}; };
//------------------------------------ //------------------------------------
@ -148,7 +147,7 @@ const PlanCtlgsList = ({ planCtlgs = [], selectedPlanCtlg, filter, setFilter, on
> >
<ListItemText <ListItemText
primary={<Typography sx={STYLES.PLANS_LIST_ITEM_PRIMARY}>{p.SNAME}</Typography>} primary={<Typography sx={STYLES.PLANS_LIST_ITEM_PRIMARY}>{p.SNAME}</Typography>}
secondary={<Typography sx={{ ...STYLES.PLANS_LIST_ITEM_SECONDARY }}>{formatCountDocs(p.NCOUNT_DOCS)}</Typography>} secondary={<Typography sx={STYLES.PLANS_LIST_ITEM_SECONDARY}>{formatCountDocs(p.NCOUNT_DOCS)}</Typography>}
/> />
</ListItemButton> </ListItemButton>
))} ))}
@ -210,7 +209,9 @@ const taskDialogRenderer = ({ task, taskColors, close, handleTaskDetailOpen }) =
{task["detail_list"]} {task["detail_list"]}
</Button> </Button>
) : ( ) : (
<Typography color="textSecondary">{`Анализ отклонений недоступен: ${task["detail_list"]}`}</Typography> <Typography color="textSecondary">{`Анализ отклонений недоступен${
task["detail_list"] ? `: ${task["detail_list"]}` : ""
}`}</Typography>
)} )}
</Box> </Box>
</CardActions> </CardActions>
@ -239,7 +240,8 @@ const MechRecCostProdPlans = () => {
selectedPlanCtlgGanttDef: {}, selectedPlanCtlgGanttDef: {},
selectedPlanCtlgSpecs: [], selectedPlanCtlgSpecs: [],
selectedTaskDetail: null, selectedTaskDetail: null,
selectedTaskDetailType: null selectedTaskDetailType: null,
planSpec: null
}); });
//Состояние для фильтра каталогов //Состояние для фильтра каталогов
const [filter, setFilter] = useState({ ctlgName: "", haveDocs: false }); const [filter, setFilter] = useState({ ctlgName: "", haveDocs: false });
@ -253,7 +255,10 @@ const MechRecCostProdPlans = () => {
//Подключение к контексту взаимодействия с сервером //Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx); const { executeStored } = useContext(BackEndСtx);
// Инициализация каталогов планов //Подключение к контексту навигации
const { getNavigationSearch } = useContext(NavigationCtx);
//Инициализация каталогов планов
const initPlanCtlgs = useCallback(async () => { const initPlanCtlgs = useCallback(async () => {
if (!state.init) { if (!state.init) {
const data = await executeStored({ const data = await executeStored({
@ -307,7 +312,7 @@ const MechRecCostProdPlans = () => {
async (level = null, sort = null) => { async (level = null, sort = null) => {
const data = await executeStored({ const data = await executeStored({
stored: "PKG_P8PANELS_MECHREC.FCPRODPLANSP_GET", stored: "PKG_P8PANELS_MECHREC.FCPRODPLANSP_GET",
args: { NCRN: state.selectedPlanCtlg, NLEVEL: level, SSORT_FIELD: sort } args: { NCRN: state.selectedPlanCtlg, NLEVEL: level, SSORT_FIELD: sort, NFCPRODPLANSP: state.planSpec }
}); });
let doc = await parseProdPlanSpXML(data.COUT); let doc = await parseProdPlanSpXML(data.COUT);
setState(pv => ({ setState(pv => ({
@ -324,7 +329,7 @@ const MechRecCostProdPlans = () => {
})); }));
}, },
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
[executeStored, state.ident, state.selectedPlanCtlg] [executeStored, state.ident, state.selectedPlanCtlg, state.planSpec]
); );
//Обработка нажатия на элемент в списке каталогов планов //Обработка нажатия на элемент в списке каталогов планов
@ -335,14 +340,16 @@ const MechRecCostProdPlans = () => {
//При подключении компонента к странице //При подключении компонента к странице
useEffect(() => { useEffect(() => {
initPlanCtlgs(); const actionPrms = getNavigationSearch();
if (actionPrms.NSPRN) setState(pv => ({ ...pv, planSpec: parseInt(actionPrms.NSPRN), init: true }));
else initPlanCtlgs();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
//При смене выбранного каталога плана //При смене выбранного каталога плана или при явном указании позиции спецификации плана
useEffect(() => { useEffect(() => {
if (state.selectedPlanCtlg) loadPlanCtglSpecs(null, SORT_REP_DATE_TO); if (state.selectedPlanCtlg || state.planSpec) loadPlanCtglSpecs(null, SORT_REP_DATE_TO);
}, [state.selectedPlanCtlg, loadPlanCtglSpecs]); }, [state.selectedPlanCtlg, state.planSpec, loadPlanCtglSpecs]);
//Выбор уровня //Выбор уровня
const handleChangeSelectLevel = selectedLevel => { const handleChangeSelectLevel = selectedLevel => {
@ -368,35 +375,48 @@ const MechRecCostProdPlans = () => {
//Генерация содержимого //Генерация содержимого
return ( return (
<Box p={2}> <Box>
<Fab variant="extended" sx={STYLES.PLANS_BUTTON} onClick={() => setState(pv => ({ ...pv, showPlanList: !pv.showPlanList }))}> {!state.planSpec ? (
Каталоги планов <>
</Fab> <Fab variant="extended" sx={STYLES.PLANS_BUTTON} onClick={() => setState(pv => ({ ...pv, showPlanList: !pv.showPlanList }))}>
<Drawer Каталоги планов
anchor={"left"} </Fab>
open={state.showPlanList} <Drawer
onClose={() => setState(pv => ({ ...pv, showPlanList: false }))} anchor={"left"}
sx={STYLES.PLANS_DRAWER} open={state.showPlanList}
> onClose={() => setState(pv => ({ ...pv, showPlanList: false }))}
<PlanCtlgsList sx={STYLES.PLANS_DRAWER}
planCtlgs={filteredPlanCtgls} >
selectedPlanCtlg={state.selectedPlanCtlg} <PlanCtlgsList
filter={filter} planCtlgs={filteredPlanCtgls}
setFilter={setFilter} selectedPlanCtlg={state.selectedPlanCtlg}
onClick={handleProjectClick} filter={filter}
/> setFilter={setFilter}
</Drawer> onClick={handleProjectClick}
/>
</Drawer>
</>
) : null}
{state.init == true ? ( {state.init == true ? (
<Grid container spacing={1}> <Grid container>
<Grid item xs={12}> <Grid item xs={12}>
{state.selectedPlanCtlgSpecsLoaded ? ( {state.selectedPlanCtlgSpecsLoaded ? (
state.selectedPlanCtlgSpecs.length === 0 ? ( state.selectedPlanCtlgSpecs.length === 0 ? (
<InlineMsgInfo okBtn={false} text={"В каталоге планов отсутствуют записи спецификации"} /> <Box pt={3}>
<InlineMsgInfo
okBtn={false}
text={
state.planSpec
? "Не найдено данных для выбранной позиции плана"
: "В каталоге планов отсутствуют записи спецификации"
}
/>
</Box>
) : ( ) : (
<Box sx={STYLES.GANTT_CONTAINER} p={1}> <Box>
{state.selectedPlanCtlgMaxLevel ? ( {state.selectedPlanCtlgMaxLevel ? (
<Box sx={{ display: "table", float: "right" }}> <Box sx={STYLES.FILTERS} p={1}>
<Box sx={{ display: "table-cell", verticalAlign: "middle" }}> <Box sx={STYLES.FILTERS_DATE}>
<InputLabel id="select-label-sort">Сортировка</InputLabel> <InputLabel id="select-label-sort">Сортировка</InputLabel>
<Select <Select
labelId="select-label-sort" labelId="select-label-sort"
@ -416,7 +436,7 @@ const MechRecCostProdPlans = () => {
</MenuItem> </MenuItem>
</Select> </Select>
</Box> </Box>
<Box sx={{ display: "table-cell", verticalAlign: "middle", paddingLeft: "15px" }}> <Box sx={STYLES.FILTERS_LEVEL}>
<InputLabel id="select-label-level">До уровня</InputLabel> <InputLabel id="select-label-level">До уровня</InputLabel>
<Select <Select
labelId="select-label-level" labelId="select-label-level"
@ -440,7 +460,7 @@ const MechRecCostProdPlans = () => {
<P8PGantt <P8PGantt
{...P8P_GANTT_CONFIG_PROPS} {...P8P_GANTT_CONFIG_PROPS}
{...state.selectedPlanCtlgGanttDef} {...state.selectedPlanCtlgGanttDef}
height={GANTT_HEIGHT} containerStyle={STYLES.GANTT_CONTAINER}
titleStyle={STYLES.GANTT_TITLE} titleStyle={STYLES.GANTT_TITLE}
tasks={state.selectedPlanCtlgSpecs} tasks={state.selectedPlanCtlgSpecs}
taskDialogRenderer={prms => taskDialogRenderer({ ...prms, handleTaskDetailOpen })} taskDialogRenderer={prms => taskDialogRenderer({ ...prms, handleTaskDetailOpen })}
@ -448,7 +468,16 @@ const MechRecCostProdPlans = () => {
</Box> </Box>
) )
) : !state.selectedPlanCtlg ? ( ) : !state.selectedPlanCtlg ? (
<InlineMsgInfo okBtn={false} text={"Укажите каталог планов для отображения их спецификаций"} /> <Box pt={3}>
<InlineMsgInfo
okBtn={false}
text={
state.planSpec
? "Загружаю график для выбранной позиции плана..."
: "Укажите каталог планов для отображения их спецификаций"
}
/>
</Box>
) : null} ) : null}
</Grid> </Grid>
</Grid> </Grid>

View File

@ -81,6 +81,7 @@ create or replace package PKG_P8PANELS_MECHREC as
procedure FCPRODPLANSP_GET procedure FCPRODPLANSP_GET
( (
NCRN in number, -- Рег. номер каталога NCRN in number, -- Рег. номер каталога
NFCPRODPLANSP in number, -- Рег. номер позиции спецификации
NLEVEL in number := null, -- Уровень отбора NLEVEL in number := null, -- Уровень отбора
SSORT_FIELD in varchar2 := 'DREP_DATE_TO', -- Поле сортировки SSORT_FIELD in varchar2 := 'DREP_DATE_TO', -- Поле сортировки
COUT out clob, -- Список задач COUT out clob, -- Список задач
@ -2164,6 +2165,7 @@ create or replace package body PKG_P8PANELS_MECHREC as
procedure FCPRODPLANSP_GET procedure FCPRODPLANSP_GET
( (
NCRN in number, -- Рег. номер каталога NCRN in number, -- Рег. номер каталога
NFCPRODPLANSP in number, -- Рег. номер позиции спецификации
NLEVEL in number := null, -- Уровень отбора NLEVEL in number := null, -- Уровень отбора
SSORT_FIELD in varchar2 := 'DREP_DATE_TO', -- Поле сортировки SSORT_FIELD in varchar2 := 'DREP_DATE_TO', -- Поле сортировки
COUT out clob, -- Список задач COUT out clob, -- Список задач
@ -2210,7 +2212,8 @@ create or replace package body PKG_P8PANELS_MECHREC as
/* Считывание максимального уровня иерархии плана по каталогу */ /* Считывание максимального уровня иерархии плана по каталогу */
function PRODPLAN_MAX_LEVEL_GET function PRODPLAN_MAX_LEVEL_GET
( (
NCRN in number -- Рег. номер каталога планов NCRN in number, -- Рег. номер каталога планов
NFCPRODPLANSP in number -- Рег. номер позиции спецификации
) return number -- Максимальный уровень иерархии ) return number -- Максимальный уровень иерархии
is is
NRESULT PKG_STD.TNUMBER := 1; -- Максимальный уровень иерархии NRESULT PKG_STD.TNUMBER := 1; -- Максимальный уровень иерархии
@ -2224,7 +2227,10 @@ create or replace package body PKG_P8PANELS_MECHREC as
from FCPRODPLAN P, from FCPRODPLAN P,
FCPRODPLANSP T, FCPRODPLANSP T,
FINSTATE FS FINSTATE FS
where P.CRN = NCRN where ((NCRN is null) or ((NCRN is not null) and (P.CRN = NCRN)))
and ((NFCPRODPLANSP is null) or
((NFCPRODPLANSP is not null) and
(P.RN = (select PRN from FCPRODPLANSP where RN = NFCPRODPLANSP))))
and P.CATEGORY = NFCPRODPLAN_CATEGORY and P.CATEGORY = NFCPRODPLAN_CATEGORY
and P.STATUS = NFCPRODPLAN_STATUS and P.STATUS = NFCPRODPLAN_STATUS
and FS.RN = P.TYPE and FS.RN = P.TYPE
@ -2248,7 +2254,8 @@ create or replace package body PKG_P8PANELS_MECHREC as
and T.PRN = P.RN and T.PRN = P.RN
and T.MAIN_QUANT > 0) TMP and T.MAIN_QUANT > 0) TMP
connect by prior TMP.RN = TMP.UP_LEVEL connect by prior TMP.RN = TMP.UP_LEVEL
start with TMP.UP_LEVEL is null start with (((NCRN is not null) and (TMP.UP_LEVEL is null)) or
((NCRN is null) and (TMP.RN = NFCPRODPLANSP)))
group by level group by level
order by level) order by level)
loop loop
@ -2603,12 +2610,36 @@ create or replace package body PKG_P8PANELS_MECHREC as
end GET_TASK_COLORS; end GET_TASK_COLORS;
begin begin
/* Определяем заголовок плана */ /* Определяем заголовок плана */
FIND_ACATALOG_RN(NFLAG_SMART => 0, if (NCRN is not null) then
NCOMPANY => NCOMPANY, FIND_ACATALOG_RN(NFLAG_SMART => 0,
NVERSION => null, NCOMPANY => NCOMPANY,
SUNITCODE => 'CostProductPlans', NVERSION => null,
NRN => NCRN, SUNITCODE => 'CostProductPlans',
SNAME => SPLAN_TITLE); NRN => NCRN,
SNAME => SPLAN_TITLE);
else
if (NFCPRODPLANSP is not null) then
begin
select MR.CODE || ' - ' || MR.NAME || ', ' || T.REL_FACT || ' ' || DM.MEAS_MNEMO || ', ' ||
COALESCE(TO_CHAR(T.REP_DATE_TO, 'dd.mm.yyyy'), '<ДАТА ВЫПУСКА НЕ УКАЗА>')
into SPLAN_TITLE
from FCPRODPLANSP T,
FCMATRESOURCE MR,
DICNOMNS DN,
DICMUNTS DM
where T.RN = NFCPRODPLANSP
and T.MATRES = MR.RN
and MR.NOMENCLATURE = DN.RN
and DN.UMEAS_MAIN = DM.RN;
exception
when NO_DATA_FOUND then
PKG_MSG.RECORD_NOT_FOUND(NFLAG_SMART => 0, NDOCUMENT => NFCPRODPLANSP, SUNIT_TABLE => 'FCPRODPLANSP');
end;
else
P_EXCEPTION(0,
'Не указан каталог размещения или позиция спецификации плана.');
end if;
end if;
/* Инициализируем диаграмму Ганта */ /* Инициализируем диаграмму Ганта */
RG := PKG_P8PANELS_VISUAL.TGANTT_MAKE(STITLE => SPLAN_TITLE, RG := PKG_P8PANELS_VISUAL.TGANTT_MAKE(STITLE => SPLAN_TITLE,
NZOOM => PKG_P8PANELS_VISUAL.NGANTT_ZOOM_DAY, NZOOM => PKG_P8PANELS_VISUAL.NGANTT_ZOOM_DAY,
@ -2619,7 +2650,7 @@ create or replace package body PKG_P8PANELS_MECHREC as
/* Инициализируем описания цветов */ /* Инициализируем описания цветов */
TASK_COLORS_INIT(RG => RG); TASK_COLORS_INIT(RG => RG);
/* Определяем максимальный уровень иерархии */ /* Определяем максимальный уровень иерархии */
NMAX_LEVEL := PRODPLAN_MAX_LEVEL_GET(NCRN => NCRN); NMAX_LEVEL := PRODPLAN_MAX_LEVEL_GET(NCRN => NCRN, NFCPRODPLANSP => NFCPRODPLANSP);
/* Определяем уровень фильтра */ /* Определяем уровень фильтра */
NLEVEL_FILTER := COALESCE(NLEVEL, NMAX_LEVEL); NLEVEL_FILTER := COALESCE(NLEVEL, NMAX_LEVEL);
/* Обходим данные */ /* Обходим данные */
@ -2662,7 +2693,10 @@ create or replace package body PKG_P8PANELS_MECHREC as
FCMATRESOURCE FM, FCMATRESOURCE FM,
DICNOMNS D, DICNOMNS D,
DICMUNTS DM DICMUNTS DM
where P.CRN = NCRN where ((NCRN is null) or ((NCRN is not null) and (P.CRN = NCRN)))
and ((NFCPRODPLANSP is null) or
((NFCPRODPLANSP is not null) and
(P.RN = (select PRN from FCPRODPLANSP where RN = NFCPRODPLANSP))))
and P.CATEGORY = NFCPRODPLAN_CATEGORY and P.CATEGORY = NFCPRODPLAN_CATEGORY
and P.STATUS = NFCPRODPLAN_STATUS and P.STATUS = NFCPRODPLAN_STATUS
and FS.RN = P.TYPE and FS.RN = P.TYPE
@ -2692,7 +2726,8 @@ create or replace package body PKG_P8PANELS_MECHREC as
and D.UMEAS_MAIN = DM.RN) TMP and D.UMEAS_MAIN = DM.RN) TMP
where level <= NLEVEL_FILTER where level <= NLEVEL_FILTER
connect by prior TMP.NRN = TMP.NUP_LEVEL connect by prior TMP.NRN = TMP.NUP_LEVEL
start with TMP.NUP_LEVEL is null start with (((NCRN is not null) and (TMP.NUP_LEVEL is null)) or
((NCRN is null) and (TMP.NRN = NFCPRODPLANSP)))
order siblings by TMP.DORDER_DATE asc) order siblings by TMP.DORDER_DATE asc)
loop loop
/* Формируем описание задачи в Ганте */ /* Формируем описание задачи в Ганте */