From ca5e9b24aef6737636319bc8bc973a9c43e62ba1 Mon Sep 17 00:00:00 2001 From: Mikhail Chechnev Date: Fri, 5 Apr 2024 14:41:14 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A6=D0=98=D0=A2=D0=9A-791=20-=20=D0=9F=D0=B0?= =?UTF-8?q?=D0=BD=D0=B5=D0=BB=D1=8C=20"=D0=92=D1=8B=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=20=D0=BF=D0=BE=20=D0=A2=D0=9E=D0=B8=D0=A0"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/panels/eqs_prfrm/eqs_prfrm.js | 535 +++++++++++++++++++++++++- app/panels/eqs_prfrm/layouts.js | 167 +++++++++ db/PKG_P8PANELS_EQUIPSRV.pck | 602 ++++++++++++++++++++++++++++++ db/grants.sql | 1 + 4 files changed, 1303 insertions(+), 2 deletions(-) create mode 100644 app/panels/eqs_prfrm/layouts.js create mode 100644 db/PKG_P8PANELS_EQUIPSRV.pck diff --git a/app/panels/eqs_prfrm/eqs_prfrm.js b/app/panels/eqs_prfrm/eqs_prfrm.js index 4dc9604..d16350a 100644 --- a/app/panels/eqs_prfrm/eqs_prfrm.js +++ b/app/panels/eqs_prfrm/eqs_prfrm.js @@ -7,7 +7,32 @@ //Подключение библиотек //--------------------- -import React from "react"; //Классы React +import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React +import { + Grid, + Paper, + Box, + Link, + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + InputLabel, + FormControl, + OutlinedInput, + InputAdornment, + IconButton, + Icon, + Select, + MenuItem, + FormHelperText +} 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 { ApplicationСtx } from "../../context/application"; //Контекст приложения +import { headCellRender, dataCellRender, groupCellRender, DIGITS_REG_EXP, MONTH_NAME_REG_EXP, DAY_NAME_REG_EXP } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов //----------- //Тело модуля @@ -15,8 +40,514 @@ import React from "react"; //Классы React //Корневая панель выполнения работ const EqsPrfrm = () => { + //Собственное состояние - таблица данных + const [dataGrid, setDataGrid] = useState({ + dataLoaded: false, + columnsDef: [], + groups: [], + rows: [], + reload: false + }); + + const [filter, setFilter] = useState({ + belong: "Демопример", + prodObj: "К2", + techServ: "", + respDep: "", + fromMonth: 1, + fromYear: 2024, + toMonth: 12, + toYear: 2024 + }); + + // const [filter, setFilter] = useState({ + // belong: "", + // prodObj: "", + // techServ: "", + // respDep: "", + // fromMonth: "", + // fromYear: "", + // toMonth: "", + // toYear: ""}); + + const [info, setInfo] = useState({ cntP: 0, sumP: 0, cntF: 0, sumF: 0 }); + + //Подключение к контексту приложения + const { pOnlineShowDictionary } = useContext(ApplicationСtx); + + const [filterOpen, setFilterOpen] = useState(false); + + const [filterCopy, setFilterCopy] = useState({ ...filter }); + + const [filterLock, setFilterLock] = useState(false); + + const openFilter = () => { + setFilterOpen(true); + }; + + const closeFilter = e => { + if (filterLock && e != undefined) setFilter(filterCopy); + setFilterOpen(false); + }; + + const clearFilter = () => { + setFilter({ + belong: "", + prodObj: "", + techServ: "", + respDep: "", + fromMonth: "", + fromYear: "", + toMonth: "", + toYear: "" + }); + }; + + let yearArray = []; + let today = new Date(); + + const getYearArray = () => { + for (let i = 1990; i <= today.getFullYear(); i++) { + yearArray.push(i); + } + }; + + const monthArray = ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"]; + + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //Загрузка данных таблицы с сервера + const loadData = useCallback(async () => { + if (dataGrid.reload) { + const data = await executeStored({ + stored: "PKG_P8PANELS_EQUIPSRV.EQUIPSRV_GRID", + args: { + SBELONG: filter.belong, + SPRODOBJ: filter.prodObj, + STECHSERV: filter.techServ, + SRESPDEP: filter.respDep, + NFROMMONTH: filter.fromMonth, + NFROMYEAR: filter.fromYear, + NTOMONTH: filter.toMonth, + NTOYEAR: filter.toYear + }, + respArg: "COUT", + attributeValueProcessor: (name, val) => (["caption", "name", "parent"].includes(name) ? undefined : val) + }); + let cP = 0; + let sP = 0; + let cF = 0; + let sF = 0; + let properties = []; + if (data.XROWS != null) { + data.XROWS.map(row => { + properties = []; + Object.entries(row).forEach(([key, value]) => properties.push({ name: key, data: value })); + if (properties[1].data == "Факт" || properties[2].data == "План") { + if (properties[2].data == "План") { + properties.map(p => { + if (DAY_NAME_REG_EXP.test(p.name)) cP = cP + 1; + }); + } else if (properties[1].data == "Факт") { + properties.map(p => { + if (DAY_NAME_REG_EXP.test(p.name)) cF = cF + 1; + }); + } + } else { + properties.map(p => { + if (MONTH_NAME_REG_EXP.test(p.name)) { + let str = p.data; + let m = []; + let i = 0; + while ((m = DIGITS_REG_EXP.exec(str)) != null) { + if (i == 0) sP = sP + Number(m[0].replace(",", ".")); + else { + sF = sF + Number(m[0].replace(",", ".")); + } + i++; + } + } + }); + } + }); + } + setInfo({ cntP: cP, sumP: sP, cntF: cF, sumF: sF }); + setDataGrid(pv => ({ + ...pv, + columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef, + rows: [...(data.XROWS || [])], + groups: [...(data.XGROUPS || [])], + dataLoaded: true, + reload: false + })); + } + }, [dataGrid.reload, filter, executeStored]); + + //пользовательский параметр JuridicalPerson системы + const getJurPers = useCallback(async () => { + const data = await executeStored({ + stored: "PKG_P8PANELS_EQUIPSRV.GET_JUR_PERS_PRM", + respArg: "CRES" + }); + setFilter(pv => ({ ...pv, belong: data })); + }, [executeStored]); + + useEffect(() => { + if (filterOpen) { + setFilterCopy({ ...filter }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [filterOpen]); + + //При необходимости обновить данные таблицы + useEffect(() => { + loadData(); + }, [loadData, dataGrid.reload]); + //Генерация содержимого - return
Выполнение работ ТОиР
; + return ( +
+ {getYearArray()} + + Фильтр отбора + theme.palette.grey[500] + }} + > + close + + + + + + Принадлежность + + { + pOnlineShowDictionary({ + unitCode: "JuridicalPersons", + callBack: res => + res.success === true + ? setFilter(pv => ({ ...pv, belong: res.outParameters.out_CODE })) + : null + }); + }} + edge="end" + > + list + + + } + aria-describedby="belong-outlined-helper-text" + label="Принадлежность" + /> + + + {filter.belong ? null : ( + + *Обязательное поле + + )} + + + + Значение по умолчанию + + + + + + + + Производственный объект + + { + pOnlineShowDictionary({ + unitCode: "EquipConfiguration", + callBack: res => + res.success === true + ? setFilter(pv => ({ ...pv, prodObj: res.outParameters.out_CODE })) + : null + }); + }} + edge="end" + > + list + + + } + aria-describedby="prodObj-outlined-helper-text" + label="Производственный объект" + /> + {filter.prodObj ? null : ( + + *Обязательное поле + + )} + + + + + Техническая служба + + { + pOnlineShowDictionary({ + unitCode: "INS_DEPARTMENT", + callBack: res => + res.success === true + ? setFilter(pv => ({ ...pv, techServ: res.outParameters.out_CODE })) + : null + }); + }} + edge="end" + > + list + + + } + label="Техническая служба" + /> + + + + + Ответственное подразделение + + { + pOnlineShowDictionary({ + unitCode: "INS_DEPARTMENT", + callBack: res => + res.success === true + ? setFilter(pv => ({ ...pv, respDep: res.outParameters.out_CODE })) + : null + }); + }} + edge="end" + > + list + + + } + label="Ответственное подразделение" + /> + + + + + + Начало периода: + + + + Месяц + + {filter.fromMonth ? null : ( + + *Обязательное поле + + )} + + + + + Год + + {filter.fromYear ? null : ( + + *Обязательное поле + + )} + + + + + + + + Конец периода: + + + + Месяц + + {filter.toMonth ? null : ( + + *Обязательное поле + + )} + + + + + Год + + {filter.toYear ? null : ( + + *Обязательное поле + + )} + + + + + + + + + + + + + + Фильтр отбора: {filter.belong ? `Принадлежность: ${filter.belong}` : ""}{" "} + {filter.prodObj ? `Производственный объект: ${filter.prodObj}` : ""} {filter.techServ ? `Техническая служба: ${filter.techServ}` : ""}{" "} + {filter.respDep ? `Ответственное подразделение: ${filter.respDep}` : ""}{" "} + {filter.fromMonth && filter.fromYear + ? `Начало периода: ${filter.fromMonth < 10 ? "0" + filter.fromMonth : filter.fromMonth}.${filter.fromYear}` + : ""}{" "} + {filter.toMonth && filter.toYear + ? `Конец периода: ${filter.toMonth < 10 ? "0" + filter.toMonth : filter.toMonth}.${filter.toYear}` + : ""} + + {dataGrid.dataLoaded ? ( + + + + + headCellRender({ ...prms }, filter.techServ, info.cntP, info.sumP, info.cntF, info.sumF)} + dataCellRender={prms => dataCellRender({ ...prms })} + groupCellRender={prms => groupCellRender({ ...prms })} + showCellRightBorder={true} + /> + + + + + ) : null} +
+ ); }; //---------------- diff --git a/app/panels/eqs_prfrm/layouts.js b/app/panels/eqs_prfrm/layouts.js new file mode 100644 index 0000000..ed11f27 --- /dev/null +++ b/app/panels/eqs_prfrm/layouts.js @@ -0,0 +1,167 @@ +/* + Парус 8 - + Дополнительная разметка и вёрстка клиентских элементов +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { Grid, Stack } from "@mui/material"; +import React from "react"; //Классы React + +//--------- +//Константы +//--------- + +//Шаблон чисел и имён ячеек дат +export const DIGITS_REG_EXP = /\d+,?\d*/g; +export const MONTH_NAME_REG_EXP = /_\d{4}_\d{1,2}/; +export const DAY_NAME_REG_EXP = /_\d{4}_\d{1,2}_\d{1,2}/; + +let curParent = ""; +let x = 0; + +//----------- +//Тело модуля +//----------- +const formatDate = date => { + const [year, month, day] = date.substring(1).split("_"); + let nd; + if (day == null) nd = `${month < 10 ? "0" + month : month}.${year}`; + else nd = `${day < 10 ? "0" + day : day}.${month < 10 ? "0" + month : month}.${year}`; + return nd; +}; + +export const headCellRender = ({ columnDef }, podr, cntP, sumP, cntF, sumF) => { + let cellStyle = { border: "1px solid rgba(0, 0, 0)", textAlign: "center" }; + let cellProps = {}; + let data = columnDef.caption; + + if (columnDef.expandable) { + // поменять расположение + для развёртывания + } + if (columnDef.name == "STEST") cellStyle = { display: "none" }; + if (columnDef.name == "SINFO" || columnDef.name == "SINFO2") { + cellProps = { colSpan: 2 }; + if (columnDef.name == "SINFO") { + cellStyle = { ...cellStyle, padding: "unset" }; + data = ( + + + + Подразделение: + + + {podr} + + + Кол-во ремонтов, план: + + + {cntP} + + + Трудоемкость, час. план: + + + {sumP} + + + Кол-во ремонтов, факт: + + + {cntF} + + + Трудоемкость, час. факт: + + + {sumF} + + + + ); + } + } + + if (columnDef.visible && DAY_NAME_REG_EXP.test(columnDef.name)) { + cellStyle = { ...cellStyle, paddingLeft: "5px", paddingRight: "5px", minWidth: "25px", maxWidth: "25px" }; + } + + return { cellStyle, cellProps, data }; +}; + +export const dataCellRender = ({ row, columnDef }) => { + let cellStyle = { + padding: "2px", + border: "1px solid rgba(0, 0, 0)", + textAlign: "center" + }; + let cellProps = {}; + let data = " "; + + if (row["SINFO2"] == undefined) { + if (columnDef.name == "STEST") cellProps = { colSpan: 2 }; + if (columnDef.name == "SINFO2") cellStyle = { display: "none" }; + if (columnDef.parent == "" && columnDef.expandable == true && columnDef.expanded == false) { + curParent = columnDef.name; + return { cellStyle: { ...cellStyle, height: "25px" }, data }; + } else if (columnDef.name != "SINFO2" && columnDef.parent != "" && columnDef.expandable == false && columnDef.expanded == true) { + if (columnDef.name.endsWith("_1")) { + curParent = columnDef.parent; + const [year, month] = curParent.substring(1).split("_"); + x = new Date(year, month, 0).getDate(); + cellProps = { colSpan: x }; + data = row[curParent]; + return { cellStyle, cellProps, data }; + } else { + cellStyle = { display: "none" }; + } + } + } + if (columnDef.name == "STEST" && row["SINFO2"] == "План") { + cellStyle = { ...cellStyle }; + cellProps = { rowSpan: 2 }; + } + if (columnDef.name == "STEST" && row["SINFO2"] == "Факт") { + cellStyle = { display: "none" }; + } + + switch (row[columnDef.name]) { + case "blue": + cellStyle = { ...cellStyle, backgroundColor: "royalblue", border: "1px solid rgba(0, 0, 0)" }; + cellProps = { title: formatDate(columnDef.name) }; + return { cellStyle, cellProps, data }; + case "green": + cellStyle = { ...cellStyle, backgroundColor: "lawngreen", border: "1px solid rgba(0, 0, 0)" }; + cellProps = { title: formatDate(columnDef.name) }; + return { cellStyle, cellProps, data }; + case "red": + cellStyle = { ...cellStyle, backgroundColor: "crimson", border: "1px solid rgba(0, 0, 0)" }; + cellProps = { title: formatDate(columnDef.name) }; + return { cellStyle, cellProps, data }; + case "green red": + case "red green": + cellStyle = { ...cellStyle, padding: "unset" }; + cellProps = { title: formatDate(columnDef.name) }; + data = ( + + + +

