ЦИТК-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,
CardHeader,
CardContent,
CardActions
CardActions,
Tooltip
} from "@mui/material"; //Интерфейсные элементы
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
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_TO = "DREP_DATE_TO";
//Максимальное количество элементов
const MAX_TASKS = 10000;
//Стили
const STYLES = {
PLANS_FINDER: { marginTop: "10px", marginLeft: "10px", width: "93%" },
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_PRIMARY: { wordWrap: "break-word" },
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_DRAWER: {
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;
//Документов
@ -134,11 +152,43 @@ const formatCountDocs = nCountDocs => {
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 (
<div>
<Box sx={STYLES.PLANS_LIST_CONTAINER}>
<Box sx={STYLES.PLANS_LIST_FILTER_CONTAINER}>
<TextField
sx={STYLES.PLANS_FINDER}
name="planFilter"
@ -152,37 +202,96 @@ const PlanCtlgsList = ({ planCtlgs = [], selectedPlanCtlg, filter, setFilter, on
></TextField>
<FormGroup sx={STYLES.PLANS_CHECKBOX_HAVEDOCS}>
<FormControlLabel
control={<Checkbox checked={filter.haveDocs} onChange={event => setFilter(pv => ({ ...pv, haveDocs: event.target.checked }))} />}
control={
<Checkbox checked={filter.haveDocs} onChange={event => setFilter(pv => ({ ...pv, haveDocs: event.target.checked }))} />
}
label="Только с планами"
labelPlacement="end"
/>
</FormGroup>
<List>
{planCtlgs.map(p => (
{planCtlgs.map(ctlg => (
<Box key={ctlg.NRN}>
<ListItemButton
sx={p.NCOUNT_DOCS == 0 ? STYLES.PLANS_LIST_ITEM_ZERODOCS : null}
key={p.NRN}
selected={p.NRN === selectedPlanCtlg}
onClick={() => (onClick ? onClick(p) : null)}
sx={ctlg.NCOUNT_DOCS == 0 ? STYLES.PLANS_LIST_ITEM_ZERODOCS : null}
key={ctlg.NRN}
selected={ctlg.NRN === selectedPlanCtlg}
onClick={() => (onCtlgClick ? onCtlgClick(ctlg) : null)}
disabled={ctlg.NCOUNT_DOCS == 0}
>
<ListItemText
primary={<Typography sx={STYLES.PLANS_LIST_ITEM_PRIMARY}>{p.SNAME}</Typography>}
secondary={<Typography sx={STYLES.PLANS_LIST_ITEM_SECONDARY}>{formatCountDocs(p.NCOUNT_DOCS)}</Typography>}
primary={<Typography sx={STYLES.PLANS_LIST_ITEM_PRIMARY}>{ctlg.SNAME}</Typography>}
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>
</div>
</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 = {
planCtlgs: PropTypes.array,
selectedPlans: PropTypes.array,
selectedPlanCtlg: PropTypes.number,
onClick: PropTypes.func,
selectedPlansElements: PropTypes.number,
onCtlgClick: PropTypes.func,
onCtlgPlanClick: PropTypes.func,
filter: PropTypes.object,
setFilter: PropTypes.func
setFilter: PropTypes.func,
onCtlgPlansOk: PropTypes.func,
onCtlgPlansCancel: PropTypes.func
};
//Генерация диалога задачи
@ -251,6 +360,8 @@ const MechRecCostProdPlans = () => {
showPlanList: false,
planCtlgs: [],
planCtlgsLoaded: false,
selectedPlans: [],
selectedPlansElements: 0,
selectedPlanCtlgSpecsLoaded: false,
selectedPlanCtlg: null,
selectedPlanCtlgMaxLevel: null,
@ -258,6 +369,9 @@ const MechRecCostProdPlans = () => {
selectedPlanCtlgOutOfLimit: 0,
selectedPlanCtlgSort: null,
selectedPlanCtlgMenuItems: null,
loadedCtlg: null,
loadedPlans: [],
loadedElements: 0,
gantt: {},
selectedTaskDetail: null,
selectedTaskDetailType: null,
@ -273,7 +387,7 @@ const MechRecCostProdPlans = () => {
const { InlineMsgInfo } = useContext(MessagingСtx);
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
//Подключение к контексту навигации
const { getNavigationSearch } = useContext(NavigationCtx);
@ -288,54 +402,28 @@ const MechRecCostProdPlans = () => {
stored: "PKG_P8PANELS_MECHREC.FCPRODPLAN_PP_CTLG_INIT",
args: {},
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 }));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [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(
async (level = null, sort = null) => {
const data = await executeStored({
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);
setState(pv => ({
@ -344,21 +432,71 @@ const MechRecCostProdPlans = () => {
selectedPlanCtlgLevel: level || level === 0 ? level : data.NMAX_LEVEL,
selectedPlanCtlgOutOfLimit: data.NOUT_OF_LIMIT,
selectedPlanCtlgSort: sort,
selectedPlanCtlgMenuItems: state.selectedPlanCtlgMenuItems
? state.selectedPlanCtlgMenuItems
: [...Array(data.NMAX_LEVEL).keys()].map(el => el + 1),
selectedPlanCtlgMenuItems: [...Array(data.NMAX_LEVEL).keys()].map(el => el + 1),
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
[executeStored, state.ident, state.selectedPlanCtlg, state.planSpec]
[executeStored, state.selectedPlanCtlg, state.selectedPlans, state.planSpec]
);
//Обработка нажатия на элемент в списке каталогов планов
const handleProjectClick = project => {
if (state.selectedPlanCtlg != project.NRN) selectPlan(project.NRN);
else unselectPlan();
const handleCtlgClick = project => {
//Если этот каталог не был выбран
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(() => {
if (state.selectedPlanCtlg || state.planSpec) loadPlanCtglSpecs(null, SORT_REP_DATE_TO);
}, [state.selectedPlanCtlg, state.planSpec, loadPlanCtglSpecs]);
if (state.planSpec) loadPlanCtglSpecs(null, SORT_REP_DATE_TO);
}, [state.planSpec, loadPlanCtglSpecs]);
//Выбор уровня
const handleChangeSelectLevel = selectedLevel => {
@ -415,18 +553,18 @@ const MechRecCostProdPlans = () => {
<Fab variant="extended" sx={STYLES.PLANS_BUTTON} onClick={() => setState(pv => ({ ...pv, showPlanList: !pv.showPlanList }))}>
Каталоги планов
</Fab>
<Drawer
anchor={"left"}
open={state.showPlanList}
onClose={() => setState(pv => ({ ...pv, showPlanList: false }))}
sx={STYLES.PLANS_DRAWER}
>
<Drawer anchor={"left"} open={state.showPlanList} onClose={handleSelectedPlansCancel} sx={STYLES.PLANS_DRAWER}>
<PlanCtlgsList
planCtlgs={filteredPlanCtgls}
selectedPlans={state.selectedPlans}
selectedPlanCtlg={state.selectedPlanCtlg}
selectedPlansElements={state.selectedPlansElements}
filter={filter}
setFilter={setFilter}
onClick={handleProjectClick}
onCtlgClick={handleCtlgClick}
onCtlgPlanClick={handleCtlgPlanClick}
onCtlgPlansOk={handleSelectedPlansOk}
onCtlgPlansCancel={handleSelectedPlansCancel}
/>
</Drawer>
</>
@ -508,14 +646,14 @@ const MechRecCostProdPlans = () => {
/>
</Box>
)
) : !state.selectedPlanCtlg ? (
) : !state.loadedCtlg ? (
<Box pt={3}>
<InlineMsgInfo
okBtn={false}
text={
state.planSpec
? "Загружаю график для выбранной позиции плана..."
: "Укажите каталог планов для отображения их спецификаций"
: "Укажите каталог планов или планы для отображения их спецификаций"
}
/>
</Box>

View File

@ -81,6 +81,7 @@ create or replace package PKG_P8PANELS_MECHREC as
procedure FCPRODPLANSP_GET
(
NCRN in number, -- Рег. номер каталога
CFCPRODPLANS in clob, -- Список отмеченных планов (разделитель - ";")
NFCPRODPLANSP in number, -- Рег. номер позиции спецификации
NLEVEL in number := null, -- Уровень отбора
SSORT_FIELD in varchar2 := 'DREP_DATE_TO', -- Поле сортировки
@ -513,6 +514,28 @@ create or replace package body PKG_P8PANELS_MECHREC as
end;
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
(
@ -2259,6 +2282,7 @@ create or replace package body PKG_P8PANELS_MECHREC as
procedure FCPRODPLANSP_GET
(
NCRN in number, -- Рег. номер каталога
CFCPRODPLANS in clob, -- Список отмеченных планов (разделитель - ";")
NFCPRODPLANSP in number, -- Рег. номер позиции спецификации
NLEVEL in number := null, -- Уровень отбора
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(); -- Организация сеанса
NTASK_CLASS PKG_STD.TNUMBER; -- Класс задачи (см. константы NCLASS_*)
NLEVEL_FILTER PKG_STD.TNUMBER; -- Уровень для фильтра
NPLANS_IDENT PKG_STD.TREF; -- Идентификатор отмеченных планов
/* Объединение значений в строковое представление */
function MAKE_INFO
@ -2308,6 +2333,7 @@ create or replace package body PKG_P8PANELS_MECHREC as
procedure PRODPLAN_MAX_LEVEL_GET
(
NCRN in number, -- Рег. номер каталога планов
NPLANS_IDENT in number, -- Идентификатор отмеченных планов
NFCPRODPLANSP in number, -- Рег. номер позиции спецификации
NMAX_LEVEL out number, -- Максимальный уровень иерархии
NOUT_OF_LIMIT out number -- Признак превышения лимита (0 - нет, 1 - да)
@ -2328,6 +2354,12 @@ create or replace package body PKG_P8PANELS_MECHREC as
FCPRODPLANSP T,
FINSTATE FS
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
((NFCPRODPLANSP is not null) and
(P.RN = (select PRN from FCPRODPLANSP where RN = NFCPRODPLANSP))))
@ -2711,12 +2743,18 @@ create or replace package body PKG_P8PANELS_MECHREC as
begin
/* Определяем заголовок плана */
if (NCRN is not null) then
/* Считываем каталог */
FIND_ACATALOG_RN(NFLAG_SMART => 0,
NCOMPANY => NCOMPANY,
NVERSION => null,
SUNITCODE => 'CostProductPlans',
NRN => NCRN,
SNAME => SPLAN_TITLE);
/* Если есть выбранные планы */
if (CFCPRODPLANS is not null) then
/* Инициализируем отмеченные планы в селектлисте */
UTL_FCPRODPLAN_IDENT_INIT(CFCPRODPLANS => CFCPRODPLANS, NIDENT => NPLANS_IDENT);
end if;
else
if (NFCPRODPLANSP is not null) then
begin
@ -2751,6 +2789,7 @@ create or replace package body PKG_P8PANELS_MECHREC as
TASK_COLORS_INIT(RG => RG);
/* Определяем максимальный уровень иерархии */
PRODPLAN_MAX_LEVEL_GET(NCRN => NCRN,
NPLANS_IDENT => NPLANS_IDENT,
NFCPRODPLANSP => NFCPRODPLANSP,
NMAX_LEVEL => NMAX_LEVEL,
NOUT_OF_LIMIT => NOUT_OF_LIMIT);
@ -2797,6 +2836,12 @@ create or replace package body PKG_P8PANELS_MECHREC as
DICNOMNS D,
DICMUNTS DM
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
((NFCPRODPLANSP is not null) and
(P.RN = (select PRN from FCPRODPLANSP where RN = NFCPRODPLANSP))))
@ -2917,6 +2962,13 @@ create or replace package body PKG_P8PANELS_MECHREC as
end loop;
/* Формируем список */
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;
/* Инициализация каталогов раздела "Планы и отчеты производства изделий" для панели "Производственная программа" */
@ -2925,6 +2977,33 @@ create or replace package body PKG_P8PANELS_MECHREC as
COUT out clob -- Список каталогов раздела "Планы и отчеты производства изделий"
)
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
/* Начинаем формирование XML */
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
and P.CATEGORY = NFCPRODPLAN_CATEGORY
and P.STATUS = NFCPRODPLAN_STATUS
and P.COMPANY = GET_SESSION_COMPANY()
and P.COMPANY = NCOMPANY
and FS.RN = P.TYPE
and FS.CODE = SFCPRODPLAN_TYPE
and exists (select PSP.RN
@ -2967,7 +3046,7 @@ create or replace package body PKG_P8PANELS_MECHREC as
where T.DOCNAME = 'CostProductPlans'
and T.SIGNS = 1
and T.DOCNAME = UL.UNITCODE
and T.COMPANY = GET_SESSION_COMPANY()
and T.COMPANY = NCOMPANY
and (UL.SHOW_INACCESS_CTLG = 1 or exists
(select null from V_USERPRIV UP where UP.CATALOG = T.RN) or exists
(select null
@ -2977,12 +3056,56 @@ create or replace package body PKG_P8PANELS_MECHREC as
start with T1.CRN = T.RN))
order by T.NAME asc)
loop
/* Открываем план */
/* Открываем каталог плана */
PKG_XFAST.DOWN_NODE(SNAME => 'XFCPRODPLAN_CRNS');
/* Описываем план */
/* Описываем каталог */
PKG_XFAST.ATTR(SNAME => 'NRN', NVALUE => REC.NRN);
PKG_XFAST.ATTR(SNAME => 'SNAME', SVALUE => REC.SNAME);
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();
end loop;