From 140e255904633576c9134d858f64111a168275d8 Mon Sep 17 00:00:00 2001 From: Dollerino Date: Wed, 8 May 2024 15:50:34 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A6=D0=98=D0=A2=D0=9A-826,=20=D0=A6=D0=98?= =?UTF-8?q?=D0=A2=D0=9A-827,=20=D0=A6=D0=98=D0=A2=D0=9A-828,=20=D0=A6?= =?UTF-8?q?=D0=98=D0=A2=D0=9A-841=20-=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=BE=D0=B2=D1=8B=D1=85?= =?UTF-8?q?=20=D0=BF=D0=B0=D0=BD=D0=B5=D0=BB=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/panels/mech_rec_assembly_mon/backend.js | 341 +++++++++ .../mech_rec_assembly_mon/blocks/cardBlock.js | 102 +++ .../blocks/cardDetail.js | 289 ++++++++ .../elements/progressBox.js | 76 ++ app/panels/mech_rec_assembly_mon/hooks.js | 27 + app/panels/mech_rec_assembly_mon/index.js | 16 + .../mech_rec_assembly_mon.js | 226 ++++++ .../mech_rec_assembly_mon/styles/themes.js | 66 ++ .../mech_rec_cost_jobs_manage/backend.js | 337 +++++++++ .../mech_rec_cost_jobs_manage/fcjobssp.js | 422 +++++++++++ app/panels/mech_rec_cost_jobs_manage/hooks.js | 23 + app/panels/mech_rec_cost_jobs_manage/index.js | 16 + .../mech_rec_cost_jobs_manage.js | 206 ++++++ app/panels/mech_rec_dept_cost_jobs/index.js | 16 + .../mech_rec_dept_cost_jobs.js | 153 ++++ .../fcroutlst.js | 328 +++++++++ .../fcroutlstord.js | 124 ++++ .../mech_rec_dept_cost_prod_plans/hooks.js | 23 + .../incomefromdeps.js | 120 +++ .../mech_rec_dept_cost_prod_plans/index.js | 16 + .../mech_rec_dept_cost_prod_plans.js | 445 +++++++++++ db/PKG_P8PANELS_MECHREC.pck | 695 +++++++++++++++++- p8panels.config | 44 ++ 23 files changed, 4107 insertions(+), 4 deletions(-) create mode 100644 app/panels/mech_rec_assembly_mon/backend.js create mode 100644 app/panels/mech_rec_assembly_mon/blocks/cardBlock.js create mode 100644 app/panels/mech_rec_assembly_mon/blocks/cardDetail.js create mode 100644 app/panels/mech_rec_assembly_mon/elements/progressBox.js create mode 100644 app/panels/mech_rec_assembly_mon/hooks.js create mode 100644 app/panels/mech_rec_assembly_mon/index.js create mode 100644 app/panels/mech_rec_assembly_mon/mech_rec_assembly_mon.js create mode 100644 app/panels/mech_rec_assembly_mon/styles/themes.js create mode 100644 app/panels/mech_rec_cost_jobs_manage/backend.js create mode 100644 app/panels/mech_rec_cost_jobs_manage/fcjobssp.js create mode 100644 app/panels/mech_rec_cost_jobs_manage/hooks.js create mode 100644 app/panels/mech_rec_cost_jobs_manage/index.js create mode 100644 app/panels/mech_rec_cost_jobs_manage/mech_rec_cost_jobs_manage.js create mode 100644 app/panels/mech_rec_dept_cost_jobs/index.js create mode 100644 app/panels/mech_rec_dept_cost_jobs/mech_rec_dept_cost_jobs.js create mode 100644 app/panels/mech_rec_dept_cost_prod_plans/fcroutlst.js create mode 100644 app/panels/mech_rec_dept_cost_prod_plans/fcroutlstord.js create mode 100644 app/panels/mech_rec_dept_cost_prod_plans/hooks.js create mode 100644 app/panels/mech_rec_dept_cost_prod_plans/incomefromdeps.js create mode 100644 app/panels/mech_rec_dept_cost_prod_plans/index.js create mode 100644 app/panels/mech_rec_dept_cost_prod_plans/mech_rec_dept_cost_prod_plans.js diff --git a/app/panels/mech_rec_assembly_mon/backend.js b/app/panels/mech_rec_assembly_mon/backend.js new file mode 100644 index 0000000..59f1cda --- /dev/null +++ b/app/panels/mech_rec_assembly_mon/backend.js @@ -0,0 +1,341 @@ +//--------------------- +//Подключение библиотек +//--------------------- + +import { useState, useCallback, useEffect, useContext } from "react"; //Классы React +import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером +import { object2Base64XML } from "../../core/utils"; //Вспомогательные функции + +//--------- +//Константы +//--------- + +//Размер страницы данных +const DATA_GRID_PAGE_SIZE = 10; + +//----------- +//Тело модуля +//----------- + +//Хук для основной таблицы панели +const useMechRecAssemblyMon = () => { + //Собственное состояние + let [state, setState] = useState({ + init: false, + showPlanList: false, + planCtlgs: [], + planCtlgsLoaded: false, + selectedPlanCtlg: { NRN: null, SNAME: null, NMIN_YEAR: null, NMAX_YEAR: null }, + plans: [], + plansLoaded: false, + selectedPlan: { NRN: null, SNUMB: null, NPROGRESS: null, SDETAIL: null, NYEAR: null } + }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //Инициализация каталогов планов + const initPlanCtlgs = useCallback(async () => { + if (!state.init) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCPRODPLAN_CTLG_INIT", + args: {}, + respArg: "COUT", + isArray: name => name === "XFCPRODPLAN_CRNS" + }); + setState(pv => ({ ...pv, init: true, planCtlgs: [...(data?.XFCPRODPLAN_CRNS || [])], planCtlgsLoaded: true })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [state.init, executeStored]); + + //Получение информации о планах каталога + const loadPlans = useCallback( + async NCRN => { + if (NCRN) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCPRODPLAN_GET", + args: { NCRN: NCRN }, + respArg: "COUT", + isArray: name => name === "XFCPRODPLAN_INFO" + }); + setState(pv => ({ ...pv, init: true, plans: [...(data?.XFCPRODPLAN_INFO || [])], plansLoaded: true })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, + [executeStored] + ); + + //Выбор каталога планов + const selectPlan = project => { + setState(pv => ({ + ...pv, + selectedPlanCtlg: project, + showPlanList: false + })); + }; + + //Сброс выбора каталога планов + const unselectPlan = () => + setState(pv => ({ + ...pv, + selectedPlanCtlg: { NRN: null, SNAME: null, NMIN_YEAR: null, NMAX_YEAR: null }, + showPlanList: false + })); + + //При подключении компонента к странице + useEffect(() => { + initPlanCtlgs(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + //При изменении каталога + useEffect(() => { + //Если каталог выбран + if (state.selectedPlanCtlg) { + loadPlans(state.selectedPlanCtlg.NRN); + } else { + setState(pv => ({ ...pv, plans: [], plansLoaded: false })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [state.selectedPlanCtlg]); + + return [state, setState, selectPlan, unselectPlan]; +}; + +//Хук для информации по производственным составам +const useCostProductComposition = nProdPlan => { + //Собственное состояние + let [costProductComposition, setCostProductComposition] = useState({ + init: false, + showPlanList: false, + products: [], + selectedProduct: null + }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //Инициализация производственных составов + const initCostProductComposition = useCallback(async () => { + if (!costProductComposition.init) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCPRODCMP_DETAILS_GET", + args: { NFCPRODPLAN: nProdPlan }, + respArg: "COUT", + isArray: name => name === "XFCPRODCMP" + }); + setCostProductComposition(pv => ({ ...pv, init: true, products: [...(data?.XFCPRODCMP || [])], productsLoaded: true })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [costProductComposition.init, executeStored]); + + //При подключении компонента к странице + useEffect(() => { + initCostProductComposition(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return [costProductComposition, setCostProductComposition]; +}; + +//Хук для таблицы маршрутных листов +const useCostRouteLists = (plan, product) => { + //Собственное состояние - таблица данных + const [costRouteLists, setCostRouteLists] = useState({ + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true, + selectedProduct: null + }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //Загрузка данных таблицы с сервера + const loadData = useCallback( + async () => { + if (costRouteLists.reload) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCROUTLST_MON_DG_GET", + args: { + NPRODCMPSP: product, + NFCPRODPLAN: plan, + CORDERS: { VALUE: object2Base64XML(costRouteLists.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + NPAGE_NUMBER: costRouteLists.pageNumber, + NPAGE_SIZE: DATA_GRID_PAGE_SIZE, + NINCLUDE_DEF: costRouteLists.dataLoaded ? 0 : 1 + }, + respArg: "COUT" + }); + setCostRouteLists(pv => ({ + ...pv, + columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef, + rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])], + dataLoaded: true, + reload: false, + morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE + })); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [costRouteLists.reload, costRouteLists.orders, costRouteLists.dataLoaded, costRouteLists.pageNumber, executeStored, SERV_DATA_TYPE_CLOB] + ); + + //При изменении продукта + useEffect(() => { + //Если продукт указан + if (product) { + //Принудительно обновляем состояние + setCostRouteLists(pv => ({ + ...pv, + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true, + selectedProduct: null + })); + //Загружаем данные с учетом выбранного продукта + loadData(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [product]); + + //При необходимости обновить данные таблицы + useEffect(() => { + //Если продукт указан и необходимо стандартное обновление + if (product) { + loadData(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [costRouteLists.reload, loadData]); + + //При изменении плана + useEffect(() => { + setCostRouteLists(pv => ({ + ...pv, + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true, + selectedProduct: null + })); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [plan]); + + return [costRouteLists, setCostRouteLists]; +}; + +//Хук для таблицы комплектовочных ведомостей +const useCostDeliverySheets = (plan, product) => { + //Собственное состояние - таблица данных + const [costDeliverySheets, setCostDeliverySheets] = useState({ + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true, + selectedProduct: null + }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //Загрузка данных таблицы с сервера + const loadData = useCallback( + async () => { + if (costDeliverySheets.reload) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCDELIVSH_DG_GET", + args: { + NPRODCMPSP: product, + NFCPRODPLAN: plan, + CORDERS: { VALUE: object2Base64XML(costDeliverySheets.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + NPAGE_NUMBER: costDeliverySheets.pageNumber, + NPAGE_SIZE: DATA_GRID_PAGE_SIZE, + NINCLUDE_DEF: costDeliverySheets.dataLoaded ? 0 : 1 + }, + respArg: "COUT" + }); + setCostDeliverySheets(pv => ({ + ...pv, + columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef, + rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])], + dataLoaded: true, + reload: false, + morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE + })); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [ + costDeliverySheets.reload, + costDeliverySheets.orders, + costDeliverySheets.dataLoaded, + costDeliverySheets.pageNumber, + executeStored, + SERV_DATA_TYPE_CLOB + ] + ); + + //При изменении продукта + useEffect(() => { + //Если продукт указан + if (product) { + //Принудительно обновляем состояние + setCostDeliverySheets(pv => ({ + ...pv, + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true + })); + //Загружаем данные с учетом выбранного продукта + loadData(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [product]); + + //При необходимости обновить данные таблицы + useEffect(() => { + //Если продукт указан и необходимо стандартное обновление + if (product) { + loadData(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [costDeliverySheets.reload, loadData]); + + //При изменении плана + useEffect(() => { + setCostDeliverySheets(pv => ({ + ...pv, + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true, + selectedProduct: null + })); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [plan]); + + return [costDeliverySheets, setCostDeliverySheets]; +}; + +export { useMechRecAssemblyMon, useCostProductComposition, useCostRouteLists, useCostDeliverySheets }; diff --git a/app/panels/mech_rec_assembly_mon/blocks/cardBlock.js b/app/panels/mech_rec_assembly_mon/blocks/cardBlock.js new file mode 100644 index 0000000..fc1d05d --- /dev/null +++ b/app/panels/mech_rec_assembly_mon/blocks/cardBlock.js @@ -0,0 +1,102 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий + Панель мониторинга: Информация об объекте +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Typography, Box, ImageList, ImageListItem } from "@mui/material"; //Интерфейсные элементы +import { ProgressBox } from "../elements/progressBox"; //Блок информации по прогрессу объекта + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + PLAN_INFO: { + display: "flex", + justifyContent: "center", + alignItems: "center", + flexDirection: "column", + gap: "24px", + border: "1px solid", + borderRadius: "25px" + } +}; + +//------------------------------------ +//Вспомогательные функции и компоненты +//------------------------------------ + +//Картинка объекта +const CardImage = ({ card }) => { + return ( + + + + {"Image + {/* {"Image */} + + + + ); +}; + +//Контроль свойств - Заголовок первого уровня +CardImage.propTypes = { + card: PropTypes.object +}; + +//----------- +//Тело модуля +//----------- + +//Информация об объекте +const CardBlock = ({ card, handleCardClick }) => { + return ( + handleCardClick(card)}> + + + + Номер борта + + {card.SNUMB} + + + + + Год выпуска: + + + {card.NYEAR} + + + + ); +}; + +//Контроль свойств - Заголовок первого уровня +CardBlock.propTypes = { + card: PropTypes.object, + handleCardClick: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { CardBlock }; diff --git a/app/panels/mech_rec_assembly_mon/blocks/cardDetail.js b/app/panels/mech_rec_assembly_mon/blocks/cardDetail.js new file mode 100644 index 0000000..3304045 --- /dev/null +++ b/app/panels/mech_rec_assembly_mon/blocks/cardDetail.js @@ -0,0 +1,289 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий + Панель мониторинга: Детализация по объекту +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Box, Grid, Container, Button, Typography } from "@mui/material"; //Интерфейсные элементы +import { ProgressBox } from "../elements/progressBox"; //Блок информации по прогрессу объекта +import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../../components/p8p_data_grid"; //Таблица данных +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../config_wrapper"; //Подключение компонентов к настройкам приложения +import { useCostProductComposition, useCostRouteLists, useCostDeliverySheets } from "../backend"; //Компоненты панели + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + TABLE_INFO_MAIN: { + display: "flex", + justifyContent: "center", + alignItems: "center", + flexDirection: "column", + border: "1px solid", + borderRadius: "25px", + height: "35vh" + }, + TABLE_INFO_SUB: { + margin: "21.6px 0px", + maxHeight: "100%", + overflow: "auto", + textAlign: "center", + width: "100%" + }, + DETAIL_INFO: { + display: "flex", + justifyContent: "space-around", + alignItems: "center", + border: "1px solid", + borderRadius: "25px", + height: "17vh" + }, + PRODUCT_SELECTOR: { + display: "flex", + justifyContent: "center", + alignItems: "center", + flexDirection: "column", + border: "1px solid", + borderRadius: "25px", + height: "53vh", + marginTop: "16px" + }, + PLAN_INFO_MAIN: { + display: "flex", + flexDirection: "column", + gap: "16px" + }, + PLAN_INFO_SUB: { + display: "flex", + justifyContent: "space-between", + width: "280px", + borderBottom: "1px solid" + } +}; + +//------------------------------------ +//Вспомогательные функции и компоненты +//------------------------------------ + +//Информация об объекте +const CardDetailInfo = ({ cardInfo }) => { + return ( + <> + + + + Номер борта: + + {cardInfo.SNUMB} + + + + Год выпуска: + + {cardInfo.NYEAR} + + + + + ); +}; + +//Контроль свойств - Информация об объекте +CardDetailInfo.propTypes = { + cardInfo: PropTypes.object +}; + +//Детали объекта +const CardSelector = ({ products, setCostProductComposition }) => { + //При выборе детали в SVG + const handleProductClick = product => { + setCostProductComposition(pv => ({ ...pv, selectedProduct: product })); + }; + + return ( + <> + + {products.map(el => ( + + ))} + + + ); +}; + +//Контроль свойств - Детали объекта +CardSelector.propTypes = { + products: PropTypes.array, + setCostProductComposition: PropTypes.func +}; + +//Генерация представления ячейки заголовка +const headCellRender = ({ columnDef }) => { + //Описываем общий стиль + let cellStyle = { padding: "2px 5px", fontSize: "12px", textAlign: "center", lineHeight: "1rem" }; + let stackProps = { justifyContent: "center" }; + //Дополнительные свойства + switch (columnDef.name) { + case "NREMN_LABOUR": + //Добавляем максимальную ширину + cellStyle = { ...cellStyle, maxWidth: "90px" }; + break; + case "NDEFICIT": + //Добавляем максимальную ширину + cellStyle = { ...cellStyle, maxWidth: "55px" }; + break; + case "NAPPLICABILITY": + //Добавляем максимальную ширину + cellStyle = { ...cellStyle, maxWidth: "90px" }; + break; + default: + break; + } + return { + stackProps, + cellStyle + }; +}; + +//Генерация заливки строки исходя от значений +const dataCellRender = ({ row, columnDef }) => { + //Описываем общий стиль + let cellStyle = { padding: "2px 5px", fontSize: "12px" }; + //Для всех кроме содержания и номенклатуры добавляем выравнивание + switch (columnDef.name) { + case "SOPERATION": + break; + case "SNOMEN": + break; + default: + //Добавляем выравнивание + cellStyle = { ...cellStyle, textAlign: "center" }; + break; + } + return { + cellStyle, + data: row[columnDef] + }; +}; + +//----------- +//Тело модуля +//----------- + +//Детализация по объекту +const CardDetail = ({ card, handleBackClick }) => { + //Собственное состояние - данные производственных составов SVG + const [costProductComposition, setCostProductComposition] = useCostProductComposition(card.NRN); + //Собственное состояние - таблица данных маршрутных листов + const [costRouteLists, setCostRouteLists] = useCostRouteLists(card.NRN, costProductComposition.selectedProduct); + //Собственное состояние - таблица данных комплектовочных ведомостей + const [сostDeliverySheets, setСostDeliverySheets] = useCostDeliverySheets(card.NRN, costProductComposition.selectedProduct); + + //При изменении состояния сортировки маршрутных листов + const costRouteListsOrderChanged = ({ orders }) => setCostRouteLists(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true })); + + //При изменении количества отображаемых страниц маршрутных листов + const costRouteListsPagesCountChanged = () => setCostRouteLists(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true })); + + //При изменении состояния сортировки комплектовочных ведомостей + const СostDeliverySheetsOrderChanged = ({ orders }) => setСostDeliverySheets(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true })); + + //При изменении количества отображаемых страниц комплектовочных ведомостей + const СostDeliverySheetsPagesCountChanged = () => setСostDeliverySheets(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true })); + + return ( + + + + + + + {!costRouteLists.dataLoaded ? ( + Выберите агрегат самолёта, чтобы увидеть информацию + ) : costRouteLists.rows.length === 0 ? ( + Нет данных по МК + ) : ( + <> + Маршрутная карта + + + )} + + + + + {!сostDeliverySheets.dataLoaded ? ( + Выберите агрегат самолёта, чтобы увидеть информацию + ) : сostDeliverySheets.rows.length === 0 ? ( + Нет данных по КВ + ) : ( + <> + Дефицит по КВ + + + )} + + + + + + + + + + + + + + ); +}; + +//Контроль свойств - Детализация по объекту +CardDetail.propTypes = { + card: PropTypes.object, + handleBackClick: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { CardDetail }; diff --git a/app/panels/mech_rec_assembly_mon/elements/progressBox.js b/app/panels/mech_rec_assembly_mon/elements/progressBox.js new file mode 100644 index 0000000..ea8b511 --- /dev/null +++ b/app/panels/mech_rec_assembly_mon/elements/progressBox.js @@ -0,0 +1,76 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий + Панель мониторинга: Блок информации по прогрессу объекта +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Typography, Box } from "@mui/material"; //Интерфейсные элементы + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + PROGRESS_INFO: { + display: "flex", + justifyContent: "center", + alignItems: "center", + flexDirection: "column", + margin: "0px 32px", + borderRadius: "50%" + } +}; + +//------------------------------------ +//Вспомогательные функции и компоненты +//------------------------------------ + +//----------- +//Тело модуля +//----------- + +//Детализация по объекту + +//Блок информации по прогрессу объекта +const ProgressBox = ({ prms }) => { + //Инициализируем цвет тени + let boxShadow = null; + //Определяем цвет тени + switch (true) { + case prms.NPROGRESS >= 70: + boxShadow = "0 0 30px #21d21e66"; + break; + case prms.NPROGRESS >= 40: + boxShadow = "0 0 30px #fddd3566"; + break; + case prms.NPROGRESS >= 10: + boxShadow = "0 0 30px #ea5c4966"; + break; + default: + boxShadow = "0 0 30px #d3d3d3"; + } + //Возвращаем блок + return ( + + {`${prms.NPROGRESS}%`} + {prms.SDETAIL} + + ); +}; + +//Контроль свойств - Блок информации по прогрессу объекта +ProgressBox.propTypes = { + prms: PropTypes.object +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { ProgressBox }; diff --git a/app/panels/mech_rec_assembly_mon/hooks.js b/app/panels/mech_rec_assembly_mon/hooks.js new file mode 100644 index 0000000..136c16c --- /dev/null +++ b/app/panels/mech_rec_assembly_mon/hooks.js @@ -0,0 +1,27 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий + Кастомные хуки +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React + +//----------- +//Тело модуля +//----------- + +//Клиентский отбор каталогов по поисковой фразе и наличию планов +export const useFilteredPlanCtlgs = (planCtlgs, filter) => { + const filteredPlanCtlgs = React.useMemo(() => { + return planCtlgs.filter( + catalog => + catalog.SNAME.toString().toLowerCase().includes(filter.ctlgName) && + (filter.haveDocs ? catalog.NCOUNT_DOCS > 0 : catalog.NCOUNT_DOCS >= 0) + ); + }, [planCtlgs, filter]); + + return filteredPlanCtlgs; +}; diff --git a/app/panels/mech_rec_assembly_mon/index.js b/app/panels/mech_rec_assembly_mon/index.js new file mode 100644 index 0000000..083b5ff --- /dev/null +++ b/app/panels/mech_rec_assembly_mon/index.js @@ -0,0 +1,16 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий + Панель мониторинга: Точка входа +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { MechRecAssemblyMon } from "./mech_rec_assembly_mon"; //Корневая панель мониторинга сборки изделий + +//---------------- +//Интерфейс модуля +//---------------- + +export const RootClass = MechRecAssemblyMon; diff --git a/app/panels/mech_rec_assembly_mon/mech_rec_assembly_mon.js b/app/panels/mech_rec_assembly_mon/mech_rec_assembly_mon.js new file mode 100644 index 0000000..2d51675 --- /dev/null +++ b/app/panels/mech_rec_assembly_mon/mech_rec_assembly_mon.js @@ -0,0 +1,226 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий + Панель мониторинга: Корневая панель мониторинга сборки изделий +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useContext } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { + Drawer, + Fab, + Box, + List, + ListItemButton, + ListItemText, + Typography, + Grid, + TextField, + FormGroup, + FormControlLabel, + Checkbox, + Container +} from "@mui/material"; //Интерфейсные элементы +import { ThemeProvider } from "@mui/material/styles"; //Подключение темы +import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений +import { CardBlock } from "./blocks/cardBlock"; //Информация об объекте +import { CardDetail } from "./blocks/cardDetail"; //Детализация по объекту +import { theme } from "./styles/themes.js"; //Стиль темы +import { useFilteredPlanCtlgs } from "./hooks"; //Вспомогательные хуки +import { useMechRecAssemblyMon } from "./backend"; //Хук корневой панели мониторинга сборки изделий + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + PLANS_FINDER: { marginTop: "10px", marginLeft: "10px", width: "93%" }, + PLANS_CHECKBOX_HAVEDOCS: { alignContent: "space-around" }, + 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_BUTTON: { position: "absolute" }, + PLANS_DRAWER: { + width: "350px", + display: "inline-block", + flexShrink: 0, + [`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box" } + } +}; + +//------------------------------------ +//Вспомогательные функции и компоненты +//------------------------------------ + +//Склонения для документов +const DECLINATIONS = ["план", "плана", "планов"]; + +//Форматирование для отображения количества документов +const formatCountDocs = nCountDocs => { + //Получаем последнюю цифру в значении + let num = (nCountDocs % 100) % 10; + //Документов + if (nCountDocs > 10 && nCountDocs < 20) return `${nCountDocs} ${DECLINATIONS[2]}`; + //Документа + if (num > 1 && num < 5) return `${nCountDocs} ${DECLINATIONS[1]}`; + //Документ + if (num == 1) return `${nCountDocs} ${DECLINATIONS[0]}`; + //Документов + return `${nCountDocs} ${DECLINATIONS[2]}`; +}; + +//Список каталогов планов +const PlanCtlgsList = ({ planCtlgs = [], selectedPlanCtlg, filter, setFilter, onClick } = {}) => { + //Генерация содержимого + return ( +
+ { + setFilter(pv => ({ ...pv, ctlgName: event.target.value })); + }} + > + + setFilter(pv => ({ ...pv, haveDocs: event.target.checked }))} />} + label="Только с планами" + labelPlacement="end" + /> + + + {planCtlgs.map(p => ( + (onClick ? onClick({ NRN: p.NRN, SNAME: p.SNAME, NMIN_YEAR: p.NMIN_YEAR, NMAX_YEAR: p.NMAX_YEAR }) : null)} + > + {p.SNAME}} + secondary={{formatCountDocs(p.NCOUNT_DOCS)}} + /> + + ))} + +
+ ); +}; + +//Контроль свойств - Список каталогов планов +PlanCtlgsList.propTypes = { + planCtlgs: PropTypes.array, + selectedPlanCtlg: PropTypes.number, + onClick: PropTypes.func, + filter: PropTypes.object, + setFilter: PropTypes.func +}; + +//----------- +//Тело модуля +//----------- + +//Корневая панель мониторинга сборки изделий +const MechRecAssemblyMon = () => { + //Собственное состояние + const [state, setState, selectPlan, unselectPlan] = useMechRecAssemblyMon(); + + //Состояние для фильтра каталогов + const [filter, setFilter] = useState({ ctlgName: "", haveDocs: false }); + + //Массив отфильтрованных каталогов + const filteredPlanCtgls = useFilteredPlanCtlgs(state.planCtlgs, filter); + + //Подключение к контексту сообщений + const { InlineMsgInfo } = useContext(MessagingСtx); + + //Обработка нажатия на элемент в списке каталогов планов + const handleProjectClick = project => { + if (state.selectedPlanCtlg.NRN != project.NRN) selectPlan(project); + else unselectPlan(); + }; + + //Обработка нажатия на карточку объекта + const handleCardClick = plan => { + setState(pv => ({ + ...pv, + selectedPlan: { NRN: plan.NRN, SNUMB: plan.SNUMB, NPROGRESS: plan.NPROGRESS, SDETAIL: plan.SDETAIL, NYEAR: plan.NYEAR } + })); + }; + + //Обработка нажатия на кнопку "Назад" + const handleBackClick = () => { + setState(pv => ({ ...pv, selectedPlan: { NRN: null, SNUMB: null, NPROGRESS: null, SDETAIL: null, NYEAR: null } })); + }; + + //Генерация содержимого + return ( + + + setState(pv => ({ ...pv, showPlanList: !pv.showPlanList }))}> + Программы + + setState(pv => ({ ...pv, showPlanList: false }))} + sx={STYLES.PLANS_DRAWER} + > + + + {state.init == true ? ( + state.selectedPlanCtlg.NRN ? ( + <> + + {`${state.selectedPlanCtlg.SNAME} на ${state.selectedPlanCtlg.NMIN_YEAR}г. - ${state.selectedPlanCtlg.NMAX_YEAR}г.`} + + {state.plansLoaded == true ? ( + state.selectedPlan.NRN ? ( + + ) : ( + + + {state.plans.map(el => ( + = 5 ? 2.4 : 12 / state.plans.length} + key={el.NRN} + display="flex" + justifyContent="center" + > + + + ))} + + + ) + ) : null} + + ) : ( + + ) + ) : null} + + + ); +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { MechRecAssemblyMon }; diff --git a/app/panels/mech_rec_assembly_mon/styles/themes.js b/app/panels/mech_rec_assembly_mon/styles/themes.js new file mode 100644 index 0000000..37818ee --- /dev/null +++ b/app/panels/mech_rec_assembly_mon/styles/themes.js @@ -0,0 +1,66 @@ +import { createTheme } from "@mui/material/styles"; //Интерфейсные элементы + +//Описание темы +const theme = createTheme({ + palette: { + text: { + secondary: { fontColor: "rgba(0, 0, 0, 0.298)" } + } + }, + typography: { + h1: { + fontSize: "40px", + fontWeight: 400, + textAlign: "center" + }, + h2: { + fontSize: "40px", + fontWeight: 700, + textAlign: "center" + }, + h3: { + fontSize: "30px", + fontWeight: 700, + textAlign: "center" + }, + h4: { + fontSize: "16px", + fontWeight: 400, + textAlign: "center" + }, + subtitle1: { + fontSize: "30px", + fontWeight: 400, + textAlign: "center" + }, + subtitle2: { + fontSize: "20px", + fontWeight: 700, + textAlign: "center" + }, + UDO_body1: { + fontSize: "14px", + fontWeight: 400, + textAlign: "center", + wordWrap: "break-word", + letterSpacing: "0.00938em", + lineHeight: "1.5" + }, + UDO_body2: { + fontSize: "12px", + fontWeight: 400, + whiteSpace: "pre-line", + textAlign: "center", + wordWrap: "break-word", + letterSpacing: "0.00938em", + lineHeight: "1.5" + }, + body3: { + fontSize: "9px", + whiteSpace: "pre-line", + textAlign: "center" + } + } +}); + +export { theme }; diff --git a/app/panels/mech_rec_cost_jobs_manage/backend.js b/app/panels/mech_rec_cost_jobs_manage/backend.js new file mode 100644 index 0000000..ee013e8 --- /dev/null +++ b/app/panels/mech_rec_cost_jobs_manage/backend.js @@ -0,0 +1,337 @@ +//--------------------- +//Подключение библиотек +//--------------------- + +import { useState, useCallback, useEffect, useContext } from "react"; //Классы React +import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером +import { object2Base64XML } from "../../core/utils"; //Вспомогательные функции + +//--------- +//Константы +//--------- + +//Размер страницы данных +const DATA_GRID_PAGE_SIZE = 5; +const DATA_GRID_PAGE_FCEQUIPMENT = 10; + +//--------------------------------------------- +//Вспомогательные функции форматирования данных +//--------------------------------------------- + +//Переиницализация выбранных значений строк (необходимо при сортировке или добавлении записей строк) +const updatingSelected = (rows, selectedRows) => { + //Если полученный массив строк не пустой + if (rows.length > 0 && selectedRows.length > 0) { + //Устанавливаем выбор там, где он был установлен + let updatedRows = rows.map(item => { + if (selectedRows.includes(item.NRN)) { + return { ...item, NSELECT: 1 }; + } else { + return item; + } + }); + return updatedRows; + } + //Возвращаем + return rows; +}; + +//----------- +//Тело модуля +//----------- + +//Хук для таблицы маршрутных листов +const useCostRouteLists = (task, processIdent) => { + //Собственное состояние - таблица данных + const [costRouteLists, setCostRouteLists] = useState({ + task: null, + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + selectedRows: [], + reload: true, + pageNumber: 1, + morePages: true + }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //Загрузка данных таблицы с сервера + const loadData = useCallback(async () => { + if (costRouteLists.reload) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCJOBSSP_FCROUTLST_DG_GET", + args: { + NFCJOBS: task, + CORDERS: { VALUE: object2Base64XML(costRouteLists.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + NPAGE_NUMBER: costRouteLists.pageNumber, + NPAGE_SIZE: DATA_GRID_PAGE_SIZE, + NINCLUDE_DEF: costRouteLists.dataLoaded ? 0 : 1 + }, + respArg: "COUT", + attributeValueProcessor: (name, val) => (["NSELECT"].includes(name) ? val === 1 : val) + }); + setCostRouteLists(pv => ({ + ...pv, + task: task, + columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef, + rows: + pv.pageNumber == 1 + ? updatingSelected([...(data.XROWS || [])], costRouteLists.selectedRows) + : updatingSelected([...pv.rows, ...(data.XROWS || [])], costRouteLists.selectedRows), + dataLoaded: true, + reload: false, + morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE + })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + costRouteLists.reload, + costRouteLists.filters, + costRouteLists.orders, + costRouteLists.dataLoaded, + costRouteLists.pageNumber, + executeStored, + SERV_DATA_TYPE_CLOB + ]); + + //Добавление/удаление записи в селектлисте + const modifySelectList = useCallback( + async prms => { + try { + if (prms.NSELECT) { + await executeStored({ + stored: "PKG_P8PANELS_MECHREC.SELECTLIST_FCROUTLST_ADD", + args: { NIDENT: processIdent, NFCROUTLST: prms.NFCROUTLST } + }); + } else { + await executeStored({ + stored: "PKG_P8PANELS_MECHREC.SELECTLIST_FCROUTLST_DEL", + args: { NIDENT: processIdent, NFCROUTLST: prms.NFCROUTLST } + }); + } + } catch (e) { + throw new Error(e.message); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [executeStored] + ); + + //При необходимости обновить данные таблицы + useEffect(() => { + loadData(); + }, [costRouteLists.reload, loadData]); + + //При изменении сменного задания + useEffect(() => { + setCostRouteLists(pv => ({ + ...pv, + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + selectedRows: [], + reload: true, + pageNumber: 1, + morePages: true + })); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [task]); + + return [costRouteLists, setCostRouteLists, modifySelectList]; +}; + +//Хук для таблицы операций +const useCostJobsSpecs = (task, fcroutlstList, processIdent) => { + //Собственное состояние - таблица данных + const [costJobsSpecs, setCostJobsSpecs] = useState({ + task: null, + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + selectedRows: [], + reload: true, + pageNumber: 1, + morePages: true + }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //Загрузка данных таблицы с сервера + const loadData = useCallback(async () => { + if (costJobsSpecs.reload) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCJOBSSP_DG_GET", + args: { + NFCJOBS: task, + NIDENT: processIdent, + //SFCROUTLST_LIST: fcroutlstList.join(","), + CORDERS: { VALUE: object2Base64XML(costJobsSpecs.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + NPAGE_NUMBER: costJobsSpecs.pageNumber, + NPAGE_SIZE: DATA_GRID_PAGE_SIZE, + NINCLUDE_DEF: costJobsSpecs.dataLoaded ? 0 : 1 + }, + respArg: "COUT", + attributeValueProcessor: (name, val) => (["NSELECT"].includes(name) ? val === 1 : val) + }); + setCostJobsSpecs(pv => ({ + ...pv, + task: task, + columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef, + rows: + pv.pageNumber == 1 + ? updatingSelected([...(data.XROWS || [])], costJobsSpecs.selectedRows) + : updatingSelected([...pv.rows, ...(data.XROWS || [])], costJobsSpecs.selectedRows), + dataLoaded: true, + reload: false, + morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE + })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + costJobsSpecs.reload, + costJobsSpecs.filters, + costJobsSpecs.orders, + costJobsSpecs.dataLoaded, + costJobsSpecs.pageNumber, + executeStored, + SERV_DATA_TYPE_CLOB + ]); + + //Выдача задания + const issueCostJobsSpecs = useCallback( + async prms => { + try { + await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCJOBSSP_ISSUE", + args: { NFCJOBS: prms.NFCJOBS, SFCJOBSSP_LIST: prms.SFCJOBSSP_LIST } + }); + } catch (e) { + throw new Error(e.message); + } + }, + [executeStored] + ); + + //При необходимости обновить данные таблицы + useEffect(() => { + loadData(); + }, [costJobsSpecs.reload, loadData]); + + //При изменении сменного задания + useEffect(() => { + setCostJobsSpecs(pv => ({ + ...pv, + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + selectedRows: [], + reload: true, + pageNumber: 1, + morePages: true + })); + }, [task, fcroutlstList]); + + return [costJobsSpecs, setCostJobsSpecs, issueCostJobsSpecs]; +}; + +//Хук для таблицы рабочих центров +const useCostEquipment = () => { + //Собственное состояние - таблица данных + const [costEquipment, setCostEquipment] = useState({ + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + selectedRows: [], + selectedLoaded: false, + reload: true, + pageNumber: 1, + morePages: true + }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //Загрузка данных таблицы с сервера + const loadData = useCallback(async () => { + if (costEquipment.reload) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCEQUIPMENT_DG_GET", + args: { + CORDERS: { VALUE: object2Base64XML(costEquipment.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + NPAGE_NUMBER: costEquipment.pageNumber, + NPAGE_SIZE: DATA_GRID_PAGE_FCEQUIPMENT, + NINCLUDE_DEF: costEquipment.dataLoaded ? 0 : 1 + }, + respArg: "COUT", + attributeValueProcessor: (name, val) => (["NSELECT"].includes(name) ? val === 1 : val) + }); + setCostEquipment(pv => ({ + ...pv, + columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef, + rows: + pv.pageNumber == 1 + ? updatingSelected([...(data.XROWS || [])], costEquipment.selectedRows) + : updatingSelected([...pv.rows, ...(data.XROWS || [])], costEquipment.selectedRows), + dataLoaded: true, + reload: false, + morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_FCEQUIPMENT + })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + costEquipment.reload, + costEquipment.filters, + costEquipment.orders, + costEquipment.dataLoaded, + costEquipment.pageNumber, + executeStored, + SERV_DATA_TYPE_CLOB + ]); + + //Включение оборудования в операции + const includeCostEquipment = useCallback( + async prms => { + try { + await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCJOBSSP_INC_FCEQUIPMENT", + args: { NFCEQUIPMENT: prms.NFCEQUIPMENT, NFCJOBS: prms.NFCJOBS, SFCJOBSSP_LIST: prms.SFCJOBSSP_LIST } + }); + } catch (e) { + throw new Error(e.message); + } + }, + [executeStored] + ); + + //Исключение оборудования из операции + const excludeCostEquipment = useCallback( + async prms => { + try { + await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCJOBSSP_EXC_FCEQUIPMENT", + args: { NFCEQUIPMENT: prms.NFCEQUIPMENT, NFCJOBS: prms.NFCJOBS, SFCJOBSSP_LIST: prms.SFCJOBSSP_LIST } + }); + } catch (e) { + throw new Error(e.message); + } + }, + [executeStored] + ); + + //При необходимости обновить данные таблицы + useEffect(() => { + loadData(); + }, [costEquipment.reload, loadData]); + + return [costEquipment, setCostEquipment, includeCostEquipment, excludeCostEquipment]; +}; + +export { useCostRouteLists, useCostJobsSpecs, useCostEquipment, updatingSelected }; diff --git a/app/panels/mech_rec_cost_jobs_manage/fcjobssp.js b/app/panels/mech_rec_cost_jobs_manage/fcjobssp.js new file mode 100644 index 0000000..de2c159 --- /dev/null +++ b/app/panels/mech_rec_cost_jobs_manage/fcjobssp.js @@ -0,0 +1,422 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Выдача сменного задания + Компонент панели: Таблица информации об операциях сменного задания +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Typography, Box, Checkbox, Grid, Icon, Button } from "@mui/material"; //Интерфейсные элементы +import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения +import { useCostRouteLists, useCostJobsSpecs, useCostEquipment } from "./backend"; //Собственные хуки таблиц + +//--------- +//Константы +//--------- + +const sUnitCostRouteLists = "CostRouteLists"; //Мнемокод раздела маршрутных листов +const sUnitCostJobsSpecs = "CostJobsSpecs"; //Мнемокод раздела операций +const sUnitCostEquipment = "CostEquipment"; //Мнемокод раздела рабочих центров + +//Стили +const STYLES = { + CONTAINER: { textAlign: "center" }, + TABLE: { paddingTop: "15px" }, + TABLE_SUM: { textAlign: "right", paddingTop: "5px", paddingRight: "15px" }, + TABLE_BUTTONS: { display: "flex", justifyContent: "flex-end" }, + CHECK_BOX: { textAlign: "center" }, + OPERATIONS_SEPARATOR: { padding: "3px 0px", backgroundColor: "lightblue" }, + INFORMATION_HALF: { minWidth: "50%", maxWidth: "50%", textAlign: "center" } +}; + +//--------------------------------------------- +//Вспомогательные функции форматирования данных +//--------------------------------------------- + +//Формирование списка отмеченных записей +function selectedReducer(accumulator, current) { + if (current.NSELECT == 1) { + accumulator.push(current.NRN); + } + return accumulator; +} + +//Форматирование значения ячейки +const dataCellRender = ({ row, columnDef, handleSelectChange, sUnit, selectedEquip }) => { + //Инициализируем доступность выбора + let disabled = false; + //Если это рабочие центры + if (sUnit === sUnitCostEquipment) { + //Для колонки выбора + if (columnDef.name === "NSELECT") { + return { + data: ( + + handleSelectChange(row["NRN"], sUnit, row["NCOEFF"] <= row["NLOADING"])} + /> + + ) + }; + } + //Если оборудование загружено + if (row["NCOEFF"] <= row["NLOADING"]) { + //Если поле не поле выбора + if (columnDef.name !== "NSELECT") { + return { + cellStyle: { color: "lightgrey" }, + data: row[columnDef.name] + }; + } + } + } + //Если это операции + if (sUnit === sUnitCostJobsSpecs) { + //Если "Оборудование план" операции сходится с выбранным оборудованием + if (selectedEquip.includes(row["NEQUIP_PLAN"])) { + //Если колонка выбора + if (columnDef.name === "NSELECT") { + return { + cellStyle: { backgroundColor: "#bce0de" }, + data: ( + + handleSelectChange(row["NRN"], sUnit)} + /> + + ) + }; + } else { + return { + cellStyle: { backgroundColor: "#bce0de" }, + data: row[columnDef.name] + }; + } + } + } + //Для колонки выбора + if (columnDef.name === "NSELECT") { + return { + data: ( + + handleSelectChange(row["NRN"], sUnit)} + /> + + ) + }; + } + return { + data: row[columnDef.name] + }; +}; + +//Генерация представления ячейки заголовка группы +export const headCellRender = ({ columnDef }) => { + if (columnDef.name === "NSELECT") { + return { + stackStyle: { padding: "2px", justifyContent: "space-around" }, + data: done + }; + } else { + return { + stackStyle: { padding: "2px" }, + data: columnDef.caption + }; + } +}; + +//----------- +//Тело модуля +//----------- + +//Таблица информации об операциях сменного задания +const CostJobsSpecsDataGrid = ({ task, processIdent, clearSelectlist }) => { + //Собственное состояние - таблица данных маршрутных листов + const [costRouteLists, setCostRouteLists, modifySelectList] = useCostRouteLists(task, processIdent); + + //Собственное состояние - таблица данных операций + const [costJobsSpecs, setCostJobsSpecs, issueCostJobsSpecs] = useCostJobsSpecs(task, costRouteLists.selectedRows, processIdent); + + //Собственное состояние - таблица рабочих центров + const [costEquipment, setCostEquipment, includeCostEquipment, excludeCostEquipment] = useCostEquipment(); + + //При изменении состояния сортировки маршрутных листов + const costRouteListOrderChanged = ({ orders }) => setCostRouteLists(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true })); + + //При изменении количества отображаемых страниц маршрутных листов + const costRouteListPagesCountChanged = () => setCostRouteLists(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true })); + + //При изменении состояния сортировки операций + const costJobsSpecOrderChanged = ({ orders }) => setCostJobsSpecs(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true })); + + //При изменении количества отображаемых страниц операций + const costJobsSpecPagesCountChanged = () => setCostJobsSpecs(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true })); + + //При изменении состояния сортировки рабочих центров + const costEquipmentOrderChanged = ({ orders }) => setCostEquipment(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true })); + + //При изменении количества отображаемых страниц рабочих центров + const costEquipmentPagesCountChanged = () => setCostEquipment(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true })); + + //При включении оборудования в операции + const costJobsSpecIncludeCostEquipment = () => { + //Делаем асинхронно, чтобы при ошибке ничего не обновлять + const includeAsync = async () => { + //Включаем оборудование в операции + try { + await includeCostEquipment({ + NFCEQUIPMENT: costEquipment.selectedRows[0], + NFCJOBS: task, + SFCJOBSSP_LIST: costJobsSpecs.selectedRows.join(";") + }); + //Необходимо обновить все данные + setCostJobsSpecs(pv => ({ ...pv, selectedRows: [], reload: true })); + setCostEquipment(pv => ({ ...pv, selectedRows: [], selectedLoaded: false, reload: true })); + } catch (e) { + throw new Error(e.message); + } + }; + //Включаем оборудование асинхронно + includeAsync(); + }; + + //При исключении оборудования из операции + const costJobsSpecExcludeCostEquipment = () => { + //Делаем асинхронно, чтобы при ошибке ничего не обновлять + const excludeAsync = async () => { + //Включаем оборудование в операции + try { + await excludeCostEquipment({ + NFCEQUIPMENT: costEquipment.selectedRows[0], + NFCJOBS: task, + SFCJOBSSP_LIST: costJobsSpecs.selectedRows.join(";") + }); + //Необходимо обновить данные о маршрутных листах и оборудовании + setCostJobsSpecs(pv => ({ ...pv, selectedRows: [], reload: true })); + setCostEquipment(pv => ({ ...pv, selectedRows: [], reload: true })); + } catch (e) { + throw new Error(e.message); + } + }; + //Исключаем операции асинхронно + excludeAsync(); + }; + + //Выдача задания операции + const costJobsSpecIssue = () => { + //Делаем асинхронно, чтобы при ошибке ничего не обновлять + const issueAsync = async () => { + //Включаем оборудование в операции + try { + await issueCostJobsSpecs({ + NFCJOBS: task, + SFCJOBSSP_LIST: costJobsSpecs.selectedRows.join(";") + }); + //Необходимо обновить данные о маршрутных листах и оборудовании + clearSelectlist(processIdent); + setCostRouteLists(pv => ({ ...pv, selectedRows: [], reload: true })); + setCostEquipment(pv => ({ ...pv, selectedRows: [], reload: true })); + } catch (e) { + throw new Error(e.message); + } + }; + //Выдаем задание асинхронно + issueAsync(); + }; + + //При изменение состояния выбора + const handleSelectChange = (NRN, sUnit, selectedLoaded) => { + //Инициализируем строки таблицы + let rows = []; + //Индекс элемента в массиве + let indexRow = null; + //Исходим от раздела + switch (sUnit) { + //Маршрутные листы + case sUnitCostRouteLists: + //Инициализируем маршрутными листами + rows = costRouteLists.rows; + //Определяем индекс элемента в массиве + indexRow = rows.findIndex(obj => obj.NRN == NRN); + //Изменяем значение выбора + rows[indexRow].NSELECT = !rows[indexRow].NSELECT; + //Добавляем/удаляем маршрутный лист из селектлиста + modifySelectList({ NFCROUTLST: NRN, NSELECT: rows[indexRow].NSELECT }); + //Актуализируем строки + setCostRouteLists(pv => ({ ...pv, rows: rows, selectedRows: rows.reduce(selectedReducer, []) })); + //Выходим + break; + //Операции + case sUnitCostJobsSpecs: + //Инициализируем операциями + rows = costJobsSpecs.rows; + //Определяем индекс элемента в массиве + indexRow = rows.findIndex(obj => obj.NRN == NRN); + //Изменяем значение выбора + rows[indexRow].NSELECT = !rows[indexRow].NSELECT; + //Актуализируем строки + setCostJobsSpecs(pv => ({ ...pv, rows: rows, selectedRows: rows.reduce(selectedReducer, []) })); + //Выходим + break; + //Рабочие центры + case sUnitCostEquipment: + //Инициализируем рабочими центрами + rows = costEquipment.rows; + //Определяем индекс элемента в массиве + indexRow = rows.findIndex(obj => obj.NRN == NRN); + //Изменяем значение выбора + rows[indexRow].NSELECT = !rows[indexRow].NSELECT; + //Актуализируем строки + setCostEquipment(pv => ({ ...pv, rows: rows, selectedRows: rows.reduce(selectedReducer, []), selectedLoaded: selectedLoaded })); + //Выходим + break; + default: + return; + } + }; + + //Генерация содержимого + return ( +
+ + + Маршрутные листы + {costRouteLists.dataLoaded ? ( + <> + + + + + dataCellRender({ ...prms, handleSelectChange, sUnit: sUnitCostRouteLists })} + headCellRender={prms => headCellRender({ ...prms })} + /> + {costRouteLists.selectedRows.length > 0 ? ( + <> + Операции выбранных маршрутных листов + + dataCellRender({ + ...prms, + handleSelectChange, + sUnit: sUnitCostJobsSpecs, + selectedEquip: costEquipment.selectedRows + }) + } + headCellRender={prms => headCellRender({ ...prms })} + /> + + ) : null} + + + ) : null} + + + Рабочие центры + {costEquipment.dataLoaded ? ( + <> + + + + + + + + + dataCellRender({ + ...prms, + handleSelectChange, + sUnit: sUnitCostEquipment, + selectedEquip: costEquipment.selectedRows + }) + } + headCellRender={prms => headCellRender({ ...prms })} + /> + + + ) : null} + + +
+ ); +}; + +//Контроль свойств - Таблица информации об операциях сменного задания +CostJobsSpecsDataGrid.propTypes = { + task: PropTypes.number.isRequired, + processIdent: PropTypes.number, + clearSelectlist: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { CostJobsSpecsDataGrid }; diff --git a/app/panels/mech_rec_cost_jobs_manage/hooks.js b/app/panels/mech_rec_cost_jobs_manage/hooks.js new file mode 100644 index 0000000..76b4637 --- /dev/null +++ b/app/panels/mech_rec_cost_jobs_manage/hooks.js @@ -0,0 +1,23 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Выдача сменного задания + Кастомные хуки +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React + +//----------- +//Тело модуля +//----------- + +//Клиентский отбор сменных заданий по поисковой фразе +export const useFilteredFcjobs = (jobs, filter) => { + const filteredJobs = React.useMemo(() => { + return jobs.filter(catalog => catalog.SDOC_INFO.toString().toLowerCase().includes(filter.jobName)); + }, [jobs, filter]); + + return filteredJobs; +}; diff --git a/app/panels/mech_rec_cost_jobs_manage/index.js b/app/panels/mech_rec_cost_jobs_manage/index.js new file mode 100644 index 0000000..4e787b9 --- /dev/null +++ b/app/panels/mech_rec_cost_jobs_manage/index.js @@ -0,0 +1,16 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Выдача сменного задания + Панель мониторинга: Точка входа +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { MechRecCostJobs } from "./mech_rec_cost_jobs_manage"; //Корневая панель выдачи сменного задания + +//---------------- +//Интерфейс модуля +//---------------- + +export const RootClass = MechRecCostJobs; diff --git a/app/panels/mech_rec_cost_jobs_manage/mech_rec_cost_jobs_manage.js b/app/panels/mech_rec_cost_jobs_manage/mech_rec_cost_jobs_manage.js new file mode 100644 index 0000000..84ffc8b --- /dev/null +++ b/app/panels/mech_rec_cost_jobs_manage/mech_rec_cost_jobs_manage.js @@ -0,0 +1,206 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Выдача сменного задания + Панель мониторинга: Корневая панель выдачи сменного задания +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useContext, useState, useCallback, useEffect } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Drawer, Fab, Box, List, ListItemButton, ListItemText, Typography, TextField } from "@mui/material"; //Интерфейсные элементы +import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером +import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений +import { useFilteredFcjobs } from "./hooks"; //Вспомогательные хуки +import { CostJobsSpecsDataGrid } from "./fcjobssp"; //Собственные хуки таблиц + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + JOBS_FINDER: { marginTop: "10px", marginLeft: "10px", width: "93%" }, + JOBS_LIST_ITEM_PRIMARY: { wordWrap: "break-word" }, + JOBS_BUTTON: { position: "absolute" }, + JOBS_DRAWER: { + width: "350px", + display: "inline-block", + flexShrink: 0, + [`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box" } + }, + CONTAINER: { margin: "5px 0px", textAlign: "center" } +}; + +//------------------------------------ +//Вспомогательные функции и компоненты +//------------------------------------ + +//Список сменных заданий +const JobList = ({ jobs = [], selectedJob, filter, setFilter, onClick } = {}) => { + //Генерация содержимого + return ( +
+ { + setFilter(pv => ({ ...pv, jobName: event.target.value })); + }} + > + + {jobs.map(p => ( + (onClick ? onClick(p) : null)}> + {p.SDOC_INFO}} /> + + ))} + +
+ ); +}; + +//Контроль свойств - Список каталогов планов +JobList.propTypes = { + jobs: PropTypes.array, + selectedJob: PropTypes.object, + onClick: PropTypes.func, + filter: PropTypes.object, + setFilter: PropTypes.func +}; + +//----------- +//Тело модуля +//----------- + +//Корневая панель выдачи сменного задания +const MechRecCostJobs = () => { + //Собственное состояние - таблица данных + const [state, setState] = useState({ + init: false, + showJobList: false, + jobList: [], + jobListLoaded: false, + selectedJob: {}, + processIdent: null, + dataLoaded: false + }); + + //Состояние для фильтра каталогов + const [filter, setFilter] = useState({ jobName: "" }); + + //Массив отфильтрованных каталогов + const filteredJobs = useFilteredFcjobs(state.jobList, filter); + + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //Подключение к контексту сообщений + const { InlineMsgInfo } = useContext(MessagingСtx); + + //Инициализация каталогов планов + const initPlans = useCallback(async () => { + if (!state.init) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCJOBS_INIT", + args: {}, + respArg: "COUT", + fullResponse: true, + isArray: name => name === "XFCJOBS" + }); + setState(pv => ({ + ...pv, + init: true, + jobList: [...(data.XPAYLOAD?.XFCJOBS || [])], + jobListLoaded: true, + processIdent: data.XPAYLOAD.XINFO.NPROCESS_IDENT + })); + } + }, [state.init, executeStored]); + + //При подключении компонента к странице + useEffect(() => { + initPlans(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + //Очистка селектлиста по идентификатору процесса + const clearSelectlist = useCallback( + async NIDENT => { + console.log(NIDENT); + try { + await executeStored({ + stored: "P_SELECTLIST_CLEAR", + args: { NIDENT: NIDENT } + }); + } catch (e) { + throw new Error(e.message); + } + }, + [executeStored] + ); + + //Выбор плана + const selectJob = job => { + //Очищаем селектлист + clearSelectlist(state.processIdent); + //Обновляем состояние + setState(pv => ({ + ...pv, + selectedJob: job, + showJobList: false, + dataLoaded: false + })); + }; + + //Сброс выбора плана + const unselectJob = () => { + //Очищаем селектлист + clearSelectlist(state.processIdent); + //Обновляем состояние + setState(pv => ({ + ...pv, + selectedJob: {}, + showJobList: false, + dataLoaded: false + })); + }; + + //Обработка нажатия на элемент в списке планов + const handleJobClick = job => { + if (state.selectedJob.NRN != job.NRN) selectJob(job); + else unselectJob(); + }; + + //Генерация содержимого + return ( + + setState(pv => ({ ...pv, showJobList: !pv.showJobList }))}> + Сменные задания + + setState(pv => ({ ...pv, showJobList: false }))} sx={STYLES.JOBS_DRAWER}> + + +
+ {state.selectedJob.NRN ? ( + <> + {`Сменное задание "${state.selectedJob.SSUBDIV}" на ${state.selectedJob.SPERIOD}`} + + + ) : !state.selectedJob.NRN ? ( + + ) : null} +
+
+ ); +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { MechRecCostJobs }; diff --git a/app/panels/mech_rec_dept_cost_jobs/index.js b/app/panels/mech_rec_dept_cost_jobs/index.js new file mode 100644 index 0000000..ec84fd1 --- /dev/null +++ b/app/panels/mech_rec_dept_cost_jobs/index.js @@ -0,0 +1,16 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Загрузка цеха + Панель мониторинга: Точка входа +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { MechRecDeptCostJobs } from "./mech_rec_dept_cost_jobs"; //Корневая панель загрузки цеха + +//---------------- +//Интерфейс модуля +//---------------- + +export const RootClass = MechRecDeptCostJobs; diff --git a/app/panels/mech_rec_dept_cost_jobs/mech_rec_dept_cost_jobs.js b/app/panels/mech_rec_dept_cost_jobs/mech_rec_dept_cost_jobs.js new file mode 100644 index 0000000..3595bee --- /dev/null +++ b/app/panels/mech_rec_dept_cost_jobs/mech_rec_dept_cost_jobs.js @@ -0,0 +1,153 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Загрузка цеха + Панель мониторинга: Корневая панель загрузки цеха +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React +import { Typography, Box } from "@mui/material"; //Интерфейсные элементы +import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции +import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения +import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером + +//--------- +//Константы +//--------- + +//Размер страницы данных +const DATA_GRID_PAGE_SIZE = 5; + +//Стили +const STYLES = { + CONTAINER: { textAlign: "center", paddingTop: "20px" }, + TITLE: { paddingBottom: "15px" } +}; + +//------------------------------------ +//Вспомогательные функции и компоненты +//------------------------------------ + +//Генерация заливки строки исходя от значений +const dataCellRender = ({ row, columnDef }) => { + //Описываем общие свойства + let cellProps = { title: row[columnDef.name] }; + //Описываем общий стиль + let cellStyle = { padding: "8px", maxWidth: "300px", textOverflow: "ellipsis", overflow: "hidden", whiteSpace: "pre" }; + // + if (columnDef.name.match(/N.*_VALUE/) && row[columnDef.name]) { + if (row[`${columnDef.name.substring(0, 12)}_TYPE`] === 0) { + cellStyle = { ...cellStyle, backgroundColor: "lightgrey" }; + } else { + cellStyle = { ...cellStyle, backgroundColor: "lightgreen" }; + } + } + return { + cellProps, + cellStyle, + data: row[columnDef] + }; +}; + +//----------- +//Тело модуля +//----------- + +//Корневая панель загрузки цеха +const MechRecDeptCostJobs = () => { + //Собственное состояние - таблица данных + const [costJobs, setCostJobs] = useState({ + subdiv: null, + dataLoaded: false, + columnsDef: [], + filters: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true + }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //Загрузка данных таблицы с сервера + const loadData = useCallback(async () => { + if (costJobs.reload) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCJOBS_DEP_LOAD_DG_GET", + args: { + CFILTERS: { VALUE: object2Base64XML(costJobs.filters, { arrayNodeName: "filters" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + CORDERS: { VALUE: object2Base64XML(costJobs.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + NPAGE_NUMBER: costJobs.pageNumber, + NPAGE_SIZE: DATA_GRID_PAGE_SIZE, + NINCLUDE_DEF: costJobs.dataLoaded ? 0 : 1 + }, + respArg: "COUT", + isArray: name => name === "XCOLUMNS_DEF" || name === "XROWS", + attributeValueProcessor: (name, val) => (name === "caption" ? undefined : val) + }); + setCostJobs(pv => ({ + ...pv, + subdiv: data.XINFO.SSUBDIV, + columnsDef: data.XFCJOBS.XDATA.XCOLUMNS_DEF ? [...data.XFCJOBS.XDATA.XCOLUMNS_DEF] : pv.columnsDef, + rows: pv.pageNumber == 1 ? [...(data.XFCJOBS.XDATA.XROWS || [])] : [...pv.rows, ...(data.XFCJOBS.XDATA.XROWS || [])], + dataLoaded: true, + reload: false, + morePages: (data.XFCJOBS.XDATA.XROWS || []).length >= DATA_GRID_PAGE_SIZE + })); + } + }, [costJobs.reload, costJobs.filters, costJobs.orders, costJobs.dataLoaded, costJobs.pageNumber, executeStored, SERV_DATA_TYPE_CLOB]); + + //При изменении состояния фильтра + const handleFilterChanged = ({ filters }) => setCostJobs(pv => ({ ...pv, filters: [...filters], pageNumber: 1, reload: true })); + + //При изменении состояния сортировки + const handleOrderChanged = ({ orders }) => setCostJobs(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true })); + + //При изменении количества отображаемых страниц + const handlePagesCountChanged = () => setCostJobs(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true })); + + //При необходимости обновить данные таблицы + useEffect(() => { + loadData(); + }, [costJobs.reload, loadData]); + + //Генерация содержимого + return ( +
+ + {costJobs.dataLoaded ? ( + <> + + {`Загрузка станков "${costJobs.subdiv}"`} + + + dataCellRender({ ...prms })} + /> + + + ) : null} + +
+ ); +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { MechRecDeptCostJobs }; diff --git a/app/panels/mech_rec_dept_cost_prod_plans/fcroutlst.js b/app/panels/mech_rec_dept_cost_prod_plans/fcroutlst.js new file mode 100644 index 0000000..ca13ec0 --- /dev/null +++ b/app/panels/mech_rec_dept_cost_prod_plans/fcroutlst.js @@ -0,0 +1,328 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Производственный план цеха + Компонент панели: Таблица маршрутных листов +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useCallback, useEffect, useContext } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Typography, Box, Paper, IconButton, Icon, Dialog, DialogContent, DialogActions, Button, TextField } from "@mui/material"; //Интерфейсные элементы +import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения +import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером +import { object2Base64XML } from "../../core/utils"; //Вспомогательные функции +import { CostRouteListsOrdDataGrid } from "./fcroutlstord"; //Состояние таблицы заказов маршрутных листов +import { ApplicationСtx } from "../../context/application"; //Контекст приложения + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + CONTAINER: { textAlign: "center" }, + TABLE: { paddingTop: "15px" }, + TABLE_SUM: { textAlign: "right", paddingTop: "5px", paddingRight: "15px" }, + DIALOG_EDITOR: { maxWidth: "250px" }, + DIALOG_BUTTONS: { marginTop: "10px", width: "240px" } +}; + +//--------------------------------------------- +//Вспомогательные функции форматирования данных +//--------------------------------------------- + +//Генерация представления расширения строки +export const rowExpandRender = ({ row }) => { + return ( + + + + ); +}; + +//Форматирование значений колонок +const dataCellRender = ({ row, columnDef, handlePriorEditOpen, handleOrderEditOpen }) => { + //!!! Пока отключено - не удалять + // switch (columnDef.name) { + // case "NPRIOR_PARTY": + // return { + // data: ( + // <> + // {row["NPRIOR_PARTY"]} + // handlePriorEditOpen(row["NRN"], row["NPRIOR_PARTY"])}> + // edit + // + // + // ) + // }; + // case "NCHANGE_FACEACC": + // return { + // data: ( + // + // handleOrderEditOpen(row["NRN"], row["SPROD_ORDER"])}> + // inventory + // + // + // ) + // }; + // } + return { + data: row[columnDef] + }; +}; + +//----------- +//Тело модуля +//----------- + +//Таблица маршрутных листов +const CostRouteListsDataGrid = ({ task }) => { + //Собственное состояние - таблица данных + const [costRouteLists, setCostRouteLists] = useState({ + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true, + editPriorNRN: null, + editPriorValue: null, + editOrderNRN: null, + editOrderValue: null + }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //Подключение к контексту приложения + const { pOnlineShowDictionary } = useContext(ApplicationСtx); + + //Размер страницы данных + const DATA_GRID_PAGE_SIZE = 5; + + //Загрузка данных таблицы с сервера + const loadData = useCallback(async () => { + if (costRouteLists.reload) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCROUTLST_DEPT_DG_GET", + args: { + NFCPRODPLANSP: task, + CORDERS: { VALUE: object2Base64XML(costRouteLists.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + NPAGE_NUMBER: costRouteLists.pageNumber, + NPAGE_SIZE: DATA_GRID_PAGE_SIZE, + NINCLUDE_DEF: costRouteLists.dataLoaded ? 0 : 1 + }, + respArg: "COUT" + }); + setCostRouteLists(pv => ({ + ...pv, + columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef, + rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])], + dataLoaded: true, + reload: false, + morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE + })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + costRouteLists.reload, + costRouteLists.filters, + costRouteLists.orders, + costRouteLists.dataLoaded, + costRouteLists.pageNumber, + executeStored, + SERV_DATA_TYPE_CLOB + ]); + + //При необходимости обновить данные таблицы + useEffect(() => { + loadData(); + }, [costRouteLists.reload, loadData]); + + //При изменении состояния сортировки + const handleOrderChanged = ({ orders }) => setCostRouteLists(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true })); + + //При изменении количества отображаемых страниц + const handlePagesCountChanged = () => setCostRouteLists(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true })); + + //При открытии изменения приоритета партии + const handlePriorEditOpen = (NRN, nPriorValue) => { + setCostRouteLists(pv => ({ ...pv, editPriorNRN: NRN, editPriorValue: nPriorValue })); + }; + + //При закрытии изменения приоритета партии + const handlePriorEditClose = () => { + setCostRouteLists(pv => ({ ...pv, editPriorNRN: null, editPriorValue: null })); + }; + + //При изменении значения приоритета партии + const handlePriorFormChanged = e => { + setCostRouteLists(pv => ({ ...pv, editPriorValue: e.target.value })); + }; + + //Изменение приоритета + const priorChange = useCallback( + async (NRN, PriorValue, rows) => { + try { + await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCROUTLST_PRIOR_PARTY_UPDATE", + args: { NFCROUTLST: NRN, SPRIOR_PARTY: PriorValue } + }); + //Изменяем значение приоритета у нужного + rows[rows.findIndex(obj => obj.NRN == NRN)].NPRIOR_PARTY = PriorValue; + //Актуализируем строки таблицы + setCostRouteLists(pv => ({ ...pv, rows: rows })); + //Закрываем окно + handlePriorEditClose(); + } catch (e) { + throw new Error(e.message); + } + }, + [executeStored] + ); + + //При нажатии на изменение приоритета партии + const handlePriorChange = () => { + //Изменяем значение + priorChange(costRouteLists.editPriorNRN, costRouteLists.editPriorValue, costRouteLists.rows); + }; + + //При открытии изменения заказа + const handleOrderEditOpen = (NRN, sProdOrderValue) => { + setCostRouteLists(pv => ({ ...pv, editOrderNRN: NRN, editOrderValue: sProdOrderValue })); + }; + + //При закрытии изменения заказа + const handleOrderEditClose = () => { + setCostRouteLists(pv => ({ ...pv, editOrderNRN: null, editOrderValue: null })); + }; + + //Изменение заказа + const setEditOrderValue = value => { + console.log(value); + setCostRouteLists(pv => ({ ...pv, editOrderValue: value })); + }; + + //При изменении значения заказа + const handleOrderFormChanged = e => { + setEditOrderValue(e.target.value); + }; + + //При нажатии на изменение заказа + const handleOrderChange = () => { + //Изменяем значение + //priorChange(costRouteLists.editPriorNRN, costRouteLists.editPriorValue); + //Закрываем окно + handleOrderEditClose(); + }; + + //Генерация содержимого + return ( +
+ Маршрутные листы + {costRouteLists.dataLoaded ? ( + <> + + dataCellRender({ ...prms, handlePriorEditOpen, handleOrderEditOpen })} + /> + + + ) : null} + {costRouteLists.editPriorNRN ? ( + handlePriorEditClose(null)} sx={STYLES.DIALOG_EDITOR}> + + + + + + + + + + + + + ) : null} + {costRouteLists.editOrderNRN ? ( + handleOrderEditClose(null)} sx={STYLES.DIALOG_EDITOR}> + + + + + + + + + + + + + + + + ) : null} +
+ ); +}; + +//Контроль свойств - Таблица маршрутных листов +CostRouteListsDataGrid.propTypes = { + task: PropTypes.number.isRequired +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { CostRouteListsDataGrid }; diff --git a/app/panels/mech_rec_dept_cost_prod_plans/fcroutlstord.js b/app/panels/mech_rec_dept_cost_prod_plans/fcroutlstord.js new file mode 100644 index 0000000..ec4ca3b --- /dev/null +++ b/app/panels/mech_rec_dept_cost_prod_plans/fcroutlstord.js @@ -0,0 +1,124 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Производственный план цеха + Компонент панели: Таблица заказов маршрутного листа +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useCallback, useEffect, useContext } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Typography } from "@mui/material"; //Интерфейсные элементы +import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения +import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером +import { object2Base64XML } from "../../core/utils"; //Вспомогательные функции + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + CONTAINER: { margin: "5px 0px", textAlign: "center" } +}; + +//----------- +//Тело модуля +//----------- + +//Таблица заказов маршрутного листа +const CostRouteListsOrdDataGrid = ({ mainRowRN }) => { + //Собственное состояние - таблица данных + const [costRouteListsOrd, setCostRouteListsOrd] = useState({ + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true + }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //Размер страницы данных + const DATA_GRID_PAGE_SIZE = 10; + + //Загрузка данных таблицы с сервера + const loadData = useCallback(async () => { + if (costRouteListsOrd.reload) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCROUTLSTORD_DEPT_DG_GET", + args: { + NFCROUTLST: mainRowRN, + CORDERS: { VALUE: object2Base64XML(costRouteListsOrd.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + NPAGE_NUMBER: costRouteListsOrd.pageNumber, + NPAGE_SIZE: DATA_GRID_PAGE_SIZE, + NINCLUDE_DEF: costRouteListsOrd.dataLoaded ? 0 : 1 + }, + respArg: "COUT" + }); + setCostRouteListsOrd(pv => ({ + ...pv, + columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef, + rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])], + dataLoaded: true, + reload: false, + morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE + })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + costRouteListsOrd.reload, + costRouteListsOrd.filters, + costRouteListsOrd.orders, + costRouteListsOrd.dataLoaded, + costRouteListsOrd.pageNumber, + executeStored, + SERV_DATA_TYPE_CLOB + ]); + + //При необходимости обновить данные таблицы + useEffect(() => { + loadData(); + }, [costRouteListsOrd.reload, loadData]); + + //При изменении состояния сортировки + const handleOrderChanged = ({ orders }) => setCostRouteListsOrd(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true })); + + //При изменении количества отображаемых страниц + const handlePagesCountChanged = () => setCostRouteListsOrd(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true })); + + //Генерация содержимого + return ( +
+ Заказы + {costRouteListsOrd.dataLoaded ? ( + + ) : null} +
+ ); +}; + +//Контроль свойств - Таблица заказов маршрутного листа +CostRouteListsOrdDataGrid.propTypes = { + mainRowRN: PropTypes.number.isRequired +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { CostRouteListsOrdDataGrid }; diff --git a/app/panels/mech_rec_dept_cost_prod_plans/hooks.js b/app/panels/mech_rec_dept_cost_prod_plans/hooks.js new file mode 100644 index 0000000..0cef903 --- /dev/null +++ b/app/panels/mech_rec_dept_cost_prod_plans/hooks.js @@ -0,0 +1,23 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Производственный план цеха + Кастомные хуки +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React + +//----------- +//Тело модуля +//----------- + +//Клиентский отбор каталогов по поисковой фразе и наличию планов +export const useFilteredPlans = (plans, filter) => { + const filteredPlans = React.useMemo(() => { + return plans.filter(catalog => catalog.SDOC_INFO.toString().toLowerCase().includes(filter.planName)); + }, [plans, filter]); + + return filteredPlans; +}; diff --git a/app/panels/mech_rec_dept_cost_prod_plans/incomefromdeps.js b/app/panels/mech_rec_dept_cost_prod_plans/incomefromdeps.js new file mode 100644 index 0000000..e59aef0 --- /dev/null +++ b/app/panels/mech_rec_dept_cost_prod_plans/incomefromdeps.js @@ -0,0 +1,120 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Производственный план цеха + Компонент панели: Таблица сдачи продукции +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useCallback, useEffect, useContext } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Typography, Box } from "@mui/material"; //Интерфейсные элементы +import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения +import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером +import { object2Base64XML, formatDateRF } from "../../core/utils"; //Вспомогательные функции + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + CONTAINER: { textAlign: "center" }, + TABLE: { paddingTop: "15px" } +}; + +//----------- +//Тело модуля +//----------- + +//Таблица сдачи продукции +const IncomFromDepsDataGrid = ({ task }) => { + //Собственное состояние - таблица данных + const [incomFromDeps, setIncomFromDeps] = useState({ + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true + }); + + //Размер страницы данных + const DATA_GRID_PAGE_SIZE = 10; + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //Загрузка данных таблицы с сервера + const loadData = useCallback(async () => { + if (incomFromDeps.reload) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.INCOMEFROMDEPS_DEPT_DG_GET", + args: { + NFCPRODPLANSP: task, + CORDERS: { VALUE: object2Base64XML(incomFromDeps.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + NPAGE_NUMBER: incomFromDeps.pageNumber, + NPAGE_SIZE: DATA_GRID_PAGE_SIZE, + NINCLUDE_DEF: incomFromDeps.dataLoaded ? 0 : 1 + }, + attributeValueProcessor: (name, val) => (["DDUE_DATE"].includes(name) ? formatDateRF(val) : val), + respArg: "COUT" + }); + setIncomFromDeps(pv => ({ + ...pv, + columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef, + rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])], + dataLoaded: true, + reload: false, + morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE + })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [incomFromDeps.reload, incomFromDeps.orders, incomFromDeps.dataLoaded, incomFromDeps.pageNumber, executeStored, SERV_DATA_TYPE_CLOB]); + + //При необходимости обновить данные таблицы + useEffect(() => { + loadData(); + }, [incomFromDeps.reload, loadData]); + + //При изменении состояния сортировки + const handleOrderChanged = ({ orders }) => setIncomFromDeps(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true })); + + //При изменении количества отображаемых страниц + const handlePagesCountChanged = () => setIncomFromDeps(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true })); + + //Генерация содержимого + return ( +
+ Сдача продукции + + {incomFromDeps.dataLoaded ? ( + + ) : null} + +
+ ); +}; + +//Контроль свойств - Таблица сдачи продукции +IncomFromDepsDataGrid.propTypes = { + task: PropTypes.number.isRequired +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { IncomFromDepsDataGrid }; diff --git a/app/panels/mech_rec_dept_cost_prod_plans/index.js b/app/panels/mech_rec_dept_cost_prod_plans/index.js new file mode 100644 index 0000000..21d3907 --- /dev/null +++ b/app/panels/mech_rec_dept_cost_prod_plans/index.js @@ -0,0 +1,16 @@ +/* + Парус 8 - Панели мониторинга - ПУДП - Производственный план цеха + Панель мониторинга: Точка входа +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { MechRecDeptCostProdPlans } from "./mech_rec_dept_cost_prod_plans"; //Корневая панель производственного плана цеха + +//---------------- +//Интерфейс модуля +//---------------- + +export const RootClass = MechRecDeptCostProdPlans; diff --git a/app/panels/mech_rec_dept_cost_prod_plans/mech_rec_dept_cost_prod_plans.js b/app/panels/mech_rec_dept_cost_prod_plans/mech_rec_dept_cost_prod_plans.js new file mode 100644 index 0000000..2dbd8f7 --- /dev/null +++ b/app/panels/mech_rec_dept_cost_prod_plans/mech_rec_dept_cost_prod_plans.js @@ -0,0 +1,445 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Производственный план цеха + Панель мониторинга: Корневая панель производственного плана цеха +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useContext, useState, useCallback, useEffect } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { + Drawer, + Fab, + Box, + List, + ListItemButton, + ListItemText, + Typography, + TextField, + Link, + Dialog, + DialogContent, + DialogActions, + Button +} from "@mui/material"; //Интерфейсные элементы +import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером +import { useFilteredPlans } from "./hooks"; //Вспомогательные хуки +import { object2Base64XML } from "../../core/utils"; //Вспомогательные функции +import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения +import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений +import { IncomFromDepsDataGrid } from "./incomefromdeps"; //Таблица сдачи продукции +import { CostRouteListsDataGrid } from "./fcroutlst"; //Таблица маршрутных листов + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + PLANS_FINDER: { marginTop: "10px", marginLeft: "10px", width: "93%" }, + PLANS_LIST_ITEM_PRIMARY: { wordWrap: "break-word" }, + PLANS_BUTTON: { position: "absolute" }, + PLANS_DRAWER: { + width: "350px", + display: "inline-block", + flexShrink: 0, + [`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box" } + }, + CONTAINER: { paddingTop: "40px", margin: "5px 0px", textAlign: "center" }, + PLAN_FACT_VALUE: { textAlign: "center", display: "flex", justifyContent: "center" }, + PLAN_FACT_DELIMITER: { padding: "0px 5px" }, + FACT_VALUE: { color: "blue" } +}; + +//------------------------------------ +//Вспомогательные функции и компоненты +//------------------------------------ + +//Генерация представления ячейки заголовка группы +export const groupCellRender = ({ group }) => ({ + cellStyle: { padding: "2px" }, + data: group.caption +}); + +//Генерация заливки строки исходя от значений +const dataCellRender = ({ row, columnDef, handleProdOrderClick, handleMatresCodeClick }) => { + //Описываем общие свойства + let cellProps = { title: row[columnDef.name] }; + //Описываем общий стиль + let cellStyle = { padding: "8px", maxWidth: "300px", textOverflow: "ellipsis", overflow: "hidden", whiteSpace: "pre" }; + //Для колонки "Статус" + if (columnDef.name === "SSTATUS") { + //Факт === План + if (row["NMAIN_QUANT"] === row["NREL_FACT"]) { + return { + cellProps, + cellStyle: { backgroundColor: "lightgreen", ...cellStyle }, + data: row[columnDef] + }; + } + //План <= (Факт + Запущено) + if (row["NMAIN_QUANT"] <= row["NREL_FACT"] + row["NFCROUTLST_QUANT"]) { + return { + cellProps, + cellStyle: { backgroundColor: "lightblue", ...cellStyle }, + data: row[columnDef] + }; + } + //Сумма "Количество план" = 0 или < "План" + if (row["NSUM_PLAN"] === 0 || (row["NSUM_PLAN"] !== 0 && row["NSUM_PLAN"] < row["NMAIN_QUANT"])) { + //"Факт" >= "План" + if (row["NREL_FACT"] >= row["NMAIN_QUANT"]) { + return { + cellProps, + cellStyle: { backgroundColor: "#F0E68C", ...cellStyle }, + data: row[columnDef] + }; + } + } else { + //Сумма "Количество факт" >= сумма "Количество план" + if (row["NSUM_FACT"] >= row["NSUM_PLAN"]) { + return { + cellProps, + cellStyle: { backgroundColor: "#F0E68C", ...cellStyle }, + data: row[columnDef] + }; + } + } + return { + cellProps, + cellStyle: { backgroundColor: "lightcoral", ...cellStyle }, + data: row[columnDef] + }; + } + //Для колонки даты + if (columnDef.name.indexOf("PLAN_FACT") >= 0) { + //Получаем текущий день + let curDay = new Date().getDate().toString().padStart(2, "0"); + //Формируем regex для проверки + let regex = new RegExp(`N_${curDay}.*`, "g"); + //Если это значение текущего дня + if (columnDef.name.match(regex)) { + cellStyle = { ...cellStyle, backgroundColor: "lightgrey" }; + } + //Если в колонке есть значени + if (row[columnDef.name]) { + //Разбиваем его на план/факт + let values = row[columnDef.name].split("/"); + //Разбиваем значения на блоки + return { + cellProps, + cellStyle, + data: ( + + {values[0]} + / + {values[1]} + + ) + }; + } else { + //Если значения нет + return { + cellProps, + cellStyle, + data: row[columnDef] + }; + } + } + //Для колонки "Заказ" + if (columnDef.name === "SPROD_ORDER") { + return { + cellProps, + cellStyle, + data: ( + handleProdOrderClick(row["NRN"])}> + {row[columnDef.name]} + + ) + }; + } + //Для колонки "Обозначение" + if (columnDef.name === "SMATRES_CODE") { + return { + cellProps, + cellStyle, + data: ( + handleMatresCodeClick(row["NRN"])}> + {row[columnDef.name]} + + ) + }; + } + return { + cellProps, + cellStyle, + data: row[columnDef] + }; +}; + +//Список каталогов планов +const PlanList = ({ plans = [], selectedPlan, filter, setFilter, onClick } = {}) => { + //Генерация содержимого + return ( +
+ { + setFilter(pv => ({ ...pv, planName: event.target.value })); + }} + > + + {plans.map(p => ( + (onClick ? onClick(p) : null)}> + {p.SDOC_INFO}} /> + + ))} + +
+ ); +}; + +//Контроль свойств - Список каталогов планов +PlanList.propTypes = { + plans: PropTypes.array, + selectedPlan: PropTypes.object, + onClick: PropTypes.func, + filter: PropTypes.object, + setFilter: PropTypes.func +}; + +//----------- +//Тело модуля +//----------- + +//Корневая панель производственного плана цеха +const MechRecDeptCostProdPlans = () => { + //Собственное состояние - таблица данных + const [state, setState] = useState({ + init: false, + showPlanList: false, + showIncomeFromDeps: null, + showFcroutelst: null, + planList: [], + planListLoaded: false, + selectedPlan: {}, + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true + }); + + //Состояние для фильтра каталогов + const [filter, setFilter] = useState({ planName: "" }); + + //Массив отфильтрованных каталогов + const filteredPlanCtgls = useFilteredPlans(state.planList, filter); + + //Размер страницы данных + const DATA_GRID_PAGE_SIZE = 10; + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //Подключение к контексту сообщений + const { InlineMsgInfo } = useContext(MessagingСtx); + + // Инициализация каталогов планов + const initPlans = useCallback(async () => { + if (!state.init) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCPRODPLAN_DEPT_INIT", + args: {}, + respArg: "COUT", + isArray: name => name === "XFCPRODPLANS" + }); + setState(pv => ({ ...pv, init: true, planList: [...(data?.XFCPRODPLANS || [])], planListLoaded: true })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [state.init, executeStored]); + + //Загрузка данных таблицы с сервера + const loadData = useCallback( + async NRN => { + if (state.reload && NRN) { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCPRODPLANSP_DEPT_DG_GET", + args: { + NFCPRODPLAN: NRN, + CORDERS: { VALUE: object2Base64XML(state.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + NPAGE_NUMBER: state.pageNumber, + NPAGE_SIZE: DATA_GRID_PAGE_SIZE, + NINCLUDE_DEF: state.dataLoaded ? 0 : 1 + }, + respArg: "COUT", + attributeValueProcessor: (name, val) => (name === "caption" ? undefined : val) + }); + setState(pv => ({ + ...pv, + columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef, + rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])], + dataLoaded: true, + reload: false, + morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE + })); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [state.reload, state.orders, state.dataLoaded, state.pageNumber, executeStored, SERV_DATA_TYPE_CLOB] + ); + + //При необходимости обновить данные таблицы + useEffect(() => { + if (state.selectedPlan.NRN) { + loadData(state.selectedPlan.NRN); + } else { + setState(pv => ({ ...pv, dataLoaded: false, columnsDef: [], orders: null, rows: [], reload: true, pageNumber: 1, morePages: true })); + } + }, [state.selectedPlan, state.reload, loadData]); + + //При подключении компонента к странице + useEffect(() => { + initPlans(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + //Выбор плана + const selectPlan = plan => { + setState(pv => ({ + ...pv, + showIncomeFromDeps: null, + showFcroutelst: null, + selectedPlan: plan, + showPlanList: false, + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true + })); + }; + + //Сброс выбора плана + const unselectPlan = () => + setState(pv => ({ + ...pv, + showIncomeFromDeps: null, + showFcroutelst: null, + selectedPlan: {}, + showPlanList: false, + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true + })); + + //Обработка нажатия на элемент в списке планов + const handlePlanClick = plan => { + if (state.selectedPlan.NRN != plan.NRN) selectPlan(plan); + else unselectPlan(); + }; + + //При изменении состояния сортировки + const handleOrderChanged = ({ orders }) => setState(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true })); + + //При изменении количества отображаемых страниц + const handlePagesCountChanged = () => setState(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true })); + + //При нажатии на "Заказ" + const handleProdOrderClick = planSp => { + setState(pv => ({ ...pv, showIncomeFromDeps: planSp })); + }; + + //При нажатии на "Обозначение" + const handleMatresCodeClick = planSp => { + setState(pv => ({ ...pv, showFcroutelst: planSp })); + }; + + //Генерация содержимого + return ( + + setState(pv => ({ ...pv, showPlanList: !pv.showPlanList }))}> + Планы + + setState(pv => ({ ...pv, showPlanList: false }))} + sx={STYLES.PLANS_DRAWER} + > + + +
+ {state.dataLoaded ? ( + <> + {`Производственный план цеха "${state.selectedPlan.SSUBDIV}" на ${state.selectedPlan.SPERIOD}`} + dataCellRender({ ...prms, handleProdOrderClick, handleMatresCodeClick })} + groupCellRender={groupCellRender} + /> + + ) : !state.selectedPlan.NRN ? ( + + ) : null} +
+ {state.showIncomeFromDeps ? ( + handleProdOrderClick(null)} fullWidth maxWidth="xl"> + + + + + + + + ) : null} + {state.showFcroutelst ? ( + handleMatresCodeClick(null)} fullWidth maxWidth="xl"> + + + + + + + + ) : null} +
+ ); +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { MechRecDeptCostProdPlans }; diff --git a/db/PKG_P8PANELS_MECHREC.pck b/db/PKG_P8PANELS_MECHREC.pck index 2a98f26..78d250c 100644 --- a/db/PKG_P8PANELS_MECHREC.pck +++ b/db/PKG_P8PANELS_MECHREC.pck @@ -61,6 +61,50 @@ create or replace package PKG_P8PANELS_MECHREC as ( COUT out clob -- Список каталогов раздела "Планы и отчеты производства изделий" ); + + /* Получение таблицы маршрутных листов, связанных с производственным составом */ + procedure FCROUTLST_MON_DG_GET + ( + NPRODCMPSP in number, -- Рег. номер производственного состава + NFCPRODPLAN in number, -- Рег. номер план + NPAGE_NUMBER in number, -- Номер страницы (игнорируется при NPAGE_SIZE=0) + NPAGE_SIZE in number, -- Количество записей на странице (0 - все) + CORDERS in clob, -- Сортировки + NINCLUDE_DEF in number, -- Признак включения описания колонок таблицы в ответ + COUT out clob -- Сериализованная таблица данных + ); + + /* Получение таблицы комплектовочных ведомостей, связанных с производственным составом */ + procedure FCDELIVSH_DG_GET + ( + NPRODCMPSP in number, -- Рег. номер производственного состава + NFCPRODPLAN in number, -- Рег. номер план + NPAGE_NUMBER in number, -- Номер страницы (игнорируется при NPAGE_SIZE=0) + NPAGE_SIZE in number, -- Количество записей на странице (0 - все) + CORDERS in clob, -- Сортировки + NINCLUDE_DEF in number, -- Признак включения описания колонок таблицы в ответ + COUT out clob -- Сериализованная таблица данных + ); + + /* Считывание деталий для выбора SVG */ + procedure FCPRODCMP_DETAILS_GET + ( + NFCPRODPLAN in number, -- Рег. номер плана + COUT out clob -- Сериализованная таблица данных + ); + + /* Получение таблицы записей "Планы и отчеты производства изделий" */ + procedure FCPRODPLAN_GET + ( + NCRN in number, -- Рег. номер каталога + COUT out clob -- Сериализованная таблица данных + ); + + /* Инициализация каталогов раздела "Планы и отчеты производства изделий" */ + procedure FCPRODPLAN_CTLG_INIT + ( + COUT out clob -- Список каталогов раздела "Планы и отчеты производства изделий" + ); end PKG_P8PANELS_MECHREC; / @@ -74,13 +118,13 @@ create or replace package body PKG_P8PANELS_MECHREC as SBG_COLOR_BLACK constant PKG_STD.TSTRING := '#00000080'; -- Цвет заливки черный STEXT_COLOR_ORANGE constant PKG_STD.TSTRING := '#FF8C00'; -- Цвет текста оранжевый STEXT_COLOR_GREY constant PKG_STD.TSTRING := '#555'; -- Цвет текста серый - - /* Константы - параметры отборов планов */ + + /* Константы - параметры отборов планов ("Производственная программа") */ NFCPRODPLAN_CATEGORY constant PKG_STD.TNUMBER := 1; -- Категория планов "Производственная программа" NFCPRODPLAN_STATUS constant PKG_STD.TNUMBER := 2; -- Статус планов "Утвержден" SFCPRODPLAN_TYPE constant PKG_STD.TSTRING := 'План'; -- Тип планов (мнемокод состояния) NMAX_TASKS constant PKG_STD.TNUMBER := 10000; -- Максимальное количество отображаемых задач - + /* Константы - классы задач плана ("Производственная программа") */ NCLASS_WO_DEFICIT constant PKG_STD.TNUMBER := 0; -- Без дефицита выпуска NCLASS_PART_DEFICIT constant PKG_STD.TNUMBER := 1; -- С частичным дефицитом выпуска @@ -88,7 +132,7 @@ create or replace package body PKG_P8PANELS_MECHREC as NCLASS_WITH_DEFICIT constant PKG_STD.TNUMBER := 3; -- С дефицитом запуска или датой меньше текущей NCLASS_FUTURE_DATE constant PKG_STD.TNUMBER := 4; -- Дата анализа еще не наступила NCLASS_WO_LINKS constant PKG_STD.TNUMBER := 5; -- Задача без связи - + /* Константы - типы задач плана, содержание детализации ("Производственная программа") */ NTASK_TYPE_RL_WITH_GP constant PKG_STD.TNUMBER := 0; -- Маршрутные листы с развертыванием товарных запасов NTASK_TYPE_RL_WITH_DL constant PKG_STD.TNUMBER := 1; -- Маршрутные листы с развертыванием комплектаций @@ -96,6 +140,16 @@ create or replace package body PKG_P8PANELS_MECHREC as NTASK_TYPE_INC_DEPS_RL constant PKG_STD.TNUMBER := 3; -- Приход из подразделений и маршрутные листы NTASK_TYPE_RL constant PKG_STD.TNUMBER := 4; -- Маршрутные листы NTASK_TYPE_EMPTY constant PKG_STD.TNUMBER := null; -- Нет детализации + + /* Константы - параметры отборов планов ("Мониторинг сборки изделий") */ + NFCPRODPLAN_CATEGORY_MON constant PKG_STD.TNUMBER := 0; -- Категория планов "Первичный документ" + NFCPRODPLAN_STATUS_MON constant PKG_STD.TNUMBER := 2; -- Статус планов "Утвержден" + SFCPRODPLAN_TYPE_MON constant PKG_STD.TSTRING := 'План'; -- Тип планов (мнемокод состояния) + + /* Константы - параметры отборов ("Загрузка цеха") */ + SDICMUNTS_WD constant PKG_STD.TSTRING := 'н/ч'; -- Мнемокод ед. измерения нормочасов + SDICMUNTS_HOUR constant PKG_STD.TSTRING := 'час'; -- Мнемокод ед. измерения часов + /* Константы - дополнительные атрибуты */ STASK_ATTR_START_FACT constant PKG_STD.TSTRING := 'start_fact'; -- Запущено @@ -2210,6 +2264,639 @@ create or replace package body PKG_P8PANELS_MECHREC as PKG_STATE.DIAGNOSTICS_STACKED(); P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); end ACATALOG_INIT; + + /* Считывание рег. номера спецификации связанного плана */ + function FCPRODPLANSP_LINKED_GET + ( + NPRODCMPSP in number, -- Рег. номер производственного состава + NFCPRODPLAN in number -- Рег. номер план + ) return number -- Рег. номер спецификации связанного плана + is + NRESULT PKG_STD.TREF; -- Рег. номер спецификации связанного плана + begin + /* Считываем запись */ + begin + select S.RN + into NRESULT + from FCPRODPLAN T, + FCPRODPLANSP S + where T.RN = (select P.RN + from DOCLINKS L, + FCPRODPLAN P + where L.IN_DOCUMENT = NFCPRODPLAN + and L.IN_UNITCODE = 'CostProductPlans' + and L.OUT_UNITCODE = 'CostProductPlans' + and P.RN = L.OUT_DOCUMENT + and P.CATEGORY = 1 + and ROWNUM = 1) + and S.PRN = T.RN + and S.PRODCMPSP = NPRODCMPSP; + exception + when others then + NRESULT := null; + end; + /* Возвращаем результат */ + return NRESULT; + end FCPRODPLANSP_LINKED_GET; + + /* Получение таблицы маршрутных листов, связанных с производственным составом */ + procedure FCROUTLST_MON_DG_GET + ( + NPRODCMPSP in number, -- Рег. номер производственного состава + NFCPRODPLAN in number, -- Рег. номер план + NPAGE_NUMBER in number, -- Номер страницы (игнорируется при NPAGE_SIZE=0) + NPAGE_SIZE in number, -- Количество записей на странице (0 - все) + CORDERS in clob, -- Сортировки + NINCLUDE_DEF in number, -- Признак включения описания колонок таблицы в ответ + COUT out clob -- Сериализованная таблица данных + ) + is + NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса + RO PKG_P8PANELS_VISUAL.TORDERS; -- Сортировки + RDG PKG_P8PANELS_VISUAL.TDATA_GRID; -- Описание таблицы + RDG_ROW PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы + NROW_FROM PKG_STD.TREF; -- Номер строки с + NROW_TO PKG_STD.TREF; -- Номер строки по + CSQL clob; -- Буфер для запроса + ICURSOR integer; -- Курсор для исполнения запроса + NFCPRODPLANSP PKG_STD.TREF; -- Рег. номер спецификации связанного плана + NFCROUTLST_IDENT PKG_STD.TREF; -- Рег. номер идентификатора отмеченных записей маршрутных листов + NDICMUNTS_WD PKG_STD.TREF; -- Рег. номер ед. измерения нормочасов + begin + /* Читем сортировки */ + RO := PKG_P8PANELS_VISUAL.TORDERS_FROM_XML(CORDERS => CORDERS); + /* Преобразуем номер и размер страницы в номер строк с и по */ + PKG_P8PANELS_VISUAL.UTL_ROWS_LIMITS_CALC(NPAGE_NUMBER => NPAGE_NUMBER, + NPAGE_SIZE => NPAGE_SIZE, + NROW_FROM => NROW_FROM, + NROW_TO => NROW_TO); + /* Инициализируем таблицу данных */ + RDG := PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE(); + /* Описываем колонки таблицы данных */ + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NRN', + SCAPTION => 'Рег. номер', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SNUMB', + SCAPTION => '% п/п', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => true); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SOPERATION', + SCAPTION => 'Содержание работ', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => true); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SEXECUTOR', + SCAPTION => 'Исполнитель', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => true); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NREMN_LABOUR', + SCAPTION => 'Остаточная трудоемкость, в н/ч', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => true); + /* Считываем рег. номер спецификации связанного плана */ + NFCPRODPLANSP := FCPRODPLANSP_LINKED_GET(NPRODCMPSP => NPRODCMPSP, NFCPRODPLAN => NFCPRODPLAN); + /* Если спецификация считалась */ + if (NFCPRODPLANSP is not null) then + /* Инициализируем список маршрутных листов */ + UTL_FCROUTLST_IDENT_INIT(NFCPRODPLANSP => NFCPRODPLANSP, NIDENT => NFCROUTLST_IDENT); + /* Считываем единицу измерения нормочасов */ + FIND_DICMUNTS_CODE(NFLAG_SMART => 0, + NFLAG_OPTION => 0, + NCOMPANY => NCOMPANY, + SMEAS_MNEMO => SDICMUNTS_WD, + NRN => NDICMUNTS_WD); + begin + /* Добавляем подсказку совместимости */ + CSQL := PKG_SQL_BUILD.COMPATIBLE(SSQL => CSQL); + /* Формируем запрос */ + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => 'select *'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from (select D.*,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => PKG_SQL_BUILD.SQLROWNUM() || ' NROW'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from (select SF.RN NRN,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' TRIM(SH.NUMB) SNUMB,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' COALESCE(SH.OPER_UK, FT.NAME) SOPERATION,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' (select I.CODE from INS_DEPARTMENT I where SF.SUBDIV = I.RN) SEXECUTOR,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' F_DICMUNTS_BASE_RECALC_QUANT(' || PKG_SQL_BUILD.WRAP_NUM(NVALUE => 0) || ','); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' :NCOMPANY,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' SF.MUNIT,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' SF.T_SHT_PLAN - SF.LABOUR_FACT,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' :NDICMUNTS_WD) NREMN_LABOUR'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from FCROUTLST F,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' FCROUTLSTSP SF,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' FCROUTSHTSP SH left outer join FCOPERTYPES FT on SH.OPER_TPS = FT.RN'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where F.RN in (select SL."DOCUMENT"'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from SELECTLIST SL'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where SL.IDENT = :NFCROUTLST_IDENT'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and SL.UNITCODE = ' || PKG_SQL_BUILD.WRAP_STR(SVALUE => 'CostRouteLists') || ')'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and SF.PRN = F.RN'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and SH.RN = SF.FCROUTSHTSP'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and F.COMPANY = :NCOMPANY'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' %ORDER_BY%) D) F'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where F.NROW between :NROW_FROM and :NROW_TO'); + /* Учтём сортировки */ + PKG_P8PANELS_VISUAL.TORDERS_SET_QUERY(RDATA_GRID => RDG, RORDERS => RO, SPATTERN => '%ORDER_BY%', CSQL => CSQL); + /* Разбираем его */ + ICURSOR := PKG_SQL_DML.OPEN_CURSOR(SWHAT => 'SELECT'); + PKG_SQL_DML.PARSE(ICURSOR => ICURSOR, SQUERY => CSQL); + /* Делаем подстановку параметров */ + PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NCOMPANY', NVALUE => NCOMPANY); + PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NROW_FROM', NVALUE => NROW_FROM); + PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NROW_TO', NVALUE => NROW_TO); + PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NDICMUNTS_WD', NVALUE => NDICMUNTS_WD); + PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NFCROUTLST_IDENT', NVALUE => NFCROUTLST_IDENT); + /* Описываем структуру записи курсора */ + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 1); + PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 2); + PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 3); + PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 4); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 5); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 6); + /* Делаем выборку */ + if (PKG_SQL_DML.EXECUTE(ICURSOR => ICURSOR) = 0) then + null; + end if; + /* Обходим выбранные записи */ + while (PKG_SQL_DML.FETCH_ROWS(ICURSOR => ICURSOR) > 0) + loop + /* Добавляем колонки с данными */ + PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLN(RROW => RDG_ROW, + SNAME => 'NRN', + ICURSOR => ICURSOR, + NPOSITION => 1, + BCLEAR => true); + PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, + SNAME => 'SNUMB', + ICURSOR => ICURSOR, + NPOSITION => 2); + PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, + SNAME => 'SOPERATION', + ICURSOR => ICURSOR, + NPOSITION => 3); + PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, + SNAME => 'SEXECUTOR', + ICURSOR => ICURSOR, + NPOSITION => 4); + PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLN(RROW => RDG_ROW, + SNAME => 'NREMN_LABOUR', + ICURSOR => ICURSOR, + NPOSITION => 5); + /* Добавляем строку в таблицу */ + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + end loop; + exception + when others then + PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR); + raise; + end; + end if; + /* Очищаем отмеченные маршрутные листы */ + P_SELECTLIST_CLEAR(NIDENT => NFCROUTLST_IDENT); + /* Сериализуем описание */ + COUT := PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => NINCLUDE_DEF); + exception + when others then + /* Очищаем отмеченные маршрутные листы */ + P_SELECTLIST_CLEAR(NIDENT => NFCROUTLST_IDENT); + raise; + end FCROUTLST_MON_DG_GET; + + /* Получение таблицы комплектовочных ведомостей, связанных с производственным составом */ + procedure FCDELIVSH_DG_GET + ( + NPRODCMPSP in number, -- Рег. номер производственного состава + NFCPRODPLAN in number, -- Рег. номер план + NPAGE_NUMBER in number, -- Номер страницы (игнорируется при NPAGE_SIZE=0) + NPAGE_SIZE in number, -- Количество записей на странице (0 - все) + CORDERS in clob, -- Сортировки + NINCLUDE_DEF in number, -- Признак включения описания колонок таблицы в ответ + COUT out clob -- Сериализованная таблица данных + ) + is + NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса + RO PKG_P8PANELS_VISUAL.TORDERS; -- Сортировки + RDG PKG_P8PANELS_VISUAL.TDATA_GRID; -- Описание таблицы + RDG_ROW PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы + NROW_FROM PKG_STD.TREF; -- Номер строки с + NROW_TO PKG_STD.TREF; -- Номер строки по + CSQL clob; -- Буфер для запроса + ICURSOR integer; -- Курсор для исполнения запроса + NFCPRODPLANSP PKG_STD.TREF; -- Рег. номер спецификации связанного плана + begin + /* Читем сортировки */ + RO := PKG_P8PANELS_VISUAL.TORDERS_FROM_XML(CORDERS => CORDERS); + /* Преобразуем номер и размер страницы в номер строк с и по */ + PKG_P8PANELS_VISUAL.UTL_ROWS_LIMITS_CALC(NPAGE_NUMBER => NPAGE_NUMBER, + NPAGE_SIZE => NPAGE_SIZE, + NROW_FROM => NROW_FROM, + NROW_TO => NROW_TO); + /* Инициализируем таблицу данных */ + RDG := PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE(); + /* Описываем колонки таблицы данных */ + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NRN', + SCAPTION => 'Рег. номер', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SSUBDIV', + SCAPTION => 'Цех', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => true); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SNOMEN', + SCAPTION => 'Номенклатура', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => true); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NQUANT_PROD', + SCAPTION => 'Применяемость на одно ВС', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => true); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SPROVIDER', + SCAPTION => 'Поставщик', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => true); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NDEFICIT', + SCAPTION => 'Дефицит', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => true); + /* Считываем рег. номер спецификации связанного плана */ + NFCPRODPLANSP := FCPRODPLANSP_LINKED_GET(NPRODCMPSP => NPRODCMPSP, NFCPRODPLAN => NFCPRODPLAN); + /* Если спецификация считалась */ + if (NFCPRODPLANSP is not null) then + begin + /* Добавляем подсказку совместимости */ + CSQL := PKG_SQL_BUILD.COMPATIBLE(SSQL => CSQL); + /* Формируем запрос */ + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => 'select *'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from (select D.*,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => PKG_SQL_BUILD.SQLROWNUM() || ' NROW'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from (select T.RN NRN,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' (select I.CODE from INS_DEPARTMENT I where T.SUBDIV = I.RN) SSUBDIV,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' NM.NOMEN_NAME SNOMEN,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.QUANT_PROD NQUANT_PROD,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' (select I2.CODE from INS_DEPARTMENT I2 where T.PR_SUBDIV = I2.RN) SPROVIDER,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.DEFICIT NDEFICIT'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from DOCLINKS D,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' FCDELIVSHSP T,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' FCMATRESOURCE F,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' DICNOMNS NM'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where D.IN_DOCUMENT = :NFCPRODPLANSP'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and D.IN_UNITCODE = ' || PKG_SQL_BUILD.WRAP_STR(SVALUE => 'CostProductPlansSpecs')); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and D.OUT_UNITCODE = ' || PKG_SQL_BUILD.WRAP_STR(SVALUE => 'CostDeliverySheets')); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and T.PRN = D.OUT_DOCUMENT'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and T.COMPANY = :NCOMPANY'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and T.MATRES = F.RN'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and F.NOMENCLATURE = NM.RN'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' %ORDER_BY%) D) F'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where F.NROW between :NROW_FROM and :NROW_TO'); + /* Учтём сортировки */ + PKG_P8PANELS_VISUAL.TORDERS_SET_QUERY(RDATA_GRID => RDG, RORDERS => RO, SPATTERN => '%ORDER_BY%', CSQL => CSQL); + /* Разбираем его */ + ICURSOR := PKG_SQL_DML.OPEN_CURSOR(SWHAT => 'SELECT'); + PKG_SQL_DML.PARSE(ICURSOR => ICURSOR, SQUERY => CSQL); + /* Делаем подстановку параметров */ + PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NCOMPANY', NVALUE => NCOMPANY); + PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NROW_FROM', NVALUE => NROW_FROM); + PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NROW_TO', NVALUE => NROW_TO); + PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NFCPRODPLANSP', NVALUE => NFCPRODPLANSP); + /* Описываем структуру записи курсора */ + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 1); + PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 2); + PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 3); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 4); + PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 5); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 6); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 7); + /* Делаем выборку */ + if (PKG_SQL_DML.EXECUTE(ICURSOR => ICURSOR) = 0) then + null; + end if; + /* Обходим выбранные записи */ + while (PKG_SQL_DML.FETCH_ROWS(ICURSOR => ICURSOR) > 0) + loop + /* Добавляем колонки с данными */ + PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLN(RROW => RDG_ROW, + SNAME => 'NRN', + ICURSOR => ICURSOR, + NPOSITION => 1, + BCLEAR => true); + PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, + SNAME => 'SSUBDIV', + ICURSOR => ICURSOR, + NPOSITION => 2); + PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, + SNAME => 'SNOMEN', + ICURSOR => ICURSOR, + NPOSITION => 3); + PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLN(RROW => RDG_ROW, + SNAME => 'NQUANT_PROD', + ICURSOR => ICURSOR, + NPOSITION => 4); + PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, + SNAME => 'SPROVIDER', + ICURSOR => ICURSOR, + NPOSITION => 5); + PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, + SNAME => 'NDEFICIT', + ICURSOR => ICURSOR, + NPOSITION => 6); + /* Добавляем строку в таблицу */ + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + end loop; + exception + when others then + PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR); + raise; + end; + end if; + /* Сериализуем описание */ + COUT := PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => NINCLUDE_DEF); + end FCDELIVSH_DG_GET; + + /* Считывание деталий для выбора SVG */ + procedure FCPRODCMP_DETAILS_GET + ( + NFCPRODPLAN in number, -- Рег. номер плана + COUT out clob -- Сериализованная таблица данных + ) + is + NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса + NDOC_PROP PKG_STD.TREF; -- Рег. номер свойства "ID" + NFCPRODPLANSP PKG_STD.TREF; -- Рег. номер связанной спецификации плана + begin + /* Начинаем формирование XML */ + PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_); + /* Считываем свойство документа */ + FIND_DOCS_PROPS_CODE_EX(NFLAG_SMART => 0, + NCOMPVERS => NCOMPANY, + SUNITCODE => 'CostProductCompositionSpec', + SPROPCODE => 'ID', + NRN => NDOC_PROP); + /* Открываем корень */ + PKG_XFAST.DOWN_NODE(SNAME => 'XDATA'); + /* Цикл по планам и отчетам производства изделий */ + for REC in (select S.RN NRN, + (select F.NAME from FCMATRESOURCE F where F.RN = S.MTR_RES) SNAME, + PV.NUM_VALUE NID + from FCPRODPLANSP T, + FCPRODCMPSP S, + DOCS_PROPS_VALS PV + where T.PRN = NFCPRODPLAN + and S.PRN = T.PRODCMP + and PV.DOCS_PROP_RN = NDOC_PROP + and PV.UNIT_RN = S.RN) + loop + /* Получаем рег. номер связанной спецификации плана */ + NFCPRODPLANSP := FCPRODPLANSP_LINKED_GET(NPRODCMPSP => REC.NRN, NFCPRODPLAN => NFCPRODPLAN); + /* Открываем план */ + PKG_XFAST.DOWN_NODE(SNAME => 'XFCPRODCMP'); + /* Описываем план */ + PKG_XFAST.ATTR(SNAME => 'NRN', NVALUE => REC.NRN); + PKG_XFAST.ATTR(SNAME => 'SNAME', SVALUE => REC.SNAME); + PKG_XFAST.ATTR(SNAME => 'NID', NVALUE => REC.NID); + PKG_XFAST.ATTR(SNAME => 'NFCPRODPLANSP', NVALUE => NFCPRODPLANSP); + /* Закрываем план */ + PKG_XFAST.UP(); + end loop; + /* Закрываем корень */ + PKG_XFAST.UP(); + /* Сериализуем */ + COUT := PKG_XFAST.SERIALIZE_TO_CLOB(); + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + exception + when others then + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end FCPRODCMP_DETAILS_GET; + + /* Получение таблицы записей "Планы и отчеты производства изделий" */ + procedure FCPRODPLAN_GET + ( + NCRN in number, -- Рег. номер каталога + COUT out clob -- Сериализованная таблица данных + ) + is + NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса + NPROGRESS PKG_STD.TNUMBER; -- Прогресс плана + + /* Получение номера плана из примечания */ + function NUMB_BY_NOTE_GET + ( + SNOTE in varchar2 -- Примечание + ) return varchar2 -- Номер плана + is + begin + /* Возвращаем результат */ + return TRIM(SUBSTR(SNOTE, INSTR(SNOTE, '№')+1, length(SNOTE))); + end NUMB_BY_NOTE_GET; + + /* Получение детализации по прогрессу */ + function DETAIL_BY_PROGRESS_GET + ( + NPROGRESS in number -- Прогресс + ) return varchar2 -- Детализация по прогрессу + is + SRESULT PKG_STD.TSTRING; -- Детализация по прогрессу + begin + /* Определяем детализацию по прогрессу */ + case + when (NPROGRESS >= 70) then + SRESULT := 'Основная сборка: Стыковка агрегатов выполнена'; + when (NPROGRESS >= 40) then + SRESULT := 'Изготовление агрегатов: Фюзеляж и ОЧК не переданы в цех ОС'; + when (NPROGRESS >= 10) then + SRESULT := 'Изготовление ДСЕ: Фюзеляж и ОЧК не укомлектованы ДСЕ'; + else + SRESULT := 'Изготовление ДСЕ не начато'; + end case; + /* Возвращаем результат */ + return SRESULT; + end DETAIL_BY_PROGRESS_GET; + begin + /* Начинаем формирование XML */ + PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_); + /* Открываем корень */ + PKG_XFAST.DOWN_NODE(SNAME => 'XDATA'); + /* Цикл по планам и отчетам производства изделий */ + for REC in (select P.RN NRN, + P.NOTE SNOTE, + D_YEAR(EN.STARTDATE) NYEAR, + COALESCE(SUM(SP.LABOUR_FACT), 0) NLABOUR_FACT, + COALESCE(SUM(SP.LABOUR_NORM), 0) NLABOUR_NORM, + (select M.BDATA + from FILELINKS M, + FILELINKSUNITS U + where M.COMPANY = NCOMPANY + and U.TABLE_PRN = P.RN + and U.UNITCODE = 'CostProductPlans' + and M.RN = U.FILELINKS_PRN + and M.BDATA is not null + and rownum = 1) BIMAGE + from FCPRODPLAN P left outer join FCPRODPLANSP SP on P.RN = SP.PRN and ((SP.LABOUR_NORM is not null) or (SP.LABOUR_FACT is not null)), + FINSTATE FS, + ENPERIOD EN + where P.CRN = NCRN + and P.CATEGORY = NFCPRODPLAN_CATEGORY_MON + and P.STATUS = NFCPRODPLAN_STATUS_MON + and FS.RN = P.TYPE + and FS.CODE = SFCPRODPLAN_TYPE_MON + and EN.RN = P.CALC_PERIOD + and exists (select /*+ INDEX(UP I_USERPRIV_JUR_PERS_ROLEID) */ + null + from USERPRIV UP + where UP.JUR_PERS = P.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 = P.JUR_PERS + and UP.UNITCODE = 'CostProductPlans' + and UP.AUTHID = UTILIZER()) + group by P.RN, P.NOTE, EN.STARTDATE + order by EN.STARTDATE asc) + loop + /* Открываем план */ + PKG_XFAST.DOWN_NODE(SNAME => 'XFCPRODPLAN_INFO'); + /* Описываем план */ + PKG_XFAST.ATTR(SNAME => 'NRN', NVALUE => REC.NRN); + PKG_XFAST.ATTR(SNAME => 'SNUMB', SVALUE => NUMB_BY_NOTE_GET(SNOTE => REC.SNOTE)); + /* Определяем прогресс */ + if (REC.NLABOUR_NORM = 0) then + /* Не можем определить прогресс */ + NPROGRESS := 0; + else + /* Если факта нет */ + if (REC.NLABOUR_FACT = 0) then + /* Не можем определить прогресс */ + NPROGRESS := 0; + else + /* Не можем определить прогресс */ + NPROGRESS := REC.NLABOUR_FACT / REC.NLABOUR_NORM; + end if; + end if; + PKG_XFAST.ATTR(SNAME => 'NPROGRESS', NVALUE => NPROGRESS); + PKG_XFAST.ATTR(SNAME => 'SDETAIL', SVALUE => DETAIL_BY_PROGRESS_GET(NPROGRESS => NPROGRESS)); + PKG_XFAST.ATTR(SNAME => 'NYEAR', NVALUE => REC.NYEAR); + PKG_XFAST.VALUE(lbVALUE => REC.BIMAGE); + /* Закрываем план */ + PKG_XFAST.UP(); + end loop; + /* Закрываем корень */ + PKG_XFAST.UP(); + /* Сериализуем */ + COUT := PKG_XFAST.SERIALIZE_TO_CLOB(); + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + exception + when others then + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end FCPRODPLAN_GET; + + /* Инициализация каталогов раздела "Планы и отчеты производства изделий" */ + procedure FCPRODPLAN_CTLG_INIT + ( + COUT out clob -- Список каталогов раздела "Планы и отчеты производства изделий" + ) + is + begin + /* Начинаем формирование XML */ + PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_); + /* Открываем корень */ + PKG_XFAST.DOWN_NODE(SNAME => 'XDATA'); + /* Цикл по планам и отчетам производства изделий */ + for REC in (select TMP.NRN, + TMP.SNAME, + count(P.RN) NCOUNT_DOCS, + min(D_YEAR(P.DOCDATE)) NMIN_YEAR, + max(D_YEAR(P.DOCDATE)) NMAX_YEAR + from (select T.RN as NRN, + T.NAME as SNAME + from ACATALOG T, + UNITLIST UL + where T.DOCNAME = 'CostProductPlans' + and T.SIGNS = 1 + and T.DOCNAME = UL.UNITCODE + and (UL.SHOW_INACCESS_CTLG = 1 or exists + (select null from V_USERPRIV UP where UP.CATALOG = T.RN) or exists + (select null + from ACATALOG T1 + where exists (select null from V_USERPRIV UP where UP.CATALOG = T1.RN) + connect by prior T1.RN = T1.CRN + start with T1.CRN = T.RN)) + order by T.NAME asc) TMP + left outer join FCPRODPLAN P + on TMP.NRN = P.CRN + and P.CATEGORY = NFCPRODPLAN_CATEGORY_MON + and P.STATUS = NFCPRODPLAN_STATUS_MON + and exists (select /*+ INDEX(UP I_USERPRIV_JUR_PERS_ROLEID) */ + null + from USERPRIV UP + where UP.JUR_PERS = P.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 = P.JUR_PERS + and UP.UNITCODE = 'CostProductPlans' + and UP.AUTHID = UTILIZER()) + left outer join FINSTATE FS + on P.TYPE = FS.RN + and FS.CODE = SFCPRODPLAN_TYPE_MON + group by TMP.NRN, + TMP.SNAME + order by TMP.SNAME 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); + PKG_XFAST.ATTR(SNAME => 'NMIN_YEAR', NVALUE => REC.NMIN_YEAR); + PKG_XFAST.ATTR(SNAME => 'NMAX_YEAR', NVALUE => REC.NMAX_YEAR); + /* Закрываем план */ + PKG_XFAST.UP(); + end loop; + /* Закрываем корень */ + PKG_XFAST.UP(); + /* Сериализуем */ + COUT := PKG_XFAST.SERIALIZE_TO_CLOB(); + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + exception + when others then + /* Завершаем формирование XML */ + PKG_XFAST.EPILOGUE(); + /* Вернем ошибку */ + PKG_STATE.DIAGNOSTICS_STACKED(); + P_EXCEPTION(0, PKG_STATE.SQL_ERRM()); + end FCPRODPLAN_CTLG_INIT; end PKG_P8PANELS_MECHREC; / diff --git a/p8panels.config b/p8panels.config index e7d4535..2b88bf9 100644 --- a/p8panels.config +++ b/p8panels.config @@ -21,6 +21,10 @@ + + + + @@ -84,6 +88,46 @@ icon="calendar_month" showInPanelsList="true" preview="./img/mech_rec_cost_prod_plans.jpg"/> + + + +