diff --git a/app/panels/prj_info/filter.js b/app/panels/prj_info/filter.js new file mode 100644 index 0000000..3642f49 --- /dev/null +++ b/app/panels/prj_info/filter.js @@ -0,0 +1,170 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Фильтр +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Grid, Chip, Stack, Input, InputAdornment, IconButton, Icon } from "@mui/material"; //Интерфейсные элементы +import { FILTER_INITIAL, FILTER_ITEMS, PRICE_STRUCT_STATUS, PROJECT_STATE, FilterDialog } from "./filter_dialog"; //Компонент "Диалог фильтра" + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + CONTAINER: { paddingTop: "10px" }, + FILTER: { maxWidth: "99vw" }, + SEARCH_GRID_ITEM: { paddingRight: "15px" } +}; + +//------------------------------------ +//Вспомогательные функции и компоненты +//------------------------------------ + +//Элемент фильтра +const FilterItem = ({ caption, value, defaultValue, onClick, onDelete }) => { + //При нажатии на элемент + const handleClick = () => (onClick ? onClick() : null); + + //При нажатии на удаление элемента + const handleDelete = () => (onDelete ? onDelete() : null); + + //Генерация содержимого + return ( + + {caption}: {value || defaultValue} + + } + variant="outlined" + onClick={handleClick} + onDelete={onDelete ? handleDelete : null} + /> + ); +}; + +//Контроль свойств компонента - Элемент фильтра +FilterItem.propTypes = { + caption: PropTypes.string.isRequired, + value: PropTypes.any, + defaultValue: PropTypes.string.isRequired, + onClick: PropTypes.func, + onDelete: PropTypes.func +}; + +//----------- +//Тело модуля +//----------- + +//Фильтр +const Filter = ({ values, onChange }) => { + //Собственное состояние - отображение диалога ввода значений фильтра + const [isOpen, setIsOpen] = useState(false); + + //Собственное состояние - строка поиска + const [search, setSearch] = useState(values.search); + + //Передача сообщения об измении фильтра родителю + const notifyChange = values => (onChange ? onChange(values) : null); + + //При закрытии диалога с сохранением значений + const handleFilterDialogOk = values => { + setIsOpen(false); + notifyChange(values); + }; + + //При закрытии диалога без сохранения значений + const handleFilterDialogCancel = () => setIsOpen(false); + + //При нажатии на фильтр + const handleClick = () => setIsOpen(true); + + //При выполнении поиска + const handleDoSearch = (clear = false) => { + if (clear === true) setSearch(""); + notifyChange({ ...values, search: clear === true ? "" : search }); + }; + + //При изменении значения в строке поиска + const handleSearchChange = e => setSearch(e.target.value); + + //При нажатии клавиши в строке поиска + const handleSearchKeyPress = e => ([13, 27].includes(e.keyCode) ? handleDoSearch(e.keyCode == 27) : null); + + //Формирование функции обработки очистки элемента фильтар + const buildFilterItemClearHandler = сode => + values[сode] != FILTER_INITIAL[сode] ? () => notifyChange({ ...values, [сode]: FILTER_INITIAL[сode] }) : null; + + //Генерация содержимого + return ( + + + {isOpen ? : null} + + + + item.value == values.priceStructStatus)?.name} + onDelete={buildFilterItemClearHandler("priceStructStatus")} + /> + item.value == values.prjState)?.name} + onDelete={buildFilterItemClearHandler("prjState")} + /> + + + + + handleDoSearch(true)}> + clear + + + search + + + } + onKeyDown={handleSearchKeyPress} + onChange={handleSearchChange} + /> + + + ); +}; + +//Контроль свойств компонента - Фильтр +Filter.propTypes = { + values: FILTER_ITEMS.isRequired, + onChange: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { FILTER_INITIAL, Filter }; diff --git a/app/panels/prj_info/filter_dialog.js b/app/panels/prj_info/filter_dialog.js new file mode 100644 index 0000000..c9d8f90 --- /dev/null +++ b/app/panels/prj_info/filter_dialog.js @@ -0,0 +1,147 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Диалог фильтра +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useContext } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Button, Dialog, DialogTitle, DialogContent, DialogActions } from "@mui/material"; //Интерфейсные элементы +import { ApplicationСtx } from "../../context/application"; //Контекст приложения +import { BUTTONS } from "../../../app.text"; //Типовые тексты +import { APP_STYLES } from "../../../app.styles"; //Типовые стили +import { FormField } from "./layouts"; //Общие компоненты панели + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + DIALOG_CONTENT: { overflowY: "auto", ...APP_STYLES.SCROLL } +}; + +//Структура фильтра +const FILTER_ITEMS = PropTypes.shape({ + prjType: PropTypes.string, + insDep: PropTypes.string, + priceStructStatus: PropTypes.number.isRequired, + prjState: PropTypes.number.isRequired, + search: PropTypes.string +}); + +//Начальное состояние фильтра +const FILTER_INITIAL = { prjType: "", insDep: "", priceStructStatus: 0, prjState: 0, search: "" }; + +//Статусы структуры цены +const PRICE_STRUCT_STATUS = [ + { value: 0, name: "Все" }, + { value: 1, name: "Есть статьи с расходом больше 90%" }, + { value: 2, name: "Есть статьи с перерасходом" } +]; + +//Состояния проекта +const PROJECT_STATE = [ + { value: 0, name: "Все" }, + { value: 1, name: "Открытые" }, + { value: 2, name: "Неоткрытые" } +]; + +//----------- +//Тело модуля +//----------- + +//Диалог фильтра +const FilterDialog = ({ valuesInitial, onOk, onCancel }) => { + //Собственное состояние элементов фильтра + const [values, setValues] = useState({ ...valuesInitial }); + + //Подключение к контексту приложения + const { pOnlineShowDictionary } = useContext(ApplicationСtx); + + //Изменение элемента формы фильтра + const handleValueChanged = (name, value) => setValues(pv => ({ ...pv, [name]: value })); + + //Сброс настроек фильтра + const handleResetClick = () => setValues({ ...FILTER_INITIAL }); + + //Сохранение фильтра + const handleOkClick = () => (onOk ? onOk(values) : null); + + //Отмена фильтра + const handleCancelClick = () => (onCancel ? onCancel() : null); + + //Выбор значения элемента формы из словаря + const selectFromDictionary = (unitCode, name, applyValue) => { + pOnlineShowDictionary({ + unitCode, + showMethod: "main", + inputParameters: [{ name: "in_CODE", value: values[name] }], + callBack: res => applyValue(res.success ? [{ name, value: res.outParameters.out_CODE }] : null) + }); + }; + + //Генерация содержимого + return ( + + Фильтр отбора + + selectFromDictionary("ProjectTypes", "prjType", applyValue)} + /> + selectFromDictionary("INS_DEPARTMENT", "insDep", applyValue)} + /> + + + + + + + + + + ); +}; + +//Контроль свойств компонента - Диалог фильтра +FilterDialog.propTypes = { + valuesInitial: FILTER_ITEMS.isRequired, + onOk: PropTypes.func, + onCancel: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { FILTER_ITEMS, FILTER_INITIAL, PRICE_STRUCT_STATUS, PROJECT_STATE, FilterDialog }; diff --git a/app/panels/prj_info/index.js b/app/panels/prj_info/index.js new file mode 100644 index 0000000..2a01b83 --- /dev/null +++ b/app/panels/prj_info/index.js @@ -0,0 +1,16 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Панель мониторинга: точка входа +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { PrjInfo } from "./prj_info"; //Корневая панель информации о проектах + +//---------------- +//Интерфейс модуля +//---------------- + +export const RootClass = PrjInfo; diff --git a/app/panels/prj_info/layouts.js b/app/panels/prj_info/layouts.js new file mode 100644 index 0000000..bd65b4c --- /dev/null +++ b/app/panels/prj_info/layouts.js @@ -0,0 +1,168 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Общие дополнительная разметка и вёрстка клиентских элементов +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useEffect } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Box, Icon, Input, InputAdornment, FormControl, Select, InputLabel, MenuItem, IconButton, Typography, Switch, Stack } from "@mui/material"; //Интерфейсные компоненты + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + STATE: value => ({ color: value === 1 ? "green" : "black" }), + COST_STATUS: color => ({ color, verticalAlign: "middle" }), + COST_READY: value => ({ color: value <= 30 ? "red" : value >= 80 ? "green" : "#e2af00" }), + TOGGLE_COLOR: checked => ({ color: checked ? "#006dd9" : "lightgrey" }) +}; + +//----------- +//Тело модуля +//----------- + +//Поле ввода формы +const FormField = ({ elementCode, elementValue, labelText, onChange, dictionary, list, type, ...other }) => { + //Значение элемента + const [value, setValue] = useState(elementValue); + + //При получении нового значения из вне + useEffect(() => { + setValue(elementValue); + }, [elementValue]); + + //Выбор значения из словаря + const handleDictionaryClick = () => + dictionary ? dictionary(res => (res ? res.map(i => handleChange({ target: { name: i.name, value: i.value } })) : null)) : null; + + //Изменение значения элемента (по событию) + const handleChange = e => { + setValue(e.target.value); + if (onChange) onChange(e.target.name, e.target.value); + }; + + //Генерация содержимого + return ( + + + {list ? ( + <> + + {labelText} + + + + ) : ( + <> + + {labelText} + + + + list + + + ) : null + } + {...(type ? { type } : {})} + onChange={handleChange} + /> + + )} + + + ); +}; + +//Контроль свойств - Поле ввода формы +FormField.propTypes = { + elementCode: PropTypes.string.isRequired, + elementValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.instanceOf(Date)]), + labelText: PropTypes.string.isRequired, + onChange: PropTypes.func, + dictionary: PropTypes.func, + list: PropTypes.array, + type: PropTypes.string +}; + +//Переключатель +const Toggle = ({ labels, checked, onChange }) => { + //Обработка переключения + const handleChange = event => (onChange ? onChange(event.target.checked) : null); + + //Генерация содержимого + return ( + + {labels[0]} + + {labels[1]} + + ); +}; + +//Контроль свойств компонента - Переключатель +Toggle.propTypes = { + labels: PropTypes.arrayOf(PropTypes.string).isRequired, + checked: PropTypes.bool.isRequired, + onChange: PropTypes.func +}; + +//Формирование значения для колонки "Статус структуры цены" +const formatCostStatusValue = ({ value, onClick, type = 1 }) => { + const [text, color] = + value == 0 + ? ["Без отклонений", "lightgreen"] + : value == 1 + ? [type == 1 ? "Есть статьи с расходом более 90%" : "Расход более 90%", "#ffdf71"] + : value == 2 + ? [type == 1 ? "Есть статьи с перерасходом" : "Перерасход", "#eb6b6b"] + : ["Не определено", "lightgray"]; + return onClick ? ( + + + circle + + + ) : ( + + circle + + ); +}; + +//Формирование значения для колонки "Готов (%, зтраты)" +const formatCostReadyValue = value => { + return {value}; +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { STYLES as COMMON_STYLES, FormField, Toggle, formatCostStatusValue, formatCostReadyValue }; diff --git a/app/panels/prj_info/prj_info.js b/app/panels/prj_info/prj_info.js new file mode 100644 index 0000000..46b6737 --- /dev/null +++ b/app/panels/prj_info/prj_info.js @@ -0,0 +1,27 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Корневой компонент панели +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import { Projects } from "./projects"; //Список проектов + +//----------- +//Тело модуля +//----------- + +//Корневой компонент панели "Информация о проектах" +const PrjInfo = () => { + //Генерация содержимого + return ; +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { PrjInfo }; diff --git a/app/panels/prj_info/projects.js b/app/panels/prj_info/projects.js new file mode 100644 index 0000000..76b4776 --- /dev/null +++ b/app/panels/prj_info/projects.js @@ -0,0 +1,70 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Список проектов +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useContext } from "react"; //Классы React +import { P8PDataGrid } from "../../components/p8p_data_grid"; //Таблица данных +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения +import { ApplicationСtx } from "../../context/application"; //Контекст приложения +import { useProjectsDataGrid } from "./projects_hooks"; //Хуки списка проектов +import { FILTER_INITIAL, Filter } from "./filter"; //Компонент "Фильтр" +import { PROJECTS_STYLES, projectDataCellRender, projectRowExpandRender } from "./projects_layouts"; //Дополнительная разметка и вёрстка клиентских элементов + +//----------- +//Тело модуля +//----------- + +//Список проектов +const Projects = () => { + //Собственное состояние + const [projects, setProjects] = useState({ pageNumber: 1, orders: [], filter: { ...FILTER_INITIAL } }); + + //Состояние таблицы проектов + const [projectsDataGrid] = useProjectsDataGrid({ ...projects.filter, pageNumber: projects.pageNumber, orders: projects.orders }); + + //Подключение к контексту приложения + const { pOnlineShowDocument } = useContext(ApplicationСtx); + + //Отображение записи проекта в штатном разделе + const showProject = async rn => pOnlineShowDocument({ unitCode: "Projects", document: rn, modal: false }); + + //При изменении количества отображаемых страниц + const handlePagesCountChanged = () => setProjects(pv => ({ ...pv, pageNumber: pv.pageNumber + 1 })); + + //При изменении состояния сортировки + const handleOrderChanged = ({ orders }) => setProjects(pv => ({ ...pv, orders: [...orders], pageNumber: 1 })); + + //При изменении фильтра + const handleFilterChanged = values => setProjects(pv => ({ ...pv, filter: { ...values }, pageNumber: 1 })); + + //Генерация содержимого + return ( + <> + + {projectsDataGrid.init ? ( + projectDataCellRender({ ...prms, showProject })} + rowExpandRender={projectRowExpandRender} + /> + ) : null} + + ); +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { Projects }; diff --git a/app/panels/prj_info/projects_hooks.js b/app/panels/prj_info/projects_hooks.js new file mode 100644 index 0000000..667f0bc --- /dev/null +++ b/app/panels/prj_info/projects_hooks.js @@ -0,0 +1,82 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Список проектов: пользовательские хуки для взаимодействия с сервером +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { useState, useContext, useEffect } from "react"; //Классы React +import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером +import { object2Base64XML, formatDateRF } from "../../core/utils"; //Вспомогательные функции +import config from "../../../app.config"; //Настройки приложения + +//--------- +//Константы +//--------- + +//Размер страницы данных +const DATA_GRID_PAGE_SIZE = config.SYSTEM.PAGE_SIZE; + +//----------- +//Тело модуля +//----------- + +//Получение данных проектов с сервера +const useProjectsDataGrid = ({ prjType, insDep, priceStructStatus, prjState, search, pageNumber, orders }) => { + //Собственное состояние - флаг загрузки + const [isLoading, setLoading] = useState(false); + + //Собственное состояние - таблица данных + const [data, setData] = useState({ init: false, morePages: true }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //При необходимости обновить данные таблицы + useEffect(() => { + //Загрузка данных таблицы с сервера + const loadData = async () => { + try { + setLoading(true); + const data = await executeStored({ + stored: "PKG_P8PANELS_PROJECTS.INFO_PROJECTS_DG", + args: { + SPRJ_TYPE: prjType, + SINS_DEPARTMENT: insDep, + NCOST_STATUS: priceStructStatus, + NSTATE: prjState, + SSEARCH: search, + CORDERS: { VALUE: object2Base64XML(orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + NPAGE_NUMBER: pageNumber, + NPAGE_SIZE: DATA_GRID_PAGE_SIZE, + NINCLUDE_DEF: pageNumber == 1 ? 1 : 0 + }, + respArg: "COUT", + loader: true, + attributeValueProcessor: (name, val) => (["DBEGPLAN", "DENDPLAN"].includes(name) ? formatDateRF(val) : val) + }); + setData(pv => ({ + ...pv, + columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef || [], + rows: pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...(pv.rows || []), ...(data.XDATA_GRID.rows || [])], + morePages: DATA_GRID_PAGE_SIZE == 0 ? false : (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE, + init: true + })); + } finally { + setLoading(false); + } + }; + loadData(); + }, [prjType, insDep, priceStructStatus, prjState, search, pageNumber, orders, executeStored, SERV_DATA_TYPE_CLOB]); + + //Возвращаем интерфейс хука + return [data, isLoading]; +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { useProjectsDataGrid }; diff --git a/app/panels/prj_info/projects_layouts.js b/app/panels/prj_info/projects_layouts.js new file mode 100644 index 0000000..0fe7af6 --- /dev/null +++ b/app/panels/prj_info/projects_layouts.js @@ -0,0 +1,96 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Список проектов: дополнительная разметка и вёрстка клиентских элементов +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import { Icon, Stack, Paper, Link } from "@mui/material"; //Интерфейсные элементы +import { P8P_DATA_GRID_MORE_HEIGHT } from "../../components/p8p_data_grid"; //Таблица данных +import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы +import { APP_STYLES } from "../../../app.styles"; //Типовые стили +import { COMMON_STYLES, formatCostStatusValue, formatCostReadyValue } from "./layouts"; //Общие стили и разметка панели +import { Stages } from "./stages"; //Компонент "Этапы проекта" + +//--------- +//Константы +//--------- + +//Высота фильтра (пиксели) +const FILTER_HEIGHT = "60px"; + +//Стили +const STYLES = { + DATA_GRID_CONTAINER: morePages => ({ + height: `calc(100vh - ${APP_BAR_HEIGHT} - ${FILTER_HEIGHT} - ${morePages ? P8P_DATA_GRID_MORE_HEIGHT : "0px"} - 8px)`, + ...APP_STYLES.SCROLL + }) +}; + +//----------- +//Тело модуля +//----------- + +//Формирование значения для колонки "Состояние" проекта +const formatPrjStateValue = value => { + const [text, icon] = + value == 0 + ? ["Зарегистрирован", "app_registration"] + : value == 1 + ? ["Открыт", "lock_open"] + : value == 2 + ? ["Остановлен", "do_not_disturb_on"] + : value == 3 + ? ["Закрыт", "lock_outline"] + : value == 4 + ? ["Согласован", "thumb_up_alt"] + : ["Исполнение прекращено", "block"]; + return ( + + + {icon} + + + ); +}; + +//Форматирование ячеек таблицы "Проекты" +const projectDataCellRender = ({ row, columnDef, showProject }) => { + //Формирование представлений + switch (columnDef.name) { + case "NCOST_STATUS": + return { cellProps: { align: "center" }, data: formatCostStatusValue({ value: row[columnDef.name] }) }; + case "NCOST_READY": + return { cellProps: { align: "center" }, data: formatCostReadyValue(row[columnDef.name]) }; + case "NSTATE": + return { cellProps: { align: "center" }, data: formatPrjStateValue(row[columnDef.name]) }; + case "SCODE": + return { + data: ( + showProject(row["NRN"])}> + {row[columnDef.name]} + + ) + }; + default: + return { data: row[columnDef.name] }; + } +}; + +//Генерация представления расширения строки таблицы "Проектов" +const projectRowExpandRender = ({ row }) => { + return ( + + + + ); +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { STYLES as PROJECTS_STYLES, projectDataCellRender, projectRowExpandRender }; diff --git a/app/panels/prj_info/stage_detail.js b/app/panels/prj_info/stage_detail.js new file mode 100644 index 0000000..326729a --- /dev/null +++ b/app/panels/prj_info/stage_detail.js @@ -0,0 +1,173 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Детальная информация об этапе проекта +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useContext } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Grid, Box, Typography, Paper, Drawer, IconButton, Icon } from "@mui/material"; //Интерфейсные элементы +import { ApplicationСtx } from "../../context/application"; //Контекст приложения +import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером +import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений +import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы +import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных +import { P8PChart } from "../../components/p8p_chart"; //График +import { P8PAppInlineError } from "../../components/p8p_app_message"; //Встраиваемое сообщение об ошибке +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения +import { useStageDetailInfoDataGrid, useStageDetailArtsDataGrid, useStageDetailArtsChart } from "./stage_detail_hooks"; //Хуки детализации этапов проекта +import { Toggle } from "./layouts"; //Общая разметка и компоненты панели +import { + STAGE_DETAIL_STYLES, + stageDetailInfoHeadCellRender, + stageDetailInfoDataCellRender, + stageDetailArtsHeadCellRender, + stageDetailArtsDataCellRender +} from "./stage_detail_layouts"; //Дополнительная разметка и вёрстка клиентских элементов + +//------------------------------------ +//Вспомогательные функции и компоненты +//------------------------------------ + +//Данные этапа +const StageDetailData = ({ stageRn }) => { + //Собственное состояние + const [state, setState] = useState({ artsDisplayType: 0, artsChartType: 0 }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //Подключение к контексту приложения + const { pOnlineShowUnit } = useContext(ApplicationСtx); + + //Подключение к контексту сообщений + const { showMsgErr } = useContext(MessagingСtx); + + //Отображение журнала затрат (фактического, по рег. номеру ЛС и статьи затрат) + const showCostNotesFact = async ({ faceAccRn, artclRn }) => { + const data = await executeStored({ + stored: "PKG_P8PANELS_PROJECTS.INFO_FCCOSTNOTES_FACT_SELECT", + args: { NFACEACC: faceAccRn, NFPDARTCL: artclRn } + }); + if (data.NIDENT) pOnlineShowUnit({ unitCode: "CostNotes", inputParameters: [{ name: "in_IDENT", value: data.NIDENT }] }); + else showMsgErr(TEXTS.NO_DATA_FOUND); + }; + + //Состояние таблицы с информацией об этапе + const [stageDeatilInfoDataGrid] = useStageDetailInfoDataGrid({ stageRn }); + + //Состояние таблицы с данными структуры цены + const [stageDeatilArtsDataGrid] = useStageDetailArtsDataGrid({ stageRn }); + + //Состояние графика с данными структуры цены + const [stageDeatilArtsChart] = useStageDetailArtsChart({ stageRn, display: state.artsDisplayType == 1, type: state.artsChartType }); + + //При изменении способа отображения структуры цены + const handleArtsDisplayTypeChange = checked => setState(pv => ({ ...pv, artsDisplayType: checked ? 1 : 0 })); + + //При изменении типа данных графика структуры цены + const handleArtsChartTypeChange = checked => setState(pv => ({ ...pv, artsChartType: checked ? 1 : 0 })); + + //Отработка нажатия на график + const handleChartClick = ({ item }) => + state.artsChartType === 1 && item.NFACEACC && item.NFPDARTCL + ? showCostNotesFact({ faceAccRn: item.NFACEACC, artclRn: item.NFPDARTCL }) + : null; + + //Генерация содержимого + return ( + + + + Сведения + + {stageDeatilInfoDataGrid.init ? ( + + ) : null} + + + + + Структура цены + + + + {state.artsDisplayType === 0 ? ( + stageDeatilArtsDataGrid.init ? ( + stageDetailArtsDataCellRender({ ...prms, showCostNotesFact })} + /> + ) : null + ) : ( + + + + {stageDeatilArtsDataGrid?.rows?.length > 0 ? ( + stageDeatilArtsChart.init ? ( + + ) : null + ) : ( + + )} + + + )} + + + ); +}; + +//Контроль свойств компонента - Данные этапа +StageDetailData.propTypes = { + stageRn: PropTypes.number +}; + +//----------- +//Тело модуля +//----------- + +//Детальная информация об этапе проекта +const StageDetail = ({ stageRn, stageName, isOpen, onClose }) => { + return ( + + + + close + + {`Этап: ${stageName}`} + + + + ); +}; + +//Контроль свойств компонента - Детальная информация об этапе проекта +StageDetail.propTypes = { + stageRn: PropTypes.number, + stageName: PropTypes.string, + isOpen: PropTypes.bool.isRequired, + onClose: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { StageDetail }; diff --git a/app/panels/prj_info/stage_detail_hooks.js b/app/panels/prj_info/stage_detail_hooks.js new file mode 100644 index 0000000..0a7563a --- /dev/null +++ b/app/panels/prj_info/stage_detail_hooks.js @@ -0,0 +1,126 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Детальная информация об этапе проекта: пользовательские хуки для взаимодействия с сервером +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { useState, useContext, useEffect } from "react"; //Классы React +import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером + +//----------- +//Тело модуля +//----------- + +//Детали этапа проекта - информация об этапе +const useStageDetailInfoDataGrid = ({ stageRn }) => { + //Собственное состояние - флаг загрузки + const [isLoading, setLoading] = useState(false); + + //Собственное состояние - таблица данных + const [data, setData] = useState({ init: false }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //При необходимости обновить данные таблицы + useEffect(() => { + //Загрузка данных таблицы с сервера + const loadData = async () => { + try { + setLoading(true); + const data = await executeStored({ + stored: "PKG_P8PANELS_PROJECTS.INFO_STAGE_DTL_DG", + args: { NPROJECTSTAGE: stageRn }, + respArg: "COUT", + loader: true + }); + setData(pv => ({ ...pv, ...data.XDATA_GRID, init: true })); + } finally { + setLoading(false); + } + }; + if (stageRn) loadData(); + }, [stageRn, executeStored]); + + //Возвращаем интерфейс хука + return [data, isLoading]; +}; + +//Детали этапа проекта - структура цены - таблица данных +const useStageDetailArtsDataGrid = ({ stageRn }) => { + //Собственное состояние - флаг загрузки + const [isLoading, setLoading] = useState(false); + + //Собственное состояние - таблица данных + const [data, setData] = useState({ init: false }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //При необходимости обновить данные таблицы + useEffect(() => { + //Загрузка данных таблицы с сервера + const loadData = async () => { + try { + setLoading(true); + const artsData = await executeStored({ + stored: "PKG_P8PANELS_PROJECTS.INFO_STAGE_ARTS_DG", + args: { NPROJECTSTAGE: stageRn }, + respArg: "COUT", + loader: true + }); + setData(pv => ({ ...pv, ...artsData.XDATA_GRID, init: true })); + } finally { + setLoading(false); + } + }; + if (stageRn) loadData(); + }, [stageRn, executeStored, SERV_DATA_TYPE_CLOB]); + + //Возвращаем интерфейс хука + return [data, isLoading]; +}; + +//Детали этапа проекта - структура цены - график +const useStageDetailArtsChart = ({ stageRn, display, type }) => { + //Собственное состояние - флаг загрузки + const [isLoading, setLoading] = useState(false); + + //Собственное состояние - график + const [data, setData] = useState({ init: false, currentType: null }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //При необходимости обновить данные таблицы + useEffect(() => { + //Загрузка данных таблицы с сервера + const loadData = async () => { + try { + setLoading(true); + const data = await executeStored({ + stored: "PKG_P8PANELS_PROJECTS.INFO_STAGE_ARTS_CHART", + args: { NPROJECTSTAGE: stageRn, NTYPE: type }, + respArg: "COUT", + loader: true + }); + setData(pv => ({ ...pv, ...data.XCHART, currentType: type, init: true })); + } finally { + setLoading(false); + } + }; + if (stageRn && display && data.currentType != type) loadData(); + }, [stageRn, display, type, data.currentType, executeStored]); + + //Возвращаем интерфейс хука + return [data, isLoading]; +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { useStageDetailInfoDataGrid, useStageDetailArtsDataGrid, useStageDetailArtsChart }; diff --git a/app/panels/prj_info/stage_detail_layouts.js b/app/panels/prj_info/stage_detail_layouts.js new file mode 100644 index 0000000..d4b22e2 --- /dev/null +++ b/app/panels/prj_info/stage_detail_layouts.js @@ -0,0 +1,148 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Детальная информация об этапе проекта: дополнительная разметка и вёрстка клиентских элементов +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import { Link } from "@mui/material"; //Интерфейсные элементы +import { APP_STYLES } from "../../../app.styles"; //Типовые стили +import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы +import { formatNumberRFCurrency } from "../../core/utils"; //Вспомогательные функции +import { formatCostStatusValue } from "./layouts"; //Общие стили и разметка панели +import { formatStageStatusValue } from "./stages_layouts"; //Cтили и разметка списка этапов проекта + +//--------- +//Константы +//--------- + +//Высота заголовка информационного блока +const DATA_AREA_HEADER_HEIGHT = "52px"; + +//Стили +const STYLES = { + STAGE_DETAIL_DRAWER: { flexShrink: 0, [`& .MuiDrawer-paper`]: { width: "70%", boxSizing: "border-box", ...APP_STYLES.SCROLL } }, + STAGE_DETAIL_HEADER: { + height: APP_BAR_HEIGHT, + paddingLeft: "24px", + backgroundColor: "#1976d2", + display: "flex", + alignItems: "center", + justifyContent: "flex-start" + }, + STAGE_DETAIL_CLOSE_BUTTON: { color: "white", marginBottom: "3px" }, + DATA_AREA_CONTAINER: { paddingLeft: "10px", paddingRight: "10px" }, + DATA_AREA: { height: `calc(100vh - ${APP_BAR_HEIGHT} - ${DATA_AREA_HEADER_HEIGHT} - 10px)`, overflowY: "auto", ...APP_STYLES.SCROLL }, + DATA_AREA_HEADER_CONTAINER: { display: "flex", justifyContent: "space-between" }, + DATA_AREA_HEADER: { paddingTop: "10px", paddingBottom: "10px" }, + DATA_GRID_HEADER: { fontSize: "10pt", padding: "6px 10px" }, + DATA_GRID_CELL: value => ({ fontSize: "9pt", padding: "6px 10px", ...(value ? { color: value > 0 ? "green" : "red" } : {}) }), + CHART_CONTAINER: { paddingTop: "20px" }, + CHART: { maxHeight: "60vh", display: "flex", justifyContent: "center" } +}; + +//----------- +//Тело модуля +//----------- + +//Форматирование заголовков колонок таблицы "Сведения" +const stageDetailInfoHeadCellRender = ({ columnDef }) => { + //Инициализируем общий стиль ячеек + let cellStyle = STYLES.DATA_GRID_HEADER; + //Формирование представлений + switch (columnDef.name) { + case "SATTR": + return { cellStyle, stackProps: { justifyContent: "left" } }; + case "SVALUE": + return { cellStyle, stackProps: { justifyContent: "right" } }; + default: + return { cellStyle: cellStyle }; + } +}; + +//Форматирование ячеек строк таблицы "Сведения" +const stageDetailInfoDataCellRender = ({ row, columnDef }) => { + //Инициализируем общий стиль ячеек + let cellStyle = STYLES.DATA_GRID_CELL(); + //Формирование представлений + switch (columnDef.name) { + case "SATTR": + return { cellStyle: { ...cellStyle, color: "#1976d2" }, cellProps: { align: "left" } }; + case "SVALUE": { + const res = { cellStyle, cellProps: { align: "right" } }; + if (["NCOST_SUM", "NSTAGE_COST_SUM"].includes(row["SCODE"])) + res.data = row["SVALUE"] || row["SVALUE"] === 0 ? formatNumberRFCurrency(row["SVALUE"]) : "-"; + if (row["SCODE"] == "NSTATE") + res.data = formatStageStatusValue({ value: parseInt(row["SVALUE"]), addText: true, justifyContent: "right" }); + return res; + } + default: + return { cellStyle }; + } +}; + +//Форматирование заголовков колонок таблицы "Структура затрат" +const stageDetailArtsHeadCellRender = ({ columnDef }) => { + //Инициализируем общий стиль ячеек + let cellStyle = STYLES.DATA_GRID_HEADER; + //Формирование представлений + switch (columnDef.name) { + case "NSTATE": + return { cellStyle: { ...cellStyle, justifyContent: "center" }, stackStyle: { justifyContent: "center" } }; + default: + return { cellStyle: cellStyle }; + } +}; + +//Форматирование ячеек строк таблицы "Структура затрат" +const stageDetailArtsDataCellRender = ({ row, columnDef, showCostNotesFact }) => { + //Инициализируем общий стиль ячеек + let cellStyle = STYLES.DATA_GRID_CELL; + //Формирование представлений + switch (columnDef.name) { + case "NCOST_STATUS": + return { + cellProps: { align: "center" }, + data: formatCostStatusValue({ value: row[columnDef.name], type: 0 }) + }; + case "NPLAN_SUM": + case "NPLAN_FACT_SUM": + return { + cellStyle: cellStyle(columnDef.name == "NPLAN_FACT_SUM" ? row[columnDef.name] : null), + data: row[columnDef.name] || row[columnDef.name] === 0 ? formatNumberRFCurrency(row[columnDef.name]) : "-" + }; + case "NFACT_SUM": + return { + cellStyle: cellStyle(), + data: + row[columnDef.name] || row[columnDef.name] === 0 ? ( + row[columnDef.name] > 0 ? ( + showCostNotesFact({ faceAccRn: row["NFACEACC"], artclRn: row["NFPDARTCL"] })}> + {formatNumberRFCurrency(row[columnDef.name])} + + ) : ( + formatNumberRFCurrency(row[columnDef.name]) + ) + ) : ( + "-" + ) + }; + default: + return { cellStyle: cellStyle(), data: row[columnDef.name] }; + } +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { + STYLES as STAGE_DETAIL_STYLES, + stageDetailInfoHeadCellRender, + stageDetailInfoDataCellRender, + stageDetailArtsHeadCellRender, + stageDetailArtsDataCellRender +}; diff --git a/app/panels/prj_info/stages.js b/app/panels/prj_info/stages.js new file mode 100644 index 0000000..5db113b --- /dev/null +++ b/app/panels/prj_info/stages.js @@ -0,0 +1,95 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Список этапов проекта +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState, useContext } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Typography } from "@mui/material"; //Интерфейсные элементы +import { ApplicationСtx } from "../../context/application"; //Контекст приложения +import { P8PDataGrid } from "../../components/p8p_data_grid"; //Таблица данных +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения +import { useStagesDataGrid } from "./stages_hooks"; //Хуки списка этапов проекта +import { STAGES_STYLES, projectStageDataCellRender } from "./stages_layouts"; //Дополнительная разметка и вёрстка клиентских элементов +import { StageDetail } from "./stage_detail"; //Компонент "Информация об этапе проекта" + +//----------- +//Тело модуля +//----------- + +//Список этапов проекта +const Stages = ({ projectRn, projectCode }) => { + //Собственное состояние + const [stages, setStages] = useState({ pageNumber: 1, orders: [] }); + + //Состояние таблицы этапов + const [stagesDataGrid] = useStagesDataGrid({ ...stages, projectRn }); + + //Состояние информации о этапе + const [stageInfo, setStageInfo] = useState({ showInfo: false, stage: null, sFaceAcc: null }); + + //Подключение к контексту приложения + const { pOnlineShowUnit } = useContext(ApplicationСtx); + + //Отображение записи этапа проекта в штатном разделе + const showProjectStage = (prn, rn) => { + pOnlineShowUnit({ + unitCode: "Projects", + inputParameters: [ + { name: "in_RN", value: prn }, + { name: "in_STAGE_RN", value: rn } + ], + modal: false + }); + }; + + //Отображение деталей этапа + const showStageDetails = stage => setStageInfo(pv => ({ ...pv, showInfo: true, stage: stage["NRN"], sFaceAcc: stage["SFACEACC"] })); + + //При изменении количества отображаемых страниц + const handlePagesCountChanged = () => setStages(pv => ({ ...pv, pageNumber: pv.pageNumber + 1 })); + + //При изменении состояния сортировки + const handleOrderChanged = ({ orders }) => setStages(pv => ({ ...pv, orders: [...orders], pageNumber: 1 })); + + //Генерация содержимого + return stagesDataGrid.init ? ( + <> +
+ + {`Этапы проекта "${projectCode}"`} + + projectStageDataCellRender({ ...prms, showProjectStage, showStageDetails })} + /> +
+ setStageInfo(pv => ({ ...pv, showInfo: false, stage: null, sFaceAcc: null }))} + /> + + ) : null; +}; + +//Контроль свойств компонента - Список этапов проекта +Stages.propTypes = { + projectRn: PropTypes.number.isRequired, + projectCode: PropTypes.string.isRequired +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { Stages }; diff --git a/app/panels/prj_info/stages_hooks.js b/app/panels/prj_info/stages_hooks.js new file mode 100644 index 0000000..fafc828 --- /dev/null +++ b/app/panels/prj_info/stages_hooks.js @@ -0,0 +1,78 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Список этапов проекта: пользовательские хуки для взаимодействия с сервером +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { useState, useContext, useEffect } from "react"; //Классы React +import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером +import { object2Base64XML, formatDateRF } from "../../core/utils"; //Вспомогательные функции +import config from "../../../app.config"; //Настройки приложения + +//--------- +//Константы +//--------- + +//Размер страницы данных +const DATA_GRID_PAGE_SIZE = config.SYSTEM.PAGE_SIZE; + +//----------- +//Тело модуля +//----------- + +//Этапы проекта +const useStagesDataGrid = ({ projectRn, pageNumber, orders }) => { + //Собственное состояние - флаг загрузки + const [isLoading, setLoading] = useState(false); + + //Собственное состояние - таблица данных + const [data, setData] = useState({ init: false, morePages: true }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + + //При необходимости обновить данные таблицы + useEffect(() => { + //Загрузка данных таблицы с сервера + const loadData = async () => { + try { + setLoading(true); + const data = await executeStored({ + stored: "PKG_P8PANELS_PROJECTS.INFO_STAGES_DG", + args: { + NPROJECT: projectRn, + CORDERS: { VALUE: object2Base64XML(orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, + NPAGE_NUMBER: pageNumber, + NPAGE_SIZE: DATA_GRID_PAGE_SIZE, + NINCLUDE_DEF: pageNumber == 1 ? 1 : 0 + }, + respArg: "COUT", + loader: true, + attributeValueProcessor: (name, val) => (["DBEGPLAN", "DENDPLAN"].includes(name) ? formatDateRF(val) : val) + }); + setData(pv => ({ + ...pv, + columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef || [], + rows: pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...(pv.rows || []), ...(data.XDATA_GRID.rows || [])], + morePages: DATA_GRID_PAGE_SIZE == 0 ? false : (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE, + init: true + })); + } finally { + setLoading(false); + } + }; + if (projectRn) loadData(); + }, [projectRn, orders, pageNumber, executeStored, SERV_DATA_TYPE_CLOB]); + + //Возвращаем интерфейс хука + return [data, isLoading]; +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { useStagesDataGrid }; diff --git a/app/panels/prj_info/stages_layouts.js b/app/panels/prj_info/stages_layouts.js new file mode 100644 index 0000000..0846ffd --- /dev/null +++ b/app/panels/prj_info/stages_layouts.js @@ -0,0 +1,86 @@ +/* + Парус 8 - Панели мониторинга - ПУП - Информация о проектах + Список этапов проекта: дополнительная разметка и вёрстка клиентских элементов +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import { Icon, Stack, Link } from "@mui/material"; //Интерфейсные элементы +import { formatNumberRFCurrency } from "../../core/utils"; //Спомогательные функции +import { COMMON_STYLES, formatCostStatusValue, formatCostReadyValue } from "./layouts"; //Общие стили и разметка панели + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + CONTAINER: { textAlign: "center", paddingTop: "10px", backgroundColor: "lightcyan" }, + TITLE: { fontSize: "13pt", paddingBottom: "10px" }, + DATA_GRID_CONTAINER: { backgroundColor: "lightcyan" } +}; + +//----------- +//Тело модуля +//----------- + +//Формирование значения для колонки "Состояние" этапа +const formatStageStatusValue = ({ value, addText = false, justifyContent = "center" }) => { + const [text, icon] = + value == 0 + ? ["Зарегистрирован", "app_registration"] + : value == 1 + ? ["Открыт", "lock_open"] + : value == 2 + ? ["Закрыт", "lock_outline"] + : value == 3 + ? ["Согласован", "thumb_up_alt"] + : value == 4 + ? ["Исполнение прекращено", "block"] + : ["Остановлен", "do_not_disturb_on"]; + return ( + + + {icon} + + {addText == true ? text : null} + + ); +}; + +//Форматирование ячеек таблицы "Этапы проекта" +const projectStageDataCellRender = ({ row, columnDef, showProjectStage, showStageDetails }) => { + //Формирование представлений + switch (columnDef.name) { + case "NCOST_STATUS": + return { + cellProps: { align: "center" }, + data: formatCostStatusValue({ value: row[columnDef.name], onClick: () => showStageDetails(row) }) + }; + case "NCOST_READY": + return { cellProps: { align: "center" }, data: formatCostReadyValue(row[columnDef.name]) }; + case "NSTATE": + return { cellProps: { align: "center" }, data: formatStageStatusValue({ value: row[columnDef.name] }) }; + case "SFACEACC": + return { + data: ( + showProjectStage(row["NPRN"], row["NRN"])}> + {row[columnDef.name]} + + ) + }; + case "NCOST_SUM": + return { data: formatNumberRFCurrency(row[columnDef.name]) }; + default: + return { data: row[columnDef.name] }; + } +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { STYLES as STAGES_STYLES, projectStageDataCellRender, formatStageStatusValue }; diff --git a/db/PKG_P8PANELS_PROJECTS.pck b/db/PKG_P8PANELS_PROJECTS.pck index 424a7b0..ef88481 100644 --- a/db/PKG_P8PANELS_PROJECTS.pck +++ b/db/PKG_P8PANELS_PROJECTS.pck @@ -17,6 +17,24 @@ create or replace package PKG_P8PANELS_PROJECTS as /* Типы данных - коллекция статей этапа проекта */ type TSTAGE_ARTS is table of TSTAGE_ART; + + /* Типы данных - статьи структуры цены этапа проекта (для панели "Информация о проектах") */ + type TINFO_STAGE_ART is record + ( + NRN CONTRPRCLC.RN%type, -- Рег. номер записи структуры цены + NCOST_STATUS PKG_STD.TNUMBER, -- Состояние затрат статьи (-1 - не определено, 0 - без отклонений, 1 - расходом больше 90%, 2 - перерасход) + NFACEACC FACEACC.RN%type, -- Рег. номер лицевого счёта затрат связанного этапа проекта + NFPDARTCL FPDARTCL.RN%type, -- Рег. номер статьи затрат + SFPDARTCL FPDARTCL.CODE%type, -- Код статьи затрат + NPLAN_SUM PKG_STD.TSUMM, -- Сумма затрат (план) + NFACT_SUM PKG_STD.TSUMM, -- Сумма затрат (факт) + NPLAN_FACT_SUM PKG_STD.TSUMM, -- Отклонение (план - факт) + SCURNAMES CURNAMES.INTCODE%type, -- Код валюты + NCOST_CTL boolean -- Подлежит контролю затрат + ); + + /* Типы данных - коллекция статей структуры цены этапа проекта (для панели "Информация о проектах") */ + type TINFO_STAGE_ARTS is table of TINFO_STAGE_ART; /* Отбор проектов */ procedure COND; @@ -352,7 +370,7 @@ create or replace package PKG_P8PANELS_PROJECTS as COUT out clob -- Сериализованная таблица данных ); - /* Получение списка проектов */ + /* Получение списка проектов */ procedure JB_PRJCTS_LIST ( NIDENT in number, -- Идентификатор процесса @@ -377,7 +395,7 @@ create or replace package PKG_P8PANELS_PROJECTS as COUT out clob -- Список проектов ); - /* Получение списка для детализации трудоёмкости по ФОТ периода балансировки */ + /* Получение списка для детализации трудоёмкости по ФОТ периода балансировки */ procedure JB_PERIODS_PLAN_FOT_LIST ( NJB_PERIODS in number, -- Рег. номер записи периода в буфере балансировки @@ -454,6 +472,89 @@ create or replace package PKG_P8PANELS_PROJECTS as NIDENT in out number, -- Идентификатор процесса (null - сгенерировать новый, !null - удалить старые данные и пересоздать с указанным идентификатором) NRESOURCE_STATUS out number -- Состояние ресурсов (0 - без отклонений, 1 - есть отклонения) ); + + /* Получение состояния структуры цены проекта/этапа проекта */ + function INFO_COST_STATUS_GET + ( + NPROJECT in number, -- Рег. номер проекта + NPROJECTSTAGE in number := null -- Рег. номер этапа (null - по всем) + ) return number; -- Состояние структуры цены (-1 - не определено, 0 - без отклонений, 1 - есть статьи с расходом больше 90%, 2 - есть статьи с перерасходом) + + /* Получение % готовности проекта/этапа проекта (по затратам) */ + function INFO_COST_READY_GET + ( + NPROJECT in number, -- Рег. номер проекта + NPROJECTSTAGE in number := null -- Рег. номер этапа (null - по всем) + ) return number; -- % готовности + + /* Подбор записей журнала затрат (факт) */ + procedure INFO_FCCOSTNOTES_FACT_SELECT + ( + NFACEACC in number, -- Рег. номер лицевого счета + NFPDARTCL in number, -- Рег. номер статьи затрат + NIDENT out number -- Идентификатор буфера подобранных (списка отмеченных записей, null - не найдено) + ); + + /* Поиск активной структуры цены (утвеждена или согласована, актуальна по датам, активна) */ + function INFO_CONTRPRSTRUCT_ACTIVE_FIND + ( + NPROJECTSTAGE in number -- Рег. номер этапа проекта + ) return number; -- Рег. номер записи структуры цены + + /* Список проектов (таблица данных) */ + procedure INFO_PROJECTS_DG + ( + SPRJ_TYPE in varchar2 := null, -- Мнемокод типа проекта + SINS_DEPARTMENT in varchar2 := null, -- Мнемокод штатного подразделения + NCOST_STATUS in number := 0, -- Статус структуры цены (0 - все, 1 - есть статьи с расходом больше 90%, 2 - есть статьи с перерасходом) + NSTATE in number := 0, -- Состояние (0 - все, 1 - открытые, 2 - неоткрытые, т.е. с прочими статусами) + SSEARCH in varchar2 := null, -- Строка поиска + NPAGE_NUMBER in number, -- Номер страницы (игнорируется при NPAGE_SIZE=0) + NPAGE_SIZE in number, -- Количество записей на странице (0 - все) + CORDERS in clob, -- Сортировки + NINCLUDE_DEF in number, -- Признак включения описания колонок таблицы в ответ + COUT out clob -- Сериализованная таблица данных + ); + + /* Список этапов проекта (таблица данных) */ + procedure INFO_STAGES_DG + ( + NPROJECT 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 INFO_STAGE_DTL_DG + ( + NPROJECTSTAGE in number, -- Рег. номер этапа проекта + COUT out clob -- Сериализованная таблица данных + ); + + /* Структура цены этапа проекта (состав статей) */ + procedure INFO_STAGE_ARTS_GET + ( + NPROJECTSTAGE in number, -- Рег. номер этапа проекта + RINFO_STAGE_ARTS out TINFO_STAGE_ARTS -- Список статей структуры цены этапа проекта + ); + + /* Структура цены этапа проекта (таблица данных) */ + procedure INFO_STAGE_ARTS_DG + ( + NPROJECTSTAGE in number, -- Рег. номер этапа проекта + COUT out clob -- Сериализованная таблица данных + ); + + /* Структура цены этапа проекта (график) */ + procedure INFO_STAGE_ARTS_CHART + ( + NPROJECTSTAGE in number, -- Рег. номер этапа проекта + NTYPE in number, -- Тип диаграммы (0 - по плану, 1 - по факту) + COUT out clob -- Сериализованный график + ); end PKG_P8PANELS_PROJECTS; / @@ -6622,5 +6723,987 @@ create or replace package body PKG_P8PANELS_PROJECTS as JB_PERIODS_RECALC(NIDENT => NIDENT, NINITIAL => 1, NRESOURCE_STATUS => NRESOURCE_STATUS); end JB_INIT; + /* Формирование подсказки для колонки "Статус структуры цены" */ + function INFO_COST_STATUS_HINT + ( + NTYPE in number -- Тип подсказки (0 - для статьи, 1 - для проекта/этапа) + ) return varchar2 -- Подсказка + is + SICON_STYLE PKG_STD.TSTRING; -- Стиль иконки + SICON PKG_STD.TSTRING; -- Иконка + begin + SICON_STYLE := 'font-family: ''Material Icons''; font-size: 0.8em;'; + SICON := 'circle'; + if (NTYPE = 0) then + return 'Контроль фактических затрат:
' || + '' || SICON || ' Перерасход - фактические затраты превышают плановые
' || + '' || SICON || ' Расход более 90% - фактические затраты составляют 90% (и более) от плановых
' || + '' || SICON || ' Без отклонений - фактические затраты менее 90% от плановых
' || + '' || SICON || ' Не определено - контроль затрат по статье не проводится'; + else + return 'Контроль фактических затрат в разрезе статей структуры цены:
' || + '' || SICON || ' Есть статьи с перерасходом - в составе структуры цены проекта/этапа есть статьи, фактические затраты по которым превышают плановые
' || + '' || SICON || ' Есть статьи с расходом более 90% - в составе структуры цены проекта/этапа есть статьи, фактические затраты по которым составляют 90% (и более) от плановых
' || + '' || SICON || ' Без отклонений - в составе структуры цены проекта/этапа все статьи, имеют объем фактических затраты менее 90% от плановых
' || + '' || SICON || ' Не определено - в составе структуры цены проекта/этапа нет статей, по которым ведется контроль затрат'; + end if; + end INFO_COST_STATUS_HINT; + + /* Формирование подсказки для колонки "Готов (%, затраты)" */ + function INFO_COST_READY_HINT + return varchar2 -- Подсказка + is + begin + return 'Процент готовности по затратам расчитывается как отношение плановой себестоимости проекта/этапа (по структуре цены) к фактической, зафиксированной на данный момент в разделе "Журнал затрат". Цветом выделены диапазоны:
' || + '30% и менее - низкая степень готовности
' || + 'От 31% до 79% - средняя степень готовности
' || + '80% и более - высокая степень готовности
'; + end INFO_COST_READY_HINT; + + /* Формирование подсказки для колонки "Состояние" */ + function INFO_STATE_HINT + return varchar2 -- Подсказка + is + SCONT_STYLE PKG_STD.TSTRING; -- Стиль контейнера + SICON_STYLE PKG_STD.TSTRING; -- Стиль иконки + begin + SCONT_STYLE := 'display: flex; align-items: center;'; + SICON_STYLE := 'font-family: ''Material Icons''; font-size: 1.5em;'; + return 'app_registration - Зарегистрирован' || + 'lock_open - Открыт' || + 'do_not_disturb_on - Остановлен' || + 'lock_outline - Закрыт' || + 'thumb_up_alt - Согласован' || + 'block - Исполнение прекращено'; + end INFO_STATE_HINT; + + /* Получение состояния структуры цены проекта/этапа проекта */ + function INFO_COST_STATUS_GET + ( + NPROJECT in number, -- Рег. номер проекта + NPROJECTSTAGE in number := null -- Рег. номер этапа (null - по всем) + ) return number -- Состояние структуры цены (-1 - не определено, 0 - без отклонений, 1 - есть статьи с расходом больше 90%, 2 - есть статьи с перерасходом) + is + RINFO_STAGE_ARTS TINFO_STAGE_ARTS; -- Список статей структуры цены + NRES PKG_STD.TNUMBER := -1; -- Состояние структуры цены + begin + /* Обходим этапы проекта */ + for C in (select PS.RN + from PROJECTSTAGE PS + where PS.PRN = NPROJECT + and ((NPROJECTSTAGE is null) or ((NPROJECTSTAGE is not null) and (PS.RN = NPROJECTSTAGE)))) + loop + /* Сформируем список статей структуры цены */ + INFO_STAGE_ARTS_GET(NPROJECTSTAGE => C.RN, RINFO_STAGE_ARTS => RINFO_STAGE_ARTS); + /* Обходим собранные статьи */ + if ((RINFO_STAGE_ARTS is not null) and (RINFO_STAGE_ARTS.COUNT > 0)) then + for I in RINFO_STAGE_ARTS.FIRST .. RINFO_STAGE_ARTS.LAST + loop + /* Чем хуже (выше) отклонение статьи - тем выше оно у этапа/проекта */ + if (RINFO_STAGE_ARTS(I).NCOST_STATUS > NRES) then + NRES := RINFO_STAGE_ARTS(I).NCOST_STATUS; + end if; + /* Если мы уже добрались до самого худшего отклонения - дальше можно не смотреть */ + if (NRES = 2) then + return NRES; + end if; + end loop; + end if; + end loop; + /* Возвращаем полученное состояние */ + return NRES; + end INFO_COST_STATUS_GET; + + /* Получение % готовности проекта/этапа проекта (по затратам) */ + function INFO_COST_READY_GET + ( + NPROJECT in number, -- Рег. номер проекта + NPROJECTSTAGE in number := null -- Рег. номер этапа (null - по всем) + ) return number -- % готовности + is + RINFO_STAGE_ARTS TINFO_STAGE_ARTS; -- Список статей структуры цены + NCOST_FACT PKG_STD.TLNUMBER := 0; -- Сумма фактических затрат по проекту + NCOST_PLAN PKG_STD.TLNUMBER := 0; -- Сумма плановых затрат по проекту + NRES PKG_STD.TNUMBER := 0; -- Буфер для результата + begin + /* Обходим этапы проекта */ + for C in (select PS.RN + from PROJECTSTAGE PS + where PS.PRN = NPROJECT + and ((NPROJECTSTAGE is null) or ((NPROJECTSTAGE is not null) and (PS.RN = NPROJECTSTAGE)))) + loop + /* Сформируем список статей структуры цены этапа */ + INFO_STAGE_ARTS_GET(NPROJECTSTAGE => C.RN, RINFO_STAGE_ARTS => RINFO_STAGE_ARTS); + /* Обходим собранные статьи */ + if ((RINFO_STAGE_ARTS is not null) and (RINFO_STAGE_ARTS.COUNT > 0)) then + for I in RINFO_STAGE_ARTS.FIRST .. RINFO_STAGE_ARTS.LAST + loop + /* Суммируем затраты по статьям, подлежащим котролю затрат */ + if (RINFO_STAGE_ARTS(I).NCOST_CTL) then + NCOST_PLAN := NCOST_PLAN + RINFO_STAGE_ARTS(I).NPLAN_SUM; + NCOST_FACT := NCOST_FACT + RINFO_STAGE_ARTS(I).NFACT_SUM; + end if; + end loop; + end if; + end loop; + /* Если есть и фактические затраты и плановая себестоимость */ + if ((NCOST_FACT > 0) and (NCOST_PLAN > 0)) then + /* Отношение фактических затрат к плановым - искомый % готовности */ + NRES := ROUND(NCOST_FACT / NCOST_PLAN * 100, 0); + /* Если фактические затраты превысили плановые, то % может быть > 100, но это бессмысленно в данном показателе - откорректируем ситуацию */ + if (NRES > 100) then + NRES := 100; + end if; + end if; + /* Вернём рассчитанное */ + return NRES; + end INFO_COST_READY_GET; + + /* Подбор записей журнала затрат (факт) */ + procedure INFO_FCCOSTNOTES_FACT_SELECT + ( + NFACEACC in number, -- Рег. номер лицевого счета + NFPDARTCL in number, -- Рег. номер статьи затрат + NIDENT out number -- Идентификатор буфера подобранных (списка отмеченных записей, null - не найдено) + ) + is + NSELECTLIST PKG_STD.TREF; -- Рег. номер добавленной записи буфера подобранных + begin + /* Подберём записи журнала затрат */ + for C in (select CN.COMPANY, + CN.RN + from FCCOSTNOTES CN, + FINSTATE FS + where CN.PROD_ORDER = NFACEACC + and CN.COST_ARTICLE = NFPDARTCL + and CN.COST_TYPE = FS.RN + and FS.TYPE = 1 + and exists (select null from V_USERPRIV UP where UP.CATALOG = CN.CRN)) + loop + /* Сформируем идентификатор буфера */ + if (NIDENT is null) then + NIDENT := GEN_IDENT(); + end if; + /* Добавим подобранное в список отмеченных записей */ + P_SELECTLIST_BASE_INSERT(NIDENT => NIDENT, + NCOMPANY => C.COMPANY, + NDOCUMENT => C.RN, + SUNITCODE => 'CostNotes', + SACTIONCODE => null, + NCRN => null, + NDOCUMENT1 => null, + SUNITCODE1 => null, + SACTIONCODE1 => null, + NRN => NSELECTLIST); + end loop; + end INFO_FCCOSTNOTES_FACT_SELECT; + + /* Расчет суммы фактических затрат по журналу затрат */ + function INFO_FCCOSTNOTES_FACT_GET + ( + NFACEACC in number, -- Рег. номер лицевого счета + NFPDARTCL in number -- Рег. номер статьи затрат + ) return number -- Сумма-факт по статье + is + NRES PKG_STD.TNUMBER; -- Буфер для рузультата + begin + /* Суммируем факт по лицевому счёту затрат этапа и указанной статье */ + select COALESCE(sum(CN.COST_BSUM), 0) + into NRES + from FCCOSTNOTES CN, + FINSTATE FS + where CN.PROD_ORDER = NFACEACC + and CN.COST_ARTICLE = NFPDARTCL + and CN.COST_TYPE = FS.RN + and FS.TYPE = 1; + /* Возвращаем результат */ + return NRES; + end INFO_FCCOSTNOTES_FACT_GET; + + /* Поиск активной структуры цены (утвеждена или согласована, актуальна по датам, активна) */ + function INFO_CONTRPRSTRUCT_ACTIVE_FIND + ( + NPROJECTSTAGE in number -- Рег. номер этапа проекта + ) return number -- Рег. номер записи структуры цены + is + begin + /* Подбираем актуальную по дате, активную, утверждённую или согласованную */ + for C in (select CSP.RN + from PROJECTSTAGE PS, + STAGES CS, + CONTRPRSTRUCT CSP + where PS.RN = NPROJECTSTAGE + and PS.FACEACCCUST = CS.FACEACC + and CS.RN = CSP.PRN + and CSP.SIGN_ACT = 1 + and CSP.STATE in (1, 2) + and ((CSP.DATE_FROM is null) or ((CSP.DATE_FROM is not null) and (CSP.DATE_FROM <= sysdate))) + and ((CSP.DATE_TO is null) or ((CSP.DATE_TO is not null) and (CSP.DATE_TO >= sysdate))) + order by CSP.DATE_TO desc) + loop + /* Возвращаем первую */ + return C.RN; + end loop; + /* Ничего не нашли */ + return null; + end INFO_CONTRPRSTRUCT_ACTIVE_FIND; + + /* Получение флага необходимости контроля затрат по статье */ + function INFO_FPDARTCL_COST_IS_CTL + ( + NRN in number -- Рег. номер статьи затрат + ) return boolean -- Признак необходимости контроля затрат + is + NCOMPANY PKG_STD.TREF; -- Рег. номер организации + NCTL_COST_DP PKG_STD.TREF; -- Рег. номер доп. свойства, определяющего необходимость контроля затрат по статье + begin + /* Обратимся к статье затрат */ + for C in (select T.VERSION, + T.RN + from FPDARTCL T + where T.RN = NRN) + loop + /* Определим организацию */ + FIND_COMPANY_BY_VERSION(NVERSION => C.VERSION, SUNITCODE => 'FinPlanArticles', NCOMPANY => NCOMPANY); + /* Определим дополнительные свойства - необходимость контроля затрат */ + FIND_DOCS_PROPS_CODE(NFLAG_SMART => 1, NCOMPANY => NCOMPANY, SCODE => SDP_SCTL_COST, NRN => NCTL_COST_DP); + /* Для статей, подлежащих контролю затрат */ + if (UPPER(F_DOCS_PROPS_GET_STR_VALUE(NPROPERTY => NCTL_COST_DP, SUNITCODE => 'FinPlanArticles', NDOCUMENT => C.RN)) = + UPPER(SYES)) then + return true; + end if; + end loop; + /* Нет статьи (ну или мы раньше не вышли) - нет контроля */ + return false; + end INFO_FPDARTCL_COST_IS_CTL; + + /* Список проектов (таблица данных) */ + procedure INFO_PROJECTS_DG + ( + SPRJ_TYPE in varchar2 := null, -- Мнемокод типа проекта + SINS_DEPARTMENT in varchar2 := null, -- Мнемокод штатного подразделения + NCOST_STATUS in number := 0, -- Статус структуры цены (0 - все, 1 - есть статьи с расходом больше 90%, 2 - есть статьи с перерасходом) + NSTATE in number := 0, -- Состояние (0 - все, 1 - открытые, 2 - неоткрытые, т.е. с прочими статусами) + SSEARCH in varchar2 := null, -- Строка поиска + 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.TDG_ORDERS; -- Сортировки + RDG PKG_P8PANELS_VISUAL.TDG; -- Описание таблицы + RDG_ROW PKG_P8PANELS_VISUAL.TDG_ROW; -- Строка таблицы + NROW_FROM PKG_STD.TREF; -- Номер строки с + NROW_TO PKG_STD.TREF; -- Номер строки по + SSEARCH_PREPARED PKG_STD.TSTRING; -- Подготовленная для использования в запросе поисковая строка + CSQL clob; -- Буфер для запроса + ICURSOR integer; -- Курсор для исполнения запроса + begin + /* Читаем сортировки */ + RO := PKG_P8PANELS_VISUAL.TDG_ORDERS_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); + /* Подготовим поисковую строку для использования в запросе*/ + if (SSEARCH is not null) then + PKG_P8PANELS_BASE.UTL_SEARCH_PREPARE(SSEARCH => SSEARCH, SSEARCH_PREPARED => SSEARCH_PREPARED); + end if; + /* Инициализируем таблицу данных */ + RDG := PKG_P8PANELS_VISUAL.TDG_MAKE(); + /* Добавляем в таблицу описание колонок */ + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NRN', + SCAPTION => 'Рег. номер', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NCOST_STATUS', + SCAPTION => 'Статус', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + SHINT => INFO_COST_STATUS_HINT(NTYPE => 1), + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SCODE', + SCAPTION => 'Код', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SNAME_USL', + SCAPTION => 'Условное наименование', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SNAME', + SCAPTION => 'Наименование', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SEXT_CUST', + SCAPTION => 'Заказчик', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SDOC_OSN', + SCAPTION => 'Договор', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NSTATE', + SCAPTION => 'Состояние', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + SHINT => INFO_STATE_HINT(), + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'DBEGPLAN', + SCAPTION => 'Дата начала', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'DENDPLAN', + SCAPTION => 'Дата окончания', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NCOST_READY', + SCAPTION => 'Готов (%, затраты)', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + SHINT => INFO_COST_READY_HINT(), + BORDER => true); + /* Обходим данные */ + 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 P.RN NRN,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' P.CODE SCODE,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' P.NAME_USL SNAME_USL,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' P.NAME SNAME,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' EC.AGNABBR SEXT_CUST,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' P.DOC_OSN SDOC_OSN,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' P."STATE" NSTATE,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' P.BEGPLAN DBEGPLAN,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' P.ENDPLAN DENDPLAN,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => PKG_SQL_BUILD.PKG_NAME(SNAME => 'PKG_P8PANELS_PROJECTS.INFO_COST_STATUS_GET') || '(P.RN) NCOST_STATUS,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => PKG_SQL_BUILD.PKG_NAME(SNAME => 'PKG_P8PANELS_PROJECTS.INFO_COST_READY_GET') || '(P.RN) NCOST_READY'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from PROJECT P'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' left outer join AGNLIST EC on P.EXT_CUST = EC.RN'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' left outer join INS_DEPARTMENT SR on P.SUBDIV_RESP = SR.RN,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' PRJTYPE PT'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where P.COMPANY = :NCOMPANY'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and P.PRJTYPE = PT.RN'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and ((:SPRJ_TYPE is null) or ((:SPRJ_TYPE is not null) and (PT.CODE = :SPRJ_TYPE)))'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and ((:SINS_DEPARTMENT is null) or ((:SINS_DEPARTMENT is not null) and (SR.CODE = :SINS_DEPARTMENT)))'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and ((:NCOST_STATUS = 0) or ((:NCOST_STATUS <> 0) and (' || PKG_SQL_BUILD.PKG_NAME(SNAME => 'PKG_P8PANELS_PROJECTS.INFO_COST_STATUS_GET') || '(P.RN) = :NCOST_STATUS)))'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and ((:NSTATE = 0) or ((:NSTATE <> 0) and (((:NSTATE = 1) and (P.STATE = 1)) or ((:NSTATE = 2) and (P.STATE <> 1)))))'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and ((:SSEARCH_PREPARED is null) or ((:SSEARCH_PREPARED is not null) and (LOWER(P.CODE || P.NAME || P.NAME_USL || EC.AGNABBR) like LOWER(:SSEARCH_PREPARED))))'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and exists (select null from V_USERPRIV UP where UP."CATALOG" = P.CRN)'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and exists (select null from V_USERPRIV UP where UP.JUR_PERS = P.JUR_PERS and UP.UNITCODE = ' || PKG_SQL_BUILD.WRAP_STR(SVALUE => 'Projects') || ')'); + 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.TDG_ORDERS_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_STR(ICURSOR => ICURSOR, SNAME => 'SPRJ_TYPE', SVALUE => SPRJ_TYPE); + PKG_SQL_DML.BIND_VARIABLE_STR(ICURSOR => ICURSOR, SNAME => 'SINS_DEPARTMENT', SVALUE => SINS_DEPARTMENT); + PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NCOST_STATUS', NVALUE => NCOST_STATUS); + PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NSTATE', NVALUE => NSTATE); + PKG_SQL_DML.BIND_VARIABLE_STR(ICURSOR => ICURSOR, SNAME => 'SSEARCH_PREPARED', SVALUE => SSEARCH_PREPARED); + 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.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_STR(ICURSOR => ICURSOR, IPOSITION => 5); + PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 6); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 7); + PKG_SQL_DML.DEFINE_COLUMN_DATE(ICURSOR => ICURSOR, IPOSITION => 8); + PKG_SQL_DML.DEFINE_COLUMN_DATE(ICURSOR => ICURSOR, IPOSITION => 9); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 10); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 11); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 12); + /* Делаем выборку */ + 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.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW, SNAME => 'NRN', ICURSOR => ICURSOR, NPOSITION => 1, BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SCODE', ICURSOR => ICURSOR, NPOSITION => 2); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SNAME_USL', ICURSOR => ICURSOR, NPOSITION => 3); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SNAME', ICURSOR => ICURSOR, NPOSITION => 4); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SEXT_CUST', ICURSOR => ICURSOR, NPOSITION => 5); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SDOC_OSN', ICURSOR => ICURSOR, NPOSITION => 6); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW, SNAME => 'NSTATE', ICURSOR => ICURSOR, NPOSITION => 7); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLD(RROW => RDG_ROW, SNAME => 'DBEGPLAN', ICURSOR => ICURSOR, NPOSITION => 8); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLD(RROW => RDG_ROW, SNAME => 'DENDPLAN', ICURSOR => ICURSOR, NPOSITION => 9); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW, SNAME => 'NCOST_STATUS', ICURSOR => ICURSOR, NPOSITION => 10); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW, SNAME => 'NCOST_READY', ICURSOR => ICURSOR, NPOSITION => 11); + /* Добавляем строку в таблицу */ + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + end loop; + /* Освобождаем курсор */ + PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR); + exception + when others then + PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR); + raise; + end; + /* Сериализуем описание */ + COUT := PKG_P8PANELS_VISUAL.TDG_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => NINCLUDE_DEF); + end INFO_PROJECTS_DG; + + /* Список этапов проекта (таблица данных) */ + procedure INFO_STAGES_DG + ( + NPROJECT 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.TDG_ORDERS; -- Сортировки + RDG PKG_P8PANELS_VISUAL.TDG; -- Описание таблицы + RDG_ROW PKG_P8PANELS_VISUAL.TDG_ROW; -- Строка таблицы + NROW_FROM PKG_STD.TREF; -- Номер строки с + NROW_TO PKG_STD.TREF; -- Номер строки по + CSQL clob; -- Буфер для запроса + ICURSOR integer; -- Курсор для исполнения запроса + begin + /* Читаем сортировки */ + RO := PKG_P8PANELS_VISUAL.TDG_ORDERS_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.TDG_MAKE(); + /* Добавляем в таблицу описание колонок */ + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NRN', + SCAPTION => 'Рег. номер', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NPRN', + SCAPTION => 'Рег. номер проекта', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NCOST_STATUS', + SCAPTION => 'Статус', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + SHINT => INFO_COST_STATUS_HINT(NTYPE => 1), + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SFACEACC', + SCAPTION => 'Заказ/этап', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'DBEGPLAN', + SCAPTION => 'Начало (план)', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'DENDPLAN', + SCAPTION => 'Окончание (план)', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NCOST_SUM', + SCAPTION => 'Стоимость', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SSUBDIV_RESP', + SCAPTION => 'Исполнитель', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SNAME', + SCAPTION => 'Содержание', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NCOST_READY', + SCAPTION => 'Готов (%, затраты)', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + SHINT => INFO_COST_READY_HINT(), + BORDER => true); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NSTATE', + SCAPTION => 'Состояние', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + SHINT => INFO_STATE_HINT(), + BORDER => true); + /* Обходим данные */ + 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 => ' T.PRN NPRN,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' FAC.NUMB SFACEACC,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.BEGPLAN DBEGPLAN,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.ENDPLAN DENDPLAN,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.COST_SUM NCOST_SUM,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' (select I.CODE from INS_DEPARTMENT I where I.RN = T.SUBDIV_RESP) SSUBDIV_RESP,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.NAME SNAME,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => PKG_SQL_BUILD.PKG_NAME(SNAME => 'PKG_P8PANELS_PROJECTS.INFO_COST_STATUS_GET') || '(T.PRN, T.RN) NCOST_STATUS,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => PKG_SQL_BUILD.PKG_NAME(SNAME => 'PKG_P8PANELS_PROJECTS.INFO_COST_READY_GET') || '(T.PRN, T.RN) NCOST_READY,'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.STATE NSTATE'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from PROJECTSTAGE T'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' left outer join FACEACC FAC on T.FACEACC = FAC.RN'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where T.COMPANY = :NCOMPANY'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and T.PRN = :NPROJECT'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and exists (select null from V_USERPRIV UP where UP."CATALOG" = T.CRN)'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and exists (select null from V_USERPRIV UP where UP.JUR_PERS = T.JUR_PERS and UP.UNITCODE = ' || PKG_SQL_BUILD.WRAP_STR(SVALUE => 'Projects') || ')'); + 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.TDG_ORDERS_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 => 'NPROJECT', NVALUE => NPROJECT); + 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.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 1); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 2); + PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 3); + PKG_SQL_DML.DEFINE_COLUMN_DATE(ICURSOR => ICURSOR, IPOSITION => 4); + PKG_SQL_DML.DEFINE_COLUMN_DATE(ICURSOR => ICURSOR, IPOSITION => 5); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 6); + PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 7); + PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 8); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 9); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 10); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 11); + /* Делаем выборку */ + 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.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW, SNAME => 'NRN', ICURSOR => ICURSOR, NPOSITION => 1, BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW, SNAME => 'NPRN', ICURSOR => ICURSOR, NPOSITION => 2); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SFACEACC', ICURSOR => ICURSOR, NPOSITION => 3); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLD(RROW => RDG_ROW, SNAME => 'DBEGPLAN', ICURSOR => ICURSOR, NPOSITION => 4); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLD(RROW => RDG_ROW, SNAME => 'DENDPLAN', ICURSOR => ICURSOR, NPOSITION => 5); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW, SNAME => 'NCOST_SUM', ICURSOR => ICURSOR, NPOSITION => 6); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SSUBDIV_RESP', ICURSOR => ICURSOR, NPOSITION => 7); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SNAME', ICURSOR => ICURSOR, NPOSITION => 8); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW, SNAME => 'NCOST_STATUS', ICURSOR => ICURSOR, NPOSITION => 9); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW, SNAME => 'NCOST_READY', ICURSOR => ICURSOR, NPOSITION => 10); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW, SNAME => 'NSTATE', ICURSOR => ICURSOR, NPOSITION => 11); + /* Добавляем строку в таблицу */ + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + end loop; + /* Освобождаем курсор */ + PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR); + exception + when others then + PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR); + raise; + end; + /* Сериализуем описание */ + COUT := PKG_P8PANELS_VISUAL.TDG_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => NINCLUDE_DEF); + end INFO_STAGES_DG; + + /* Информация об этапе проекта (таблица данных) */ + procedure INFO_STAGE_DTL_DG + ( + NPROJECTSTAGE in number, -- Рег. номер этапа проекта + COUT out clob -- Сериализованная таблица данных + ) + is + RDG PKG_P8PANELS_VISUAL.TDG; -- Описание таблицы + RDG_ROW PKG_P8PANELS_VISUAL.TDG_ROW; -- Строка таблицы + begin + /* Инициализируем таблицу данных */ + RDG := PKG_P8PANELS_VISUAL.TDG_MAKE(); + /* Добавляем в таблицу описание колонок */ + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SCODE', + SCAPTION => 'Код', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SATTR', + SCAPTION => 'Атрибут', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SVALUE', + SCAPTION => 'Значение', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR); + /* Обратимся к данным этапа проекта */ + for REC in (select (select F.NUMB from FACEACC F where F.RN = T.FACEACC) SFACEACC, + trim(T.NUMB) SNUMB, + M.NAME_USL SNAME_USL, + T.NAME SNAME, + (select A.AGNABBR from AGNLIST A where A.RN = M.EXT_CUST) SEXT_CUST, + M.DOC_OSN SDOC_OSN, + (select TO_CHAR(C.DOC_DATE, 'dd.mm.yyyy') + from DOCLINKS DL, CONTRACTS C + where DL.IN_DOCUMENT = M.RN and DL.IN_UNITCODE = 'Projects' and DL.OUT_UNITCODE = 'Contracts' and C.RN = DL.OUT_DOCUMENT and ROWNUM = 1) SDOC_OSN_DATE, + M.COST_SUM NCOST_SUM, + T.COST_SUM NSTAGE_COST_SUM, + (select CUR.INTCODE from CURNAMES CUR where CUR.RN = M.CURNAMES) SCURNAMES, + (select I.CODE from INS_DEPARTMENT I where I.RN = T.SUBDIV_RESP) SSUBDIV_RESP, + (select A.AGNABBR from AGNLIST A where A.RN = T.RESPONSIBLE) SRESPONSIBLE, + TO_CHAR(T.BEGPLAN, 'dd.mm.yyyy') SBEGPLAN, + TO_CHAR(T.ENDPLAN, 'dd.mm.yyyy') SENDPLAN, + T.STATE NSTATE, + T.NAME SCONTENT, + T.NOTE SNOTE + from PROJECTSTAGE T, + PROJECT M + where T.RN = NPROJECTSTAGE + and M.RN = T.PRN) + loop + /* Формируем группу - Общие характеристики */ + PKG_P8PANELS_VISUAL.TDG_ADD_GROUP(RDATA_GRID => RDG, SNAME => 'COMMON', SCAPTION => 'Общие', BEXPANDABLE => true, BEXPANDED => true); + RDG_ROW := PKG_P8PANELS_VISUAL.TDG_ROW_MAKE(SGROUP => 'COMMON'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SFACEACC', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Заказ'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SFACEACC); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SNUMB', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Номер этапа'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SNUMB); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SNAME_USL', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Тема'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SNAME_USL); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SNAME', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Наименование'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SNAME); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SEXT_CUST', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Заказчик'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SEXT_CUST); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SDOC_OSN', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Договор'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SDOC_OSN); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SDOC_OSN_DATE', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Дата договора'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SDOC_OSN_DATE); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'NCOST_SUM', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Стоимость заказа'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.NCOST_SUM); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'NSTAGE_COST_SUM', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Стоимость этапа'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.NSTAGE_COST_SUM); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SCURNAMES', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Валюта'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SCURNAMES); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + /* Формируем группу - Контроль */ + PKG_P8PANELS_VISUAL.TDG_ADD_GROUP(RDATA_GRID => RDG, SNAME => 'CONTROL', SCAPTION => 'Контроль', BEXPANDABLE => true, BEXPANDED => true); + RDG_ROW := PKG_P8PANELS_VISUAL.TDG_ROW_MAKE(SGROUP => 'CONTROL'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SSUBDIV_RESP', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Головной исполнитель'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SSUBDIV_RESP); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SRESPONSIBLE', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Ответственный'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SRESPONSIBLE); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + /* Формируем группу - Работы */ + PKG_P8PANELS_VISUAL.TDG_ADD_GROUP(RDATA_GRID => RDG, SNAME => 'WORKS', SCAPTION => 'Работы', BEXPANDABLE => true, BEXPANDED => true); + RDG_ROW := PKG_P8PANELS_VISUAL.TDG_ROW_MAKE(SGROUP => 'WORKS'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SBEGPLAN', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Начало работ'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SBEGPLAN); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SENDPLAN', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Окончание работ'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SENDPLAN); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'NSTATE', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Состояние'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.NSTATE); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SCONTENT', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Содержание'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SCONTENT); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + /* Формируем группу - Дополнительно */ + PKG_P8PANELS_VISUAL.TDG_ADD_GROUP(RDATA_GRID => RDG, SNAME => 'EXTRA', SCAPTION => 'Дополнительно', BEXPANDABLE => true, BEXPANDED => false); + RDG_ROW := PKG_P8PANELS_VISUAL.TDG_ROW_MAKE(SGROUP => 'EXTRA'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCODE', SVALUE => 'SNOTE', BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SATTR', SVALUE => 'Примечание'); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SVALUE', SVALUE => REC.SNOTE); + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + end loop; + /* Сериализуем описание */ + COUT := PKG_P8PANELS_VISUAL.TDG_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1); + end INFO_STAGE_DTL_DG; + + /* Структура цены этапа проекта (состав статей) */ + procedure INFO_STAGE_ARTS_GET + ( + NPROJECTSTAGE in number, -- Рег. номер этапа проекта + RINFO_STAGE_ARTS out TINFO_STAGE_ARTS -- Список статей структуры цены этапа проекта + ) + is + I PKG_STD.TNUMBER; -- Счётчик статей в результирующей коллекции + begin + /* Инициализируем коллекцию */ + RINFO_STAGE_ARTS := TINFO_STAGE_ARTS(); + /* Подбираем активную структуру цены этапа проекта и её обходим статьи */ + for C in (select CSPA.RN NRN, + -1 NCOST_STATUS, + PS.FACEACC NFACEACC, + A.RN NFPDARTCL, + A.CODE SFPDARTCL, + CSPA.COST_SUM NPLAN_SUM, + null NFACT_SUM, + null NPLAN_FACT_SUM, + C.INTCODE SCURNAMES + from PROJECTSTAGE PS, + STAGES CS, + FACEACC FC, + CONTRPRSTRUCT CSP, + CONTRPRCLC CSPA, + FPDARTCL A, + CURNAMES C + where PS.RN = NPROJECTSTAGE + and PS.FACEACCCUST = CS.FACEACC + and CS.FACEACC = FC.RN + and FC.CURRENCY = C.RN + and CSP.PRN = CS.RN + and CSP.RN = INFO_CONTRPRSTRUCT_ACTIVE_FIND(PS.RN) + and CSPA.PRN = CSP.RN + and CSPA.COST_ARTICLE = A.RN + and exists (select null from V_USERPRIV UP where UP.CATALOG = PS.CRN) + and exists (select null + from V_USERPRIV UP + where UP.JUR_PERS = PS.JUR_PERS + and UP.UNITCODE = 'Projects') + and exists (select null from V_USERPRIV UP where UP.CATALOG = CS.CRN) + and exists (select null + from V_USERPRIV UP + where UP.JUR_PERS = CS.JUR_PERS + and UP.UNITCODE = 'Contracts') + order by CSPA.NUMB) + loop + /* Добавим строку в коллекцию */ + RINFO_STAGE_ARTS.EXTEND(); + I := RINFO_STAGE_ARTS.LAST; + /* Наполним её значениями из хранилища */ + RINFO_STAGE_ARTS(I).NRN := C.NRN; + RINFO_STAGE_ARTS(I).NCOST_STATUS := C.NCOST_STATUS; + RINFO_STAGE_ARTS(I).NFACEACC := C.NFACEACC; + RINFO_STAGE_ARTS(I).NFPDARTCL := C.NFPDARTCL; + RINFO_STAGE_ARTS(I).SFPDARTCL := C.SFPDARTCL; + RINFO_STAGE_ARTS(I).NPLAN_SUM := C.NPLAN_SUM; + RINFO_STAGE_ARTS(I).NFACT_SUM := C.NFACT_SUM; + RINFO_STAGE_ARTS(I).NPLAN_FACT_SUM := C.NPLAN_FACT_SUM; + RINFO_STAGE_ARTS(I).SCURNAMES := C.SCURNAMES; + RINFO_STAGE_ARTS(I).NCOST_CTL := INFO_FPDARTCL_COST_IS_CTL(NRN => C.NFPDARTCL); + /* Для статей, подлежащих контролю затрат */ + if (RINFO_STAGE_ARTS(I).NCOST_CTL) then + /* Определим фактические затраты */ + RINFO_STAGE_ARTS(I).NFACT_SUM := INFO_FCCOSTNOTES_FACT_GET(NFACEACC => RINFO_STAGE_ARTS(I).NFACEACC, + NFPDARTCL => RINFO_STAGE_ARTS(I).NFPDARTCL); + /* Определим отклонение */ + RINFO_STAGE_ARTS(I).NPLAN_FACT_SUM := RINFO_STAGE_ARTS(I).NPLAN_SUM - RINFO_STAGE_ARTS(I).NFACT_SUM; + /* Определим статус затрат - если сумма фактических затрат больше плановых - это перерасход*/ + if (RINFO_STAGE_ARTS(I).NFACT_SUM > RINFO_STAGE_ARTS(I).NPLAN_SUM) then + RINFO_STAGE_ARTS(I).NCOST_STATUS := 2; + else + /* Если отношение факт/план >= 0.9 - опасность */ + if ((RINFO_STAGE_ARTS(I).NPLAN_SUM <> 0) and + (RINFO_STAGE_ARTS(I).NFACT_SUM / RINFO_STAGE_ARTS(I).NPLAN_SUM >= 0.9)) then + RINFO_STAGE_ARTS(I).NCOST_STATUS := 1; + else + /* Отклонений нет */ + RINFO_STAGE_ARTS(I).NCOST_STATUS := 0; + end if; + end if; + end if; + end loop; + end INFO_STAGE_ARTS_GET; + + /* Структура цены этапа проекта (таблица данных) */ + procedure INFO_STAGE_ARTS_DG + ( + NPROJECTSTAGE in number, -- Рег. номер этапа проекта + COUT out clob -- Сериализованная таблица данных + ) + is + RINFO_STAGE_ARTS TINFO_STAGE_ARTS; -- Список статей структуры цены + RDG PKG_P8PANELS_VISUAL.TDG; -- Описание таблицы + RDG_ROW PKG_P8PANELS_VISUAL.TDG_ROW; -- Строка таблицы + begin + /* Инициализируем таблицу данных */ + RDG := PKG_P8PANELS_VISUAL.TDG_MAKE(); + /* Добавляем в таблицу описание колонок */ + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NRN', + SCAPTION => 'Рег. номер', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NCOST_STATUS', + SCAPTION => 'Статус', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + SHINT => INFO_COST_STATUS_HINT(NTYPE => 0)); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NFACEACC', + SCAPTION => 'Рег. номер лицевого счета затрат', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NFPDARTCL', + SCAPTION => 'Рег. номер статьи', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SFPDARTCL', + SCAPTION => 'Статья', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NPLAN_SUM', + SCAPTION => 'План', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NFACT_SUM', + SCAPTION => 'Факт', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NPLAN_FACT_SUM', + SCAPTION => 'Отклонение', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SCURNAMES', + SCAPTION => 'Валюта', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR); + /* Сформируем список статей структуры цены */ + INFO_STAGE_ARTS_GET(NPROJECTSTAGE => NPROJECTSTAGE, RINFO_STAGE_ARTS => RINFO_STAGE_ARTS); + /* Обходим собранные статьи */ + if ((RINFO_STAGE_ARTS is not null) and (RINFO_STAGE_ARTS.COUNT > 0)) then + for I in RINFO_STAGE_ARTS.FIRST .. RINFO_STAGE_ARTS.LAST + loop + /* Добавляем колонки с данными */ + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, + SNAME => 'NRN', + NVALUE => RINFO_STAGE_ARTS(I).NRN, + BCLEAR => true); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, + SNAME => 'NCOST_STATUS', + NVALUE => RINFO_STAGE_ARTS(I).NCOST_STATUS); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NFACEACC', NVALUE => RINFO_STAGE_ARTS(I).NFACEACC); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NFPDARTCL', NVALUE => RINFO_STAGE_ARTS(I).NFPDARTCL); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SFPDARTCL', SVALUE => RINFO_STAGE_ARTS(I).SFPDARTCL); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NPLAN_SUM', NVALUE => RINFO_STAGE_ARTS(I).NPLAN_SUM); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NFACT_SUM', NVALUE => RINFO_STAGE_ARTS(I).NFACT_SUM); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, + SNAME => 'NPLAN_FACT_SUM', + NVALUE => RINFO_STAGE_ARTS(I).NPLAN_FACT_SUM); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCURNAMES', SVALUE => RINFO_STAGE_ARTS(I).SCURNAMES); + /* Добавляем строку в таблицу */ + PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); + end loop; + end if; + /* Сериализуем описание */ + COUT := PKG_P8PANELS_VISUAL.TDG_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1); + end INFO_STAGE_ARTS_DG; + + /* Структура цены этапа проекта (график) */ + procedure INFO_STAGE_ARTS_CHART + ( + NPROJECTSTAGE in number, -- Рег. номер этапа проекта + NTYPE in number, -- Тип диаграммы (0 - по плану, 1 - по факту) + COUT out clob -- Сериализованный график + ) + is + RINFO_STAGE_ARTS TINFO_STAGE_ARTS; -- Список статей структуры цены + RCH PKG_P8PANELS_VISUAL.TCHART; -- График + RCH_DS PKG_P8PANELS_VISUAL.TCHART_DATASET; -- Набор данных + RATTR_VALS PKG_P8PANELS_VISUAL.TCHART_DATASET_ITEM_ATTR_VALS; -- Атрибуты элемента набора данных + begin + /* Сформируем заголовок графика */ + RCH := PKG_P8PANELS_VISUAL.TCHART_MAKE(STYPE => PKG_P8PANELS_VISUAL.SCHART_TYPE_PIE, + SLGND_POS => PKG_P8PANELS_VISUAL.SCHART_LGND_POS_RIGHT); + /* Сформируем набор данных графика */ + RCH_DS := PKG_P8PANELS_VISUAL.TCHART_DATASET_MAKE(SCAPTION => 'Сумма'); + /* Сформируем список статей структуры цены */ + INFO_STAGE_ARTS_GET(NPROJECTSTAGE => NPROJECTSTAGE, RINFO_STAGE_ARTS => RINFO_STAGE_ARTS); + /* Обходим собранные статьи */ + if ((RINFO_STAGE_ARTS is not null) and (RINFO_STAGE_ARTS.COUNT > 0)) then + for I in RINFO_STAGE_ARTS.FIRST .. RINFO_STAGE_ARTS.LAST + loop + /* Только статьи, подлежащие контролю затрат */ + if (INFO_FPDARTCL_COST_IS_CTL(NRN => RINFO_STAGE_ARTS(I).NFPDARTCL)) then + /* Добавим метку для статьи */ + PKG_P8PANELS_VISUAL.TCHART_ADD_LABEL(RCHART => RCH, SLABEL => RINFO_STAGE_ARTS(I).SFPDARTCL); + /* Сформируем дополнительные атрибуты для клиентского приложения - лицевой счет и рег. номер статьи затрат (будут применяться при открытии ЖЗ)*/ + PKG_P8PANELS_VISUAL.TCHART_DATASET_ITM_ATTR_VL_ADD(RATTR_VALS => RATTR_VALS, + SNAME => 'NFACEACC', + SVALUE => TO_CHAR(RINFO_STAGE_ARTS(I).NFACEACC), + BCLEAR => true); + PKG_P8PANELS_VISUAL.TCHART_DATASET_ITM_ATTR_VL_ADD(RATTR_VALS => RATTR_VALS, + SNAME => 'NFPDARTCL', + SVALUE => TO_CHAR(RINFO_STAGE_ARTS(I).NFPDARTCL)); + /* Исходим от типа диаграммы */ + if (NTYPE = 0) then + /* Добавим данные по плану */ + PKG_P8PANELS_VISUAL.TCHART_DATASET_ADD_ITEM(RDATASET => RCH_DS, + NVALUE => RINFO_STAGE_ARTS(I).NPLAN_SUM, + RATTR_VALS => RATTR_VALS); + else + /* Добавим данные по факту */ + PKG_P8PANELS_VISUAL.TCHART_DATASET_ADD_ITEM(RDATASET => RCH_DS, + NVALUE => RINFO_STAGE_ARTS(I).NFACT_SUM, + RATTR_VALS => RATTR_VALS); + end if; + end if; + end loop; + end if; + /* Добавим набор данных в график */ + PKG_P8PANELS_VISUAL.TCHART_ADD_DATASET(RCHART => RCH, RDATASET => RCH_DS); + /* Сериализуем описание */ + COUT := PKG_P8PANELS_VISUAL.TCHART_TO_XML(RCHART => RCH, NINCLUDE_DEF => 1); + end INFO_STAGE_ARTS_CHART; + end PKG_P8PANELS_PROJECTS; / diff --git a/img/prj_info.jpg b/img/prj_info.jpg new file mode 100644 index 0000000..dd39446 Binary files /dev/null and b/img/prj_info.jpg differ diff --git a/p8panels.config b/p8panels.config index 2f6647b..0fb505c 100644 --- a/p8panels.config +++ b/p8panels.config @@ -8,6 +8,7 @@ + @@ -59,6 +60,16 @@ icon="insights" showInPanelsList="true" preview="./img/prj_graph.jpg"/> +