ЦИТК-968 - Доработка панели "Производственная программа"

This commit is contained in:
Dollerino 2025-06-24 13:55:27 +03:00
parent 11f29bcf0c
commit 47d6b0cdb1
2 changed files with 367 additions and 106 deletions

View File

@ -36,7 +36,8 @@ import {
Card, Card,
CardHeader, CardHeader,
CardContent, CardContent,
CardActions CardActions,
Tooltip
} 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"; //Контекст сообщений
@ -55,19 +56,34 @@ import { IncomFromDepsDataGrid } from "./datagrids/incomefromdeps"; //Табли
//--------- //---------
//Склонения для документов //Склонения для документов
const DECLINATIONS = ["план", "плана", "планов"]; const PLANS_DECLINATIONS = ["план", "плана", "планов"];
const SPEC_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 MAX_TASKS = 10000;
//Стили //Стили
const STYLES = { const STYLES = {
PLANS_FINDER: { marginTop: "10px", marginLeft: "10px", width: "93%" }, PLANS_FINDER: { marginTop: "10px", marginLeft: "10px", width: "93%" },
PLANS_CHECKBOX_HAVEDOCS: { alignContent: "space-around" }, PLANS_CHECKBOX_HAVEDOCS: { alignContent: "space-around" },
PLANS_LIST_CONTAINER: { height: "100%", display: "flex", flexDirection: "column", justifyContent: "space-between" },
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_LIST_ITEM_PLAN: {
backgroundColor: "#c7e7f1",
"&:hover": { backgroundColor: `#c7e7f1`, filter: "brightness(0.92) !important" }
},
PLANS_LIST_ITEM_PLAN_FIELD: {
marginLeft: "15px"
},
PLANS_LIST_FILTER_CONTAINER: { height: "calc(100% - 55px)", overflowY: "auto" },
PLANS_LIST_BUTTONS_CONTAINER: { display: "flex", justifyContent: "space-around", paddingBottom: "10px", height: "45px" },
PLANS_LIST_BUTTON: { minWidth: "125px", height: "35px" },
PLANS_BUTTON: { position: "absolute", top: `calc(${APP_BAR_HEIGHT} + 16px)`, left: "16px" }, PLANS_BUTTON: { position: "absolute", top: `calc(${APP_BAR_HEIGHT} + 16px)`, left: "16px" },
PLANS_DRAWER: { PLANS_DRAWER: {
width: "350px", width: "350px",
@ -121,7 +137,9 @@ const parseProdPlanSpXML = async xmlDoc => {
}; };
//Форматирование для отображения количества документов //Форматирование для отображения количества документов
const formatCountDocs = nCountDocs => { const formatCountDocs = (nCountDocs, nType = 0) => {
//Склонение документов
let DECLINATIONS = nType === 0 ? PLANS_DECLINATIONS : SPEC_DECLINATIONS;
//Получаем последнюю цифру в значении //Получаем последнюю цифру в значении
let num = (nCountDocs % 100) % 10; let num = (nCountDocs % 100) % 10;
//Документов //Документов
@ -134,55 +152,146 @@ const formatCountDocs = nCountDocs => {
return `${nCountDocs} ${DECLINATIONS[2]}`; return `${nCountDocs} ${DECLINATIONS[2]}`;
}; };
//Изменение информации об отмеченных планах
const updateCtlgPlanInfo = (selectedPlans, plan) => {
//Результат изменений
let res = { selectedPlans: [...selectedPlans] || [], selectedPlansElements: plan.NCOUNT_DOCS };
//Определяем наличие в отмеченных планах
let selectedIndex = res.selectedPlans.indexOf(plan.NRN);
//Если уже есть в отмеченных - удаляем, нет - добавляем
if (selectedIndex > -1) {
//Удаляем план из выбранных
res.selectedPlans.splice(selectedIndex, 1);
//Переворачиваем сумму документов
res.selectedPlansElements = res.selectedPlansElements * -1;
} else {
//Добавляем план в выбранные
res.selectedPlans.push(plan.NRN);
}
//Возвращаем результат
return res;
};
//Список каталогов планов //Список каталогов планов
const PlanCtlgsList = ({ planCtlgs = [], selectedPlanCtlg, filter, setFilter, onClick } = {}) => { const PlanCtlgsList = ({
planCtlgs = [],
selectedPlans = [],
selectedPlanCtlg,
selectedPlansElements,
filter,
setFilter,
onCtlgClick,
onCtlgPlanClick,
onCtlgPlansOk,
onCtlgPlansCancel
} = {}) => {
//Генерация содержимого //Генерация содержимого
return ( return (
<div> <Box sx={STYLES.PLANS_LIST_CONTAINER}>
<TextField <Box sx={STYLES.PLANS_LIST_FILTER_CONTAINER}>
sx={STYLES.PLANS_FINDER} <TextField
name="planFilter" sx={STYLES.PLANS_FINDER}
label="Каталог" name="planFilter"
value={filter.ctlgName} label="Каталог"
variant="standard" value={filter.ctlgName}
fullWidth variant="standard"
onChange={event => { fullWidth
setFilter(pv => ({ ...pv, ctlgName: event.target.value })); onChange={event => {
}} setFilter(pv => ({ ...pv, ctlgName: event.target.value }));
></TextField> }}
<FormGroup sx={STYLES.PLANS_CHECKBOX_HAVEDOCS}> ></TextField>
<FormControlLabel <FormGroup sx={STYLES.PLANS_CHECKBOX_HAVEDOCS}>
control={<Checkbox checked={filter.haveDocs} onChange={event => setFilter(pv => ({ ...pv, haveDocs: event.target.checked }))} />} <FormControlLabel
label="Только с планами" control={
labelPlacement="end" <Checkbox checked={filter.haveDocs} onChange={event => setFilter(pv => ({ ...pv, haveDocs: event.target.checked }))} />
/> }
</FormGroup> label="Только с планами"
<List> labelPlacement="end"
{planCtlgs.map(p => ( />
<ListItemButton </FormGroup>
sx={p.NCOUNT_DOCS == 0 ? STYLES.PLANS_LIST_ITEM_ZERODOCS : null} <List>
key={p.NRN} {planCtlgs.map(ctlg => (
selected={p.NRN === selectedPlanCtlg} <Box key={ctlg.NRN}>
onClick={() => (onClick ? onClick(p) : null)} <ListItemButton
> sx={ctlg.NCOUNT_DOCS == 0 ? STYLES.PLANS_LIST_ITEM_ZERODOCS : null}
<ListItemText key={ctlg.NRN}
primary={<Typography sx={STYLES.PLANS_LIST_ITEM_PRIMARY}>{p.SNAME}</Typography>} selected={ctlg.NRN === selectedPlanCtlg}
secondary={<Typography sx={STYLES.PLANS_LIST_ITEM_SECONDARY}>{formatCountDocs(p.NCOUNT_DOCS)}</Typography>} onClick={() => (onCtlgClick ? onCtlgClick(ctlg) : null)}
/> disabled={ctlg.NCOUNT_DOCS == 0}
</ListItemButton> >
))} <ListItemText
</List> primary={<Typography sx={STYLES.PLANS_LIST_ITEM_PRIMARY}>{ctlg.SNAME}</Typography>}
</div> secondary={<Typography sx={STYLES.PLANS_LIST_ITEM_SECONDARY}>{formatCountDocs(ctlg.NCOUNT_DOCS, 0)}</Typography>}
/>
</ListItemButton>
{ctlg.NRN === selectedPlanCtlg && ctlg.XCRN_PLANS.length > 1
? ctlg.XCRN_PLANS.map(plan => (
<ListItemButton
sx={plan.NCOUNT_DOCS == 0 ? STYLES.PLANS_LIST_ITEM_ZERODOCS : STYLES.PLANS_LIST_ITEM_PLAN}
key={plan.NRN}
disabled={plan.NCOUNT_DOCS == 0}
onClick={() => (onCtlgPlanClick ? onCtlgPlanClick(plan) : null)}
>
<ListItemText
sx={STYLES.PLANS_LIST_ITEM_PLAN_FIELD}
primary={<Typography sx={STYLES.PLANS_LIST_ITEM_PRIMARY}>{plan.SNAME}</Typography>}
secondary={
<Typography sx={STYLES.PLANS_LIST_ITEM_SECONDARY}>
{formatCountDocs(plan.NCOUNT_DOCS, 1)}
</Typography>
}
/>
{plan.NCOUNT_DOCS !== 0 ? <Checkbox checked={selectedPlans.includes(plan.NRN)} /> : null}
</ListItemButton>
))
: null}
</Box>
))}
</List>
</Box>
<Box sx={STYLES.PLANS_LIST_BUTTONS_CONTAINER}>
<Tooltip
title={
!selectedPlanCtlg
? "Не выбран каталог планов"
: selectedPlans.length === 0
? "Не выбраны планы каталога"
: selectedPlansElements > MAX_TASKS
? `Выбранные планы превышают максимум элементов (выбрано: ${selectedPlansElements}, максимум: ${MAX_TASKS})`
: null
}
>
<Box>
<Button
sx={STYLES.PLANS_LIST_BUTTON}
variant="contained"
disabled={selectedPlans.length === 0 || selectedPlansElements > MAX_TASKS}
onClick={onCtlgPlansOk}
>
Применить
</Button>
</Box>
</Tooltip>
<Button sx={STYLES.PLANS_LIST_BUTTON} variant="contained" onClick={onCtlgPlansCancel}>
Отмена
</Button>
</Box>
</Box>
); );
}; };
//Контроль свойств - Список каталогов планов //Контроль свойств - Список каталогов планов
PlanCtlgsList.propTypes = { PlanCtlgsList.propTypes = {
planCtlgs: PropTypes.array, planCtlgs: PropTypes.array,
selectedPlans: PropTypes.array,
selectedPlanCtlg: PropTypes.number, selectedPlanCtlg: PropTypes.number,
onClick: PropTypes.func, selectedPlansElements: PropTypes.number,
onCtlgClick: PropTypes.func,
onCtlgPlanClick: PropTypes.func,
filter: PropTypes.object, filter: PropTypes.object,
setFilter: PropTypes.func setFilter: PropTypes.func,
onCtlgPlansOk: PropTypes.func,
onCtlgPlansCancel: PropTypes.func
}; };
//Генерация диалога задачи //Генерация диалога задачи
@ -251,6 +360,8 @@ const MechRecCostProdPlans = () => {
showPlanList: false, showPlanList: false,
planCtlgs: [], planCtlgs: [],
planCtlgsLoaded: false, planCtlgsLoaded: false,
selectedPlans: [],
selectedPlansElements: 0,
selectedPlanCtlgSpecsLoaded: false, selectedPlanCtlgSpecsLoaded: false,
selectedPlanCtlg: null, selectedPlanCtlg: null,
selectedPlanCtlgMaxLevel: null, selectedPlanCtlgMaxLevel: null,
@ -258,6 +369,9 @@ const MechRecCostProdPlans = () => {
selectedPlanCtlgOutOfLimit: 0, selectedPlanCtlgOutOfLimit: 0,
selectedPlanCtlgSort: null, selectedPlanCtlgSort: null,
selectedPlanCtlgMenuItems: null, selectedPlanCtlgMenuItems: null,
loadedCtlg: null,
loadedPlans: [],
loadedElements: 0,
gantt: {}, gantt: {},
selectedTaskDetail: null, selectedTaskDetail: null,
selectedTaskDetailType: null, selectedTaskDetailType: null,
@ -273,7 +387,7 @@ const MechRecCostProdPlans = () => {
const { InlineMsgInfo } = useContext(MessagingСtx); const { InlineMsgInfo } = useContext(MessagingСtx);
//Подключение к контексту взаимодействия с сервером //Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx); const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
//Подключение к контексту навигации //Подключение к контексту навигации
const { getNavigationSearch } = useContext(NavigationCtx); const { getNavigationSearch } = useContext(NavigationCtx);
@ -288,54 +402,28 @@ const MechRecCostProdPlans = () => {
stored: "PKG_P8PANELS_MECHREC.FCPRODPLAN_PP_CTLG_INIT", stored: "PKG_P8PANELS_MECHREC.FCPRODPLAN_PP_CTLG_INIT",
args: {}, args: {},
respArg: "COUT", respArg: "COUT",
isArray: name => name === "XFCPRODPLAN_CRNS" isArray: name => ["XFCPRODPLAN_CRNS", "XCRN_PLANS"].includes(name)
}); });
setState(pv => ({ ...pv, init: true, planCtlgs: [...(data?.XFCPRODPLAN_CRNS || [])], planCtlgsLoaded: true })); setState(pv => ({ ...pv, init: true, planCtlgs: [...(data?.XFCPRODPLAN_CRNS || [])], planCtlgsLoaded: true }));
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [state.init, executeStored]); }, [state.init, executeStored]);
//Выбор каталога планов
const selectPlan = project => {
setState(pv => ({
...pv,
selectedPlanCtlg: project,
selectedPlanCtlgSpecsLoaded: false,
selectedPlanCtlgMaxLevel: null,
selectedPlanCtlgLevel: null,
selectedPlanCtlgOutOfLimit: 0,
selectedPlanCtlgSort: null,
selectedPlanCtlgMenuItems: null,
gantt: {},
showPlanList: false,
selectedTaskDetail: null,
selectedTaskDetailType: null
}));
};
//Сброс выбора каталога планов
const unselectPlan = () =>
setState(pv => ({
...pv,
selectedPlanCtlgSpecsLoaded: false,
selectedPlanCtlg: null,
selectedPlanCtlgMaxLevel: null,
selectedPlanCtlgLevel: null,
selectedPlanCtlgOutOfLimit: 0,
selectedPlanCtlgSort: null,
selectedPlanCtlgMenuItems: null,
gantt: {},
showPlanList: false,
selectedTaskDetail: null,
selectedTaskDetailType: null
}));
//Загрузка списка спецификаций каталога планов //Загрузка списка спецификаций каталога планов
const loadPlanCtglSpecs = useCallback( const loadPlanCtglSpecs = useCallback(
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, NFCPRODPLANSP: state.planSpec } args: {
NCRN: state.selectedPlanCtlg,
CFCPRODPLANS: {
VALUE: state.selectedPlans.length > 0 ? state.selectedPlans.join(";") : null,
SDATA_TYPE: SERV_DATA_TYPE_CLOB
},
NLEVEL: level,
SSORT_FIELD: sort,
NFCPRODPLANSP: state.planSpec
}
}); });
let doc = await parseProdPlanSpXML(data.COUT); let doc = await parseProdPlanSpXML(data.COUT);
setState(pv => ({ setState(pv => ({
@ -344,21 +432,71 @@ const MechRecCostProdPlans = () => {
selectedPlanCtlgLevel: level || level === 0 ? level : data.NMAX_LEVEL, selectedPlanCtlgLevel: level || level === 0 ? level : data.NMAX_LEVEL,
selectedPlanCtlgOutOfLimit: data.NOUT_OF_LIMIT, selectedPlanCtlgOutOfLimit: data.NOUT_OF_LIMIT,
selectedPlanCtlgSort: sort, selectedPlanCtlgSort: sort,
selectedPlanCtlgMenuItems: state.selectedPlanCtlgMenuItems selectedPlanCtlgMenuItems: [...Array(data.NMAX_LEVEL).keys()].map(el => el + 1),
? state.selectedPlanCtlgMenuItems
: [...Array(data.NMAX_LEVEL).keys()].map(el => el + 1),
selectedPlanCtlgSpecsLoaded: true, selectedPlanCtlgSpecsLoaded: true,
gantt: { ...doc, tasks: [...(doc?.tasks || [])] } gantt: { ...doc, tasks: [...(doc?.tasks || [])] },
loadedCtlg: state.selectedPlanCtlg,
loadedPlans: [...state.selectedPlans],
loadedElements: state.selectedPlansElements,
showPlanList: false
})); }));
}, },
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
[executeStored, state.ident, state.selectedPlanCtlg, state.planSpec] [executeStored, state.selectedPlanCtlg, state.selectedPlans, state.planSpec]
); );
//Обработка нажатия на элемент в списке каталогов планов //Обработка нажатия на элемент в списке каталогов планов
const handleProjectClick = project => { const handleCtlgClick = project => {
if (state.selectedPlanCtlg != project.NRN) selectPlan(project.NRN); //Если этот каталог не был выбран
else unselectPlan(); if (state.selectedPlanCtlg != project.NRN) {
//Если выбран уже загруженный - укажем информацию о том, как он загружен
if (project.NRN === state.loadedCtlg) {
setState(pv => ({
...pv,
selectedPlanCtlg: project.NRN,
selectedPlans: [...pv.loadedPlans],
selectedPlansElements: pv.loadedElements
}));
} else {
setState(pv => ({
...pv,
selectedPlanCtlg: project.NRN,
selectedPlans: project.XCRN_PLANS.length === 1 ? [project.XCRN_PLANS[0].NRN] : [],
selectedPlansElements: 0
}));
}
} else {
setState(pv => ({ ...pv, selectedPlanCtlg: null, selectedPlans: [], selectedPlansElements: 0 }));
}
};
//Обработка нажатия на элемент в списке планов каталога
const handleCtlgPlanClick = plan => {
//Считываем обновленную информацию об отмеченных планах
let newPlansInfo = updateCtlgPlanInfo(state.selectedPlans, plan);
//Обновляем список отмеченных планов
setState(pv => ({
...pv,
selectedPlans: [...newPlansInfo.selectedPlans],
selectedPlansElements: pv.selectedPlansElements + newPlansInfo.selectedPlansElements
}));
};
//Обработка нажатия "ОК" при отборе планов
const handleSelectedPlansOk = () => {
//Загружаем диаграмму
loadPlanCtglSpecs(null, SORT_REP_DATE_TO);
};
//Обработка нажатия "Отмена" при отборе планов
const handleSelectedPlansCancel = () => {
setState(pv => ({
...pv,
selectedPlanCtlg: pv.loadedCtlg,
selectedPlans: [...pv.loadedPlans] || [],
selectedPlansElements: pv.loadedElements,
showPlanList: false
}));
}; };
//При подключении компонента к странице //При подключении компонента к странице
@ -371,8 +509,8 @@ const MechRecCostProdPlans = () => {
//При смене выбранного каталога плана или при явном указании позиции спецификации плана //При смене выбранного каталога плана или при явном указании позиции спецификации плана
useEffect(() => { useEffect(() => {
if (state.selectedPlanCtlg || state.planSpec) loadPlanCtglSpecs(null, SORT_REP_DATE_TO); if (state.planSpec) loadPlanCtglSpecs(null, SORT_REP_DATE_TO);
}, [state.selectedPlanCtlg, state.planSpec, loadPlanCtglSpecs]); }, [state.planSpec, loadPlanCtglSpecs]);
//Выбор уровня //Выбор уровня
const handleChangeSelectLevel = selectedLevel => { const handleChangeSelectLevel = selectedLevel => {
@ -415,18 +553,18 @@ const MechRecCostProdPlans = () => {
<Fab variant="extended" sx={STYLES.PLANS_BUTTON} onClick={() => setState(pv => ({ ...pv, showPlanList: !pv.showPlanList }))}> <Fab variant="extended" sx={STYLES.PLANS_BUTTON} onClick={() => setState(pv => ({ ...pv, showPlanList: !pv.showPlanList }))}>
Каталоги планов Каталоги планов
</Fab> </Fab>
<Drawer <Drawer anchor={"left"} open={state.showPlanList} onClose={handleSelectedPlansCancel} sx={STYLES.PLANS_DRAWER}>
anchor={"left"}
open={state.showPlanList}
onClose={() => setState(pv => ({ ...pv, showPlanList: false }))}
sx={STYLES.PLANS_DRAWER}
>
<PlanCtlgsList <PlanCtlgsList
planCtlgs={filteredPlanCtgls} planCtlgs={filteredPlanCtgls}
selectedPlans={state.selectedPlans}
selectedPlanCtlg={state.selectedPlanCtlg} selectedPlanCtlg={state.selectedPlanCtlg}
selectedPlansElements={state.selectedPlansElements}
filter={filter} filter={filter}
setFilter={setFilter} setFilter={setFilter}
onClick={handleProjectClick} onCtlgClick={handleCtlgClick}
onCtlgPlanClick={handleCtlgPlanClick}
onCtlgPlansOk={handleSelectedPlansOk}
onCtlgPlansCancel={handleSelectedPlansCancel}
/> />
</Drawer> </Drawer>
</> </>
@ -508,14 +646,14 @@ const MechRecCostProdPlans = () => {
/> />
</Box> </Box>
) )
) : !state.selectedPlanCtlg ? ( ) : !state.loadedCtlg ? (
<Box pt={3}> <Box pt={3}>
<InlineMsgInfo <InlineMsgInfo
okBtn={false} okBtn={false}
text={ text={
state.planSpec state.planSpec
? "Загружаю график для выбранной позиции плана..." ? "Загружаю график для выбранной позиции плана..."
: "Укажите каталог планов для отображения их спецификаций" : "Укажите каталог планов или планы для отображения их спецификаций"
} }
/> />
</Box> </Box>

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, -- Рег. номер каталога
CFCPRODPLANS in clob, -- Список отмеченных планов (разделитель - ";")
NFCPRODPLANSP 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', -- Поле сортировки
@ -513,6 +514,28 @@ create or replace package body PKG_P8PANELS_MECHREC as
end; end;
end UTL_FCROUTLST_GET; end UTL_FCROUTLST_GET;
/* Иницализация выбранных планов */
procedure UTL_FCPRODPLAN_IDENT_INIT
(
CFCPRODPLANS in clob, -- Список отмеченных планов (разделитель - ";")
NIDENT out number -- Идентификатор отмеченных записей
)
is
NFCPRODPLAN PKG_STD.TREF; -- Рег. номер плана
NTMP PKG_STD.TREF; -- Буфер
begin
/* Генерируем идентификатор */
NIDENT := GEN_IDENT();
/* Обходим исполнителей */
for I in 1 .. STRCNT(source => CFCPRODPLANS, DELIMETER => ';')
loop
/* Считываем рег. номер плана */
NFCPRODPLAN := TO_NUMBER(STRTOK(source => CFCPRODPLANS, DELIMETER => ';', ITEM => I));
/* Добавляем в селектлист */
P_SELECTLIST_INSERT(NIDENT => NIDENT, NDOCUMENT => NFCPRODPLAN, SUNITCODE => 'CostProductPlans', NRN => NTMP);
end loop;
end UTL_FCPRODPLAN_IDENT_INIT;
/* Считывания рег. номера подразделения пользователя */ /* Считывания рег. номера подразделения пользователя */
function UTL_SUBDIV_RN_GET function UTL_SUBDIV_RN_GET
( (
@ -2259,6 +2282,7 @@ create or replace package body PKG_P8PANELS_MECHREC as
procedure FCPRODPLANSP_GET procedure FCPRODPLANSP_GET
( (
NCRN in number, -- Рег. номер каталога NCRN in number, -- Рег. номер каталога
CFCPRODPLANS in clob, -- Список отмеченных планов (разделитель - ";")
NFCPRODPLANSP 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', -- Поле сортировки
@ -2284,6 +2308,7 @@ create or replace package body PKG_P8PANELS_MECHREC as
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
NTASK_CLASS PKG_STD.TNUMBER; -- Класс задачи (см. константы NCLASS_*) NTASK_CLASS PKG_STD.TNUMBER; -- Класс задачи (см. константы NCLASS_*)
NLEVEL_FILTER PKG_STD.TNUMBER; -- Уровень для фильтра NLEVEL_FILTER PKG_STD.TNUMBER; -- Уровень для фильтра
NPLANS_IDENT PKG_STD.TREF; -- Идентификатор отмеченных планов
/* Объединение значений в строковое представление */ /* Объединение значений в строковое представление */
function MAKE_INFO function MAKE_INFO
@ -2308,6 +2333,7 @@ create or replace package body PKG_P8PANELS_MECHREC as
procedure PRODPLAN_MAX_LEVEL_GET procedure PRODPLAN_MAX_LEVEL_GET
( (
NCRN in number, -- Рег. номер каталога планов NCRN in number, -- Рег. номер каталога планов
NPLANS_IDENT in number, -- Идентификатор отмеченных планов
NFCPRODPLANSP in number, -- Рег. номер позиции спецификации NFCPRODPLANSP in number, -- Рег. номер позиции спецификации
NMAX_LEVEL out number, -- Максимальный уровень иерархии NMAX_LEVEL out number, -- Максимальный уровень иерархии
NOUT_OF_LIMIT out number -- Признак превышения лимита (0 - нет, 1 - да) NOUT_OF_LIMIT out number -- Признак превышения лимита (0 - нет, 1 - да)
@ -2328,6 +2354,12 @@ create or replace package body PKG_P8PANELS_MECHREC as
FCPRODPLANSP T, FCPRODPLANSP T,
FINSTATE FS FINSTATE FS
where ((NCRN is null) or ((NCRN is not null) and (P.CRN = NCRN))) where ((NCRN is null) or ((NCRN is not null) and (P.CRN = NCRN)))
and ((NPLANS_IDENT is null) or
((NPLANS_IDENT is not null) and
(P.RN in (select SL.DOCUMENT
from SELECTLIST SL
where SL.IDENT = NPLANS_IDENT
and SL.UNITCODE = 'CostProductPlans'))))
and ((NFCPRODPLANSP is null) or and ((NFCPRODPLANSP is null) or
((NFCPRODPLANSP is not null) and ((NFCPRODPLANSP is not null) and
(P.RN = (select PRN from FCPRODPLANSP where RN = NFCPRODPLANSP)))) (P.RN = (select PRN from FCPRODPLANSP where RN = NFCPRODPLANSP))))
@ -2711,12 +2743,18 @@ create or replace package body PKG_P8PANELS_MECHREC as
begin begin
/* Определяем заголовок плана */ /* Определяем заголовок плана */
if (NCRN is not null) then if (NCRN is not null) then
/* Считываем каталог */
FIND_ACATALOG_RN(NFLAG_SMART => 0, FIND_ACATALOG_RN(NFLAG_SMART => 0,
NCOMPANY => NCOMPANY, NCOMPANY => NCOMPANY,
NVERSION => null, NVERSION => null,
SUNITCODE => 'CostProductPlans', SUNITCODE => 'CostProductPlans',
NRN => NCRN, NRN => NCRN,
SNAME => SPLAN_TITLE); SNAME => SPLAN_TITLE);
/* Если есть выбранные планы */
if (CFCPRODPLANS is not null) then
/* Инициализируем отмеченные планы в селектлисте */
UTL_FCPRODPLAN_IDENT_INIT(CFCPRODPLANS => CFCPRODPLANS, NIDENT => NPLANS_IDENT);
end if;
else else
if (NFCPRODPLANSP is not null) then if (NFCPRODPLANSP is not null) then
begin begin
@ -2750,9 +2788,10 @@ create or replace package body PKG_P8PANELS_MECHREC as
/* Инициализируем описания цветов */ /* Инициализируем описания цветов */
TASK_COLORS_INIT(RG => RG); TASK_COLORS_INIT(RG => RG);
/* Определяем максимальный уровень иерархии */ /* Определяем максимальный уровень иерархии */
PRODPLAN_MAX_LEVEL_GET(NCRN => NCRN, PRODPLAN_MAX_LEVEL_GET(NCRN => NCRN,
NFCPRODPLANSP => NFCPRODPLANSP, NPLANS_IDENT => NPLANS_IDENT,
NMAX_LEVEL => NMAX_LEVEL, NFCPRODPLANSP => NFCPRODPLANSP,
NMAX_LEVEL => NMAX_LEVEL,
NOUT_OF_LIMIT => NOUT_OF_LIMIT); NOUT_OF_LIMIT => NOUT_OF_LIMIT);
/* Определяем уровень фильтра */ /* Определяем уровень фильтра */
NLEVEL_FILTER := COALESCE(NLEVEL, NMAX_LEVEL); NLEVEL_FILTER := COALESCE(NLEVEL, NMAX_LEVEL);
@ -2797,6 +2836,12 @@ create or replace package body PKG_P8PANELS_MECHREC as
DICNOMNS D, DICNOMNS D,
DICMUNTS DM DICMUNTS DM
where ((NCRN is null) or ((NCRN is not null) and (P.CRN = NCRN))) where ((NCRN is null) or ((NCRN is not null) and (P.CRN = NCRN)))
and ((NPLANS_IDENT is null) or
((NPLANS_IDENT is not null) and
(P.RN in (select SL.DOCUMENT
from SELECTLIST SL
where SL.IDENT = NPLANS_IDENT
and SL.UNITCODE = 'CostProductPlans'))))
and ((NFCPRODPLANSP is null) or and ((NFCPRODPLANSP is null) or
((NFCPRODPLANSP is not null) and ((NFCPRODPLANSP is not null) and
(P.RN = (select PRN from FCPRODPLANSP where RN = NFCPRODPLANSP)))) (P.RN = (select PRN from FCPRODPLANSP where RN = NFCPRODPLANSP))))
@ -2917,14 +2962,48 @@ create or replace package body PKG_P8PANELS_MECHREC as
end loop; end loop;
/* Формируем список */ /* Формируем список */
COUT := PKG_P8PANELS_VISUAL.TGANTT_TO_XML(RGANTT => RG); COUT := PKG_P8PANELS_VISUAL.TGANTT_TO_XML(RGANTT => RG);
/* Очищаем отмеченные планы */
P_SELECTLIST_CLEAR(NIDENT => NPLANS_IDENT);
exception
when others then
/* Очищаем отмеченные планы */
P_SELECTLIST_CLEAR(NIDENT => NPLANS_IDENT);
raise;
end FCPRODPLANSP_GET; end FCPRODPLANSP_GET;
/* Инициализация каталогов раздела "Планы и отчеты производства изделий" для панели "Производственная программа" */ /* Инициализация каталогов раздела "Планы и отчеты производства изделий" для панели "Производственная программа" */
procedure FCPRODPLAN_PP_CTLG_INIT procedure FCPRODPLAN_PP_CTLG_INIT
( (
COUT out clob -- Список каталогов раздела "Планы и отчеты производства изделий" COUT out clob -- Список каталогов раздела "Планы и отчеты производства изделий"
) )
is is
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
/* Считывание количества спецификаций плана */
function FCPRODPLANSP_COUNT_GET
(
NFCPRODPLAN in number -- Рег. номер плана
) return number -- Количество документов спецификации плана
is
NRESULT PKG_STD.TNUMBER; -- Количество документов спецификации плана
begin
/* Считываем количество спецификаций */
begin
select count(T.RN)
into NRESULT
from FCPRODPLAN P,
FCPRODPLANSP T,
FINSTATE FS
where P.RN = NFCPRODPLAN
and T.PRN = P.RN
and T.MAIN_QUANT > 0;
exception
when others then
NRESULT := 0;
end;
/* Возвращаем результат */
return NRESULT;
end FCPRODPLANSP_COUNT_GET;
begin begin
/* Начинаем формирование XML */ /* Начинаем формирование XML */
PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_); PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
@ -2939,7 +3018,7 @@ create or replace package body PKG_P8PANELS_MECHREC as
where P.CRN = T.RN where P.CRN = T.RN
and P.CATEGORY = NFCPRODPLAN_CATEGORY and P.CATEGORY = NFCPRODPLAN_CATEGORY
and P.STATUS = NFCPRODPLAN_STATUS and P.STATUS = NFCPRODPLAN_STATUS
and P.COMPANY = GET_SESSION_COMPANY() and P.COMPANY = NCOMPANY
and FS.RN = P.TYPE and FS.RN = P.TYPE
and FS.CODE = SFCPRODPLAN_TYPE and FS.CODE = SFCPRODPLAN_TYPE
and exists (select PSP.RN and exists (select PSP.RN
@ -2967,7 +3046,7 @@ create or replace package body PKG_P8PANELS_MECHREC as
where T.DOCNAME = 'CostProductPlans' where T.DOCNAME = 'CostProductPlans'
and T.SIGNS = 1 and T.SIGNS = 1
and T.DOCNAME = UL.UNITCODE and T.DOCNAME = UL.UNITCODE
and T.COMPANY = GET_SESSION_COMPANY() and T.COMPANY = NCOMPANY
and (UL.SHOW_INACCESS_CTLG = 1 or exists and (UL.SHOW_INACCESS_CTLG = 1 or exists
(select null from V_USERPRIV UP where UP.CATALOG = T.RN) or exists (select null from V_USERPRIV UP where UP.CATALOG = T.RN) or exists
(select null (select null
@ -2977,12 +3056,56 @@ create or replace package body PKG_P8PANELS_MECHREC as
start with T1.CRN = T.RN)) start with T1.CRN = T.RN))
order by T.NAME asc) order by T.NAME asc)
loop loop
/* Открываем план */ /* Открываем каталог плана */
PKG_XFAST.DOWN_NODE(SNAME => 'XFCPRODPLAN_CRNS'); PKG_XFAST.DOWN_NODE(SNAME => 'XFCPRODPLAN_CRNS');
/* Описываем план */ /* Описываем каталог */
PKG_XFAST.ATTR(SNAME => 'NRN', NVALUE => REC.NRN); PKG_XFAST.ATTR(SNAME => 'NRN', NVALUE => REC.NRN);
PKG_XFAST.ATTR(SNAME => 'SNAME', SVALUE => REC.SNAME); PKG_XFAST.ATTR(SNAME => 'SNAME', SVALUE => REC.SNAME);
PKG_XFAST.ATTR(SNAME => 'NCOUNT_DOCS', NVALUE => REC.NCOUNT_DOCS); PKG_XFAST.ATTR(SNAME => 'NCOUNT_DOCS', NVALUE => REC.NCOUNT_DOCS);
/* Если содержит планы */
if (REC.NCOUNT_DOCS <> 0) then
/* Цикл по планам каталога */
for PLN in (select T.RN NRN,
trim(T.PREFIX) || '-' || trim(T.NUMB) SNAME
from FCPRODPLAN T,
FINSTATE FS
where T.CRN = REC.NRN
and T.CATEGORY = NFCPRODPLAN_CATEGORY
and T.STATUS = NFCPRODPLAN_STATUS
and T.COMPANY = NCOMPANY
and FS.RN = T.TYPE
and FS.CODE = SFCPRODPLAN_TYPE
and exists (select PSP.RN
from FCPRODPLANSP PSP
where PSP.PRN = T.RN
and PSP.MAIN_QUANT > 0)
and exists (select /*+ INDEX(UP I_USERPRIV_JUR_PERS_ROLEID) */
null
from USERPRIV UP
where UP.JUR_PERS = T.JUR_PERS
and UP.UNITCODE = 'CostProductPlans'
and UP.ROLEID in (select /*+ INDEX(UR I_USERROLES_AUTHID_FK) */
UR.ROLEID
from USERROLES UR
where UR.AUTHID = UTILIZER())
union all
select /*+ INDEX(UP I_USERPRIV_JUR_PERS_AUTHID) */
null
from USERPRIV UP
where UP.JUR_PERS = T.JUR_PERS
and UP.UNITCODE = 'CostProductPlans'
and UP.AUTHID = UTILIZER()))
loop
/* Открываем описание документов каталога */
PKG_XFAST.DOWN_NODE(SNAME => 'XCRN_PLANS');
/* Описываем план */
PKG_XFAST.ATTR(SNAME => 'NRN', NVALUE => PLN.NRN);
PKG_XFAST.ATTR(SNAME => 'SNAME', SVALUE => PLN.SNAME);
PKG_XFAST.ATTR(SNAME => 'NCOUNT_DOCS', NVALUE => FCPRODPLANSP_COUNT_GET(NFCPRODPLAN => PLN.NRN));
/* Закрываем документы каталога */
PKG_XFAST.UP();
end loop;
end if;
/* Закрываем план */ /* Закрываем план */
PKG_XFAST.UP(); PKG_XFAST.UP();
end loop; end loop;