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 (
+
+
+
+
+ {/*
*/}
+
+
+
+ );
+};
+
+//Контроль свойств - Заголовок первого уровня
+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 ? (
+
+ ) : null}
+ {costRouteLists.editOrderNRN ? (
+
+ ) : 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 ? (
+
+ ) : null}
+ {state.showFcroutelst ? (
+
+ ) : 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"/>
+
+
+
+