g

+
+ +

r

+
+
+
+ ); + } + return { cellStyle, cellProps }; +}; + +export const groupCellRender = () => { + let cellStyle = { display: "none" }; + return { cellStyle }; +}; diff --git a/db/PKG_P8PANELS_EQUIPSRV.pck b/db/PKG_P8PANELS_EQUIPSRV.pck new file mode 100644 index 0000000..05183ee --- /dev/null +++ b/db/PKG_P8PANELS_EQUIPSRV.pck @@ -0,0 +1,602 @@ +create or replace package PKG_P8PANELS_EQUIPSRV as + + /* Получение значения системного параметра "JuridicalPerson" */ + procedure GET_JUR_PERS_PRM + ( + CRES out clob -- Значение параметра "JuridicalPerson" (null - если не нашли) + ); + + function HOURS_STR + ( + NHOURS in number -- Кол-во часов + ) return varchar2; + + /* Выполнение работ по ТОиР */ + procedure EQUIPSRV_GRID + ( + SBELONG in varchar2, -- Принадлежность к Юр. лицу + SPRODOBJ in varchar2, -- Производственный объект + STECHSERV in varchar2, -- Техническая служба + SRESPDEP in varchar2, -- Ответственное подразделение + NFROMMONTH in number, -- Месяц начала периода + NFROMYEAR in number, -- Год начала периода + NTOMONTH in number, -- Месяц окончания периода + NTOYEAR in number, -- Год окончания периода + COUT out clob -- График проектов + ); +end PKG_P8PANELS_EQUIPSRV; +/ +create or replace package body PKG_P8PANELS_EQUIPSRV as + + /* Получение значения системного параметра "JuridicalPerson" */ + procedure GET_JUR_PERS_PRM + ( + CRES out clob -- Значение параметра "JuridicalPerson" (null - если не нашли) + ) + is + NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации + SPARAMCODE PKG_STD.TSTRING := 'JuridicalPerson'; -- Код параметра + begin + CRES := GET_OPTIONS_STR(SCODE => SPARAMCODE, + NCOMP_VERS => NCOMPANY); + if (CRES is null) then + P_EXCEPTION(0, 'Пользовательский параметр не указан.'); + end if; + end GET_JUR_PERS_PRM; + + /* Формирование строки с кол-вом часов */ + function HOURS_STR + ( + NHOURS in number -- Кол-во часов + ) return varchar2 -- Строка с кол-вом часов + is + SRESULT PKG_STD.tSTRING; -- Строка результат + begin + if (MOD(NHOURS, 10) = 1 and MOD(NHOURS, 100) != 11) then + SRESULT := NHOURS || ' час'; + elsif ((MOD(NHOURS, 10) = 2 and MOD(NHOURS, 100) != 12) + or (MOD(NHOURS, 10) = 3 and MOD(NHOURS, 100) != 13) + or (MOD(NHOURS, 10) = 4 and MOD(NHOURS, 100) != 14)) then + SRESULT := NHOURS || ' часа'; + else + SRESULT := NHOURS || ' часов'; + end if; + /* Возвращаем результат */ + return SRESULT; + end HOURS_STR; + + /* Выполнение работ по ТОиР */ + procedure EQUIPSRV_GRID + ( + SBELONG in varchar2, -- Принадлежность к Юр. лицу + SPRODOBJ in varchar2, -- Производственный объект + STECHSERV in varchar2, -- Техническая служба + SRESPDEP in varchar2, -- Ответственное подразделение + NFROMMONTH in number, -- Месяц начала периода + NFROMYEAR in number, -- Год начала периода + NTOMONTH in number, -- Месяц окончания периода + NTOYEAR in number, -- Год окончания периода + COUT out clob -- График проектов + ) + is + NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации + SPRJ_GROUP_NAME PKG_STD.TSTRING; -- Наименование группы для проекта + BEXPANDED boolean; -- Флаг раскрытости уровня + RDG PKG_P8PANELS_VISUAL.TDATA_GRID; -- Описание таблицы + RDG_ROW0 PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы0 + RDG_ROW PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы + RDG_ROW2 PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы2 + NCURYEAR PKG_STD.tNUMBER; -- Текущий год + NCURMONTH PKG_STD.tNUMBER; -- Текущий месяц + NTOTALDAYS PKG_STD.tNUMBER; -- Дней в текущем месяце + SCURTECHOBJ PKG_STD.TSTRING := null; -- Текущий технический объект + SCURTSKCODE PKG_STD.TSTRING := null; -- Текущий вид ремонта + NFROMDATE date := TO_DATE('01.' + || LPAD(TO_CHAR(NFROMMONTH), 2, '0') + || '.' || TO_CHAR(NFROMYEAR), + 'dd.mm.yyyy'); -- Дата начала периода + NTODATE date := LAST_DAY(TO_DATE('01.' + || LPAD(TO_CHAR(NTOMONTH), 2, '0') + || '.' || TO_CHAR(NTOYEAR), + 'dd.mm.yyyy')); -- Дата конца периода + NMS PKG_STD.tNUMBER; -- Месяц начала в цикле года + NME PKG_STD.tNUMBER; -- Месяц окончания в цикле года + NYEAR_PLAN PKG_STD.tNUMBER; -- Год план + NMONTH_PLAN PKG_STD.tNUMBER; -- Месяц план + NDAY_PLAN PKG_STD.tNUMBER; -- День план + NYEAR_FACT PKG_STD.tNUMBER; -- Год факт + NMONTH_FACT PKG_STD.tNUMBER; -- Месяц факт + NDAY_FACT PKG_STD.tNUMBER; -- День факт + SPERIODNAME PKG_STD.TSTRING; -- Имя периода + SFACT_CLR PKG_STD.TSTRING; -- Цвет закрашивания фактических дат + NROWS PKG_STD.tNUMBER := 0; -- Кол-во строк в курсоре + NWORKPERDAY PKG_STD.tNUMBER(17,2) := null; -- Работы в день + CR PKG_STD.TSTRING; + SGROUP_FILLED PKG_STD.tLSTRING; -- Группы, заполненные строками план/факт + SCOLS PKG_STD.tLSTRING; -- Заполнение периодов работ + YM PKG_CONTVALLOC1S.tCONTAINER; -- Коллекция для подсчёта работ за месяц + MCLR PKG_CONTVALLOC1S.tCONTAINER; -- Коллекция для закрашивания месяцев + + cursor C1 is + select TT.NEQV_RN, + TT.NEQS_RN, + TT.NWRK_RN NRN, + TT.COMPANY NCOMPANY, + TT.NAME_WORK SWORKNAME, + EC2.CODE STECHOBJCODE, + EC2.NAME STECHOBJNAME, + JP.CODE SBELONG, + EC1.CODE SPRODOBJ, + DS.CODE STECHSERV, + DR.CODE SRESPDEP, + TT.DATEPRD_BEG DDATEPLANBEG, + TT.DATEPRD_END DDATEPLANEND, + EQJ.DATEFACT_BEG DDATEFACTBEG, + EQJ.DATEFACT_END DDATEFACTEND, + EK.CODE STECSRVKINDCODE, + EK.NAME STECSRVKINDNAME, + coalesce(EW.NSUM, + (TT.DATEPRD_END - TT.DATEPRD_BEG) * 24) NSUMWORKPLAN, + coalesce(EWJ.NSUMF, + (EQJ.DATEFACT_END - EQJ.DATEFACT_BEG) * 24) NSUMWORKFACT + from + (select B.*, + C.RN nWRK_RN, + C.PRN nWRK_PRN, + C.NAME_WORK, + C.DATEPLAN_BEG, + C.DATEPLAN_END, + C.TECSRVKIND, + C.EQCONFIG, + C.DEPTPERF, + C.DEPTTCSRV, + C.RESP_AGN + from (select EQV.RN nEQV_RN, + EQV.COMPANY, + EQV.JUR_PERS, + EQV.STATE, + EQV.DATEPRD_BEG, + EQV.DATEPRD_END, + EQS.RN nEQS_RN + from EQTCHSRV EQV, -- Графики ТОиР + DOCLINKS DL, + EQRPSHEETS EQS -- Ремонтные ведомости + where EQV.RN = DL.IN_DOCUMENT (+) + and DL.OUT_UNITCODE (+) = 'EquipRepairSheets' + and DL.OUT_DOCUMENT = EQS.RN (+)) B, + EQTCHSRWRK C + where B.nEQV_RN = C.PRN (+) + union all + select B.*, + C.RN nWRK_RN, + C.PRN nWRK_PRN, + C.NAME_WORK, + C.DATEPLAN_BEG, + C.DATEPLAN_END, + C.TECSRVKIND, + C.EQCONFIG, + C.DEPTPERF, + null DEPTTCSRV, + C.RESP_AGN + from (select null nEQV_RN, + EQS.COMPANY, + EQS.JURPERSONS JUR_PERS, + EQS.STATE, + EQS.DATEPLAN_BEG, + EQS.DATEPLAN_END, + EQS.RN nEQS_RN + from EQRPSHEETS EQS -- Ремонтные ведомости + where not exists (select 1 + from DOCLINKS DL + where DL.OUT_DOCUMENT = EQS.RN + and DL.IN_UNITCODE = 'EquipTechServices')) B, + EQRPSHWRK C + where B.nEQS_RN = C.PRN (+)) TT, + EQTECSRVKIND EK, + JURPERSONS JP, + EQCONFIG EC1, + EQCONFIG EC2, + INS_DEPARTMENT DS, + INS_DEPARTMENT DR, + DOCLINKS DL, + EQTECSRVJRNL EQJ, + (select t.prn, + sum(t.Worktimeplan * t.perform_quant) NSUM + from EQTCHSRWRC t + group by t.prn) EW, + (select t.prn, + sum(t.worktimefact * t.quantfact) NSUMF + from EQTCHSRJRNLWRC t + group by t.prn) EWJ + where TT.COMPANY = NCOMPANY + and ((TT.state in (1,2) and nEQV_RN is not null) or (TT.state in (0,2,3) and nEQV_RN is null)) + and TT.DATEPRD_BEG >= NFROMDATE + and TT.DATEPRD_END <= NTODATE + and JP.CODE = SBELONG + and EC1.CODE = SPRODOBJ + and (DS.CODE = STECHSERV or STECHSERV is null) + and (DR.CODE = SRESPDEP or SRESPDEP is null) + and TT.EQCONFIG = EC2.RN (+) + and TT.DEPTPERF = DR.RN (+) + and TT.DEPTTCSRV = DS.RN (+) + and TT.NWRK_RN = EW.PRN (+) + and EQJ.RN = EWJ.PRN (+) + and TT.TECSRVKIND = EK.RN (+) + and TT.NWRK_RN = DL.IN_DOCUMENT (+) + and ((DL.OUT_UNITCODE = 'EquipTechServiceJournal' and DL.RN is not null) or (DL.OUT_UNITCODE is null and DL.RN is null)) + and DL.OUT_DOCUMENT = EQJ.RN (+) + order by EC2.NAME, EK.CODE; + begin + /* Инициализируем таблицу данных */ + RDG := PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE(); + /* Формируем структуру заголовка */ + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'STEST', + SCAPTION => 'ТЕСТ', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SINFO', + SCAPTION => 'Информация', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SINFO2', + SCAPTION => 'Объект ремонта', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + SPARENT => 'SINFO'); + 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 => 'SWORKNAME', + SCAPTION => 'Наименование работы', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'STECHOBJCODE', + SCAPTION => 'Код тех. объекта', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'STECHOBJNAME', + SCAPTION => 'Наименование тех. объекта', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SBELONG', + SCAPTION => 'Принадлежность', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SPRODOBJ', + SCAPTION => 'Производственный объект', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'STECHSERV', + SCAPTION => 'Тех. служба', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SRESPDEP', + SCAPTION => 'Отвественное подразделение', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'STECSERVCODE', + SCAPTION => 'Вид ремонта', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'DDATEPLANBEG', + SCAPTION => 'Начало работы (план)', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'DDATEPLANEND', + SCAPTION => 'Окончание работы (план)', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'DDATEFACTBEG', + SCAPTION => 'Начало работы (факт)', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'DDATEFACTEND', + SCAPTION => 'Окончание работы (факт)', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'STECSRVKINDCODE', + SCAPTION => 'Код типа работы', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'STECSRVKINDNAME', + SCAPTION => 'Наименование типа работы', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + /* Очистка коллекций */ + PKG_CONTVALLOC1S.PURGE(YM); + PKG_CONTVALLOC1S.PURGE(MCLR); + + NCURYEAR := EXTRACT(year from sysdate); + NCURMONTH := EXTRACT(month from sysdate); + + /* Цикл по годам периода */ + for Y in NFROMYEAR .. NTOYEAR + loop + if (NFROMYEAR = NTOYEAR) then + NMS := NFROMMONTH; + NME := NTOMONTH; + else + if (Y = NFROMYEAR) then + NMS := NFROMMONTH; + NME := 12; + elsif (NFROMYEAR < Y and Y < NTOYEAR) then + NMS := 1; + NME := 12; + elsif (Y = NTOYEAR) then + NMS := 1; + NME := NTOMONTH; + end if; + end if; + + /* Цикл по месяцам года */ + for M in NMS .. NME + loop + + PKG_CONTVALLOC1S.PUTN(YM, '_' || TO_CHAR(Y) || '_' || TO_CHAR(M) || '_P', 0); + PKG_CONTVALLOC1S.PUTN(YM, '_' || TO_CHAR(Y) || '_' || TO_CHAR(M) || '_F', 0); + + if (Y = NCURYEAR and M = NCURMONTH) then + BEXPANDED := true; + else + BEXPANDED := false; + end if; + + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => '_' || TO_CHAR(Y) || '_' || TO_CHAR(M), + SCAPTION => LPAD(TO_CHAR(M), 2, '0') || ' ' || TO_CHAR(Y), + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BEXPANDABLE => true, + BEXPANDED => BEXPANDED); + NTOTALDAYS := to_number(to_char(LAST_DAY(TO_DATE('01.' || LPAD(TO_CHAR(M), 2, '0') || '.' || TO_CHAR(Y), 'dd.mm.yyyy')),'dd'), '99'); + /* Цикл по дням месяца */ + for D in 1 .. NTOTALDAYS + loop + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => '_' || TO_CHAR(Y) || '_' || TO_CHAR(M) || '_' || TO_CHAR(D), + SCAPTION => TO_CHAR(D, '99'), + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + SPARENT => '_' || TO_CHAR(Y) || '_' || TO_CHAR(M)); + end loop; + end loop; + end loop; + + /* Подсчёт кол-ва записей в курсоре */ + for Q1 in C1 + loop + NROWS := NROWS + 1; + end loop; + + /* Цикл по курсору */ + for QQ in C1 + loop + NROWS := NROWS - 1; + if (SCURTECHOBJ != QQ.STECHOBJNAME or SCURTECHOBJ is null) then + if (RDG_ROW0.RCOLS is not null) then + /* Цикл по годам периода */ + for Y in NFROMYEAR .. NTOYEAR + loop + if (NFROMYEAR = NTOYEAR) then + NMS := NFROMMONTH; + NME := NTOMONTH; + else + if (Y = NFROMYEAR) then + NMS := NFROMMONTH; + NME := 12; + elsif (NFROMYEAR < Y and Y < NTOYEAR) then + NMS := 1; + NME := 12; + elsif (Y = NTOYEAR) then + NMS := 1; + NME := NTOMONTH; + end if; + end if; + + /* Цикл по месяцам года */ + for M in NMS .. NME + loop + SPERIODNAME := '_' || TO_CHAR(Y) || '_' || TO_CHAR(M); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW0, + SNAME => SPERIODNAME, + SVALUE => 'план: ' || HOURS_STR(PKG_CONTVALLOC1S.GETN(YM, SPERIODNAME || '_P')) || ' факт: ' || HOURS_STR(PKG_CONTVALLOC1S.GETN(YM, SPERIODNAME || '_F'))); + PKG_CONTVALLOC1S.PUTN(YM, SPERIODNAME || '_P', 0); + PKG_CONTVALLOC1S.PUTN(YM, SPERIODNAME || '_F', 0); + end loop; + end loop; + + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW0); + end if; + SCURTECHOBJ := QQ.STECHOBJNAME; + SPRJ_GROUP_NAME := SCURTECHOBJ; + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_GROUP(RDATA_GRID => RDG, + SNAME => SPRJ_GROUP_NAME, + SCAPTION => QQ.STECHOBJNAME, + BEXPANDABLE => false); + RDG_ROW0 := PKG_P8PANELS_VISUAL.TROW_MAKE(SGROUP => SPRJ_GROUP_NAME); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW0, SNAME => 'STEST', SVALUE => SCURTECHOBJ); + end if; + /* Формируем имя группы для вида ремонта */ + SCURTSKCODE := SCURTECHOBJ || '_' || QQ.STECSRVKINDCODE; + /* Если по данной группе еще нет строк плана и факта */ + if (STRIN(sSUBSTR => SCURTSKCODE, sSOURCE => SGROUP_FILLED, sDELIM => ';') = 0) then + /* Добавляем строку плана */ + if (RDG_ROW.RCOLS is not null) then + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + end if; + /* Добавляем строку факта */ + if (RDG_ROW2.RCOLS is not null) then + CR := PKG_CONTVALLOC1S.FIRST_(MCLR); + /* Цикл по коллекции для закрашивания месяцев */ + for Z in 1 .. PKG_CONTVALLOC1S.COUNT_(MCLR) + loop + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW2, SNAME => CR, SVALUE => PKG_CONTVALLOC1S.GETS(MCLR, CR)); + CR := PKG_CONTVALLOC1S.NEXT_(MCLR, CR); + end loop; + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW2); + end if; + PKG_CONTVALLOC1S.PURGE(MCLR); + /* Добвим группу для вида ремонта */ + SPRJ_GROUP_NAME := SCURTSKCODE; + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_GROUP(RDATA_GRID => RDG, + SNAME => SPRJ_GROUP_NAME, + SCAPTION => QQ.STECSRVKINDCODE, + BEXPANDABLE => false); + RDG_ROW := PKG_P8PANELS_VISUAL.TROW_MAKE(SGROUP => SPRJ_GROUP_NAME); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'STEST', SVALUE => QQ.STECSRVKINDCODE); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SINFO2', SVALUE => 'План'); + RDG_ROW2 := PKG_P8PANELS_VISUAL.TROW_MAKE(SGROUP => SPRJ_GROUP_NAME); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW2, SNAME => 'SINFO2', SVALUE => 'Факт'); + /* Добавляем в заполненные группы */ + SGROUP_FILLED := SGROUP_FILLED || SPRJ_GROUP_NAME || ';'; + + end if; + /* Плановые работы */ + if (QQ.NEQV_RN is not null) then + for x in 0 .. trunc(QQ.DDATEPLANEND) - trunc(QQ.DDATEPLANBEG) + loop + NYEAR_PLAN := EXTRACT(year from QQ.DDATEPLANBEG + x); + NMONTH_PLAN := EXTRACT(month from QQ.DDATEPLANBEG + x); + NDAY_PLAN := EXTRACT(day from QQ.DDATEPLANBEG + x); + + if (x = 0) then + SPERIODNAME := '_' || TO_CHAR(NYEAR_PLAN) || '_' || NMONTH_PLAN; + + if (QQ.NSUMWORKPLAN is not null) then + PKG_CONTVALLOC1S.PUTN(YM, SPERIODNAME || '_P', PKG_CONTVALLOC1S.GETN(YM, SPERIODNAME || '_P') + QQ.NSUMWORKPLAN); + end if; + + if (STRIN(sSUBSTR => SPRJ_GROUP_NAME || ' ' || SPERIODNAME || ' PLAN', sSOURCE => SCOLS, sDELIM => ';') = 0) then + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => SPERIODNAME, SVALUE => 'blue'); + SCOLS := SCOLS || SPRJ_GROUP_NAME || ' ' || SPERIODNAME || ' PLAN;'; + end if; + end if; + SPERIODNAME := '_' || TO_CHAR(NYEAR_PLAN) || '_' || TO_CHAR(NMONTH_PLAN) || '_' || TO_CHAR(NDAY_PLAN); + if (STRIN(sSUBSTR => SPRJ_GROUP_NAME || ' ' || SPERIODNAME || ' PLAN', sSOURCE => SCOLS, sDELIM => ';') = 0) then + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => SPERIODNAME, SVALUE => 'blue'); + SCOLS := SCOLS || SPRJ_GROUP_NAME || ' ' || SPERIODNAME || ' PLAN;'; + end if; + end loop; + end if; + + /* Фактические и внеплановые работы */ + if (QQ.DDATEFACTEND is not null and QQ.DDATEFACTBEG is not null) then + if (QQ.nEQV_RN is not null) then + SFACT_CLR := 'green'; + else + SFACT_CLR := 'red'; + end if; + + NWORKPERDAY := null; + if (EXTRACT(month from QQ.DDATEFACTBEG) != EXTRACT(month from QQ.DDATEFACTEND)) then + NWORKPERDAY := QQ.NSUMWORKFACT/(round(QQ.DDATEFACTEND - QQ.DDATEFACTBEG) + 1); + NCURMONTH := EXTRACT(month from QQ.DDATEFACTBEG); + end if; + + for x in 0 .. trunc(QQ.DDATEFACTEND) - trunc(QQ.DDATEFACTBEG) + loop + NYEAR_FACT := EXTRACT(year from QQ.DDATEFACTBEG + x); + NMONTH_FACT := EXTRACT(month from QQ.DDATEFACTBEG + x); + NDAY_FACT := EXTRACT(day from QQ.DDATEFACTBEG + x); + + if (x = 0 or NCURMONTH != NMONTH_FACT) then + if (NCURMONTH != NMONTH_FACT) then + NCURMONTH := NMONTH_FACT; + end if; + SPERIODNAME := '_' || TO_CHAR(NYEAR_FACT) || '_' || NMONTH_FACT; + + if (QQ.NSUMWORKFACT is not null and NWORKPERDAY is null) then + PKG_CONTVALLOC1S.PUTN(YM, SPERIODNAME || '_F', PKG_CONTVALLOC1S.GETN(YM, SPERIODNAME || '_F') + QQ.NSUMWORKFACT); + end if; + + /* Добавление в коллекцию окрашивания месяца */ + if (PKG_CONTVALLOC1S.EXISTS_(rCONTAINER => MCLR, sROWID => SPERIODNAME) = false) then + PKG_CONTVALLOC1S.PUTS(MCLR, SPERIODNAME, SFACT_CLR); + else + if (STRIN(trim(SFACT_CLR), trim(PKG_CONTVALLOC1S.GETS(MCLR, SPERIODNAME))) = 0) then + PKG_CONTVALLOC1S.PUTS(MCLR, SPERIODNAME, PKG_CONTVALLOC1S.GETS(MCLR, SPERIODNAME) || ' ' || SFACT_CLR); + end if; + end if; + end if; + if (NWORKPERDAY is not null) then + PKG_CONTVALLOC1S.PUTN(YM, SPERIODNAME || '_F', PKG_CONTVALLOC1S.GETN(YM, SPERIODNAME || '_F') + NWORKPERDAY); + end if; + SPERIODNAME := '_' || TO_CHAR(NYEAR_FACT) || '_' || TO_CHAR(NMONTH_FACT) || '_' || TO_CHAR(NDAY_FACT); + /* Добавление окрашивания дней факта */ + if (PKG_CONTVALLOC1S.EXISTS_(rCONTAINER => MCLR, sROWID => SPERIODNAME) = false) then + PKG_CONTVALLOC1S.PUTS(MCLR, SPERIODNAME, SFACT_CLR); + else + if (trim(PKG_CONTVALLOC1S.GETS(MCLR, SPERIODNAME)) = 'green' and trim(SFACT_CLR) = 'red') then + PKG_CONTVALLOC1S.PUTS(MCLR, SPERIODNAME, SFACT_CLR); + end if; + end if; + end loop; + end if; + + if (RDG_ROW0.RCOLS is not null and NROWS = 0) then + /* Цикл по годам периода */ + for Y in NFROMYEAR .. NTOYEAR + loop + if (NFROMYEAR = NTOYEAR) then + NMS := NFROMMONTH; + NME := NTOMONTH; + else + if (Y = NFROMYEAR) then + NMS := NFROMMONTH; + NME := 12; + elsif (NFROMYEAR < Y and Y < NTOYEAR) then + NMS := 1; + NME := 12; + elsif (Y = NTOYEAR) then + NMS := 1; + NME := NTOMONTH; + end if; + end if; + + /* Цикл по месяцам года */ + for M in NMS .. NME + loop + SPERIODNAME := '_' || TO_CHAR(Y) || '_' || TO_CHAR(M); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW0, + SNAME => SPERIODNAME, + SVALUE => 'план: ' || HOURS_STR(PKG_CONTVALLOC1S.GETN(YM, SPERIODNAME || '_P')) || ' факт: ' || HOURS_STR(PKG_CONTVALLOC1S.GETN(YM, SPERIODNAME || '_F'))); + end loop; + end loop; + + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW0); + end if; + /* План для последней записи */ + if (RDG_ROW.RCOLS is not null and NROWS = 0) then + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + end if; + /* Факт для последней записи */ + if (RDG_ROW2.RCOLS is not null and NROWS = 0) then + CR := PKG_CONTVALLOC1S.FIRST_(MCLR); + for Z in 1 .. PKG_CONTVALLOC1S.COUNT_(MCLR) + loop + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW2, SNAME => CR, SVALUE => PKG_CONTVALLOC1S.GETS(MCLR, CR)); + CR := PKG_CONTVALLOC1S.NEXT_(MCLR, cr); + end loop; + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW2); + end if; + end loop; + /* Сериализуем описание */ + COUT := PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1); + PKG_CONTVALLOC1S.PURGE(YM); + end EQUIPSRV_GRID; +end PKG_P8PANELS_EQUIPSRV; +/ diff --git a/db/grants.sql b/db/grants.sql index 5fa3315..695a5a6 100644 --- a/db/grants.sql +++ b/db/grants.sql @@ -1,3 +1,4 @@ grant execute on PKG_P8PANELS to public; grant execute on PKG_P8PANELS_PROJECTS to public; grant execute on PKG_P8PANELS_SAMPLES to public; +grant execute on PKG_P8PANELS_EQUIPSRV to public;