diff --git a/app/panels/clnt_task_board/clnt_task_board.js b/app/panels/clnt_task_board/clnt_task_board.js
new file mode 100644
index 0000000..095cc2b
--- /dev/null
+++ b/app/panels/clnt_task_board/clnt_task_board.js
@@ -0,0 +1,316 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Панель мониторинга: Корневая панель доски задач
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useEffect, useState, useCallback } from "react"; //Классы React
+import { DragDropContext, Droppable } from "react-beautiful-dnd"; //Работа с drag&drop
+import { Stack, Box, IconButton, Icon } from "@mui/material"; //Интерфейсные компоненты
+import { StatusCard } from "./components/status_card.js";
+import { TaskDialog } from "./task_dialog.js"; //Компонент формы события
+import { Filter } from "./filter.js"; //Компонент фильтров
+import { useExtraData, useColorRules, useStatuses } from "./hooks/hooks.js"; //Вспомогательные хуки
+import { useTasks } from "./hooks/tasks_hooks.js"; //Хук событий
+import { useFilters } from "./hooks/filter_hooks.js"; //Вспомогательные хуки фильтра
+import { NoteDialog } from "./components/note_dialog.js"; //Диалог примечания
+import { SettingsDialog } from "./components/settings_dialog.js"; //Диалог дополнительных настроек
+import { deepCopyObject } from "../../core/utils.js"; //Вспомогательные функции
+import { COMMON_STYLES } from "./styles"; //Общие стили
+import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
+
+//---------
+//Константы
+//---------
+
+//Высота фильтра
+const FILTER_HEIGHT = "56px";
+
+//Стили
+const STYLES = {
+ CONTAINER: { width: "100%", padding: 0 },
+ BOX_FILTER: { display: "flex", alignItems: "center" },
+ ICON_BUTTON_SETTINGS: { marginLeft: "auto" },
+ STACK_STATUSES: { maxWidth: "99vw", paddingBottom: "5px", overflowX: "auto", ...COMMON_STYLES.SCROLL },
+ BOX_STATUSES: { position: "fixed", left: "8px", top: `calc(${APP_BAR_HEIGHT} + ${FILTER_HEIGHT})` }
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Корневая панель доски задач
+const ClntTaskBoard = () => {
+ //Состояние фильтров
+ const [filters, handleFiltersChange] = useFilters();
+
+ //Состояние текущего загруженного фильтра
+ const [filterTypeLoaded, setFilterTypeLoaded] = useState(filters.values.sType);
+
+ //Состояние вспомогательных диалогов
+ const [dialogsState, setDialogsState] = useState({
+ filterDialogIsOpen: filters.isSetByUser,
+ settingsDialogIsOpen: false,
+ noteDialog: { isOpen: false, callback: null },
+ taskDialogIsOpen: false
+ });
+
+ //Состояние сортировок
+ const [orders, setOrders] = useState([]);
+
+ //Состояние дополнительных данных
+ const [extraData, setExtraData, handleDocLinksLoad] = useExtraData(filters.values.sType);
+
+ //Состояние статусов событий
+ const [statuses, statusesState, setStatuses, setStatusesState] = useStatuses(filters.values.sType);
+
+ //Состояние пользовательских настроек заливки событий
+ const [colorRules, setColorRules] = useColorRules();
+
+ //Состояние событий
+ const [tasks, setTasks, onDragEnd] = useTasks(filters.values, orders);
+
+ //Состояние доступных маршрутов события
+ const [availableRoutes, setAvailableRoutes] = useState({ source: "", routes: [] });
+
+ //Состояние перетаскиваемого события
+ const [dragItem, setDragItem] = useState({ type: "", status: "" });
+
+ //При открытии/закрытии диалога фильтра
+ const handleFilterOpen = isOpen => {
+ setDialogsState(pv => ({ ...pv, filterDialogIsOpen: isOpen }));
+ };
+
+ //При открытии/закрытии диалога дополнительных настроек
+ const handleSettingsOpen = () => setDialogsState(pv => ({ ...pv, settingsDialogIsOpen: !pv.settingsDialogIsOpen }));
+
+ //При открытии/закрытии диалога примечания
+ const handleNoteOpen = (cb = null) => {
+ setDialogsState(pv => ({ ...pv, noteDialog: { isOpen: !dialogsState.noteDialog.isOpen, callback: cb ? v => cb(v) : null } }));
+ };
+
+ //При открытии/закрытии диалога события
+ const handleTaskDialogOpen = () => setDialogsState(pv => ({ ...pv, taskDialogIsOpen: !dialogsState.taskDialogIsOpen }));
+
+ //При необходимости обновить дополнительные данные
+ const handleExtraDataReload = useCallback(() => {
+ setExtraData(pv => ({ ...pv, reload: true }));
+ }, [setExtraData]);
+
+ //При необходимости обновить информацию о событиях
+ const handleTasksReload = useCallback(
+ (bAccountsReload = true) => {
+ setTasks(pv => ({ ...pv, reload: true, accountsReload: bAccountsReload }));
+ },
+ [setTasks]
+ );
+
+ //При необходимости обновить состояние статусов
+ const handleStatusesStateReload = useCallback(() => {
+ setStatusesState(pv => ({ ...pv, reload: true, sorted: false }));
+ }, [setStatusesState]);
+
+ //При изменении дополнительных настроек
+ const handleSettingsChange = (newSettings, statusesState) => {
+ setColorRules(pv => ({ ...pv, selectedColorRule: newSettings.selectedColorRule }));
+ setStatusesState({ ...statusesState, sorted: false });
+ };
+
+ //При изменении цвета карточки статуса
+ const handleSettingStatusColorChange = (changedStatus, newColor) => {
+ //Считываем массив статусов
+ let newStatuses = [...statuses];
+ //Изменяем цвет нужного статуса
+ newStatuses.find(status => status.ID === changedStatus.ID).color = newColor;
+ //Обновляем состояние
+ setStatuses([...newStatuses]);
+ };
+
+ //При изменении сортировки
+ const handleOrderChanged = columnName => {
+ //Копируем состояние сортировки
+ let newOrders = deepCopyObject(orders);
+ //Находим сортируемую колонку
+ const orderedColumn = newOrders.find(o => o.name == columnName);
+ //Определяем направление сортировки
+ const newDirection = orderedColumn?.direction == "ASC" ? "DESC" : orderedColumn?.direction == "DESC" ? null : "ASC";
+ //Если сортировка отключается - очищаем информацию о сортировке
+ if (newDirection == null && orderedColumn) newOrders.splice(newOrders.indexOf(orderedColumn), 1);
+ //Если сортировки не было - устанавливаем
+ if (newDirection != null && !orderedColumn) newOrders.push({ name: columnName, direction: newDirection });
+ //Если сортировка была и не отключается - изменяем
+ if (newDirection != null && orderedColumn) orderedColumn.direction = newDirection;
+ //Устанавливаем новую сортировку
+ setOrders(newOrders);
+ };
+
+ //При необходимости очистки доступных маршрутов события
+ const handleAvailableRoutesStateClear = () => {
+ setAvailableRoutes({ source: "", routes: [] });
+ };
+
+ //Обработка захвата перетаскиваемого объекта
+ const handleDragItemChange = (filtersType, statusCode) =>
+ setDragItem({
+ type: filtersType,
+ status: statusCode
+ });
+
+ //Обработка очистки перетаскиваемого объекта
+ const handleDragItemClear = () => {
+ setDragItem({ type: "", status: "" });
+ };
+
+ //Проверка доступности карточки события
+ const isCardAvailable = code => {
+ return availableRoutes.source === code || availableRoutes.routes.find(r => r.SDESTINATION === code) || !availableRoutes.source ? true : false;
+ };
+
+ //При изменении фильтра
+ useEffect(() => {
+ //Если изменился тип
+ if (filters.loaded && filters.values.sType) {
+ //Если тип события изменился
+ if (filterTypeLoaded !== filters.values.sType) {
+ //Обновляем информацию о дополнительных данных
+ handleExtraDataReload();
+ //Обновляем информацию о статусах
+ handleStatusesStateReload();
+ //Обновляем текущий загруженный тип события
+ setFilterTypeLoaded(filters.values.sType);
+ }
+ //Обновляем информацию о событиях
+ handleTasksReload();
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [filters.loaded, filters.values]);
+
+ //При изменении сортировки
+ useEffect(() => {
+ //Если есть все данные для загрузки событий
+ if (filters.loaded && filters.values.sType) {
+ //Обновляем информацию о событиях без обновления контрагентов
+ handleTasksReload(false);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [orders]);
+
+ //Генерация содержимого
+ return (
+
+ {dialogsState.settingsDialogIsOpen ? (
+
+ ) : null}
+ {dialogsState.taskDialogIsOpen ? (
+ handleTasksReload(true)}
+ onClose={() => {
+ handleTaskDialogOpen();
+ handleDragItemClear();
+ }}
+ />
+ ) : null}
+
+
+ d.NRN === filters.values.sDocLink) : null}
+ onFilterChange={handleFiltersChange}
+ onDocLinksLoad={handleDocLinksLoad}
+ onFilterOpen={() => handleFilterOpen(true)}
+ onFilterClose={() => handleFilterOpen(false)}
+ onTasksReload={handleTasksReload}
+ orders={orders}
+ onOrderChanged={handleOrderChanged}
+ />
+
+
+ settings
+
+
+ {dialogsState.noteDialog.isOpen ? (
+ dialogsState.noteDialog.callback(note)} onClose={handleNoteOpen} />
+ ) : null}
+ {filters.loaded && filters.values.sType && extraData.dataLoaded && tasks.loaded ? (
+ {
+ //Поиск кода текущего статуса задачи
+ let sourceCode = statuses.find(status => status.ID == path.source.droppableId).SEVNSTAT_CODE;
+ //Устанавливаем доступные маршруты события
+ setAvailableRoutes({ source: sourceCode, routes: [...extraData.evRoutes.filter(route => route.SSOURCE === sourceCode)] });
+ }}
+ onDragEnd={path => {
+ //Если есть статус назначения
+ if (path.destination) {
+ //Определяем мнемокод статуса назначения
+ let destCode = statuses.find(status => status.ID == path.destination.droppableId).SEVNSTAT_CODE;
+ //Переносим событие
+ onDragEnd({ path: path, eventPoints: extraData.evPoints, openNoteDialog: handleNoteOpen, destCode: destCode });
+ }
+ //Очищаем информацию о доступных маршрутах события
+ handleAvailableRoutesStateClear();
+ }}
+ >
+
+
+ {provided => (
+
+
+ {statusesState.sorted
+ ? statuses.map((status, index) => (
+
+
+ {provided => (
+
+
+
+ )}
+
+
+ ))
+ : null}
+
+ {provided.placeholder}
+
+ )}
+
+
+
+ ) : null}
+
+ );
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { ClntTaskBoard };
diff --git a/app/panels/clnt_task_board/components/custom_input_field.js b/app/panels/clnt_task_board/components/custom_input_field.js
new file mode 100644
index 0000000..e003ee7
--- /dev/null
+++ b/app/panels/clnt_task_board/components/custom_input_field.js
@@ -0,0 +1,174 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент: Кастомное поле ввода
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useEffect, useState } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { FormControl, InputLabel, Input, InputAdornment, IconButton, Icon, FormHelperText, Select, MenuItem, Typography } from "@mui/material"; //Интерфейсные компоненты
+import { COMMON_STYLES } from "../styles"; //Общие стили
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ HELPER_TEXT: { color: "red" },
+ SELECT_MENU: width => {
+ return { ...COMMON_STYLES.SCROLL, width: width ? width + 24 : null };
+ }
+};
+
+//---------------
+//Тело компонента
+//---------------
+
+//Кастомное поле ввода
+const CustomInputField = ({
+ elementCode,
+ elementValue,
+ labelText,
+ onChange,
+ required = false,
+ items = null,
+ emptyItem = null,
+ dictionary,
+ menuItemRender,
+ ...other
+}) => {
+ //Значение элемента
+ const [value, setValue] = useState(elementValue);
+
+ //Состояние элемента HTML (для оптимизации ширины MenuItems)
+ const [anchorEl, setAnchorEl] = useState();
+
+ //При открытии меню заливки событий
+ const handleMenuOpen = e => {
+ //Устанавливаем элемент меню
+ setAnchorEl(e.target);
+ };
+
+ //При получении нового значения из вне
+ useEffect(() => {
+ setValue(elementValue);
+ }, [elementValue]);
+
+ //Изменение значения элемента
+ const handleChange = e => {
+ setValue(e.target.value);
+ if (onChange) onChange(e.target.name, e.target.value);
+ };
+
+ //Выбор значения из словаря
+ const handleDictionaryClick = () => {
+ dictionary ? dictionary(res => (res ? handleChange({ target: { name: elementCode, value: res } }) : null)) : null;
+ };
+
+ //Генерация поля с выбором из словаря Парус
+ const renderInput = validationError => {
+ //Генерация содержимого
+ return (
+
+
+ list
+
+
+ ) : null
+ }
+ aria-describedby={`${elementCode}-helper-text`}
+ label={labelText}
+ onChange={handleChange}
+ {...other}
+ />
+ );
+ };
+
+ //Генерация поля с выпадающим списком
+ const renderSelect = (items, anchorEl, handleMenuOpen, validationError) => {
+ //Формируем общий список элементов меню
+ const menuItems = emptyItem ? [emptyItem, ...items] : [...items];
+ //Генерация содержимого
+ return (
+
+ );
+ };
+
+ //Признак ошибки валидации
+ const validationError = !value && required ? true : false;
+
+ //Генерация содержимого
+ return (
+
+ {labelText}
+ {items ? renderSelect(items, anchorEl, handleMenuOpen, validationError) : renderInput(validationError)}
+ {validationError ? (
+
+ *Обязательное поле
+
+ ) : null}
+
+ );
+};
+
+//Контроль свойств - Кастомное поле ввода
+CustomInputField.propTypes = {
+ elementCode: PropTypes.string.isRequired,
+ elementValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ labelText: PropTypes.string.isRequired,
+ required: PropTypes.bool,
+ items: PropTypes.arrayOf(PropTypes.object),
+ emptyItem: PropTypes.object,
+ dictionary: PropTypes.func,
+ onChange: PropTypes.func,
+ menuItemRender: PropTypes.func
+};
+
+//--------------------
+//Интерфейс компонента
+//--------------------
+
+export { CustomInputField };
diff --git a/app/panels/clnt_task_board/components/filter_dialog.js b/app/panels/clnt_task_board/components/filter_dialog.js
new file mode 100644
index 0000000..4b5b661
--- /dev/null
+++ b/app/panels/clnt_task_board/components/filter_dialog.js
@@ -0,0 +1,336 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент: Диалог фильтра отбора
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState, useContext, useEffect, useCallback } from "react"; //Классы React
+import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import {
+ Dialog,
+ DialogTitle,
+ IconButton,
+ Icon,
+ DialogContent,
+ DialogActions,
+ Button,
+ Box,
+ Stack,
+ Checkbox,
+ FormControlLabel,
+ Radio,
+ RadioGroup
+} from "@mui/material"; //Интерфейсные компоненты
+import { CustomInputField } from "./custom_input_field"; //Кастомное поле ввода
+import { hasValue } from "../../../core/utils"; //Вспомогательные функции
+import { EVENT_STATES } from "../layouts"; //Перечисление состояний события
+import { COMMON_STYLES } from "../styles"; //Общие стили
+import { useDictionary } from "../hooks/dict_hooks"; //Состояние открытия разделов
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ SELECT: { width: "450px" }
+};
+
+//---------------
+//Тело компонента
+//---------------
+
+//Диалог фильтра отбора
+const FilterDialog = ({ initial, onFilterChange, onFilterClose, onDocLinksLoad }) => {
+ //Собственное состояние
+ const [filter, setFilter] = useState(initial.filter);
+
+ //Состояние текущих учётных документов
+ const [curDocLinks, setCurDocLinks] = useState({ loaded: true, docLinks: initial.docLinks });
+
+ //Вспомогательные функции открытия раздела
+ const { handleCatalogTreeOpen, handleEventTypesOpen, handleAgnlistOpen, handleInsDepartmentOpen, handleCostStaffGroupsOpen } = useDictionary();
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored } = useContext(BackEndСtx);
+
+ //При изменении типа события фильтра
+ const handleTypeChange = callBack =>
+ handleEventTypesOpen({
+ sCode: filter.sType,
+ callBack: res => {
+ callBack(res.outParameters.eventtypecode);
+ }
+ });
+
+ //При изменении каталога фильтра
+ const handleCrnChange = callBack =>
+ handleCatalogTreeOpen({
+ sUnitName: "ClientEvents",
+ sName: filter.sCrnName,
+ callBack: res => {
+ callBack(res.outParameters.out_NAME);
+ }
+ });
+
+ //При изменении исполнителя фильтра
+ const handleSendPersonChange = callBack =>
+ handleAgnlistOpen({
+ sMnemo: filter.sSendPerson,
+ callBack: res => {
+ callBack(res.outParameters.agnmnemo);
+ }
+ });
+
+ //При изменении подразделения фильтра
+ const handleSendDivisionChange = callBack =>
+ handleInsDepartmentOpen({
+ sCode: filter.sSendDivision,
+ callBack: res => {
+ callBack(res.outParameters.out_CODE);
+ }
+ });
+
+ //При изменении группы пользователей фильтра
+ const handleSendUsrGrpChange = callBack =>
+ handleCostStaffGroupsOpen({
+ sCode: filter.sSendUsrGrp,
+ callBack: res => {
+ callBack(res.outParameters.out_CODE);
+ }
+ });
+
+ //Считывание подкаталогов
+ const getSubCatalogs = useCallback(async () => {
+ //Считываем каталоги
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SUBCATALOGS_GET",
+ args: {
+ SCRN_NAME: filter.sCrnName,
+ NSUBCAT: filter.bSubcatalogs ? 1 : 0
+ }
+ });
+ //Возвращаем список каталогов
+ return data.SRESULT;
+ }, [executeStored, filter.sCrnName, filter.bSubcatalogs]);
+
+ //При закрытии диалога с изменением фильтра
+ const handleDialogOk = async () => {
+ //Если указано имя каталога, но не загружен список рег. номеров
+ if (filter.sCrnName && !filter.sCrnRnList) {
+ //Загружаем список рег. номеров каталогов
+ const crns = await getSubCatalogs();
+ //Устанавливаем новый фильтр
+ onFilterChange({ ...filter, ...(crns ? { sCrnRnList: crns } : {}) });
+ //Закрываем диалог фильтра
+ onFilterClose();
+ } else {
+ //Устанавливаем новый фильтр
+ onFilterChange(filter);
+ //Закрываем диалог фильтра
+ onFilterClose();
+ }
+ };
+
+ //При очистке фильтра
+ const handleFilterClear = () => {
+ setFilter({
+ sState: EVENT_STATES[1],
+ sType: "",
+ sCrnName: "",
+ sCrnRnList: "",
+ bSubcatalogs: false,
+ sSendPerson: "",
+ sSendDivision: "",
+ sSendUsrGrp: "",
+ sDocLink: ""
+ });
+ };
+
+ //При изменении значения элемента
+ const handleFilterItemChange = (item, value) => {
+ //Если это изменение типа
+ if (item === "sType") {
+ //Указываем тип с очисткой информации об учетных документах
+ setFilter(pv => ({ ...pv, [item]: value, sDocLink: "" }));
+ setCurDocLinks(pv => ({ ...pv, loaded: false, docLinks: [] }));
+ } else {
+ //Обновляем значение поля
+ setFilter(pv => ({ ...pv, [item]: value }));
+ }
+ };
+
+ //При очистке учётного документа
+ const handleDocLinkClear = () => setFilter(pv => ({ ...pv, sDocLink: "" }));
+
+ //Обработка изменений с каталогами
+ useEffect(() => {
+ //Если каталог не указан, но галка подкаталогов установлена - снимаем её
+ if (!filter.sCrnName && filter.bSubcatalogs) setFilter(pv => ({ ...pv, bSubcatalogs: false }));
+ //Если изменился каталог и остался список рег. номеров каталогов - очищаем его
+ if (filter.sCrnName !== initial.sCrnName && filter.sCrnRnList) setFilter(pv => ({ ...pv, sCrnRnList: "" }));
+ //Если каталог равен изначальному
+ if (filter.sCrnName === initial.sCrnName) {
+ //Если признак подкаталогов равен изначальному, но список рег. номеров каталогов не соответствует - загружаем изначальный
+ if (filter.bSubcatalogs === initial.bSubcatalogs && filter.sCrnRnList !== initial.sCrnRnList) {
+ setFilter(pv => ({ ...pv, sCrnRnList: initial.sCrnRnList }));
+ }
+ //Если признак подкаталогов не равен изначальному
+ if (filter.bSubcatalogs !== initial.bSubcatalogs) {
+ //Если не установлен - считываем первый из списка рег. номеров изначальных каталогов
+ if (!filter.bSubcatalogs) {
+ setFilter(pv => ({
+ ...pv,
+ sCrnRnList: initial.sCrnRnList.split(";")[0]
+ }));
+ } else {
+ //Если установлен - очищаем список рег. номеров каталогов для последующей загрузки
+ setFilter(pv => ({ ...pv, sCrnRnList: "" }));
+ }
+ }
+ }
+ }, [filter.sCrnName, filter.sCrnRnList, filter.bSubcatalogs, initial.sCrnName, initial.sCrnRnList, initial.bSubcatalogs]);
+
+ //Генерация содержимого
+ return (
+
+
+
+ );
+};
+
+//Контроль свойств компонента - Диалог фильтра отбора
+FilterDialog.propTypes = {
+ initial: PropTypes.object.isRequired,
+ onFilterChange: PropTypes.func.isRequired,
+ onFilterClose: PropTypes.func.isRequired,
+ onDocLinksLoad: PropTypes.func
+};
+
+//--------------------
+//Интерфейс компонента
+//--------------------
+
+export { FilterDialog };
diff --git a/app/panels/clnt_task_board/components/note_dialog.js b/app/panels/clnt_task_board/components/note_dialog.js
new file mode 100644
index 0000000..2977121
--- /dev/null
+++ b/app/panels/clnt_task_board/components/note_dialog.js
@@ -0,0 +1,99 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент: Диалог примечания
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Dialog, DialogTitle, IconButton, Icon, DialogContent, DialogActions, Button, TextField } from "@mui/material"; //Интерфейсные компоненты
+import { CustomInputField } from "./custom_input_field.js"; //Кастомное поле ввода
+import { COMMON_STYLES } from "../styles"; //Общие стили
+
+//Стили
+const STYLES = {
+ DIALOG_CONTENT: { paddingTop: 0, paddingBottom: 0 }
+};
+
+//---------------
+//Тело компонента
+//---------------
+
+//Диалог примечания
+const NoteDialog = ({ noteTypes, onCallback, onClose }) => {
+ //Собственное состояние
+ const [note, setNote] = useState({ noteTypeIndex: 0, text: "" });
+
+ //При изменении примечания
+ const handleNoteChange = value => setNote(pv => ({ ...pv, text: value }));
+
+ //При изменении заголовка примечания
+ const handleNoteHeaderChange = (name, value) => {
+ setNote(pv => ({ ...pv, noteTypeIndex: value }));
+ };
+
+ //При закрытии диалога с изменением примечания
+ const handleDialogOk = () => {
+ //Передаем информацию о примечание в callback
+ onCallback({ header: noteTypes[note.noteTypeIndex], text: note.text });
+ onClose();
+ };
+
+ //Генерация содержимого
+ return (
+
+ );
+};
+
+//Контроль свойств - Диалог примечания
+NoteDialog.propTypes = {
+ noteTypes: PropTypes.array,
+ onCallback: PropTypes.func.isRequired,
+ onClose: PropTypes.func.isRequired
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { NoteDialog };
diff --git a/app/panels/clnt_task_board/components/settings_dialog.js b/app/panels/clnt_task_board/components/settings_dialog.js
new file mode 100644
index 0000000..eb8420d
--- /dev/null
+++ b/app/panels/clnt_task_board/components/settings_dialog.js
@@ -0,0 +1,131 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент: Диалог дополнительных настроек
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Dialog, DialogTitle, DialogContent, DialogActions, IconButton, Icon, Button, Box, Stack } from "@mui/material"; //Интерфейсные компоненты
+import { CustomInputField } from "./custom_input_field.js"; //Кастомное поле ввода
+import { sortAttrs, sortDest } from "../layouts.js"; //Допустимые значение поля и направления сортировки
+import { hasValue } from "../../../core/utils.js"; //Проверка наличия значения
+import { COMMON_STYLES } from "../styles"; //Общие стили
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ SELECT: { width: "100%" }
+};
+
+//---------------
+//Тело компонента
+//---------------
+
+//Диалог дополнительных настроек
+const SettingsDialog = ({ initial, onSettingsChange, onClose, ...other }) => {
+ //Состояние дополнительных настроек
+ const [colorRules, seColorRules] = useState(initial.colorRules);
+
+ //Состояние статусов
+ const [statusesState, setStatusesState] = useState(initial.statusesState);
+
+ //Изменение поля сортировки
+ const handleSortAttrChange = (item, value) => setStatusesState(pv => ({ ...pv, [item]: value }));
+
+ //Изменение направления сортировки
+ const handleSortDestChange = newDirection => setStatusesState(pv => ({ ...pv, direction: newDirection }));
+
+ //При изменении правила заливки событий
+ const handleColorRuleChange = (item, value) => {
+ //Определяем новое правило заливки
+ let newColorRule = colorRules.rules[value];
+ //Обновляем в основных настройках
+ seColorRules(pv => ({ ...pv, selectedColorRule: newColorRule ? newColorRule : {} }));
+ };
+
+ //Генерация содержимого
+ return (
+
+
+
+ );
+};
+
+//Контроль свойств компонента - Диалог дополнительных настроек
+SettingsDialog.propTypes = {
+ initial: PropTypes.object.isRequired,
+ onSettingsChange: PropTypes.func.isRequired,
+ onClose: PropTypes.func.isRequired
+};
+
+//--------------------
+//Интерфейс компонента
+//--------------------
+
+export { SettingsDialog };
diff --git a/app/panels/clnt_task_board/components/status_card.js b/app/panels/clnt_task_board/components/status_card.js
new file mode 100644
index 0000000..b3b851a
--- /dev/null
+++ b/app/panels/clnt_task_board/components/status_card.js
@@ -0,0 +1,182 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент: Карточка статуса событий
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Card, CardHeader, CardContent, Button, IconButton, Icon, Typography, Stack } from "@mui/material"; //Интерфейсные компоненты
+import { TaskCard } from "./task_card.js"; //Компонент Карточка события
+import { StatusCardSettings } from "./status_card_settings.js"; //Компонент Диалог настройки карточки событий
+import { APP_STYLES } from "../../../../app.styles"; //Типовые стили
+import { COLORS } from "../layouts.js"; //Цвета статусов
+import { APP_BAR_HEIGHT } from "../../../components/p8p_app_workspace"; //Заголовок страницы
+
+//---------
+//Константы
+//---------
+
+//Нижний отступ заголовка
+const TITLE_PADDING_BOTTOM = "16px";
+
+//Высота фильтра
+const FILTER_HEIGHT = "56px";
+
+//Стили
+const STYLES = {
+ STATUS_BLOCK: statusColor => {
+ return {
+ width: "350px",
+ height: `calc(100vh - ${APP_BAR_HEIGHT} - ${TITLE_PADDING_BOTTOM} - ${FILTER_HEIGHT} - 8px)`,
+ backgroundColor: statusColor,
+ padding: "8px"
+ };
+ },
+ BLOCK_OPACITY: isAvailable => {
+ return isAvailable ? { opacity: 1 } : { opacity: 0.5 };
+ },
+ CARD_HEADER_TITLE: {
+ textAlign: "left",
+ textOverflow: "ellipsis",
+ overflow: "hidden",
+ display: "-webkit-box",
+ hyphens: "auto",
+ WebkitBoxOrient: "vertical",
+ WebkitLineClamp: 1,
+ maxWidth: "calc(300px)",
+ width: "-webkit-fill-available",
+ fontSize: "1.2rem",
+ cursor: "default"
+ },
+ CARD_HEADER: { padding: 0 },
+ CARD_CONTENT: {
+ padding: 0,
+ paddingRight: "5px",
+ paddingBottom: "5px !important",
+ overflowY: "auto",
+ maxHeight: `calc(100vh - ${APP_BAR_HEIGHT} - ${TITLE_PADDING_BOTTOM} - ${FILTER_HEIGHT} - 85px)`,
+ ...APP_STYLES.SCROLL
+ }
+};
+
+//---------------
+//Тело компонента
+//---------------
+
+//Карточка статуса события
+const StatusCard = ({
+ tasks,
+ status,
+ statusTitle,
+ colorRules,
+ extraData,
+ filtersType,
+ isCardAvailable,
+ onTasksReload,
+ onDragItemChange,
+ onTaskDialogOpen,
+ onNoteDialogOpen,
+ onStatusColorChange,
+ placeholder
+}) => {
+ //Состояние диалога настройки
+ const [statusCardSettingsOpen, setStatusCardSettingsOpen] = useState(false);
+
+ //Открыть/закрыть диалог настройки
+ const handleStatusCardSettingsOpen = () => setStatusCardSettingsOpen(!statusCardSettingsOpen);
+
+ //При изменении цвета статуса
+ const handleStatusColorChange = newColor => {
+ onStatusColorChange(status, newColor);
+ };
+
+ //Генерация содержимого
+ return (
+
+ {statusCardSettingsOpen ? (
+
+ ) : null}
+
+
+ more_vert
+
+ }
+ title={
+
+ {statusTitle}
+
+ }
+ subheader={
+
+ }
+ sx={STYLES.CARD_HEADER}
+ />
+
+
+ {tasks.rows
+ .filter(item => item.sStatus === status.SEVNSTAT_NAME)
+ .map((item, index) => (
+ p.SEVPOINT === status.SEVNSTAT_CODE)}
+ onOpenNoteDialog={onNoteDialogOpen}
+ />
+ ))}
+ {placeholder}
+
+
+
+
+ );
+};
+
+//Контроль свойств - Карточка статуса события
+StatusCard.propTypes = {
+ tasks: PropTypes.object.isRequired,
+ status: PropTypes.object.isRequired,
+ statusTitle: PropTypes.string.isRequired,
+ colorRules: PropTypes.object.isRequired,
+ extraData: PropTypes.object.isRequired,
+ filtersType: PropTypes.string.isRequired,
+ isCardAvailable: PropTypes.func.isRequired,
+ onTasksReload: PropTypes.func.isRequired,
+ onDragItemChange: PropTypes.func.isRequired,
+ onTaskDialogOpen: PropTypes.func.isRequired,
+ onNoteDialogOpen: PropTypes.func.isRequired,
+ onStatusColorChange: PropTypes.func.isRequired,
+ placeholder: PropTypes.object.isRequired
+};
+
+//--------------------
+//Интерфейс компонента
+//--------------------
+
+export { StatusCard };
diff --git a/app/panels/clnt_task_board/components/status_card_settings.js b/app/panels/clnt_task_board/components/status_card_settings.js
new file mode 100644
index 0000000..50ece2f
--- /dev/null
+++ b/app/panels/clnt_task_board/components/status_card_settings.js
@@ -0,0 +1,109 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент: Диалог настройки карточки статуса
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Dialog, DialogTitle, IconButton, Icon, DialogContent, DialogActions, Button, Box, MenuItem, Typography } from "@mui/material"; //Интерфейсные компоненты
+import { CustomInputField } from "./custom_input_field.js"; //Кастомное поле ввода
+import { COMMON_STYLES } from "../styles"; //Общие стили
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ BCKG_COLOR: backgroundColor => ({ backgroundColor: backgroundColor })
+};
+
+//--------------------------------
+//Вспомогательные классы и функции
+//--------------------------------
+
+//Генерация элемента меню
+const menuItemRender = ({ item, key }) => {
+ //Генерация содержимого
+ return (
+
+ );
+};
+
+//---------------
+//Тело компонента
+//---------------
+
+//Диалог настройки карточки статуса
+const StatusCardSettings = ({ statusColor, availableColors, onClose, onColorChange }) => {
+ //Состояние индекса текущего цвета
+ const [colorIndex, setColorIndex] = useState(availableColors.indexOf(statusColor));
+
+ //При закрытии диалога с применением настройки статуса
+ const handleDialogOk = () => {
+ //Изменяем цвет статуса
+ onColorChange(availableColors[colorIndex]);
+ //Закрываем диалог
+ onClose();
+ };
+
+ //При изменении значения элемента
+ const handleSettingsItemChange = (item, value) => {
+ setColorIndex(value);
+ };
+
+ //Генерация содержимого
+ return (
+
+
+
+ );
+};
+
+//Контроль свойств - Диалог настройки карточки статуса
+StatusCardSettings.propTypes = {
+ statusColor: PropTypes.string.isRequired,
+ availableColors: PropTypes.arrayOf(PropTypes.string).isRequired,
+ onClose: PropTypes.func.isRequired,
+ onColorChange: PropTypes.func.isRequired
+};
+
+//--------------------
+//Интерфейс компонента
+//--------------------
+
+export { StatusCardSettings };
diff --git a/app/panels/clnt_task_board/components/task_card.js b/app/panels/clnt_task_board/components/task_card.js
new file mode 100644
index 0000000..953a2f2
--- /dev/null
+++ b/app/panels/clnt_task_board/components/task_card.js
@@ -0,0 +1,376 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент: Карточка события
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Draggable } from "react-beautiful-dnd"; //Работа с drag&drop
+import { Card, CardHeader, Typography, IconButton, Icon, Box, Menu, MenuItem, CardContent, Avatar, Stack } from "@mui/material"; //Интерфейсные компоненты
+import { TaskDialog } from "../task_dialog"; //Форма события
+import { ApplicationСtx } from "../../../context/application"; //Контекст приложения
+import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
+import { MessagingСtx } from "../../../context/messaging"; //Контекст сообщений
+import { TASK_COLORS, getTaskExpiredColor, getTaskBgColorByRule, makeCardActionsArray } from "../layouts"; //Дополнительная разметка и вёрстка клиентских элементов
+import { useDictionary } from "../hooks/dict_hooks"; //Состояние открытия разделов
+import { useTasksFunctions } from "../hooks/tasks_hooks"; //Состояние вспомогательных функций событий
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ MENU_ITEM_DELIMITER: { borderBottom: "1px solid lightgrey" },
+ CARD: (indicatorClr, bgClr) => {
+ const i = indicatorClr ? { borderLeft: `solid ${indicatorClr}` } : null;
+ const bc = bgClr ? { backgroundColor: bgClr } : null;
+ return { ...i, ...bc };
+ },
+ CARD_HEADER_TITLE: {
+ padding: "4px",
+ width: "292px",
+ display: "-webkit-box",
+ hyphens: "auto",
+ WebkitBoxOrient: "vertical",
+ WebkitLineClamp: 2,
+ overflow: "hidden"
+ },
+ CARD_HEADER: { padding: 0, cursor: "pointer" },
+ CARD_CONTENT: { padding: "4px !important" },
+ CARD_CONTENT_BOX: { display: "flex", alignItems: "center" },
+ STACK_SENDER: { alignItems: "center", marginLeft: "auto" },
+ TYPOGRAPHY_SECONDARY: {
+ color: "text.secondary",
+ fontSize: 14
+ },
+ ICON_COLOR: linked => {
+ return { color: theme => (linked ? TASK_COLORS.LINKED : theme.palette.grey[500]) };
+ }
+};
+
+//------------------------------------
+//Вспомогательные функции и компоненты
+//------------------------------------
+
+//Действия карточки события
+const CardActions = ({
+ taskRn,
+ menuItems,
+ cardActions,
+ onMethodsMenuButtonClick,
+ onMethodsMenuClose,
+ onTasksReload,
+ pointSettings,
+ onOpenNoteDialog
+}) => {
+ //При нажатии на действие меню
+ const handleActionClick = action => {
+ //Выполняем действие
+ action.func({
+ nEvent: taskRn,
+ onReload: action.tasksReload ? () => onTasksReload(action.needAccountsReload) : null,
+ onNoteOpen: pointSettings.ADDNOTE_ONSEND ? onOpenNoteDialog : null
+ });
+ //Закрываем меню действий
+ onMethodsMenuClose();
+ };
+
+ return (
+
+
+
+
+ );
+};
+
+//Контроль свойств - Действия карточки события
+CardActions.propTypes = {
+ taskRn: PropTypes.number.isRequired,
+ menuItems: PropTypes.array.isRequired,
+ cardActions: PropTypes.object.isRequired,
+ onMethodsMenuButtonClick: PropTypes.func.isRequired,
+ onMethodsMenuClose: PropTypes.func.isRequired,
+ onTasksReload: PropTypes.func,
+ pointSettings: PropTypes.object,
+ onOpenNoteDialog: PropTypes.func
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Карточка события
+const TaskCard = ({ task, index, onTasksReload, colorRule, pointSettings, onOpenNoteDialog }) => {
+ //Состояние диалога события
+ const [taskDialogOpen, setTaskDialogOpen] = useState(false);
+
+ //Состояние действий события
+ const [cardActions, setCardActions] = useState({ anchorMenuMethods: null, openMethods: false });
+
+ //Состояние списка действий меню
+ const [menuItems, setMenuItems] = useState([]);
+
+ //Вспомогательные функции открытия раздела
+ const { handleClientEventsOpen, handleClientEventsNotesOpen, handleFileLinksOpen } = useDictionary();
+
+ //Состояние вспомогательных функций событий
+ const { handleTaskStateChange, handleTaskSend } = useTasksFunctions();
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored } = useContext(BackEndСtx);
+
+ //Подключение к контексту сообщений
+ const { showMsgWarn } = useContext(MessagingСtx);
+
+ //Подключение к контексту приложения
+ const { pOnlineShowDocument } = useContext(ApplicationСtx);
+
+ //По нажатию на открытие меню действий
+ const handleMethodsMenuButtonClick = useCallback(event => {
+ setCardActions(pv => ({ ...pv, anchorMenuMethods: event.currentTarget, openMethods: true }));
+ }, []);
+
+ //При закрытии меню
+ const handleMethodsMenuClose = useCallback(() => {
+ setCardActions(pv => ({ ...pv, anchorMenuMethods: null, openMethods: false }));
+ }, []);
+
+ //При удалении контрагента
+ const handleTaskDelete = useCallback(
+ async ({ nEvent, onReload }) => {
+ await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DELETE",
+ args: { NCLNEVENTS: nEvent }
+ });
+ //Если требуется перезагрузить данные
+ onReload ? onReload() : null;
+ },
+ [executeStored]
+ );
+
+ //При возврате в предыдущую точку события
+ const handleTaskReturn = useCallback(
+ async ({ nEvent, onReload }) => {
+ await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_RETURN",
+ args: { NCLNEVENTS: nEvent }
+ });
+ //Если требуется перезагрузить данные
+ onReload ? onReload() : null;
+ },
+ [executeStored]
+ );
+
+ //По нажатию действия "Направить"
+ const handleSendAction = useCallback(
+ async ({ nEvent, onReload, onNoteOpen }) => {
+ //Выполняем направление события
+ handleTaskSend({ nEvent, onReload, onNoteOpen });
+ },
+ [handleTaskSend]
+ );
+
+ //По нажатия действия "Редактировать"
+ const handleTaskEditAction = useCallback(() => {
+ setTaskDialogOpen(true);
+ }, []);
+
+ //По нажатия действия "Редактировать в разделе"
+ const handleTaskEditClientAction = useCallback(
+ async ({ nEvent }) => {
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SELECT",
+ args: {
+ NCLNEVENTS: nEvent
+ }
+ });
+ if (data.NIDENT) {
+ //Открываем раздел "События" с фильтром по записи
+ handleClientEventsOpen({ nIdent: data.NIDENT });
+ }
+ },
+ [executeStored, handleClientEventsOpen]
+ );
+
+ //По нажатию действия "Удалить"
+ const handleTaskDeleteAction = useCallback(
+ ({ nEvent, onReload }) => {
+ showMsgWarn("Удалить событие?", () => handleTaskDelete({ nEvent, onReload }));
+ },
+ [handleTaskDelete, showMsgWarn]
+ );
+
+ //По нажатию действия "Выполнить возврат"
+ const handleTaskReturnAction = useCallback(
+ ({ nEvent, onReload }) => {
+ showMsgWarn("Выполнить возврат события в предыдущую точку?", () => handleTaskReturn({ nEvent, onReload }));
+ },
+ [handleTaskReturn, showMsgWarn]
+ );
+
+ //По нажатию действия "Примечания"
+ const handleEventNotesOpenAction = useCallback(
+ ({ nEvent }) => {
+ handleClientEventsNotesOpen({ nPrn: nEvent });
+ },
+ [handleClientEventsNotesOpen]
+ );
+
+ //По нажатию действия "Присоединенные документы"
+ const handleTaskFileLinksOpenAction = useCallback(
+ ({ nEvent }) => {
+ handleFileLinksOpen({ nPrn: nEvent, sUnitCode: "ClientEvents" });
+ },
+ [handleFileLinksOpen]
+ );
+
+ //По нажатию действия "Перейти"
+ const handleStateChangeAction = useCallback(
+ async ({ nEvent, onReload, onNoteOpen }) => {
+ //Выполняем изменения статуса события
+ handleTaskStateChange({ nEvent, onReload, onNoteOpen });
+ },
+ [handleTaskStateChange]
+ );
+
+ //При изменении ссылок в меню действий (для того, чтобы ссылка на объект менялась при реальной необходимости)
+ useEffect(() => {
+ //Устанавливаем список меню
+ setMenuItems(
+ makeCardActionsArray(
+ handleTaskEditAction,
+ handleTaskEditClientAction,
+ handleTaskDeleteAction,
+ handleStateChangeAction,
+ handleTaskReturnAction,
+ handleSendAction,
+ handleEventNotesOpenAction,
+ handleTaskFileLinksOpenAction
+ )
+ );
+ }, [
+ handleEventNotesOpenAction,
+ handleTaskFileLinksOpenAction,
+ handleSendAction,
+ handleStateChangeAction,
+ handleTaskDeleteAction,
+ handleTaskEditAction,
+ handleTaskEditClientAction,
+ handleTaskReturnAction
+ ]);
+
+ //Генерация содержимого
+ return (
+
+ {taskDialogOpen ? (
+ {
+ setTaskDialogOpen(false);
+ }}
+ />
+ ) : null}
+
+ {provided => (
+
+ {
+ menuItems.find(action =>
+ action.method === "EDIT" ? action.func(task.nRn, action.tasksReload ? onTasksReload : null) : null
+ );
+ }}
+ >
+ {task.sDescription}
+
+ }
+ sx={STYLES.CARD_HEADER}
+ action={
+
+ }
+ />
+
+
+ pOnlineShowDocument({ unitCode: task.sLinkedUnit, document: task.nLinkedRn }) : null
+ }
+ sx={STYLES.ICON_COLOR(task.nLinkedRn)}
+ disabled={!task.nLinkedRn}
+ >
+ assignment
+
+ {task.name}
+ {task.sSender ? (
+
+ {task.sSender}
+
+
+ ) : null}
+
+
+
+ )}
+
+
+ );
+};
+
+//Контроль свойств - Карточка события
+TaskCard.propTypes = {
+ task: PropTypes.object.isRequired,
+ index: PropTypes.number.isRequired,
+ onTasksReload: PropTypes.func,
+ colorRule: PropTypes.object,
+ pointSettings: PropTypes.object,
+ onOpenNoteDialog: PropTypes.func
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { TaskCard };
diff --git a/app/panels/clnt_task_board/components/task_form.js b/app/panels/clnt_task_board/components/task_form.js
new file mode 100644
index 0000000..12d5882
--- /dev/null
+++ b/app/panels/clnt_task_board/components/task_form.js
@@ -0,0 +1,158 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент панели: Форма события
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState, useEffect, useCallback } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Box, Typography, Tabs, Tab, InputAdornment, IconButton, Icon } from "@mui/material"; //Интерфейсные компоненты
+import { TaskFormTabInfo } from "./task_form_tab_info"; //Вкладка основной информации
+import { TaskFormTabExecutor } from "./task_form_tab_executor"; //Вкладка информации об исполнителе
+import { TaskFormTabProps } from "./task_form_tab_props"; //Вкладка информации со свойствами
+import { useDocsProps } from "../hooks/task_dialog_hooks"; //Хук для получения свойств раздела "События"
+import { hasValue } from "../../../core/utils";
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ CONTAINER: { margin: "5px 0px", textAlign: "center" }
+};
+
+//------------------------------------
+//Вспомогательные функции и компоненты
+//------------------------------------
+
+//Свойства вкладки
+function a11yProps(index) {
+ return {
+ id: `simple-tab-${index}`,
+ "aria-controls": `simple-tabpanel-${index}`
+ };
+}
+
+//Вкладка информации
+function CustomTabPanel(props) {
+ const { children, value, index, ...other } = props;
+ //Генерация содержимого
+ return (
+
+ {value === index && {children}}
+
+ );
+}
+
+//Контроль свойств - Вкладка информации
+CustomTabPanel.propTypes = {
+ children: PropTypes.node,
+ index: PropTypes.number.isRequired,
+ value: PropTypes.number.isRequired
+};
+
+//Формирование кнопки для открытия раздела
+export const getInputProps = (onClick, disabled = false, icon = "list") => {
+ //Генерация содержимого
+ return {
+ endAdornment: (
+
+
+ {icon}
+
+
+ )
+ };
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Форма события
+const TaskForm = ({ task, taskType, onTaskChange, editable, onEventNextNumbGet, onDPReady }) => {
+ //Состояние вкладки
+ const [tab, setTab] = useState(0);
+
+ //Состояние допустимых дополнительных свойств
+ const [docProps] = useDocsProps(taskType);
+
+ //При изменении вкладки
+ const handleTabChange = (e, newValue) => {
+ setTab(newValue);
+ };
+
+ //При изменении поля
+ const handleFieldEdit = useCallback(
+ e => {
+ onTaskChange({
+ [e.target.id]: e.target.value,
+ //Связанные значения, если меняется одно, то необходимо обнулить другое
+ ...(e.target.id === "sClntClnperson" ? { sClntClients: "" } : {}),
+ ...(e.target.id === "sClntClients" ? { sClntClnperson: "" } : {})
+ });
+ },
+ [onTaskChange]
+ );
+
+ //При изменении доп. свойства
+ const handlePropEdit = useCallback(
+ (docProp, value) => {
+ onTaskChange({ docProps: { ...task.docProps, [docProp]: value } });
+ },
+ [onTaskChange, task.docProps]
+ );
+
+ //Проверка заполненности всех обязательных доп. свойств
+ useEffect(() => {
+ //Считываем количество незаполненных обязательных свойств
+ let notFilled = docProps.props.filter(docProp => docProp.BREQUIRE === true && !hasValue(task.docProps[docProp.SFORMATTED_ID])).length;
+ //Если доп. свойства загрузились и количество незаполненных = 0 - доп. свойства готовы, иначе не готовы
+ docProps.loaded && notFilled === 0 ? onDPReady(true) : onDPReady(false);
+ }, [docProps, onDPReady, task.docProps]);
+
+ //Генерация содержимого
+ return (
+
+
+ {task.nRn ? "Исправление события" : "Добавление события"}
+
+
+
+
+ {docProps.props.length > 0 ? : null}
+
+
+
+
+
+
+
+ {docProps.props.length > 0 ? (
+
+
+
+ ) : null}
+
+ );
+};
+
+//Контроль свойств - Форма события
+TaskForm.propTypes = {
+ task: PropTypes.object.isRequired,
+ taskType: PropTypes.string.isRequired,
+ onTaskChange: PropTypes.func.isRequired,
+ editable: PropTypes.bool.isRequired,
+ onEventNextNumbGet: PropTypes.func.isRequired,
+ onDPReady: PropTypes.func.isRequired
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { TaskForm };
diff --git a/app/panels/clnt_task_board/components/task_form_tab_executor.js b/app/panels/clnt_task_board/components/task_form_tab_executor.js
new file mode 100644
index 0000000..9e681e6
--- /dev/null
+++ b/app/panels/clnt_task_board/components/task_form_tab_executor.js
@@ -0,0 +1,155 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент: Вкладка информации об исполнителе
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Box, TextField } from "@mui/material"; //Интерфейсные компоненты
+import { getInputProps } from "./task_form"; //Формирование кнопки доступа к разделу
+import dayjs from "dayjs"; //Работа с датами
+import customParseFormat from "dayjs/plugin/customParseFormat"; //Настройка пользовательского формата даты
+import { COMMON_STYLES } from "../styles"; //Общие стили
+import { useDictionary } from "../hooks/dict_hooks"; //Состояние открытия разделов
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ BOX_LEFT_ALIGN: { display: "flex", justifyContent: "flex-start" }
+};
+
+//------------------------------------
+//Вспомогательные функции и компоненты
+//------------------------------------
+
+//Подключение настройки пользовательского формата даты
+dayjs.extend(customParseFormat);
+
+//-----------
+//Тело модуля
+//-----------
+
+//Вкладка информации об исполнителе
+const TaskFormTabExecutor = ({ task, onFieldEdit }) => {
+ //Вспомогательные функции открытия раздела
+ const { handleClientPersonOpen } = useDictionary();
+
+ //При изменении сотрудника-инициатора
+ const handleInitClnpersonChange = () =>
+ handleClientPersonOpen({
+ sCode: task.sInitClnperson,
+ callBack: res => {
+ onFieldEdit({
+ target: {
+ id: "sInitClnperson",
+ value: res.outParameters.out_CODE
+ }
+ });
+ }
+ });
+
+ //Генерация содержимого
+ return (
+
+
+
+
+
+
+
+ handleInitClnpersonChange(), task.isUpdate)}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+//Контроль свойств - Вкладка информации об исполнителе
+TaskFormTabExecutor.propTypes = {
+ task: PropTypes.object.isRequired,
+ onFieldEdit: PropTypes.func.isRequired
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { TaskFormTabExecutor };
diff --git a/app/panels/clnt_task_board/components/task_form_tab_info.js b/app/panels/clnt_task_board/components/task_form_tab_info.js
new file mode 100644
index 0000000..b97f7c8
--- /dev/null
+++ b/app/panels/clnt_task_board/components/task_form_tab_info.js
@@ -0,0 +1,192 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент: Вкладка основной информации
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Box, TextField } from "@mui/material"; //Интерфейсные компоненты
+import { getInputProps } from "./task_form"; //Формирование кнопки доступа к разделу
+import { COMMON_STYLES } from "../styles"; //Общие стили
+import { useDictionary } from "../hooks/dict_hooks"; //Состояние открытия разделов
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ BOX_FEW_COLUMNS: { display: "flex", flexWrap: "wrap", justifyContent: "space-between" }
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Вкладка основной информации
+const TaskFormTabInfo = ({ task, editable, onFieldEdit, onEventNextNumbGet }) => {
+ //Вспомогательные функции открытия раздела
+ const { handleClientPersonOpen, handleCatalogTreeOpen, handleClientClientsOpen } = useDictionary();
+
+ //При изменении каталога
+ const handleCrnChange = () =>
+ handleCatalogTreeOpen({
+ sUnitName: "ClientEvents",
+ sName: task.sCrn,
+ callBack: res => {
+ onFieldEdit({
+ target: {
+ id: "sCrn",
+ value: res.outParameters.out_NAME
+ }
+ });
+ }
+ });
+
+ //При изменении клиента-сотрудника
+ const handleClntClnpersonChange = () =>
+ handleClientPersonOpen({
+ sCode: task.sClntClnperson,
+ callBack: res => {
+ onFieldEdit({
+ target: {
+ id: "sClntClnperson",
+ value: res.outParameters.out_CODE
+ }
+ });
+ }
+ });
+
+ //При изменении клиента-организации
+ const handleClntClientsChange = () =>
+ handleClientClientsOpen({
+ sCode: task.sClntClients,
+ callBack: res => {
+ onFieldEdit({
+ target: {
+ id: "sClntClients",
+ value: res.outParameters.out_CLIENT_CODE
+ }
+ });
+ }
+ });
+
+ //Генерация содержимого
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ handleClntClientsChange(), !task.sType)}
+ >
+ handleClntClnpersonChange(), !task.sType)}
+ >
+
+
+ );
+};
+
+//Контроль свойств - Вкладка основной информации
+TaskFormTabInfo.propTypes = {
+ task: PropTypes.object.isRequired,
+ editable: PropTypes.bool.isRequired,
+ onFieldEdit: PropTypes.func.isRequired,
+ onEventNextNumbGet: PropTypes.func.isRequired
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { TaskFormTabInfo };
diff --git a/app/panels/clnt_task_board/components/task_form_tab_props.js b/app/panels/clnt_task_board/components/task_form_tab_props.js
new file mode 100644
index 0000000..dc89940
--- /dev/null
+++ b/app/panels/clnt_task_board/components/task_form_tab_props.js
@@ -0,0 +1,169 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент: Вкладка информации со свойствами
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Box, TextField } from "@mui/material"; //Интерфейсные компоненты
+import { getInputProps } from "./task_form"; //Формирование кнопки доступа к разделу
+import dayjs from "dayjs"; //Работа с датами
+import customParseFormat from "dayjs/plugin/customParseFormat"; //Настройка пользовательского формата даты
+import { DP_DEFAULT_VALUE, DP_IN_VALUE, DP_RETURN_VALUE, validationError, formatSqlDate } from "../layouts"; //Дополнительная разметка и вёрстка клиентских элементов
+import { COMMON_STYLES } from "../styles"; //Общие стили
+import { useDictionary } from "../hooks/dict_hooks"; //Состояние открытия разделов
+
+//---------
+//Константы
+//---------
+
+//------------------------------------
+//Вспомогательные функции и компоненты
+//------------------------------------
+
+//Подключение настройки пользовательского формата даты
+dayjs.extend(customParseFormat);
+
+//-----------
+//Тело модуля
+//-----------
+
+//Вкладка информации со свойствами
+const TaskFormTabProps = ({ task, docProps, onPropEdit }) => {
+ //Вспомогательные функции открытия раздела
+ const { handleExtraDictionariesOpen, handleUnitOpen } = useDictionary();
+
+ //Выбор из словаря или дополнительного словаря
+ const handleDictOpen = async (docProp, curValue = null) => {
+ //Если способ выбора - словарь
+ docProp.NENTRY_TYPE === 1
+ ? handleUnitOpen({
+ sUnitCode: docProp.SUNITCODE,
+ sShowMethod: docProp.SMETHOD_CODE,
+ inputParameters: docProp.NPARAM_RN ? [{ name: docProp.SPARAM_IN_CODE, value: curValue }] : null,
+ callBack: res => {
+ onPropEdit(docProp.SFORMATTED_ID, res.outParameters[docProp.SPARAM_OUT_CODE]);
+ }
+ })
+ : //Если способ выбора - доп. словарь
+ handleExtraDictionariesOpen({
+ nRn: docProp.NEXTRA_DICT_RN,
+ sParamName: DP_IN_VALUE[docProp.NFORMAT],
+ paramValue: curValue,
+ callBack: res => {
+ onPropEdit(docProp.SFORMATTED_ID, res.outParameters[DP_RETURN_VALUE[docProp.NFORMAT]]);
+ }
+ });
+ };
+
+ //Инициализация дополнительного свойства
+ const initPropValue = prop => {
+ //Считываем значение свойства из события
+ const value = task.docProps[prop.SFORMATTED_ID];
+ //Если есть значение свойства
+ if (value) {
+ //Строка или число
+ if (prop.NFORMAT < 2) {
+ return prop.NNUM_PRECISION ? String(value).replace(".", ",") : value;
+ }
+ //Дата
+ if (prop.NFORMAT === 2) {
+ //Возвращаем значение исходя из подтипа даты
+ switch (prop.NDATA_SUBTYPE) {
+ //Дата без времени
+ case 0:
+ return dayjs(value).format("YYYY-MM-DD");
+ //Дата и время без секунд
+ case 1:
+ return dayjs(value).format("YYYY-MM-DD HH:mm");
+ //Дата и время с секундами
+ default:
+ return dayjs(value).format("YYYY-MM-DD HH:mm:ss");
+ }
+ }
+ //Если это ничего из вышестоящего - время
+ return formatSqlDate(value);
+ }
+ //Если нет значения, но это изменение события
+ if (task.nRn) {
+ //Возвращаем пустоту
+ return "";
+ }
+ //Если нет значения и это добавление события - возвращаем значение по умолчанию
+ return prop[DP_DEFAULT_VALUE[prop.NFORMAT]];
+ };
+
+ //Генерация содержимого
+ return (
+
+
+ {docProps.props.map((docProp, index) => {
+ return docProp.BSHOW_IN_GRID ? (
+ onPropEdit(e.target.id, e.target.value)}
+ inputProps={
+ (docProp.NFORMAT === 2 && docProp.NDATA_SUBTYPE === 2) || (docProp.NFORMAT === 3 && docProp.NDATA_SUBTYPE === 1)
+ ? { step: 1 }
+ : {}
+ }
+ InputProps={
+ docProp.NENTRY_TYPE > 0 ? getInputProps(() => handleDictOpen(docProp, task.docProps[docProp.SFORMATTED_ID])) : null
+ }
+ InputLabelProps={
+ docProp.NFORMAT < 2
+ ? {}
+ : {
+ shrink: true
+ }
+ }
+ required={docProp.BREQUIRE}
+ disabled={docProp.BREADONLY}
+ />
+ ) : null;
+ })}
+
+
+ );
+};
+
+//Контроль свойств - Вкладка информации со свойствами
+TaskFormTabProps.propTypes = {
+ task: PropTypes.object.isRequired,
+ docProps: PropTypes.object.isRequired,
+ onPropEdit: PropTypes.func.isRequired
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { TaskFormTabProps };
diff --git a/app/panels/clnt_task_board/filter.js b/app/panels/clnt_task_board/filter.js
new file mode 100644
index 0000000..6591bb8
--- /dev/null
+++ b/app/panels/clnt_task_board/filter.js
@@ -0,0 +1,212 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент: Фильтр отбора
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Chip, Stack, Icon, IconButton, Box, Menu, MenuItem, Typography } from "@mui/material"; //Интерфейсные компоненты
+import { FilterDialog } from "./components/filter_dialog.js"; //Диалог фильтра
+import { COMMON_STYLES } from "./styles"; //Общие стили
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ ICON_ORDERS: orders => {
+ return orders.length > 0 ? { color: "#1976d2" } : {};
+ },
+ MENU_ORDER: {
+ width: "260px"
+ },
+ MENU_ITEM_ORDER: {
+ display: "flex",
+ justifyContent: "space-between"
+ },
+ FILTERS_STACK: {
+ paddingBottom: "5px",
+ ...COMMON_STYLES.SCROLL
+ },
+ STACK_FILTER: { maxWidth: "99vw" }
+};
+
+//--------------------------
+//Вспомогательные компоненты
+//--------------------------
+
+//Элемент меню сортировок
+const SortMenuItem = ({ item, caption, orders, onOrderChanged }) => {
+ //Кнопка сортировки
+ const order = orders.find(order => order.name == item);
+ //Генерация содержимого
+ return (
+
+ );
+};
+
+//Контроль свойств компонента - Элемент меню сортировок
+SortMenuItem.propTypes = {
+ item: PropTypes.string.isRequired,
+ caption: PropTypes.string.isRequired,
+ orders: PropTypes.array,
+ onOrderChanged: PropTypes.func.isRequired
+};
+
+//Меню сортировок
+const SortMenu = ({ menuOrders, onOrdersMenuClose, orders, onOrderChanged }) => {
+ //Генерация содержимого
+ return (
+
+ );
+};
+
+//Контроль свойств компонента - Меню сортировок
+SortMenu.propTypes = {
+ menuOrders: PropTypes.object.isRequired,
+ onOrdersMenuClose: PropTypes.func.isRequired,
+ orders: PropTypes.array,
+ onOrderChanged: PropTypes.func.isRequired
+};
+
+//Элемент фильтра
+const FilterItem = ({ caption, value, onClick }) => {
+ //При нажатии на элемент
+ const handleClick = () => (onClick ? onClick() : null);
+
+ //Генерация содержимого
+ return (
+
+ {caption}
+ {value ? `:\u00A0${value}` : null}
+
+ }
+ variant="outlined"
+ onClick={handleClick}
+ />
+ );
+};
+
+//Контроль свойств компонента - Элемент фильтра
+FilterItem.propTypes = {
+ caption: PropTypes.string.isRequired,
+ value: PropTypes.any,
+ onClick: PropTypes.func
+};
+
+//---------------
+//Тело компонента
+//---------------
+
+//Фильтр отбора
+const Filter = ({
+ isFilterDialogOpen,
+ filter,
+ docLinks,
+ selectedDocLink,
+ onFilterChange,
+ onDocLinksLoad,
+ onFilterOpen,
+ onFilterClose,
+ onTasksReload,
+ orders,
+ onOrderChanged,
+ ...other
+}) => {
+ //Состояние меню сортировки
+ const [menuOrders, setMenuOrders] = useState({ anchorMenuOrders: null, openOrders: false });
+
+ //При нажатии на открытие меню сортировки
+ const handleOrdersMenuButtonClick = event => {
+ setMenuOrders(pv => ({ ...pv, anchorMenuOrders: event.currentTarget, openOrders: true }));
+ };
+
+ //При закрытии меню
+ const handleOrdersMenuClose = () => {
+ setMenuOrders(pv => ({ ...pv, anchorMenuOrders: null, openOrders: false }));
+ };
+
+ //Генерация содержимого
+ return (
+
+ {isFilterDialogOpen ? (
+
+ ) : null}
+
+
+
+ refresh
+
+
+ sort
+
+
+ filter_alt
+
+
+ {filter.sState ? : null}
+ {filter.sType ? : null}
+ {filter.sCrnName ? : null}
+ {filter.bSubcatalogs ? : null}
+ {filter.sSendPerson ? : null}
+ {filter.sSendDivision ? : null}
+ {filter.sSendUsrGrp ? (
+
+ ) : null}
+ {filter.sDocLink && selectedDocLink ? (
+
+ ) : null}
+
+
+
+
+
+ );
+};
+
+//Контроль свойств компонента - Фильтр отбора
+Filter.propTypes = {
+ isFilterDialogOpen: PropTypes.bool.isRequired,
+ filter: PropTypes.object.isRequired,
+ docLinks: PropTypes.arrayOf(PropTypes.object),
+ selectedDocLink: PropTypes.object,
+ onFilterChange: PropTypes.func.isRequired,
+ onDocLinksLoad: PropTypes.func,
+ onFilterOpen: PropTypes.func.isRequired,
+ onFilterClose: PropTypes.func.isRequired,
+ onTasksReload: PropTypes.func.isRequired,
+ orders: PropTypes.array,
+ onOrderChanged: PropTypes.func.isRequired
+};
+
+//--------------------
+//Интерфейс компонента
+//--------------------
+
+export { Filter };
diff --git a/app/panels/clnt_task_board/hooks/dict_hooks.js b/app/panels/clnt_task_board/hooks/dict_hooks.js
new file mode 100644
index 0000000..2b4ccfb
--- /dev/null
+++ b/app/panels/clnt_task_board/hooks/dict_hooks.js
@@ -0,0 +1,255 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Пользовательские хуки: Хуки открытия разделов
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import { useContext, useCallback } from "react"; //Классы React
+import { ApplicationСtx } from "../../../context/application"; //Контекст приложения
+
+//-----------
+//Тело модуля
+//-----------
+
+//Состояние открытия разделов
+const useDictionary = () => {
+ //Подключение к контексту приложения
+ const { pOnlineShowDictionary } = useContext(ApplicationСtx);
+
+ //Отображение раздела "Сотрудники"
+ const handleClientPersonOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "ClientPersons",
+ showMethod: "main",
+ inputParameters: [{ name: "in_CODE", value: prms.sCode }],
+ callBack: res => {
+ res.success ? prms.callBack(res) : null;
+ }
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Отображение раздела "Клиенты"
+ const handleClientClientsOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "ClientClients",
+ showMethod: "main",
+ inputParameters: [{ name: "in_CLIENT_CODE", value: prms.sCode }],
+ callBack: res => {
+ res.success ? prms.callBack(res) : null;
+ }
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Отображение раздела "Каталоги"
+ const handleCatalogTreeOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "CatalogTree",
+ showMethod: "main",
+ inputParameters: [
+ { name: "in_DOCNAME", value: prms.sUnitName },
+ { name: "in_NAME", value: prms.sName }
+ ],
+ callBack: res => {
+ res.success ? prms.callBack(res) : null;
+ }
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Отображение раздела "Типы событий"
+ const handleEventTypesOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "ClientEventTypes",
+ showMethod: "dictionary",
+ inputParameters: [{ name: "pos_eventtypecode", value: prms.sCode }],
+ callBack: res => {
+ res.success ? prms.callBack(res) : null;
+ }
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Отображение раздела "Контрагенты"
+ const handleAgnlistOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "AGNLIST",
+ showMethod: "agents",
+ inputParameters: [{ name: "pos_agnmnemo", value: prms.sMnemo }],
+ callBack: res => {
+ res.success ? prms.callBack(res) : null;
+ }
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Отображение раздела "Штатные подразделения"
+ const handleInsDepartmentOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "INS_DEPARTMENT",
+ inputParameters: [{ name: "in_CODE", value: prms.sCode }],
+ callBack: res => {
+ res.success ? prms.callBack(res) : null;
+ }
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Отображение раздела "Нештатные структуры"
+ const handleCostStaffGroupsOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "CostStaffGroups",
+ inputParameters: [{ name: "in_CODE", value: prms.sCode }],
+ callBack: res => {
+ res.success ? prms.callBack(res) : null;
+ }
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Отображение раздела "Дополнительные словари"
+ const handleExtraDictionariesOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "ExtraDictionaries",
+ showMethod: "values",
+ inputParameters: [
+ { name: "pos_rn", value: prms.nRn },
+ { name: prms.sParamName, value: prms.paramValue }
+ ],
+ callBack: res => {
+ res.success ? prms.callBack(res) : null;
+ }
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Отображение раздела "Маршруты событий (исполнители в точках)"
+ const handleEventRoutesPointExecutersOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "EventRoutesPointExecuters",
+ showMethod: "executers",
+ inputParameters: prms.inputParameters,
+ callBack: res => {
+ res.success ? prms.callBack(res) : null;
+ }
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Отображение раздела "События"
+ const handleClientEventsOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "ClientEvents",
+ inputParameters: [{ name: "in_Ident", value: prms.nIdent }]
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Отображение раздела "События (примечания)"
+ const handleClientEventsNotesOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "ClientEventsNotes",
+ showMethod: "main",
+ inputParameters: [{ name: "in_PRN", value: prms.nPrn }]
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Отображение раздела "Присоединенные документы"
+ const handleFileLinksOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "FileLinks",
+ showMethod: "main_link",
+ inputParameters: [
+ { name: "in_PRN", value: prms.nPrn },
+ { name: "in_UNITCODE", value: prms.sUnitCode }
+ ]
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Отображение раздела "Маршруты событий (точки перехода)"
+ const handleEventRoutesPointsPassessOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: "EventRoutesPointsPasses",
+ showMethod: "main_passes",
+ inputParameters: [
+ { name: "in_ENVTYPE_CODE", value: prms.sEventType },
+ { name: "in_ENVSTAT_CODE", value: prms.sEventStatus },
+ { name: "in_POINT", value: prms.nPoint }
+ ],
+ callBack: res => {
+ res.success ? prms.callBack(res) : null;
+ }
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ //Универсальное отображение раздела
+ const handleUnitOpen = useCallback(
+ async prms => {
+ pOnlineShowDictionary({
+ unitCode: prms.sUnitCode,
+ showMethod: prms.sShowMethod,
+ inputParameters: prms.inputParameters,
+ callBack: res => {
+ res.success ? prms.callBack(res) : null;
+ }
+ });
+ },
+ [pOnlineShowDictionary]
+ );
+
+ return {
+ handleClientPersonOpen,
+ handleClientClientsOpen,
+ handleCatalogTreeOpen,
+ handleEventTypesOpen,
+ handleAgnlistOpen,
+ handleInsDepartmentOpen,
+ handleCostStaffGroupsOpen,
+ handleExtraDictionariesOpen,
+ handleEventRoutesPointExecutersOpen,
+ handleClientEventsOpen,
+ handleClientEventsNotesOpen,
+ handleFileLinksOpen,
+ handleEventRoutesPointsPassessOpen,
+ handleUnitOpen
+ };
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { useDictionary };
diff --git a/app/panels/clnt_task_board/hooks/filter_hooks.js b/app/panels/clnt_task_board/hooks/filter_hooks.js
new file mode 100644
index 0000000..423375c
--- /dev/null
+++ b/app/panels/clnt_task_board/hooks/filter_hooks.js
@@ -0,0 +1,122 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Пользовательские хуки: Хуки фильтра
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import { useState, useEffect, useCallback } from "react"; //Классы React
+import { EVENT_STATES } from "../layouts"; //Перечисление состояний события
+import { getLocalStorageValue } from "../layouts"; //Вспомогательные функции
+
+//--------------------------
+//Вспомогательные компоненты
+//--------------------------
+
+//Проверка возможности загрузки данных фильтра из локального хранилища
+const isLocalStorageExists = () => {
+ return getLocalStorageValue("sType");
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Хук фильтра
+//const useFilters = filterOpen => {
+const useFilters = () => {
+ //Состояние фильтра
+ const [filters, setFilters] = useState({
+ loaded: false,
+ isSetByUser: !isLocalStorageExists(),
+ values: {
+ sState: EVENT_STATES[1],
+ sType: "",
+ sCrnName: "",
+ sCrnRnList: "",
+ bSubcatalogs: false,
+ sSendPerson: "",
+ sSendDivision: "",
+ sSendUsrGrp: "",
+ sDocLink: ""
+ }
+ });
+
+ //Установить значение фильтра
+ const setFilterValues = useCallback((values, isSetByUser = true) => {
+ setFilters({ loaded: true, isSetByUser: isSetByUser, values: values });
+ }, []);
+
+ //Загрузка значений фильтра из локального хранилища браузера
+ const loadLocalStorageValues = useCallback(async () => {
+ //Загружаем значения по умолчанию
+ let values = { ...filters.values };
+ //Обходим ключи объекта значений
+ for (let key in values) {
+ //Заполняем значениями из хранилища
+ switch (key) {
+ //Локальное хранилище не хранит булево, форматируем строку в булево
+ case "bSubcatalogs":
+ values[key] = getLocalStorageValue(key) === "true";
+ break;
+ //Не переносим информацию о связанных записях
+ case "sDocLink":
+ break;
+ //Переносим все остальные значения
+ default:
+ values[key] = getLocalStorageValue(key, "");
+ break;
+ }
+ }
+ //Устанавливаем значения фильтра
+ setFilterValues(values, false);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ //При изменении значений фильтра
+ const handleFiltersChange = useCallback(
+ filters => {
+ setFilterValues(filters);
+ },
+ [setFilterValues]
+ );
+
+ //Сохранение при закрытии панели
+ useEffect(() => {
+ //Обработка события закрытия
+ const onBeforeUnload = () => {
+ //Обходим ключи фильтра
+ for (let key in filters.values) {
+ //Если это не связи - сохраняем значение в хранилище
+ key !== "sDocLink" ? localStorage.setItem(key, filters.values[key] ? filters.values[key] : "") : null;
+ }
+ };
+ //Если данные были загружены и произошли изменения
+ if (filters.loaded && filters.isSetByUser) {
+ //Вешаем обработчик события закрытия
+ window.addEventListener("beforeunload", onBeforeUnload);
+ }
+ //Очищаем при размонтировании
+ return () => {
+ window.removeEventListener("beforeunload", onBeforeUnload);
+ };
+ }, [filters.loaded, filters.isSetByUser, filters.values]);
+
+ //При подключении к странице
+ useEffect(() => {
+ //Если требуется загрузить фильтр из локального хранилища
+ if (!filters.loaded && !filters.isSetByUser) {
+ loadLocalStorageValues();
+ }
+ }, [filters.isSetByUser, filters.loaded, loadLocalStorageValues]);
+
+ return [filters, handleFiltersChange];
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { useFilters };
diff --git a/app/panels/clnt_task_board/hooks/hooks.js b/app/panels/clnt_task_board/hooks/hooks.js
new file mode 100644
index 0000000..a7237d6
--- /dev/null
+++ b/app/panels/clnt_task_board/hooks/hooks.js
@@ -0,0 +1,243 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Пользовательские хуки: Хуки основных данных
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import { useState, useContext, useEffect, useCallback } from "react"; //Классы React
+import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
+import { getRandomColor, getLocalStorageValue } from "../layouts"; //Вспомогательные функции
+
+//-----------
+//Тело модуля
+//-----------
+
+//Хук дополнительных данных
+const useExtraData = filtersType => {
+ //Состояние дополнительных данных
+ const [extraData, setExtraData] = useState({
+ dataLoaded: false,
+ reload: false,
+ typeLoaded: "",
+ evRoutes: [],
+ evPoints: [],
+ noteTypes: [],
+ docLinks: []
+ });
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored } = useContext(BackEndСtx);
+
+ //Считывание учётных документов
+ const handleDocLinksLoad = useCallback(
+ async (type = filtersType) => {
+ //Считываем данные
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DOCLINKS_GET",
+ args: {
+ SEVNTYPE_CODE: type
+ },
+ isArray: name => name === "XDOCLINKS",
+ respArg: "COUT"
+ });
+ //Возвращаем учётные документы
+ return [...(data?.XDOCLINKS || [])];
+ },
+ [executeStored, filtersType]
+ );
+
+ useEffect(() => {
+ //Загрузка дополнительных данных
+ const loadExtraData = async () => {
+ //Считываем данные
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_GET_INFO_BY_CODE",
+ args: {
+ SEVNTYPE_CODE: filtersType
+ },
+ isArray: name => ["XEVROUTES", "XEVPOINTS", "XNOTETYPES"].includes(name),
+ respArg: "COUT"
+ });
+ //Форматируем типы примечаний под нужный формат
+ let noteTypes = [...(data?.XNOTETYPES || [])].reduce((prev, cur) => [...prev, cur.SNAME], []);
+ //Считываем учётные документы
+ let docLinks = await handleDocLinksLoad(filtersType);
+ //Обновляем дополнительные данные
+ setExtraData({
+ dataLoaded: true,
+ reload: false,
+ typeLoaded: filtersType,
+ evRoutes: [...(data?.XEVROUTES || [])],
+ evPoints: [...(data?.XEVPOINTS || [])],
+ noteTypes: [...noteTypes],
+ docLinks: [...docLinks]
+ });
+ };
+
+ //Если указан тип событий и необходимо обновить
+ if (extraData.reload && filtersType) {
+ //Загружаем дополнительные данные
+ if (!extraData.typeLoaded || filtersType !== extraData.typeLoaded) {
+ loadExtraData();
+ }
+ }
+ }, [executeStored, extraData.reload, extraData.typeLoaded, filtersType, handleDocLinksLoad]);
+
+ return [extraData, setExtraData, handleDocLinksLoad];
+};
+
+//Хук заливок пользовательских настроек
+const useColorRules = () => {
+ //Собственное состояние
+ const [colorRules, setColorRules] = useState({
+ loaded: false,
+ rules: [],
+ selectedColorRule: JSON.parse(getLocalStorageValue("settingsColorRule") || {})
+ });
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored } = useContext(BackEndСtx);
+
+ //При необходимости загрузки заливок
+ useEffect(() => {
+ //Считывание пользовательских настроек
+ let getColorRules = async () => {
+ //Считываем данные
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DP_RULES_GET",
+ isArray: name => name === "XRULES",
+ respArg: "COUT"
+ });
+ //Формируем массив правил заливки пользовательских настроек
+ let newColorRules = [...(data.XRULES || [])].reduce(
+ (prev, cur) => [
+ ...prev,
+ {
+ id: prev.length,
+ SFIELD: cur.SFIELD,
+ SDP_NAME: cur.SDP_NAME,
+ SCOLOR: cur.SCOLOR,
+ STYPE: cur.STYPE,
+ fromValue: cur.NFROM ?? cur.SFROM ?? cur.DFROM,
+ toValue: cur.NTO ?? cur.STO ?? cur.DTO
+ }
+ ],
+ []
+ );
+ //Устанавливаем заливки пользовательских настроек
+ setColorRules(pv => ({ ...pv, loaded: true, rules: [...newColorRules] }));
+ };
+
+ if (!colorRules.loaded) getColorRules();
+ }, [colorRules.loaded, executeStored]);
+
+ //Сохранение при закрытии панели
+ useEffect(() => {
+ //Обработка события закрытия
+ const onBeforeUnload = () => {
+ localStorage.setItem("settingsColorRule", JSON.stringify(colorRules.selectedColorRule));
+ };
+ //Вешаем обработчик события закрытия
+ window.addEventListener("beforeunload", onBeforeUnload);
+ //Очищаем при размонтировании
+ return () => {
+ window.removeEventListener("beforeunload", onBeforeUnload);
+ };
+ }, [colorRules.selectedColorRule]);
+
+ return [colorRules, setColorRules];
+};
+
+//Хук статусов событий
+const useStatuses = filterType => {
+ //Собственное состояние статусов
+ const [statuses, setStatuses] = useState([]);
+
+ //Состояние статусов
+ const [statusesState, setStatusesState] = useState({
+ sorted: false,
+ reload: true,
+ attr: getLocalStorageValue("statusesSortAttr", "SEVNSTAT_NAME"),
+ direction: getLocalStorageValue("statusesSortDirection", "asc")
+ });
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored } = useContext(BackEndСtx);
+
+ //При необходимости сортировки статусов
+ useEffect(() => {
+ //Сортируем статусы
+ const sortStatuses = unsortedStatuses => {
+ //Инициализируем поле сортировки и порядок сортировки
+ const attr = statusesState.attr;
+ const direction = statusesState.direction;
+ //Сортируем
+ let sortedStatuses = unsortedStatuses.sort((a, b) =>
+ direction === "asc" ? a[attr].localeCompare(b[attr]) : b[attr].localeCompare(a[attr])
+ );
+ //Возвращаем
+ return sortedStatuses;
+ };
+ //Загружаем и сортируем статусы
+ const loadAndSortStatuses = async filterType => {
+ //Инициализируем статусы
+ let newStatuses = [];
+ //Если требуется перезагрузка
+ if (statusesState.reload) {
+ const loadedStatuses = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVNSTATS_LOAD",
+ args: {
+ SCLNEVNTYPES: filterType
+ },
+ isArray: name => name === "XSTATUS",
+ respArg: "COUT"
+ });
+ //Загружаем статусы и инициализируем цвета
+ newStatuses = [...(loadedStatuses?.XSTATUS || [])].reduce(
+ (prev, cur) => [...prev, { ...cur, color: getRandomColor(prev.length + 1) }],
+ []
+ );
+ } else {
+ //Загружаем из состояния
+ newStatuses = [...statuses];
+ }
+ //Сортируем, если требуется
+ newStatuses = !statusesState.sorted ? sortStatuses(newStatuses) : newStatuses;
+ //Обновляем состояние статусов
+ setStatuses([...newStatuses]);
+ //Обновляем информацию о состоянии статусов
+ setStatusesState(pv => ({ ...pv, sorted: true, reload: false }));
+ };
+ //При необходимости изменения сортировки
+ if (filterType && (statusesState.reload || !statusesState.sorted)) {
+ //Считываем старые статусы или загружаем новые
+ loadAndSortStatuses(filterType);
+ }
+ }, [executeStored, filterType, statuses, statusesState.attr, statusesState.direction, statusesState.reload, statusesState.sorted]);
+
+ //Сохранение при закрытии панели
+ useEffect(() => {
+ //Обработка события закрытия
+ const onBeforeUnload = () => {
+ localStorage.setItem("statusesSortAttr", statusesState.attr);
+ localStorage.setItem("statusesSortDirection", statusesState.direction);
+ };
+ //Вешаем обработчик события закрытия
+ window.addEventListener("beforeunload", onBeforeUnload);
+ //Очищаем при размонтировании
+ return () => {
+ window.removeEventListener("beforeunload", onBeforeUnload);
+ };
+ }, [statusesState.attr, statusesState.direction]);
+
+ return [statuses, statusesState, setStatuses, setStatusesState];
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { useExtraData, useColorRules, useStatuses };
diff --git a/app/panels/clnt_task_board/hooks/task_dialog_hooks.js b/app/panels/clnt_task_board/hooks/task_dialog_hooks.js
new file mode 100644
index 0000000..55b45a7
--- /dev/null
+++ b/app/panels/clnt_task_board/hooks/task_dialog_hooks.js
@@ -0,0 +1,191 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Пользовательские хуки: Хуки диалога события
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import { useState, useContext, useEffect } from "react"; //Классы React
+import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
+
+//-----------
+//Тело модуля
+//-----------
+
+//Хук для события
+const useClientEvent = (taskRn, taskType = "", taskStatus = "") => {
+ //Собственное состояние
+ const [task, setTask] = useState({
+ init: true,
+ nRn: taskRn,
+ sCrn: "",
+ sPrefix: "",
+ sNumber: "",
+ sType: taskType,
+ sStatus: taskStatus,
+ sDescription: "",
+ sClntClients: "",
+ sClntClnperson: "",
+ dStartDate: "",
+ sInitClnperson: "",
+ sInitUser: "",
+ sInitReason: "",
+ sToCompany: "",
+ sToDepartment: "",
+ sToClnpost: "",
+ sToClnpsdep: "",
+ sToClnperson: "",
+ sToFcstaffgrp: "",
+ sToUser: "",
+ sToUsergrp: "",
+ sCurrentUser: "",
+ isUpdate: false,
+ insertDisabled: true,
+ updateDisabled: true,
+ docProps: {}
+ });
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored } = useContext(BackEndСtx);
+
+ //При инициализации события
+ useEffect(() => {
+ //Если это инициализация
+ if (task.init) {
+ //Если указан рег. номер события
+ if (taskRn) {
+ //Считывание параметров события
+ const readEvent = async () => {
+ //Считываем информацию о событии по рег. номеру
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_GET",
+ args: {
+ NCLNEVENTS: task.nRn
+ },
+ respArg: "COUT"
+ });
+ //Фильтруем доп. свойства
+ let docProps = Object.keys(data.XEVENT)
+ .filter(key => key.includes("DP_"))
+ .reduce((prev, key) => ({ ...prev, [key]: data.XEVENT[key] }), {});
+ //Устанавливаем информацию о событии
+ setTask(pv => ({
+ ...pv,
+ sCrn: data.XEVENT.SCRN,
+ sPrefix: data.XEVENT.SPREF,
+ sNumber: data.XEVENT.SNUMB,
+ sType: data.XEVENT.STYPE,
+ sStatus: data.XEVENT.SSTATUS,
+ sDescription: data.XEVENT.SDESCRIPTION,
+ sClntClients: data.XEVENT.SCLIENT_CLIENT,
+ sClntClnperson: data.XEVENT.SCLIENT_PERSON,
+ dPlanDate: data.XEVENT.SPLAN_DATE,
+ sInitClnperson: data.XEVENT.SINIT_PERSON,
+ sInitUser: data.XEVENT.SINIT_AUTHID,
+ sInitReason: data.XEVENT.SREASON,
+ sToCompany: data.XEVENT.SSEND_CLIENT,
+ sToDepartment: data.XEVENT.SSEND_DIVISION,
+ sToClnpost: data.XEVENT.SSEND_POST,
+ sToClnpsdep: data.XEVENT.SSEND_PERFORM,
+ sToClnperson: data.XEVENT.SSEND_PERSON,
+ sToFcstaffgrp: data.XEVENT.SSEND_STAFFGRP,
+ sToUser: data.XEVENT.SSEND_USER_NAME,
+ sToUsergrp: data.XEVENT.SSEND_USER_GROUP,
+ sCurrentUser: data.XEVENT.SINIT_AUTHID,
+ isUpdate: true,
+ init: false,
+ docProps: docProps
+ }));
+ };
+ //Инициализация параметров события
+ readEvent();
+ } else {
+ //Считывание изначальных параметров события
+ const initEvent = async () => {
+ //Инициализируем параметры события
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_INIT",
+ args: {
+ SEVENT_TYPE: task.sType
+ }
+ });
+ //Если есть данные
+ if (data) {
+ //Устанавливаем данные по событию
+ setTask(pv => ({
+ ...pv,
+ sPrefix: data.SPREF,
+ sNumber: data.SNUMB,
+ sCurrentUser: data.SINIT_AUTHNAME,
+ sInitClnperson: data.SINIT_PERSON,
+ sInitUser: !data.SINIT_PERSON ? data.SINIT_AUTHNAME : "",
+ init: false
+ }));
+ }
+ };
+ //Инициализация изначальных параметров события
+ initEvent();
+ }
+ }
+ if (!task.init) {
+ setTask(pv => ({ ...pv, sInitUser: !task.sInitClnperson ? task.sCurrentUser : "" }));
+ }
+ }, [executeStored, task.init, task.nRn, task.sType, task.sCurrentUser, task.sInitClnperson, taskRn]);
+
+ //Проверка доступности действия
+ useEffect(() => {
+ setTask(pv => ({
+ ...pv,
+ insertDisabled:
+ !task.sCrn ||
+ !task.sPrefix ||
+ !task.sNumber ||
+ !task.sType ||
+ !task.sStatus ||
+ !task.sDescription ||
+ (!task.sInitClnperson && !task.sInitUser),
+ updateDisabled: !task.sDescription
+ }));
+ }, [task.sCrn, task.sDescription, task.sInitClnperson, task.sInitUser, task.sNumber, task.sPrefix, task.sStatus, task.sType]);
+
+ return [task, setTask];
+};
+
+//Хук для получения свойств раздела "События"
+const useDocsProps = taskType => {
+ //Собственное состояние
+ const [docProps, setDocsProps] = useState({ loaded: false, props: [] });
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored } = useContext(BackEndСtx);
+
+ useEffect(() => {
+ //Загрузка доп. свойств
+ let getDocsProps = async () => {
+ //Считываема доп. свойства по типу события
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_PROPS_GET",
+ args: { SEVNTYPE_CODE: taskType },
+ isArray: name => name === "XPROPS",
+ respArg: "COUT"
+ });
+ //Устанавливаем доп. свойства
+ setDocsProps({ loaded: true, props: [...(data?.XPROPS || [])] });
+ };
+ //Если доп. свойства не загружены
+ if (!docProps.loaded) {
+ //Загружаем доп. свойства
+ getDocsProps();
+ }
+ }, [docProps.loaded, executeStored, taskType]);
+
+ return [docProps];
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { useClientEvent, useDocsProps };
diff --git a/app/panels/clnt_task_board/hooks/tasks_hooks.js b/app/panels/clnt_task_board/hooks/tasks_hooks.js
new file mode 100644
index 0000000..99a3fa8
--- /dev/null
+++ b/app/panels/clnt_task_board/hooks/tasks_hooks.js
@@ -0,0 +1,431 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Пользовательские хуки: Хуки событий
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import { useState, useContext, useEffect, useCallback } from "react"; //Классы React
+import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
+import { object2Base64XML } from "../../../core/utils"; //Вспомогательные функции
+import { convertFilterValuesToArray } from "../layouts"; //Вспомогательные функции
+import { useDictionary } from "./dict_hooks"; //Состояние открытия разделов
+
+//-----------
+//Тело модуля
+//-----------
+
+//Хук обработки перехода события
+const useTasksFunctions = () => {
+ //Состояние открытия раздела
+ const { handleEventRoutesPointExecutersOpen, handleEventRoutesPointsPassessOpen } = useDictionary();
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored } = useContext(BackEndСtx);
+
+ //Выполнение направления события
+ const handleSendExec = useCallback(
+ //Выполняем финальное перенаправление события
+ async ({ mainArgs, onReload = null }) => {
+ await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SEND",
+ args: { ...mainArgs }
+ });
+ //Если требуется перезагрузить данные
+ onReload ? onReload() : null;
+ },
+ [executeStored]
+ );
+
+ //При направлении события
+ const handleSend = useCallback(
+ async ({ mainArgs, onReload = null, onNoteOpen = null }) => {
+ //Если требуется добавить примечание
+ if (onNoteOpen) {
+ //Открываем примечание с коллбэком на направление события
+ onNoteOpen(async note => {
+ //Выполняем изменение статуса
+ handleSendExec({ mainArgs: { ...mainArgs, SNOTE_HEADER: note.header, SNOTE: note.text }, onReload });
+ });
+ } else {
+ //Выполняем изменение статуса
+ handleSendExec({ mainArgs, onReload });
+ }
+ },
+ [handleSendExec]
+ );
+
+ //По нажатию действия "Направить"
+ const handleTaskSend = useCallback(
+ async ({ nEvent, onReload = null, onNoteOpen = null }) => {
+ //Выполняем инициализацию параметров
+ const firstStep = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SEND",
+ args: {
+ NSTEP: 1,
+ NEVENT: nEvent
+ }
+ });
+ if (firstStep) {
+ //Открываем раздел "Маршруты событий (исполнители в точках)" для выбора исполнителя
+ handleEventRoutesPointExecutersOpen({
+ inputParameters: [
+ { name: "in_IDENT", value: firstStep.NIDENT },
+ { name: "in_EVENT", value: nEvent },
+ { name: "in_PERSON_CODE", value: firstStep.SSEND_PERSON },
+ { name: "in_USER_NAME", value: firstStep.SSEND_USER_NAME },
+ { name: "in_EVENT_TYPE", value: firstStep.SEVENT_TYPE },
+ { name: "in_EVENT_STAT", value: firstStep.SEVENT_STAT },
+ { name: "in_INIT_PERSON", value: firstStep.SINIT_PERSON },
+ { name: "in_INIT_AUTHNAME", value: firstStep.SINIT_AUTHNAME },
+ { name: "in_CLIENT_CLIENT", value: firstStep.SCLIENT_CLIENT },
+ { name: "in_CLIENT_PERSON", value: firstStep.SCLIENT_PERSON }
+ ],
+ callBack: sendPrms => {
+ //Собираем основные параметры направления события
+ const mainArgs = {
+ NIDENT: firstStep.NIDENT,
+ NSTEP: 2,
+ NEVENT: nEvent,
+ SSEND_CLIENT: sendPrms.outParameters.out_CLIENT_CODE,
+ SSEND_DIVISION: sendPrms.outParameters.out_DIVISION_CODE,
+ SSEND_POST: sendPrms.outParameters.out_POST_CODE,
+ SSEND_PERFORM: sendPrms.outParameters.out_POST_IN_DIV_CODE,
+ SSEND_PERSON: sendPrms.outParameters.out_PERSON_CODE,
+ SSEND_STAFFGRP: sendPrms.outParameters.out_STAFFGRP_CODE,
+ SSEND_USER_GROUP: sendPrms.outParameters.out_USER_GROUP_CODE,
+ SSEND_USER_NAME: sendPrms.outParameters.out_USER_NAME,
+ NSEND_PREDEFINED_EXEC: sendPrms.outParameters.out_PREDEFINED_EXEC,
+ NSEND_PREDEFINED_PROC: sendPrms.outParameters.out_PREDEFINED_PROC
+ };
+ //Перенаправляем событие
+ handleSend({ nEvent, mainArgs, onReload, onNoteOpen });
+ }
+ });
+ }
+ },
+ [executeStored, handleEventRoutesPointExecutersOpen, handleSend]
+ );
+
+ //Выполнение изменения статуса события
+ const handleStateChangeExec = useCallback(
+ async ({ mainArgs, onReload = null }) => {
+ await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
+ args: { ...mainArgs }
+ });
+ //Если требуется перезагрузить данные
+ onReload ? onReload() : null;
+ },
+ [executeStored]
+ );
+
+ //При изменении статуса события
+ const handleStateChange = useCallback(
+ async ({ mainArgs, onReload = null, onNoteOpen = null }) => {
+ //Если необходимо добавить примечание
+ if (onNoteOpen) {
+ //Открываем примечание с коллбэком на изменение статуса
+ onNoteOpen(async note => {
+ //Выполняем изменение статуса
+ handleStateChangeExec({ mainArgs: { ...mainArgs, SNOTE_HEADER: note.header, SNOTE: note.text }, onReload });
+ });
+ } else {
+ //Выполняем изменение статуса
+ handleStateChangeExec({ mainArgs, onReload });
+ }
+ },
+ [handleStateChangeExec]
+ );
+
+ //При выборе исполнителя
+ const handleExecuterSelect = useCallback(
+ async ({ nEvent, pointInfo, onReload = null, onNoteOpen = null }) => {
+ //Если требуется выбрать получателя
+ if (pointInfo.NSELECT_EXEC === 1) {
+ //Открываем раздел "Маршруты событий (исполнители в точках)" для выбора исполнителя
+ handleEventRoutesPointExecutersOpen({
+ inputParameters: [
+ { name: "in_IDENT", value: pointInfo.NIDENT },
+ { name: "in_EVENT", value: nEvent },
+ { name: "in_EVENT_TYPE", value: pointInfo.SEVENT_TYPE },
+ { name: "in_EVENT_STAT", value: pointInfo.SEVENT_STAT },
+ { name: "in_INIT_PERSON", value: pointInfo.SINIT_PERSON },
+ { name: "in_INIT_AUTHNAME", value: pointInfo.SINIT_AUTHNAME },
+ { name: "in_CLIENT_CLIENT", value: pointInfo.SCLIENT_CLIENT },
+ { name: "in_CLIENT_PERSON", value: pointInfo.SCLIENT_PERSON }
+ ],
+ callBack: sendPrms => {
+ const mainArgs = {
+ NIDENT: pointInfo.NIDENT,
+ NSTEP: 3,
+ NEVENT: nEvent,
+ SEVENT_STAT: pointInfo.SEVENT_STAT,
+ SSEND_CLIENT: sendPrms.outParameters.out_CLIENT_CODE,
+ SSEND_DIVISION: sendPrms.outParameters.out_DIVISION_CODE,
+ SSEND_POST: sendPrms.outParameters.out_POST_CODE,
+ SSEND_PERFORM: sendPrms.outParameters.out_POST_IN_DIV_CODE,
+ SSEND_PERSON: sendPrms.outParameters.out_PERSON_CODE,
+ SSEND_STAFFGRP: sendPrms.outParameters.out_STAFFGRP_CODE,
+ SSEND_USER_GROUP: sendPrms.outParameters.out_USER_GROUP_CODE,
+ SSEND_USER_NAME: sendPrms.outParameters.out_USER_NAME,
+ NSEND_PREDEFINED_EXEC: sendPrms.outParameters.out_PREDEFINED_EXEC,
+ NSEND_PREDEFINED_PROC: sendPrms.outParameters.out_PREDEFINED_PROC
+ };
+ //Выполняем изменение статуса
+ handleStateChange({ mainArgs, onReload, onNoteOpen });
+ }
+ });
+ } else {
+ //Общие аргументы
+ const mainArgs = {
+ NIDENT: pointInfo.NIDENT,
+ NSTEP: 3,
+ NEVENT: nEvent,
+ SEVENT_STAT: pointInfo.SEVENT_STAT
+ };
+ //Выполняем изменение статуса
+ handleStateChange({ mainArgs, onReload, onNoteOpen });
+ }
+ },
+ [handleEventRoutesPointExecutersOpen, handleStateChange]
+ );
+
+ //При выполнении второго шага
+ const handleMakeSecondStep = useCallback(
+ async ({ nIdent, nPass }) => {
+ //Выполняем переход на следующий шаг
+ const secondStep = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
+ args: {
+ NIDENT: nIdent,
+ NSTEP: 2,
+ NPASS: nPass
+ }
+ });
+ //Возвращаем параметры выполнения
+ return secondStep;
+ },
+ [executeStored]
+ );
+
+ //При выборе следующей точки события
+ const handleNextPointSelect = useCallback(
+ ({ nEvent, pointInfo, onReload = null, onNoteOpen = null }) => {
+ //Открываем раздел "Маршруты событий (точки перехода)" для выбора следующей точки
+ handleEventRoutesPointsPassessOpen({
+ sEventType: pointInfo.SEVENT_TYPE,
+ sEventStatus: pointInfo.SEVENT_STAT,
+ nPoint: pointInfo.NPOINT,
+ callBack: async point => {
+ //Выполняем второй шаг
+ let secondStep = await handleMakeSecondStep({ nIdent: pointInfo.NIDENT, nPass: point.outParameters.out_RN });
+ //Выполняем выбор исполнителя
+ handleExecuterSelect({
+ nEvent,
+ pointInfo: { ...pointInfo, SEVENT_STAT: point.outParameters.out_NEXT_POINT, NSELECT_EXEC: secondStep.NSELECT_EXEC },
+ onReload,
+ onNoteOpen
+ });
+ }
+ });
+ },
+ [handleEventRoutesPointsPassessOpen, handleMakeSecondStep, handleExecuterSelect]
+ );
+
+ //По нажатию действия "Перейти"
+ const handleTaskStateChange = useCallback(
+ async ({ nEvent, sNextStat = null, onReload = null, onNoteOpen = null }) => {
+ //Выполняем инициализацию параметров
+ const eventInfo = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
+ args: {
+ NSTEP: 1,
+ NEVENT: nEvent,
+ SNEXT_STAT: sNextStat
+ }
+ });
+ //Если информация о события проинициализирована
+ if (eventInfo) {
+ //Если следующий статус неопределен
+ if (!sNextStat) {
+ //Выполнение перехода с выбором точки
+ handleNextPointSelect({
+ nEvent,
+ pointInfo: eventInfo,
+ onReload,
+ onNoteOpen
+ });
+ } else {
+ //Выполняем перехода без выбора точки
+ handleExecuterSelect({
+ nEvent,
+ pointInfo: eventInfo,
+ onReload,
+ onNoteOpen
+ });
+ }
+ }
+ },
+ [executeStored, handleExecuterSelect, handleNextPointSelect]
+ );
+
+ return { handleTaskStateChange, handleTaskSend };
+};
+
+//Хук получения событий
+const useTasks = (filterValues, ordersValues) => {
+ //Состояние событий
+ const [tasks, setTasks] = useState({
+ loaded: false,
+ rows: [],
+ reload: false,
+ accountsReload: false,
+ loadedAccounts: []
+ });
+
+ //Состояние вспомогательных функций событий
+ const { handleTaskStateChange } = useTasksFunctions();
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
+
+ //Инициализация параметров события
+ const initTask = (id, task, avatar = null) => {
+ //Фильтруем доп. свойства
+ let newDocProps = Object.keys(task)
+ .filter(key => key.includes("DP_"))
+ .reduce((prev, key) => ({ ...prev, [key]: task[key] }), {});
+ //Возвращаем структуру события
+ return {
+ id: id,
+ avatar: avatar,
+ name: task.SPREF_NUMB,
+ nRn: task.NRN,
+ sCrn: "",
+ sPrefix: task.SEVPREF,
+ sNumber: task.SEVNUMB,
+ sType: task.SEVTYPE_CODE,
+ sStatus: task.SEVSTAT_NAME,
+ sDescription: task.SEVDESCR,
+ sClntClients: "",
+ sClntClnperson: "",
+ dchange_date: task.DCHANGE_DATE,
+ dStartDate: task.DREG_DATE,
+ dExpireDate: task.DEXPIRE_DATE,
+ dPlanDate: task.DPLAN_DATE,
+ sInitClnperson: task.SINIT_PERSON,
+ sInitUser: "",
+ sInitReason: "",
+ sToCompany: "",
+ sToDepartment: task.SSEND_DIVISION,
+ sToClnpost: "",
+ sToClnpsdep: "",
+ sToClnperson: task.SSEND_PERSON,
+ sToFcstaffgrp: "",
+ sToUser: "",
+ sToUsergrp: task.SSEND_USRGRP,
+ sSender: task.SSENDER,
+ sCurrentUser: "",
+ sLinkedUnit: task.SLINKED_UNIT,
+ nLinkedRn: task.NLINKED_RN,
+ docProps: newDocProps
+ };
+ };
+
+ //Взаимодействие с событием (через перенос)
+ const onDragEnd = useCallback(
+ ({ path, eventPoints, openNoteDialog, destCode }) => {
+ //Определяем нужные параметры
+ const { source, destination } = path;
+ //Если путь не указан
+ if (!destination) {
+ return;
+ }
+ //Если происходит изменение статуса
+ if (destination.droppableId !== source.droppableId) {
+ //Конвертим ID переносимого события
+ let nDraggableTaskId = parseInt(path.draggableId);
+ //Считываем строку, у которой изменяется статус
+ let task = tasks.rows.find(r => r.id === nDraggableTaskId);
+ //Изменяем статус у события
+ task.statusId = parseInt(path.destination.droppableId);
+ //Получение настройки точки назначения
+ const pointSettings = eventPoints.find(eventPoint => eventPoint.SEVPOINT === destCode);
+ //Изменяем статус события с добавлением примечания
+ handleTaskStateChange({
+ nEvent: task.nRn,
+ sNextStat: destCode,
+ onReload: () => setTasks(pv => ({ ...pv, reload: true, accountsReload: true })),
+ onNoteOpen: pointSettings.ADDNOTE_ONCHST ? openNoteDialog : null
+ });
+ }
+ },
+ [handleTaskStateChange, tasks.rows]
+ );
+
+ //При необходимости перезагрузки данных
+ useEffect(() => {
+ //Считывание данных с учетом фильтрации
+ let getTasks = async () => {
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_LOAD",
+ args: {
+ CFILTERS: {
+ VALUE: object2Base64XML(convertFilterValuesToArray(filterValues), { arrayNodeName: "filters" }),
+ SDATA_TYPE: SERV_DATA_TYPE_CLOB
+ },
+ CORDERS: { VALUE: object2Base64XML(ordersValues, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
+ NINCLUDE_ACCOUNTS: tasks.accountsReload ? 1 : 0
+ },
+ isArray: name => name === "XAGENTS",
+ respArg: "COUT"
+ });
+ //Считываем информацию о событиях
+ let events = data.XCLNEVENTS.XDATA.XDATA_GRID;
+ //Считываем иноформацию о контрагентах
+ let accounts = tasks.accountsReload ? [...(data.XAGENTS_WITH_IMG.XAGENTS || [])] : tasks.loadedAccounts;
+ //Инициализируем события
+ let newRows = [];
+ //Если есть события
+ if (events.rows) {
+ //Формируем структуру событий
+ newRows = [...(events.rows || [])].reduce(
+ (prev, cur) => [...prev, initTask(prev.length, cur, accounts.find(agent => agent.SAGNABBR === cur.SSENDER)?.BIMAGE)],
+ []
+ );
+ }
+ //Возвращаем информацию
+ return { rows: [...newRows], loadedAccounts: accounts };
+ };
+ //Считывание данных
+ let getData = async () => {
+ //Считываем информацию о задачах
+ let eventTasks = await getTasks();
+ //Загружаем данные
+ setTasks(pv => ({
+ ...pv,
+ loaded: true,
+ rows: eventTasks.rows,
+ loadedAccounts: eventTasks.loadedAccounts,
+ reload: false,
+ accountsReload: false
+ }));
+ };
+ //Если необходимо загрузить данные и указан тип событий и загружены все необходимые вспомогательные данные
+ if (tasks.reload) {
+ //Загружаем данные
+ getData();
+ }
+ }, [SERV_DATA_TYPE_CLOB, executeStored, filterValues, ordersValues, tasks.accountsReload, tasks.loadedAccounts, tasks.reload]);
+
+ return [tasks, setTasks, onDragEnd];
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { useTasksFunctions, useTasks };
diff --git a/app/panels/clnt_task_board/index.js b/app/panels/clnt_task_board/index.js
new file mode 100644
index 0000000..0324d4a
--- /dev/null
+++ b/app/panels/clnt_task_board/index.js
@@ -0,0 +1,16 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Панель мониторинга: Точка входа
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import { ClntTaskBoard } from "./clnt_task_board"; //Корневая панель выполнения работ
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export const RootClass = ClntTaskBoard;
diff --git a/app/panels/clnt_task_board/layouts.js b/app/panels/clnt_task_board/layouts.js
new file mode 100644
index 0000000..f52b9b9
--- /dev/null
+++ b/app/panels/clnt_task_board/layouts.js
@@ -0,0 +1,294 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Дополнительная разметка и вёрстка клиентских элементов
+*/
+
+//---------
+//Константы
+//---------
+
+//Перечисление "Состояние события"
+export const EVENT_STATES = Object.freeze({ 0: "Все", 1: "Не аннулированные", 2: "Аннулированные" });
+
+//Допустимые значение поля сортировки
+export const sortAttrs = [
+ { id: "SEVNSTAT_CODE", descr: "Мнемокод" },
+ { id: "SEVNSTAT_NAME", descr: "Наименование" },
+ { id: "SEVPOINT_DESCR", descr: "Описание точки маршрута" }
+];
+
+//Допустимые значения направления сортировки
+export const sortDest = [];
+sortDest[-1] = "desc";
+sortDest[1] = "asc";
+
+//Цвета статусов
+export const COLORS = [
+ "mediumSlateBlue",
+ "lightSalmon",
+ "fireBrick",
+ "orange",
+ "gold",
+ "limeGreen",
+ "yellowGreen",
+ "mediumAquaMarine",
+ "paleTurquoise",
+ "steelBlue",
+ "skyBlue",
+ "tan"
+];
+
+//Перечисление "Цвет задачи"
+export const TASK_COLORS = Object.freeze({ EXPIRED: "#ff0000", EXPIRES_SOON: "#ffdf00", LINKED: "#1e90ff" });
+
+//Перечисление Доп. свойства "Значение по умолчанию"
+export const DP_DEFAULT_VALUE = Object.freeze({ 0: "SDEFAULT_STR", 1: "NDEFAULT_NUM", 2: "DDEFAULT_DATE", 3: "NDEFAULT_NUM" });
+//Перечисление Доп. свойства "Префикс формата данных"
+export const DP_TYPE_PREFIX = Object.freeze({ 0: "S", 1: "N", 2: "D", 3: "N" });
+//Перечисление Доп. свойства "Входящее значение дополнительного словаря"
+export const DP_IN_VALUE = Object.freeze({ 0: "pos_str_value", 1: "pos_num_value", 2: "pos_date_value", 3: "pos_num_value" });
+//Перечисление Доп. свойства "Исходящее значение дополнительного словаря"
+export const DP_RETURN_VALUE = Object.freeze({ 0: "str_value", 1: "num_value", 2: "date_value", 3: "num_value" });
+
+//-----------
+//Тело модуля
+//-----------
+
+//Формирование массива из 0, 1 и более элементов
+export const makeArray = arr => {
+ return arr ? (arr.length ? arr : [arr]) : [];
+};
+
+//Конвертация формата HEX в формат RGB
+const convertHexToRGB = hex => {
+ let r = parseInt(hex.slice(1, 3), 16);
+ let g = parseInt(hex.slice(3, 5), 16);
+ let b = parseInt(hex.slice(5, 7), 16);
+ let a = 0.5;
+ r = Math.round((a * (r / 255) + a * (255 / 255)) * 255);
+ g = Math.round((a * (g / 255) + a * (255 / 255)) * 255);
+ b = Math.round((a * (b / 255) + a * (255 / 255)) * 255);
+ return "rgb(" + r + ", " + g + ", " + b + ")";
+};
+
+//Считывание заливки события по условию
+export const getTaskBgColorByRule = (task, colorRule) => {
+ //Инициализируем значения
+ let ruleCode = "";
+ //Исходя из типа определяем наименование
+ switch (colorRule.STYPE) {
+ case "number":
+ ruleCode = `N${colorRule.SFIELD}`;
+ break;
+ case "date":
+ ruleCode = `D${colorRule.SFIELD}`;
+ break;
+ default:
+ ruleCode = `S${colorRule.SFIELD}`;
+ break;
+ }
+ //Определяем цвет заливки
+ let bgColor = ruleCode && task.docProps[ruleCode] == colorRule.fromValue ? convertHexToRGB(colorRule.SCOLOR) : null;
+ //Возвращаем цвет заливки
+ return bgColor;
+};
+
+//Индикация истечения срока отработки события
+export const getTaskExpiredColor = task => {
+ //Определяем текущую дату
+ let sysDate = new Date();
+ //Определяем дату истечения срока события
+ let expireDate = task.dExpireDate ? new Date(task.dExpireDate) : null;
+ //Если дата истечения срока определена
+ if (expireDate) {
+ //Определяем разницу между датами
+ let daysDiff = ((expireDate.getTime() - sysDate.getTime()) / (1000 * 60 * 60 * 24)).toFixed(2);
+ //Если разница меньше 0 - срок истечен
+ if (daysDiff < 0) return TASK_COLORS.EXPIRED;
+ //Если разница меньше 4 - скоро истечет
+ if (daysDiff < 4) return TASK_COLORS.EXPIRES_SOON;
+ }
+ return null;
+};
+
+//Цвет из hsl формата в rgba формат
+const convertHslToRgba = (h, s, l) => {
+ s /= 100;
+ l /= 100;
+ const k = n => (n + h / 30) % 12;
+ const a = s * Math.min(l, 1 - l);
+ const f = n => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
+ return `rgba(${Math.floor(255 * f(0))},${Math.floor(255 * f(8))},${Math.floor(255 * f(4))},0.3)`;
+};
+
+//Формирование случайного цвета
+export const getRandomColor = index => {
+ const hue = index * 137.508;
+ return convertHslToRgba(hue, 50, 70);
+};
+
+//Формат дополнительного свойства типа число (длина, точность)
+const formatRegExpNum = (width, precision) =>
+ new RegExp("^(\\d{1," + (width - precision) + "}" + (precision > 0 ? "((\\.|,)\\d{1," + precision + "})?" : "") + ")?$");
+
+//Формат дополнительного свойства типа строка (длина)
+const formatRegExpStr = length => new RegExp("^.{0," + length + "}$");
+
+//Проверка валидности числа
+const isValidNum = (width, precision, value) => {
+ return formatRegExpNum(width, precision).test(value);
+};
+
+//Проверка валидности строки
+const isValidStr = (length, value) => {
+ return formatRegExpStr(length).test(value);
+};
+
+//Признак ошибки валидации
+export const validationError = (value = "", format, numWidth, numPrecision, strLength) => {
+ //Исходим от формата
+ switch (format) {
+ //Проверка строки
+ case 0:
+ return isValidStr(strLength, value);
+ //Проверка числа
+ case 1:
+ return isValidNum(numWidth, numPrecision, value);
+ //Остальное не проверяем
+ default:
+ return true;
+ }
+};
+
+//Конвертация времени в привычный формат
+export const formatSqlDate = timeStamp => {
+ //Если есть разделитель
+ if (timeStamp.indexOf(".") !== -1) {
+ //Определяем секунды
+ let seconds = 24 * 60 * 60 * timeStamp;
+ //Определяем часы
+ const hours = Math.trunc(seconds / (60 * 60));
+ //Переопределяем секунды
+ seconds = seconds % (60 * 60);
+ //Определяем минуты
+ const minutes = Math.trunc(seconds / 60);
+ //Определяем остаток секунд
+ seconds = Math.round(seconds % 60);
+ //Форматируем
+ const formattedTime = ("0" + hours).slice(-2) + ":" + ("0" + minutes).slice(-2) + ":" + ("0" + seconds).slice(-2);
+ //Возвращаем результат
+ return formattedTime;
+ }
+ return timeStamp;
+};
+
+//Считывание значений из локального хранилища
+export const getLocalStorageValue = (sName, defaultValue = null) => {
+ return localStorage.getItem(sName) ? localStorage.getItem(sName) : defaultValue;
+};
+
+//Форматирование фильтра в массив для отбора
+export const convertFilterValuesToArray = filterValues => {
+ //Инициализируем значение "с" состояния ("Все", "Не аннулированные" - 0, "Аннулированые" - 1)
+ let nClosedFrom = filterValues.sState ? ([EVENT_STATES[0], EVENT_STATES[1]].includes(filterValues.sState) ? 0 : 1) : 0;
+ //Инициализируем значение "по" состояния ("Все", "Аннулированные" - 1, "Не аннулированные" - 0)
+ let nClosedTo = filterValues.sState ? ([EVENT_STATES[0], EVENT_STATES[2]].includes(filterValues.sState) ? 1 : 0) : 0;
+ //Формируем массив значений фильтра
+ let filterValuesArray = [
+ { name: "NCLOSED", from: nClosedFrom, to: nClosedTo },
+ { name: "SEVTYPE_CODE", from: filterValues.sType, to: null },
+ { name: "NCRN", from: filterValues.sCrnRnList, to: null },
+ { name: "SSEND_PERSON", from: filterValues.sSendPerson, to: null },
+ { name: "SSEND_DIVISION", from: filterValues.sSendDivision, to: null },
+ { name: "SSEND_USRGRP", from: filterValues.sSendUsrGrp, to: null },
+ { name: "NLINKED_RN", from: filterValues.sDocLink, to: null }
+ ];
+ return filterValuesArray;
+};
+
+//Формирование массива действий карточки события
+export const makeCardActionsArray = (onEdit, onEditClient, onDelete, onStateChange, onReturn, onSend, onNotesOpen, onFileLinksOpen) => {
+ //Формируем список действий карточки
+ return [
+ {
+ method: "EDIT",
+ name: "Исправить",
+ icon: "edit",
+ visible: false,
+ delimiter: false,
+ tasksReload: false,
+ needAccountsReload: false,
+ func: onEdit
+ },
+ {
+ method: "EDIT_CLIENT",
+ name: "Исправить в разделе",
+ icon: "edit_note",
+ visible: true,
+ delimiter: false,
+ tasksReload: false,
+ needAccountsReload: false,
+ func: onEditClient
+ },
+ {
+ method: "DELETE",
+ name: "Удалить",
+ icon: "delete",
+ visible: true,
+ delimiter: true,
+ tasksReload: true,
+ needAccountsReload: false,
+ func: onDelete
+ },
+ {
+ method: "TASK_STATE_CHANGE",
+ name: "Перейти",
+ icon: "turn_right",
+ visible: true,
+ delimiter: false,
+ tasksReload: true,
+ needAccountsReload: true,
+ func: onStateChange
+ },
+ {
+ method: "TASK_RETURN",
+ name: "Выполнить возврат",
+ icon: "turn_left",
+ visible: true,
+ delimiter: false,
+ tasksReload: true,
+ needAccountsReload: true,
+ func: onReturn
+ },
+ {
+ method: "TASK_SEND",
+ name: "Направить",
+ icon: "send",
+ visible: true,
+ delimiter: true,
+ tasksReload: true,
+ needAccountsReload: true,
+ func: onSend
+ },
+ {
+ method: "NOTES",
+ name: "Примечания",
+ icon: "event_note",
+ visible: true,
+ delimiter: true,
+ tasksReload: false,
+ needAccountsReload: false,
+ func: onNotesOpen
+ },
+ {
+ method: "FILE_LINKS",
+ name: "Присоединенные документы",
+ icon: "attach_file",
+ visible: true,
+ delimiter: false,
+ tasksReload: false,
+ needAccountsReload: false,
+ func: onFileLinksOpen
+ }
+ ];
+};
diff --git a/app/panels/clnt_task_board/styles.js b/app/panels/clnt_task_board/styles.js
new file mode 100644
index 0000000..41da40a
--- /dev/null
+++ b/app/panels/clnt_task_board/styles.js
@@ -0,0 +1,48 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент: Общие стили
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import { APP_STYLES } from "../../../app.styles"; //Типовые стили
+
+//---------
+//Константы
+//---------
+
+//Общие стили
+export const COMMON_STYLES = {
+ TASK_FORM_TEXT_FIELD: (widthVal, greyDisabled = false) => ({
+ margin: "4px",
+ ...(widthVal ? { width: widthVal } : {}),
+ ...(greyDisabled
+ ? {
+ "& .MuiInputBase-input.Mui-disabled": {
+ WebkitTextFillColor: "rgba(0, 0, 0, 0.87)"
+ },
+ "& .MuiInputLabel-root.Mui-disabled": {
+ WebkitTextFillColor: "rgba(0, 0, 0, 0.6)"
+ }
+ }
+ : {})
+ }),
+ BOX_WITH_LEGEND: { border: "1px solid #939393" },
+ BOX_SINGLE_COLUMN: { display: "flex", flexDirection: "column", gap: "10px" },
+ LEGEND: { textAlign: "left" },
+ SELECT_MENU: width => {
+ return { overflowY: "auto", ...APP_STYLES.SCROLL, width: width ? width : null };
+ },
+ STACK_DOCLINKS: { alignItems: "baseline" },
+ SCROLL: { ...APP_STYLES.SCROLL, overflowY: "auto" },
+ DIALOG_ACTIONS: { justifyContent: "end", paddingRight: "24px", paddingLeft: "24px" },
+ DIALOG_CLOSE_BUTTON: {
+ position: "absolute",
+ right: 8,
+ top: 8,
+ color: theme => theme.palette.grey[500]
+ },
+ ZERO_PADDING: { padding: 0 }
+};
diff --git a/app/panels/clnt_task_board/task_dialog.js b/app/panels/clnt_task_board/task_dialog.js
new file mode 100644
index 0000000..7591f9a
--- /dev/null
+++ b/app/panels/clnt_task_board/task_dialog.js
@@ -0,0 +1,180 @@
+/*
+ Парус 8 - Панели мониторинга - УДП - Доски задач
+ Компонент панели: Диалог формы события
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState, useCallback, useContext } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Dialog, DialogContent, DialogActions, Button } from "@mui/material"; //Интерфейсные компоненты
+import { useClientEvent } from "./hooks/task_dialog_hooks"; //Хук для события
+import { TaskForm } from "./components/task_form"; //Форма события
+import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
+import { object2Base64XML } from "../../core/utils"; //Вспомогательные функции
+import { COMMON_STYLES } from "./styles"; //Общие стили
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ DIALOG_CONTENT: {
+ paddingBottom: "0px",
+ maxHeight: "740px",
+ minHeight: "740px",
+ ...COMMON_STYLES.SCROLL
+ }
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Диалог формы события
+const TaskDialog = ({ taskRn, taskType, taskStatus, editable, onTasksReload, onClose }) => {
+ //Собственное состояние
+ const [task, setTask] = useClientEvent(taskRn, taskType, taskStatus);
+
+ //Состояние заполненности всех обязательных свойств
+ const [dpReady, setDPReady] = useState(false);
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
+
+ //При изменении заполненности всех обязательных свойств
+ const handleDPReady = useCallback(v => setDPReady(v), []);
+
+ //При изменении информации о задаче
+ const handleTaskChange = useCallback(
+ newTaskValues => {
+ setTask(pv => ({ ...pv, ...newTaskValues }));
+ },
+ [setTask]
+ );
+
+ //При добавлении события
+ const handleInsertTask = async callBack => {
+ await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_INSERT",
+ args: {
+ SCRN: task.sCrn,
+ SPREF: task.sPrefix,
+ SNUMB: task.sNumber,
+ STYPE: task.sType,
+ SSTATUS: task.sStatus,
+ SPLAN_DATE: task.dPlanDate,
+ SINIT_PERSON: task.sInitClnperson,
+ SCLIENT_CLIENT: task.sClntClients,
+ SCLIENT_PERSON: task.sClntClnperson,
+ SDESCRIPTION: task.sDescription,
+ SREASON: task.sInitReason,
+ CPROPS: {
+ VALUE: object2Base64XML(
+ [
+ Object.fromEntries(
+ Object.entries(task.docProps)
+ // eslint-disable-next-line no-unused-vars
+ .filter(([_, v]) => v != (null || ""))
+ )
+ ],
+ {
+ arrayNodeName: "props"
+ }
+ ),
+ SDATA_TYPE: SERV_DATA_TYPE_CLOB
+ }
+ }
+ });
+ callBack();
+ };
+
+ //При исправлении события
+ const handleUpdateEvent = async callBack => {
+ await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_UPDATE",
+ args: {
+ NCLNEVENTS: task.nRn,
+ SCLIENT_CLIENT: task.sClntClients,
+ SCLIENT_PERSON: task.sClntClnperson,
+ SDESCRIPTION: task.sDescription,
+ CPROPS: {
+ // eslint-disable-next-line no-unused-vars
+ VALUE: object2Base64XML([Object.fromEntries(Object.entries(task.docProps).filter(([_, v]) => v != (null || "")))], {
+ arrayNodeName: "props"
+ }),
+ SDATA_TYPE: SERV_DATA_TYPE_CLOB
+ }
+ }
+ });
+ callBack();
+ };
+
+ //При считывании следующего номера события
+ const handleEventNextNumbGet = useCallback(async () => {
+ //Считываем данные
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_NEXTNUMB_GET",
+ args: {
+ SPREFIX: task.sPrefix
+ }
+ });
+ //Если данные есть
+ if (data) {
+ //Устанавливаем номер
+ setTask(pv => ({ ...pv, sNumber: data.SEVENT_NUMB }));
+ }
+ }, [executeStored, setTask, task.sPrefix]);
+
+ //Генерация содержимого
+ return (
+
+ );
+};
+
+//Контроль свойств - Диалог формы события
+TaskDialog.propTypes = {
+ taskRn: PropTypes.number,
+ taskType: PropTypes.string.isRequired,
+ taskStatus: PropTypes.string,
+ editable: PropTypes.bool,
+ onTasksReload: PropTypes.func.isRequired,
+ onClose: PropTypes.func.isRequired
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { TaskDialog };
diff --git a/db/PKG_P8PANELS_CLNTTSKBRD.pck b/db/PKG_P8PANELS_CLNTTSKBRD.pck
new file mode 100644
index 0000000..c6ebc49
--- /dev/null
+++ b/db/PKG_P8PANELS_CLNTTSKBRD.pck
@@ -0,0 +1,2705 @@
+create or replace package PKG_P8PANELS_CLNTTSKBRD as
+
+ /* Формирование набора данных с отобранными событиями */
+ procedure CLNEVENTS_LOAD
+ (
+ CFILTERS in clob, -- Фильтры
+ CORDERS in clob, -- Сортировки
+ NINCLUDE_ACCOUNTS in number := 1, -- Включать информацию о контрагентах с изображением (0 - нет, 1 - да)
+ COUT out clob -- Сериализованная таблица данных
+ );
+
+ /* Выбор события по рег. номеру */
+ procedure CLNEVENTS_SELECT
+ (
+ NCLNEVENTS in number, -- Рег. номер события
+ NIDENT out number -- Идентификатор буфера подобранных (списка отмеченных записей, null - не найдено)
+ );
+
+ /* Считывание следующего номера события */
+ procedure CLNEVENTS_NEXTNUMB_GET
+ (
+ SPREFIX in varchar2, -- Префикс события
+ SEVENT_NUMB out varchar2 -- Номер события
+ );
+
+ /* Считывание параметров события */
+ procedure CLNEVENTS_GET
+ (
+ NCLNEVENTS in number, -- Рег. номер события
+ COUT out clob -- Данные о записи события
+ );
+
+ /* Инициализация параметров события */
+ procedure CLNEVENTS_INIT
+ (
+ SEVENT_TYPE in varchar2, -- Мнемокод типа события
+ SPREF out varchar2, -- Префикс события
+ SNUMB out varchar2, -- Номер события
+ SINIT_PERSON out varchar2, -- Сотрудник - инициатор
+ SINIT_AUTHNAME out varchar2 -- Пользователь - инициатор
+ );
+
+ /* Добавление значений свойств документа */
+ procedure CLNEVENTS_PROPS_INSERT
+ (
+ NCLNEVENTS in number, -- Рег. номер события
+ SEVENT_TYPE in varchar2, -- Мнемокод типа события
+ CPROPS in clob -- Свойства документа
+ );
+
+ /* Добавление события */
+ procedure CLNEVENTS_INSERT
+ (
+ SCRN in varchar2, -- Наименование каталога
+ SPREF in varchar2, -- Префикс события
+ SNUMB in varchar2, -- Номер события
+ STYPE in varchar2, -- Тип события
+ SSTATUS in varchar2, -- Статус события
+ SPLAN_DATE in varchar2, -- Дата планируемого начала работ
+ SINIT_PERSON in varchar2, -- Инициатор
+ SCLIENT_CLIENT in varchar2, -- Клиент-инициатор
+ SCLIENT_PERSON in varchar2, -- Сотрудник-инициатор
+ SDESCRIPTION in varchar2, -- Описание
+ SREASON in varchar2, -- Причина
+ CPROPS in clob -- Свойства
+ );
+
+ /* Исправление события */
+ procedure CLNEVENTS_UPDATE
+ (
+ NCLNEVENTS in number, -- Рег. номер события
+ SCLIENT_CLIENT in varchar2, -- Клиент-инициатор
+ SCLIENT_PERSON in varchar2, -- Сотрудник-инициатор
+ SDESCRIPTION in varchar2, -- Описание
+ CPROPS in clob -- Свойства
+ );
+
+ /* Удаление события */
+ procedure CLNEVENTS_DELETE
+ (
+ NCLNEVENTS in number -- Рег. номер события
+ );
+
+ /* Изменение статуса события */
+ procedure CLNEVENTS_STATE_CHANGE
+ (
+ NIDENT in out number, -- Рег. номер отмеченных записей
+ NSTEP in out number, -- Шаг выполнения (0-прекратить выполнение, 1,2..- номер шага)
+ NEVENT in out number, -- Событие, для отбора исполнителей
+ SEVENT_TYPE in out varchar2, -- Тип события, для отбора точек перехода и исполнителей
+ SEVENT_STAT in out varchar2, -- Cтатус события, текущий - для отбора точек перехода, следующий - для отбора исполнителей
+ SINIT_PERSON in out varchar2, -- Инициатор–сотрудник, для отбора исполнителей
+ SINIT_AUTHNAME in out varchar2, -- Инициатор–пользователь, для отбора исполнителей
+ SCLIENT_CLIENT in out varchar2, -- Клиент–организация, для отбора исполнителей
+ SCLIENT_PERSON in out varchar2, -- Клиент–сотрудник, для отбора исполнителей
+ NPOINT in out number, -- Текущая точка маршрута
+ NPASS in number, -- Точка перехода
+ NSELECT_EXEC out number, -- Признак необходимости выбора исполнителя
+ SEXECUTEMETHOD out varchar2, -- Мнемокод метода
+ SSEND_CLIENT in varchar2, -- Направить – организация
+ SSEND_DIVISION in varchar2, -- Направить – подразделение
+ SSEND_POST in varchar2, -- Направить – должность
+ SSEND_PERFORM in varchar2, -- Направить – должность в подразделении
+ SSEND_PERSON in varchar2, -- Направить – сотрудник
+ SSEND_STAFFGRP in varchar2, -- Направить – нештатная структура
+ SSEND_USER_GROUP in varchar2, -- Направить – группа пользователей
+ SSEND_USER_NAME in varchar2, -- Направить – пользователь
+ NSEND_PREDEFINED_EXEC in number, -- Переадресация
+ NSEND_PREDEFINED_PROC in number, -- Процедура выбора предопределенного исполнителя
+ SNEXT_STAT in varchar2 := null, -- Следующий статус (при переносе события)
+ SNOTE_HEADER in varchar2 := null, -- Заголовок примечания (при наличии)
+ SNOTE in varchar2 := null -- Примечание (при наличии)
+ );
+
+ /* Переадресация события */
+ procedure CLNEVENTS_SEND
+ (
+ NIDENT in out number, -- Рег. номер отмеченных записей
+ NSTEP in out number, -- Шаг выполнения
+ NEVENT in out number, -- Событие, для отбора исполнителей
+ SEVENT_TYPE in out varchar2, -- Тип события, для отбора точек перехода и исполнителей
+ SEVENT_STAT in out varchar2, -- Текущий статус события, для отбора исполнителей
+ SINIT_PERSON in out varchar2, -- Инициатор–сотрудник, для отбора исполнителей
+ SINIT_AUTHNAME in out varchar2, -- Инициатор–пользователь, для отбора исполнителей
+ SCLIENT_CLIENT in out varchar2, -- Клиент–организация, для отбора исполнителей
+ SCLIENT_PERSON in out varchar2, -- Клиент–сотрудник, для отбора исполнителей
+ SEXECUTEMETHOD out varchar2, -- Мнемокод метода
+ SSEND_CLIENT in varchar2, -- Направить – организация
+ SSEND_DIVISION in varchar2, -- Направить – подразделение
+ SSEND_POST in varchar2, -- Направить – должность
+ SSEND_PERFORM in varchar2, -- Направить – должность в подразделении
+ SSEND_PERSON in out varchar2, -- Направить – сотрудник
+ SSEND_STAFFGRP in varchar2, -- Направить – нештатная структура
+ SSEND_USER_GROUP in varchar2, -- Направить – группа пользователей
+ SSEND_USER_NAME in out varchar2, -- Направить – пользователь
+ NSEND_PREDEFINED_EXEC in number, -- Переадресация
+ NSEND_PREDEFINED_PROC in number, -- Процедура выбора предопределенного исполнителя
+ SNOTE_HEADER in varchar2 := null, -- Заголовок примечания (при наличии)
+ SNOTE in varchar2 := null -- Примечание (при наличии)
+ );
+
+ /* Возврат в предыдущую точку события */
+ procedure CLNEVENTS_RETURN
+ (
+ NCLNEVENTS in number -- Рег. номер события
+ );
+
+ /* Считывание маршрутов и исполнителей события */
+ procedure CLNEVENTS_GET_INFO_BY_CODE
+ (
+ SEVNTYPE_CODE in varchar2, -- Мнемокод типа события
+ COUT out clob -- XML с параметрами фильтра по умолчанию
+ );
+
+ /* Считывание учётных документов события */
+ procedure CLNEVENTS_DOCLINKS_GET
+ (
+ SEVNTYPE_CODE in varchar2, -- Мнемокод типа события
+ COUT out clob -- XML с параметрами фильтра по умолчанию
+ );
+
+ /* Считывание списка рег. номеров дочерних каталогов (включая основной) */
+ procedure CLNEVENTS_SUBCATALOGS_GET
+ (
+ SCRN_NAME in varchar2, -- Наименование каталога
+ NSUBCAT in number := 0, -- Признак включения подкаталогов (0 - нет, 1 - да)
+ SRESULT out varchar2 -- Список рег. номеров каталогов
+ );
+
+ /* Формирование условий отбора фильтра */
+ procedure CLNEVENTS_COND;
+
+ /* Считывание настройки раздела "События" для пользователя */
+ procedure CLNEVENTS_DP_RULES_GET
+ (
+ COUT out clob -- XML с настройкой раздела
+ );
+
+ /* Считывание свойств раздела "События" */
+ procedure CLNEVENTS_PROPS_GET
+ (
+ SEVNTYPE_CODE in varchar2, -- Мнемокод типа события
+ COUT out clob -- XML со свойствами раздела
+ );
+
+ /* Загрузка статусов типа события */
+ procedure CLNEVNSTATS_LOAD
+ (
+ SCLNEVNTYPES in varchar2, -- Мнемокод типа события
+ COUT out clob -- Статусы типа события
+ );
+
+end PKG_P8PANELS_CLNTTSKBRD;
+/
+create or replace package body PKG_P8PANELS_CLNTTSKBRD as
+
+ /* Константы - мнемокоды разделов */
+ SUNIT_CLNEVENTS constant PKG_STD.TSTRING := 'ClientEvents'; -- Раздел "События"
+ SUNIT_AGNLIST constant PKG_STD.TSTRING := 'AGNLIST'; -- Раздел "Контрагенты"
+ SUNIT_DP constant PKG_STD.TSTRING := 'DocsProperties'; -- Раздел "Свойства документов"
+ SSELECTLIST_ACTION constant PKG_STD.TSTRING := 'P8Panels'; -- Код действия для селектлиста
+
+
+ /* Константы - вспомогательные */
+ SDATE_PATTERN_HH_MI constant PKG_STD.TSTRING := 'dd.mm.yyyy hh24:mi'; -- Дата и время без секунд
+
+ /* Считывание префикса типа свойства документа */
+ function UTL_DP_PREF_TYPE_GET
+ (
+ NTYPE in number, -- Тип свойства документа
+ NSHORT in number := 1 -- Признак сокращенного префикса (0 - длинный "SDP", "NDP", "DDP", 1 - сокращенный "S", "N", "D")
+ ) return varchar2 -- Префикс свойства документа
+ is
+ SRESULT PKG_STD.TSTRING; -- Префикс свойства документа
+ begin
+ /* Если требуется сокращенный */
+ if (NSHORT = 1) then
+ /* Тип строка */
+ case
+ /* Строка */
+ when (NTYPE = 0) then
+ SRESULT := 'S';
+ /* Число или время */
+ when ((NTYPE = 1) or (NTYPE = 3)) then
+ SRESULT := 'N';
+ /* Дата */
+ else
+ SRESULT := 'D';
+ end case;
+ else
+ /* Тип строка */
+ case
+ /* Строка */
+ when (NTYPE = 0) then
+ SRESULT := 'SDP';
+ /* Число или время */
+ when ((NTYPE = 1) or (NTYPE = 3)) then
+ SRESULT := 'NDP';
+ /* Дата */
+ else
+ SRESULT := 'DDP';
+ end case;
+ end if;
+ /* Возвращаем результат */
+ return SRESULT;
+ end UTL_DP_PREF_TYPE_GET;
+
+ /* Считывание отформатированного ID доп. свойства (формат "_<РЕГ_НОМЕР>") */
+ function UTL_DP_FORMATTED_ID_GET
+ (
+ NTYPE in number, -- Тип свойства документа
+ NRN in number -- Рег. номер свойства документа
+ ) return varchar2 -- Отформатированный ID (формат "_<РЕГ_НОМЕР>")
+ is
+ begin
+ /* Возвращаем результат */
+ return UTL_DP_PREF_TYPE_GET(NTYPE => NTYPE, NSHORT => 0) || '_' || TO_CHAR(NRN);
+ end UTL_DP_FORMATTED_ID_GET;
+
+ /* Считывание записи события */
+ function UTL_CLNEVENTS_GET
+ (
+ NCOMPANY in number, -- Рег. номер организации
+ NCLNEVENTS in number -- Рег. номер события
+ ) return CLNEVENTS%rowtype -- Запись события
+ is
+ RRESULT CLNEVENTS%rowtype; -- Запись события
+ begin
+ /* Считываем событие */
+ begin
+ select T.*
+ into RRESULT
+ from CLNEVENTS T
+ where T.RN = NCLNEVENTS
+ and T.COMPANY = NCOMPANY;
+ exception
+ when others then
+ P_EXCEPTION(0,
+ 'Ошибка считывания записи события с рег. номером (%s).',
+ NCLNEVENTS);
+ end;
+ /* Возвращаем результат */
+ return RRESULT;
+ end UTL_CLNEVENTS_GET;
+
+ /* Считывание мнемокодов ссылок события */
+ procedure UTL_CLNEVENTS_JOINS_GET
+ (
+ NCLNEVENTS in number, -- Рег. номер события,
+ STYPE out varchar2, -- Мнемокод типа события
+ SSTATUS out varchar2, -- Мнемокод статуса типа события
+ SINIT_PERSON out varchar2, -- Мнемокод инициатора сотрудника
+ SCLIENT_CLIENT out varchar2, -- Мнемокод клиента (организации)
+ SCLIENT_PERSON out varchar2, -- Мнемокод клиента (сотрудника)
+ SSEND_CLIENT out varchar2, -- Мнемокод направления (организация)
+ SSEND_DIVISION out varchar2, -- Мнемокод направления (подразделение)
+ SSEND_POST out varchar2, -- Мнемокод направления (должность)
+ SSEND_PERFORM out varchar2, -- Мнемокод направления (должность в подразделении)
+ SSEND_PERSON out varchar2, -- Мнемокод направления (сотрудник)
+ SSEND_STAFFGRP out varchar2, -- Мнемокод направления (нештатная структура)
+ SSEND_USER_GROUP out varchar2, -- Мнемокод направления (группа пользователей)
+ SSEND_USER_NAME out varchar2 -- Мнемокод направления (группа пользователь)
+ )
+ is
+ begin
+ /* Считываем все мнемокоды */
+ begin
+ select ET.EVNTYPE_CODE,
+ TS.EVNSTAT_CODE,
+ F_CLNPERSONS_FORMAT_CODE(IP.COMPANY, IP.CODE),
+ CC.CLIENT_CODE,
+ F_CLNPERSONS_FORMAT_CODE(CP.COMPANY, CP.CODE),
+ SC.CLIENT_CODE,
+ ID.CODE,
+ PS.CODE,
+ F_CLNPSDEP_FORMAT_CODE(PP.COMPANY, PP.CODE),
+ F_CLNPERSONS_FORMAT_CODE(SP.COMPANY, SP.CODE),
+ SG.CODE,
+ UG.CODE,
+ UL3.NAME
+ into STYPE,
+ SSTATUS,
+ SINIT_PERSON,
+ SCLIENT_CLIENT,
+ SCLIENT_PERSON,
+ SSEND_CLIENT,
+ SSEND_DIVISION,
+ SSEND_POST,
+ SSEND_PERFORM,
+ SSEND_PERSON,
+ SSEND_STAFFGRP,
+ SSEND_USER_GROUP,
+ SSEND_USER_NAME
+ from CLNEVENTS T,
+ CLNEVNTYPES ET,
+ CLNEVNTYPSTS ES,
+ CLNEVNSTATS TS,
+ CLNPERSONS IP,
+ CLNCLIENTS CC,
+ CLNPERSONS CP,
+ CLNCLIENTS SC,
+ INS_DEPARTMENT ID,
+ CLNPOSTS PS,
+ CLNPSDEP PP,
+ CLNPERSONS SP,
+ FCSTAFFGRP SG,
+ USERGRP UG,
+ USERLIST UL3
+ where T.RN = NCLNEVENTS
+ and T.EVENT_TYPE = ET.RN
+ and T.EVENT_STAT = ES.RN
+ and ES.EVENT_STATUS = TS.RN
+ and T.INIT_PERSON = IP.RN(+)
+ and T.CLIENT_CLIENT = CC.RN(+)
+ and T.CLIENT_PERSON = CP.RN(+)
+ and T.SEND_CLIENT = SC.RN(+)
+ and T.SEND_DIVISION = ID.RN(+)
+ and T.SEND_POST = PS.RN(+)
+ and T.SEND_PERFORM = PP.RN(+)
+ and T.SEND_PERSON = SP.RN(+)
+ and T.SEND_STAFFGRP = SG.RN(+)
+ and T.SEND_USER_GROUP = UG.RN(+)
+ and T.SEND_USER_AUTHID = UL3.AUTHID(+);
+ exception
+ when others then
+ P_EXCEPTION(0, 'Ошибка считывания записи события с рег. номером (%s).', NCLNEVENTS);
+ end;
+ end UTL_CLNEVENTS_JOINS_GET;
+
+ /* Считывание наименования исполнителя события */
+ function UTL_CLNEVENTS_SENDER_GET
+ (
+ NCLNEVENTS in number -- Рег. номер события
+ ) return varchar2 -- Наименование исполнителя
+ is
+ SRESULT PKG_STD.TSTRING; -- Наименование исполнителя
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
+ NAGN_VER PKG_STD.TREF; -- Версия раздела "Контрагенты"
+ begin
+ /* Считываем версию организации */
+ FIND_VERSION_BY_COMPANY(NCOMPANY => NCOMPANY, SUNITCODE => SUNIT_AGNLIST, NVERSION => NAGN_VER);
+ /* Считываем наименование исполнителя и аватар (если есть возможность) */
+ begin
+ select COALESCE(SC_A.AGNABBR,
+ ID.NAME,
+ PP.PSDEP_NAME,
+ PS.NAME,
+ SP_A.AGNABBR,
+ SG.NAME,
+ UG.NAME,
+ SUA.AGNABBR,
+ TA.AGNABBR)
+ into SRESULT
+ from CLNEVENTS T,
+ CLNCLIENTS SC,
+ AGNLIST SC_A,
+ INS_DEPARTMENT ID,
+ CLNPSDEP PP,
+ CLNPOSTS PS,
+ CLNPERSONS SP,
+ AGNLIST SP_A,
+ FCSTAFFGRP SG,
+ USERGRP UG,
+ AGNLIST TA,
+ AGNLIST SUA
+ where T.RN = NCLNEVENTS
+ and T.COMPANY = NCOMPANY
+ and T.SEND_CLIENT = SC.RN(+)
+ and SC.CLIENT_AGENT = SC_A.RN(+)
+ and T.SEND_DIVISION = ID.RN(+)
+ and T.SEND_PERFORM = PP.RN(+)
+ and T.SEND_POST = PS.RN(+)
+ and T.SEND_PERSON = SP.RN(+)
+ and SP.PERS_AGENT = SP_A.RN(+)
+ and T.SEND_STAFFGRP = SG.RN(+)
+ and T.SEND_USER_GROUP = UG.RN(+)
+ and (T.SEND_USER_AUTHID = SUA.PERS_AUTHID(+) and NAGN_VER = SUA.VERSION(+))
+ and (T.AUTHID = TA.PERS_AUTHID(+) and NAGN_VER = TA.VERSION(+));
+ exception
+ when others then
+ SRESULT := null;
+ end;
+ /* Возвращаем результат */
+ return SRESULT;
+ end UTL_CLNEVENTS_SENDER_GET;
+
+ /* Проверка возможности редактирования события в текущем статусе */
+ function UTL_CLNEVENTS_BAN_UPDATE_GET
+ (
+ NCOMPANY in number, -- Рег. номер организации
+ NCLNEVENTS in number -- Рег. номер события
+ ) return number -- Признак запрета исправления события в точке маршрута (0 - нет, 1 - да)
+ is
+ NRESULT PKG_STD.TNUMBER; -- Признак запрета исправления события в точке маршрута (0 - нет, 1 - да)
+ begin
+ /* Считываем событие */
+ begin
+ select EP.BAN_UPDATE
+ into NRESULT
+ from CLNEVENTS CE,
+ EVRTPOINTS EP
+ where CE.RN = NCLNEVENTS
+ and CE.COMPANY = NCOMPANY
+ and EP.EVENT_STATUS = CE.EVENT_STAT;
+ exception
+ when others then
+ P_EXCEPTION(0,
+ 'Ошибка считывания записи события с рег. номером (%s).',
+ NCLNEVENTS);
+ end;
+ /* Возвращаем результат */
+ return NRESULT;
+ end UTL_CLNEVENTS_BAN_UPDATE_GET;
+
+ /* Формирование блока запроса свойств документа и коллекций наименований/типов */
+ procedure UTL_CLNEVENTS_DP_QUERY_BLOCK
+ (
+ NCOMPANY in number, -- Рег. номер организации
+ NCLNEVNTYPES in number, -- Рег. номер типа события
+ NQUERY_POS in number, -- Признак позиции блока в запросе (0 - блок находится в конце запроса, 1 - за блоком есть параметры)
+ CSQL out clob, -- Буфер для блока SQL
+ TDP_NAMES in out nocopy PKG_CONTVALLOC1S.TCONTAINER, -- Коллекция с наименованиями
+ TDP_TYPES in out nocopy PKG_CONTVALLOC1S.TCONTAINER -- Коллекция с типами данных
+ )
+ is
+ SDP_CODE PKG_STD.TSTRING; -- Код свойства документа
+ begin
+ /* Цикл по свойствам документа раздела */
+ for REC in (select CEP.PROPERTY,
+ DP.NAME,
+ DP.FORMAT
+ from CLEVTPROPS CEP,
+ DOCS_PROPS DP
+ where CEP.PRN = NCLNEVNTYPES
+ and CEP.COMPANY = NCOMPANY
+ and CEP.PROPERTY = DP.RN(+)
+ order by TO_CHAR(PROPERTY))
+ loop
+ /* Формирования кода */
+ SDP_CODE := 'DP_' || REC.PROPERTY;
+ /* Добавление данных в коллекции */
+ PKG_CONTVALLOC1S.PUTS(RCONTAINER => TDP_NAMES, SROWID => SDP_CODE, SVALUE => REC.NAME);
+ PKG_CONTVALLOC1S.PUTN(RCONTAINER => TDP_TYPES, SROWID => SDP_CODE, NVALUE => REC.FORMAT);
+ /* Формирование запроса */
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL,
+ SELEMENT1 => ' (select COALESCE(V.STR_VALUE, to_char(V.NUM_VALUE), to_char(V.DATE_VALUE))');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from DOCS_PROPS_VALS V');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where T.RN = V.UNIT_RN(+)');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and V.DOCS_PROP_RN(+) = ' || REC.PROPERTY || ') as "' || SDP_CODE || '",');
+ end loop;
+ /* Если собрали данные */
+ if ((CSQL is not null) and (CSQL <> EMPTY_CLOB())) then
+ /* Добавляем запятую перед встраиваемым блоком */
+ CSQL := ',' || CSQL;
+ /* Если блок находится в конце запроса */
+ if (NQUERY_POS = 0) then
+ /* Удаляем последнюю запятую */
+ CSQL := SUBSTR(CSQL, 1, LENGTH(CSQL) - 1);
+ end if;
+ end if;
+ end UTL_CLNEVENTS_DP_QUERY_BLOCK;
+
+ /* Считывание мнемокода статуса типа события по умолчанию */
+ function UTL_CLNEVNSTATS_CODE_GET
+ (
+ NCOMPANY in number, -- Рег. номер организации
+ SEVENT_TYPE in varchar2 -- Мнемокод типа события
+ ) return varchar2 -- Мнемокод статуса типа события по умолчанию
+ is
+ SRESULT PKG_STD.TSTRING; -- Мнемокод статуса типа события по умолчанию
+ begin
+ /* Считываем мнемокод статуса типа события по умолчанию */
+ begin
+ select TMP.EVNSTAT_CODE
+ into SRESULT
+ from (select S.EVNSTAT_CODE
+ from CLNEVNTYPES T,
+ CLNEVNTYPSTS TS,
+ CLNEVNSTATS S
+ where T.COMPANY = NCOMPANY
+ and T.EVNTYPE_CODE = SEVENT_TYPE
+ and TS.PRN = T.RN
+ and F_EVRTPOINTS_VALID_START_POINT(T.COMPANY, T.RN, TS.RN, TS.DEFAULT_STATUS) = 1
+ and TS.EVENT_STATUS = S.RN
+ order by TS.DEFAULT_STATUS desc,
+ S.EVNSTAT_CODE) TMP
+ where ROWNUM = 1;
+ exception
+ when others then
+ SRESULT := null;
+ end;
+ /* Возвращаем результат */
+ return SRESULT;
+ end UTL_CLNEVNSTATS_CODE_GET;
+
+ /* Считывание описателя документа */
+ function UTL_UNITLIST_DESCR_GET
+ (
+ SUNIT_CODE in varchar2, -- Код раздела
+ NDOCUMENT in number -- Рег. номер документа в разделе
+ ) return varchar2 -- Описатель
+ is
+ SRES PKG_STD.TLSTRING; -- Буфер для результата
+ SDESCR_DEFAULT PKG_STD.TSTRING := 'Документ раздела '; -- Описатель по умолчанию
+ NDOCDESCR_RN PKG_STD.TREF; -- Рег. номер описателя
+ NDESCR_FOUND PKG_STD.TNUMBER := 1; -- Признак существования описателя
+ begin
+ /* Считываем рег. номер описателя */
+ begin
+ select T.RN
+ into NDOCDESCR_RN
+ from DOCDESCRS T
+ where T.UNITCODE = SUNIT_CODE
+ and T.USERPROC is not null
+ and ROWNUM <= 1;
+ exception
+ when NO_DATA_FOUND then
+ NDESCR_FOUND := 0;
+ end;
+ /* Если есть и описатель и передан идентификатор документа */
+ if ((NDESCR_FOUND = 1) and (NDOCUMENT is not null)) then
+ /* Исполним и вернём описатель */
+ begin
+ SRES := F_DOCDESCRS_GET_DESCRIPTION(SUNITCODE => SUNIT_CODE, NDOCUMENT => NDOCUMENT);
+ return SRES;
+ exception
+ when others then
+ return COALESCE(SDESCR_DEFAULT, 'Идентификатор - ' || TO_CHAR(NDOCUMENT));
+ end;
+ else
+ /* Описателя нет, но есть идентификатор документа */
+ if (NDOCUMENT is not null) then
+ /* Тогда вернём описатель по умолчанию или идентификатор */
+ return COALESCE(SDESCR_DEFAULT, 'Идентификатор - ' || TO_CHAR(NDOCUMENT));
+ else
+ /* Нет ничего */
+ return SDESCR_DEFAULT;
+ end if;
+ end if;
+ end UTL_UNITLIST_DESCR_GET;
+
+ /* Формирование набора данных с отобранными событиями */
+ procedure CLNEVENTS_DG_GET
+ (
+ NIDENT in number, -- Идентификатор отбора
+ CFILTERS in clob, -- Фильтры
+ CORDERS in clob, -- Сортировки
+ COUT out clob -- Сериализованная таблица данных
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
+ RF PKG_P8PANELS_VISUAL.TDG_FILTERS; -- Фильтры
+ RO PKG_P8PANELS_VISUAL.TDG_ORDERS; -- Сортировки
+ RDG PKG_P8PANELS_VISUAL.TDG; -- Описание таблицы
+ RDG_ROW PKG_P8PANELS_VISUAL.TDG_ROW; -- Строка таблицы
+ CSQL clob; -- Буфер для запроса
+ ICURSOR integer; -- Курсор для исполнения запроса
+ SEVSTAT PKG_STD.TSTRING; -- Буфер для статуса события
+ NCLNEVNTYPES PKG_STD.TREF; -- Рег. номер типа события
+ SDOC_PREF PKG_STD.TSTRING; -- Префикс события (без пробелов)
+ SDOC_NUMB PKG_STD.TSTRING; -- Номер события (без пробелов)
+ NCLNEVENTS PKG_STD.TREF; -- Рег. номер события
+ SSENDER PKG_STD.TSTRING; -- Наименование исполнителя
+ CDP_QUERY clob; -- Блок запроса со свойствами документа
+ TDP_NAMES PKG_CONTVALLOC1S.TCONTAINER; -- Коллекция с наименованиями свойств документа
+ TDP_TYPES PKG_CONTVALLOC1S.TCONTAINER; -- Коллекция с типами свойств документа
+
+ /* Считывание типа события из фильтров */
+ function CLNEVNTYPES_GET
+ (
+ NCOMPANY in number, -- Рег. номер организации
+ RFILTERS in PKG_P8PANELS_VISUAL.TDG_FILTERS -- Фильтры
+ ) return number -- Рег. номер типа события
+ is
+ SCLNEVNTYPES PKG_STD.TSTRING; -- Мнемокод типа события
+ NRESULT PKG_STD.TREF; -- Рег. номер типа события
+ begin
+ /* Обходим коллекцию фильтров */
+ for I in RFILTERS.FIRST .. RFILTERS.LAST
+ loop
+ /* Если это фильтр типа события */
+ if (RFILTERS(I).SNAME = 'SEVTYPE_CODE') then
+ /* Считываем мнемокод типа события */
+ SCLNEVNTYPES := RFILTERS(I).SFROM;
+ /* Выходим из цикла */
+ exit;
+ end if;
+ end loop;
+ /* Если мнемокод типа события был считан */
+ if (SCLNEVNTYPES is not null) then
+ /* Считываем рег. номер типа события */
+ begin
+ select T.RN
+ into NRESULT
+ from CLNEVNTYPES T
+ where T.COMPANY = NCOMPANY
+ and T.EVNTYPE_CODE = SCLNEVNTYPES;
+ exception
+ when others then
+ NRESULT := null;
+ end;
+ end if;
+ /* Возвращаем результат */
+ return NRESULT;
+ end CLNEVNTYPES_GET;
+
+ /* Добавление колонок/значений по коллекции свойств документа */
+ procedure RDG_FILL_BY_TDP
+ (
+ RDG in out nocopy PKG_P8PANELS_VISUAL.TDG, -- Описание таблицы (изменяется при добавлении колонки)
+ RDG_ROW in out nocopy PKG_P8PANELS_VISUAL.TDG_ROW, -- Строка таблицы (изменяется при заполнении значений строки)
+ TDP_NAMES in PKG_CONTVALLOC1S.TCONTAINER, -- Коллекция с наименованиями свойств документа
+ TDP_TYPES in PKG_CONTVALLOC1S.TCONTAINER, -- Коллекция с типами свойств документа
+ NFIRST_NUMB in number := 1, -- Номер первого элемента коллекции
+ NLAST_NUMB in number, -- Номер последнего элемента коллекции
+ ICURSOR in integer := null, -- Курсор для исполнения запрос (требуется при заполнении значений строки)
+ NACTION in number := 0 -- Действие (0 - добавление колонки в таблицу, 1 - заполнение значений строки, 2 - добавление колонки курсора)
+ )
+ is
+ SDP_CODE PKG_STD.TSTRING; -- Код свойства документа
+ SDP_NAME PKG_STD.TSTRING; -- Имя свойства документа
+ NDP_TYPE PKG_STD.TNUMBER; -- Тип свойства документа
+ SDP_PREF PKG_STD.TSTRING; -- Префикс свойства документа
+ begin
+ /* Получаем код первого элемента */
+ SDP_CODE := PKG_CONTVALLOC1S.FIRST_(RCONTAINER => TDP_NAMES);
+ /* Цикл по именам доп. свойств */
+ for I in NFIRST_NUMB .. NLAST_NUMB
+ loop
+ /* Получаем код следующего элемента */
+ if (I > NFIRST_NUMB) then
+ SDP_CODE := PKG_CONTVALLOC1S.NEXT_(RCONTAINER => TDP_NAMES, SROWID => SDP_CODE);
+ end if;
+ /* Если это добавление колонки курсора */
+ if (NACTION = 2) then
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => I);
+ else
+ /* Считываем тип доп. свойства */
+ NDP_TYPE := PKG_CONTVALLOC1S.GETN(RCONTAINER => TDP_TYPES, SROWID => SDP_CODE);
+ /* Определение префикса типа данных к коду */
+ SDP_PREF := UTL_DP_PREF_TYPE_GET(NTYPE => NDP_TYPE);
+ /* Если это добавление колонки в таблицу */
+ if (NACTION = 0) then
+ /* Считываем имя свойства документа */
+ SDP_NAME := PKG_CONTVALLOC1S.GETS(RCONTAINER => TDP_NAMES, SROWID => SDP_CODE);
+ /* Добавляем колонку таблицы */
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => SDP_PREF || SDP_CODE,
+ SCAPTION => SDP_NAME,
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ BVISIBLE => false);
+ else
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
+ SNAME => SDP_PREF || SDP_CODE,
+ ICURSOR => ICURSOR,
+ NPOSITION => I);
+ end if;
+ end if;
+ end loop;
+ end RDG_FILL_BY_TDP;
+ begin
+ /* Очистка коллекций информации о свойствах документа */
+ PKG_CONTVALLOC1S.PURGE(RCONTAINER => TDP_NAMES);
+ PKG_CONTVALLOC1S.PURGE(RCONTAINER => TDP_TYPES);
+ /* Читаем фильтры */
+ RF := PKG_P8PANELS_VISUAL.TDG_FILTERS_FROM_XML(CFILTERS => CFILTERS);
+ /* Читаем сортировки */
+ RO := PKG_P8PANELS_VISUAL.TDG_ORDERS_FROM_XML(CORDERS => CORDERS);
+ /* Инициализируем таблицу данных */
+ RDG := PKG_P8PANELS_VISUAL.TDG_MAKE();
+ /* Считываем рег. номер типа события */
+ NCLNEVNTYPES := CLNEVNTYPES_GET(NCOMPANY => NCOMPANY, RFILTERS => RF);
+ /* Если тип события не считан */
+ if (NCLNEVNTYPES is null) then
+ P_EXCEPTION(0,
+ 'Ошибка считывания типа события. Фильтр по типу события обязателен.');
+ end if;
+ /* Описываем колонки таблицы данных */
+ 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 => 'NCRN',
+ SCAPTION => 'Каталог',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ SCOND_FROM => 'BYEV_CRN',
+ BFILTER => true,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SEVPREF',
+ SCAPTION => 'Префикс',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SEVNUMB',
+ SCAPTION => 'Номер',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SPREF_NUMB',
+ SCAPTION => 'Префикс и номер',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ BORDER => true,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'NEVTYPE',
+ SCAPTION => 'Тип события (Рег. номер)',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SEVTYPE_CODE',
+ SCAPTION => 'Тип события',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ SCOND_FROM => 'BYEV_TYPE',
+ BFILTER => true,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'DREG_DATE',
+ SCAPTION => 'Дата регистрации',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'DCHANGE_DATE',
+ SCAPTION => 'Дата последнего изменения',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE,
+ BORDER => true,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'DEXPIRE_DATE',
+ SCAPTION => 'Дата истечения времени нахождения в точке маршрута',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'DPLAN_DATE',
+ SCAPTION => 'Дата начала работ',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE,
+ BORDER => true,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'NINIT_PERSON',
+ SCAPTION => 'Инициализатор (Рег. номер)',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SINIT_PERSON',
+ SCAPTION => 'Инициализатор',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'NEVSTAT',
+ SCAPTION => 'Статус (Рег. номер)',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SEVSTAT_NAME',
+ SCAPTION => 'Статус',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'NSEND_PERSON',
+ SCAPTION => 'Получатель (Рег. номер)',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SSEND_PERSON',
+ SCAPTION => 'Получатель',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ SCOND_FROM => 'BYEV_S_PERSON',
+ BFILTER => true,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'NAGNLIST_RN',
+ SCAPTION => 'Рег. номер контрагенты',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'NSEND_DIVISION',
+ SCAPTION => 'Подразделение (Рег. номер)',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SSEND_DIVISION',
+ SCAPTION => 'Подразделение',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ SCOND_FROM => 'BYEV_S_DIVISION',
+ BFILTER => true,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'NSEND_USRGRP',
+ SCAPTION => 'Группа пользователей (Рег. номер)',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SSEND_USRGRP',
+ SCAPTION => 'Группа пользователей',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ SCOND_FROM => 'BYEV_S_USER_GROUP',
+ BFILTER => true,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SEVDESCR',
+ SCAPTION => 'Описание',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'NCLOSED',
+ SCAPTION => 'Аннулировано',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
+ SCOND_FROM => 'BYEV_CLOSED_From',
+ SCOND_TO => 'BYEV_CLOSED_To',
+ BFILTER => true,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SLINKED_UNIT',
+ SCAPTION => 'Учётный раздел',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'NLINKED_RN',
+ SCAPTION => 'Учётный документ (Рег. номер)',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
+ SCOND_FROM => 'BYEV_LINKED_RN',
+ BFILTER => true,
+ BVISIBLE => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SSENDER',
+ SCAPTION => 'Наименование исполнителя',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ BVISIBLE => false);
+ /* Формируем блок запроса свойств документа и коллекций наименований/типов */
+ UTL_CLNEVENTS_DP_QUERY_BLOCK(NCOMPANY => NCOMPANY,
+ NCLNEVNTYPES => NCLNEVNTYPES,
+ NQUERY_POS => 0,
+ CSQL => CDP_QUERY,
+ TDP_NAMES => TDP_NAMES,
+ TDP_TYPES => TDP_TYPES);
+ /* Если есть свойства документа */
+ if (PKG_CONTVALLOC1S.COUNT_(RCONTAINER => TDP_NAMES) <> 0) then
+ /* Добавляем колонки по свойствам документа */
+ RDG_FILL_BY_TDP(RDG => RDG,
+ RDG_ROW => RDG_ROW,
+ TDP_NAMES => TDP_NAMES,
+ TDP_TYPES => TDP_TYPES,
+ NLAST_NUMB => PKG_CONTVALLOC1S.COUNT_(RCONTAINER => TDP_NAMES),
+ NACTION => 0);
+ end if;
+ /* Обходим данные */
+ begin
+ /* Добавляем подсказку совместимости */
+ CSQL := PKG_SQL_BUILD.COMPATIBLE(SSQL => CSQL);
+ /* Формируем запрос */
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => 'select T.RN NRN,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.CRN NCRN,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => PKG_SQL_BUILD.TRIM_() || '(T.EVENT_PREF) SEVPREF,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => PKG_SQL_BUILD.TRIM_() || '(T.EVENT_NUMB) SEVNUMB,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.EVENT_TYPE NEVTYPE,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' (select CET.EVNTYPE_CODE');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from CLNEVNTYPES CET');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where CET.RN = T.EVENT_TYPE');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and CET.COMPANY = T.COMPANY) SEVTYPE_CODE,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.REG_DATE DREG_DATE,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.CHANGE_DATE DCHANGE_DATE,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.EXPIRE_DATE DEXPIRE_DATE,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.PLAN_DATE DPLAN_DATE,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.INIT_PERSON NINIT_PERSON,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' (select P.CODE');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from CLNPERSONS P');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where P.RN = T.INIT_PERSON');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and P.COMPANY = T.COMPANY) SINIT_PERSON,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.EVENT_STAT NEVSTAT,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' (select CES.EVNSTAT_NAME');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from CLNEVNSTATS CES');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where CES.RN = (select CETS.EVENT_STATUS');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from CLNEVNTYPSTS CETS');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where CETS.RN = T.EVENT_STAT');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and CETS.COMPANY = T.COMPANY)');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and CES.COMPANY = T.COMPANY) SEVSTAT_NAME,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.SEND_PERSON NSEND_PERSON,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' (select P.CODE');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from CLNPERSONS P');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where P.RN = T.SEND_PERSON');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and P.COMPANY = T.COMPANY) SSEND_PERSON,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' (select P.PERS_AGENT');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from CLNPERSONS P');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where P.RN = T.SEND_PERSON');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and P.COMPANY = T.COMPANY) NAGNLIST_RN,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.SEND_DIVISION NSEND_DIVISION,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' (select D.CODE');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from INS_DEPARTMENT D');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where D.RN = T.SEND_DIVISION');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and D.COMPANY = T.COMPANY) SSEND_DIVISION,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.SEND_USER_GROUP NSEND_USRGRP,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' (select UG.CODE');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from USERGRP UG');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where UG.RN = T.SEND_USER_GROUP');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and UG.COMPANY = T.COMPANY) SSEND_USRGRP,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.EVENT_DESCR SEVDESCR,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.CLOSED NCLOSED,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.LINKED_UNIT SLINKED_UNIT,');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.LINKED_RN NLINKED_RN,');
+ /* Колонка без TRIM, только для корректной сортировки по номеру события */
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' T.EVENT_PREF || ''-'' || T.EVENT_NUMB SPREF_NUMB');
+ /* Добавление блока запроса для свойств документа */
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => CDP_QUERY);
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from CLNEVENTS T');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where T.COMPANY = :NCOMPANY');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and T.RN in (select ID from COND_BROKER_IDSMART where IDENT = :NIDENT)');
+ PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' %ORDER_BY%');
+ /* Учтём сортировки */
+ PKG_P8PANELS_VISUAL.TDG_ORDERS_SET_QUERY(RDATA_GRID => RDG,
+ RORDERS => RO,
+ SPATTERN => '%ORDER_BY%',
+ CSQL => CSQL);
+ /* Учтём фильтры */
+ PKG_P8PANELS_VISUAL.TDG_FILTERS_SET_QUERY(NIDENT => NIDENT,
+ NCOMPANY => NCOMPANY,
+ SUNIT => SUNIT_CLNEVENTS,
+ SPROCEDURE => 'PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_COND',
+ RDATA_GRID => RDG,
+ RFILTERS => RF);
+ /* Разбираем его */
+ 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 => 'NIDENT', NVALUE => NIDENT);
+ /* Описываем структуру записи курсора */
+ PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 1);
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 2);
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 3);
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 4);
+ PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 5);
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 6);
+ PKG_SQL_DML.DEFINE_COLUMN_DATE(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_DATE(ICURSOR => ICURSOR, IPOSITION => 10);
+ PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 11);
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 12);
+ PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 13);
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 14);
+ PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 15);
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 16);
+ PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 17);
+ PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 18);
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 19);
+ PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 20);
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 21);
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 22);
+ PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 23);
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 24);
+ PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 25);
+ PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 26);
+ /* Если есть свойства документа */
+ if (PKG_CONTVALLOC1S.COUNT_(RCONTAINER => TDP_NAMES) <> 0) then
+ /* Добавляем колонок в курсор по свойствам документа */
+ RDG_FILL_BY_TDP(RDG => RDG,
+ RDG_ROW => RDG_ROW,
+ TDP_NAMES => TDP_NAMES,
+ TDP_TYPES => TDP_TYPES,
+ NFIRST_NUMB => RDG.RCOL_DEFS.COUNT - PKG_CONTVALLOC1S.COUNT_(RCONTAINER => TDP_NAMES) - 1,
+ NLAST_NUMB => RDG.RCOL_DEFS.COUNT - 2,
+ ICURSOR => ICURSOR,
+ NACTION => 2);
+ end if;
+ /* Делаем выборку */
+ if (PKG_SQL_DML.EXECUTE(ICURSOR => ICURSOR) = 0) then
+ null;
+ end if;
+ /* Обходим выбранные записи */
+ while (PKG_SQL_DML.FETCH_ROWS(ICURSOR => ICURSOR) > 0)
+ loop
+ /* Считываем рег. номер события */
+ PKG_SQL_DML.COLUMN_VALUE_NUM(ICURSOR => ICURSOR, IPOSITION => 1, NVALUE => NCLNEVENTS);
+ /* Считываем статус события */
+ PKG_SQL_DML.COLUMN_VALUE_STR(ICURSOR => ICURSOR, IPOSITION => 3, SVALUE => SDOC_PREF);
+ PKG_SQL_DML.COLUMN_VALUE_STR(ICURSOR => ICURSOR, IPOSITION => 4, SVALUE => SDOC_NUMB);
+ PKG_SQL_DML.COLUMN_VALUE_STR(ICURSOR => ICURSOR, IPOSITION => 14, SVALUE => SEVSTAT);
+ /* Считываем наименование исполнителя */
+ SSENDER := UTL_CLNEVENTS_SENDER_GET(NCLNEVENTS => NCLNEVENTS);
+ /* Добавляем колонки с данными */
+ RDG_ROW := PKG_P8PANELS_VISUAL.TDG_ROW_MAKE(SGROUP => SEVSTAT);
+ /* Заполняем колонки */
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NRN', NVALUE => NCLNEVENTS);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'NCRN', ICURSOR => ICURSOR, NPOSITION => 2);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SEVPREF', SVALUE => SDOC_PREF);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SEVNUMB', SVALUE => SDOC_NUMB);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW,
+ SNAME => 'SPREF_NUMB',
+ SVALUE => SDOC_PREF || '-' || SDOC_NUMB);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW,
+ SNAME => 'NEVTYPE',
+ ICURSOR => ICURSOR,
+ NPOSITION => 5);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
+ SNAME => 'SEVTYPE_CODE',
+ ICURSOR => ICURSOR,
+ NPOSITION => 6);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLD(RROW => RDG_ROW,
+ SNAME => 'DREG_DATE',
+ ICURSOR => ICURSOR,
+ NPOSITION => 7);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLD(RROW => RDG_ROW,
+ SNAME => 'DCHANGE_DATE',
+ ICURSOR => ICURSOR,
+ NPOSITION => 8);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLD(RROW => RDG_ROW,
+ SNAME => 'DEXPIRE_DATE',
+ ICURSOR => ICURSOR,
+ NPOSITION => 9);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLD(RROW => RDG_ROW,
+ SNAME => 'DPLAN_DATE',
+ ICURSOR => ICURSOR,
+ NPOSITION => 10);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW,
+ SNAME => 'NINIT_PERSON',
+ ICURSOR => ICURSOR,
+ NPOSITION => 11);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
+ SNAME => 'SINIT_PERSON',
+ ICURSOR => ICURSOR,
+ NPOSITION => 12);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW,
+ SNAME => 'NEVSTAT',
+ ICURSOR => ICURSOR,
+ NPOSITION => 13);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SEVSTAT_NAME', SVALUE => SEVSTAT);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW,
+ SNAME => 'NSEND_PERSON',
+ ICURSOR => ICURSOR,
+ NPOSITION => 15);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
+ SNAME => 'SSEND_PERSON',
+ ICURSOR => ICURSOR,
+ NPOSITION => 16);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW,
+ SNAME => 'NAGNLIST_RN',
+ ICURSOR => ICURSOR,
+ NPOSITION => 17);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW,
+ SNAME => 'NSEND_DIVISION',
+ ICURSOR => ICURSOR,
+ NPOSITION => 18);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
+ SNAME => 'SSEND_DIVISION',
+ ICURSOR => ICURSOR,
+ NPOSITION => 19);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW,
+ SNAME => 'NSEND_USRGRP',
+ ICURSOR => ICURSOR,
+ NPOSITION => 20);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
+ SNAME => 'SSEND_USRGRP',
+ ICURSOR => ICURSOR,
+ NPOSITION => 21);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
+ SNAME => 'SEVDESCR',
+ ICURSOR => ICURSOR,
+ NPOSITION => 22);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW,
+ SNAME => 'NCLOSED',
+ ICURSOR => ICURSOR,
+ NPOSITION => 23);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
+ SNAME => 'SLINKED_UNIT',
+ ICURSOR => ICURSOR,
+ NPOSITION => 24);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLN(RROW => RDG_ROW,
+ SNAME => 'NLINKED_RN',
+ ICURSOR => ICURSOR,
+ NPOSITION => 25);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SSENDER', SVALUE => SSENDER);
+ /* Если есть свойства документа */
+ if (PKG_CONTVALLOC1S.COUNT_(RCONTAINER => TDP_NAMES) <> 0) then
+ /* Заполняем строку по свойствам документа */
+ RDG_FILL_BY_TDP(RDG => RDG,
+ RDG_ROW => RDG_ROW,
+ TDP_NAMES => TDP_NAMES,
+ TDP_TYPES => TDP_TYPES,
+ NFIRST_NUMB => RDG.RCOL_DEFS.COUNT - PKG_CONTVALLOC1S.COUNT_(RCONTAINER => TDP_NAMES) - 1,
+ NLAST_NUMB => RDG.RCOL_DEFS.COUNT - 2,
+ ICURSOR => ICURSOR,
+ NACTION => 1);
+ end if;
+ /* Добавляем строку в таблицу */
+ 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 => 0);
+ /* Очистка коллекций */
+ PKG_CONTVALLOC1S.PURGE(RCONTAINER => TDP_NAMES);
+ PKG_CONTVALLOC1S.PURGE(RCONTAINER => TDP_TYPES);
+ end CLNEVENTS_DG_GET;
+
+ /* Считывание контрагентов событий с наличием изображения */
+ procedure CLNEVENTS_AGENTS_WIMG_GET
+ (
+ NIDENT in number, -- Идентификатор отобранных записей событий
+ COUT out clob -- Информация о контрагентах с наличием изображения
+ )
+ is
+ RANGLIST AGNLIST%rowtype; -- Запись контрагента
+
+ /* Считывание записи контрагента */
+ function GET_AGNLIST_ID
+ (
+ NRN in number -- Рег. номер контрагента
+ ) return AGNLIST%rowtype -- Запись контрагента
+ is
+ RRESULT AGNLIST%rowtype; -- Запись контрагента
+ begin
+ /* Считываем запись */
+ begin
+ select T.* into RRESULT from AGNLIST T where T.RN = NRN;
+ exception
+ when others then
+ RRESULT := null;
+ end;
+ /* Возвращаем результат */
+ return RRESULT;
+ end GET_AGNLIST_ID;
+ begin
+ /* Начинаем формирование XML */
+ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
+ /* Обходим контрагентов событий, у которых есть изображение */
+ for REC in (select COALESCE(SC_A.RN, SP_A.RN) RN
+ from CLNEVENTS T,
+ CLNCLIENTS SC,
+ AGNLIST SC_A,
+ CLNPERSONS SP,
+ AGNLIST SP_A
+ where T.RN in (select ID from COND_BROKER_IDSMART where IDENT = NIDENT)
+ and ((T.SEND_CLIENT is not null) or (T.SEND_PERSON is not null))
+ and T.SEND_CLIENT = SC.RN(+)
+ and SC.CLIENT_AGENT = SC_A.RN(+)
+ and T.SEND_PERSON = SP.RN(+)
+ and SP.PERS_AGENT = SP_A.RN(+)
+ and ((DBMS_LOB.GETLENGTH(SC_A.IMAGE) <> 0) or (DBMS_LOB.GETLENGTH(SP_A.IMAGE) <> 0))
+ group by COALESCE(SC_A.RN, SP_A.RN))
+ loop
+ /* Считываем запись контрагента */
+ RANGLIST := GET_AGNLIST_ID(NRN => REC.RN);
+ /* Если запись считана */
+ if (RANGLIST.RN is not null) then
+ /* Добавляем информацию о контрагенте */
+ PKG_XFAST.DOWN_NODE(SNAME => 'XAGENTS');
+ /* Мнемокод контрагента */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SAGNABBR');
+ PKG_XFAST.VALUE(SVALUE => RANGLIST.AGNABBR);
+ PKG_XFAST.UP();
+ /* Изображение контрагента */
+ PKG_XFAST.DOWN_NODE(SNAME => 'BIMAGE');
+ PKG_XFAST.VALUE(LBVALUE => RANGLIST.IMAGE);
+ PKG_XFAST.UP();
+ PKG_XFAST.UP();
+ end if;
+ end loop;
+ /* Сериализуем */
+ COUT := PKG_XFAST.SERIALIZE_TO_CLOB();
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ end CLNEVENTS_AGENTS_WIMG_GET;
+
+ /* Загрузка информации о событиях */
+ procedure CLNEVENTS_LOAD
+ (
+ CFILTERS in clob, -- Фильтры
+ CORDERS in clob, -- Сортировки
+ NINCLUDE_ACCOUNTS in number := 1, -- Включать информацию о контрагентах с изображением (0 - нет, 1 - да)
+ COUT out clob -- Сериализованная информация о событиях
+ )
+ is
+ NIDENT PKG_STD.TREF; -- Идентификатор отбора
+ CCLNEVENTS clob; -- Информация о событиях
+ CAGNLIST_WITH_IMAGE clob; -- Информация о контрагентах с изображением
+ begin
+ /* Генерируем идентификатор */
+ NIDENT := GEN_IDENT();
+ /* Считываем данные о событиях */
+ CLNEVENTS_DG_GET(CFILTERS => CFILTERS,
+ CORDERS => CORDERS,
+ COUT => CCLNEVENTS,
+ NIDENT => NIDENT);
+ /* Если требуется включать информацию о контрагентах */
+ if (NINCLUDE_ACCOUNTS = 1) then
+ /* Считываем информацию о контрагентах с наличием изображения */
+ CLNEVENTS_AGENTS_WIMG_GET(NIDENT => NIDENT, COUT => CAGNLIST_WITH_IMAGE);
+ end if;
+ /* Начинаем формирование XML */
+ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
+ /* Открываем корень */
+ PKG_XFAST.DOWN_NODE(SNAME => 'XDATA');
+ /* Открываем события */
+ PKG_XFAST.DOWN_NODE(SNAME => 'XCLNEVENTS');
+ /* Описываем события */
+ PKG_XFAST.VALUE_XML(LCVALUE => CCLNEVENTS);
+ /* Закрываем события */
+ PKG_XFAST.UP();
+ /* Если требуется включать информацию о контрагентах */
+ if (NINCLUDE_ACCOUNTS = 1) then
+ /* Открываем информацию о контрагентах с изображением */
+ PKG_XFAST.DOWN_NODE(SNAME => 'XAGENTS_WITH_IMG');
+ /* Описываем контрагентов */
+ PKG_XFAST.VALUE_XML(LCVALUE => CAGNLIST_WITH_IMAGE);
+ /* Закрываем контрагентов */
+ PKG_XFAST.UP();
+ end if;
+ /* Закрываем корень */
+ PKG_XFAST.UP();
+ /* Сериализуем */
+ COUT := PKG_XFAST.SERIALIZE_TO_CLOB();
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ exception
+ when others then
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end CLNEVENTS_LOAD;
+
+ /* Выбор события по рег. номеру */
+ procedure CLNEVENTS_SELECT
+ (
+ NCLNEVENTS in number, -- Рег. номер события
+ NIDENT out number -- Идентификатор буфера подобранных (списка отмеченных записей, null - не найдено)
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
+ NSELECTLIST PKG_STD.TREF; -- Рег. номер добавленной записи буфера подобранных
+ NCRN PKG_STD.TREF; -- Рег. номер каталога
+
+ /* Считывание каталога события */
+ function CLNEVENTS_CRN_GET
+ (
+ NCLNEVENTS in number -- Рег. номер события
+ ) return number -- Рег. номер каталога события
+ is
+ NRESULT PKG_STD.TREF; -- Рег. номер каталога события
+ begin
+ /* Считываем каталог события */
+ begin
+ select E.CRN into NRESULT from CLNEVENTS E where E.RN = NCLNEVENTS;
+ exception
+ when others then
+ NCRN := null;
+ end;
+ /* Возвращаем результат */
+ return NRESULT;
+ end CLNEVENTS_CRN_GET;
+ begin
+ /* Считываем рег. номер каталога события */
+ NCRN := CLNEVENTS_CRN_GET(NCLNEVENTS => NCLNEVENTS);
+ /* Если каталог считан */
+ if (NCRN is not null) then
+ /* Сформируем идентификатор буфера */
+ if (NIDENT is null) then
+ NIDENT := GEN_IDENT();
+ end if;
+ /* Добавим подобранное в список отмеченных записей */
+ P_SELECTLIST_BASE_INSERT(NIDENT => NIDENT,
+ NCOMPANY => NCOMPANY,
+ NDOCUMENT => NCLNEVENTS,
+ SUNITCODE => SUNIT_CLNEVENTS,
+ SACTIONCODE => null,
+ NCRN => NCRN,
+ NDOCUMENT1 => null,
+ SUNITCODE1 => null,
+ SACTIONCODE1 => null,
+ NRN => NSELECTLIST);
+ end if;
+ end CLNEVENTS_SELECT;
+
+ /* Считывание следующего номера события */
+ procedure CLNEVENTS_NEXTNUMB_GET
+ (
+ SPREFIX in varchar2, -- Префикс события
+ SEVENT_NUMB out varchar2 -- Номер события
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
+ begin
+ /* Считываем следующий номер */
+ SEVENT_NUMB := GET_CLNEVENTS_NEXTNUMB(NCOMPANY => NCOMPANY, SPREF => SPREFIX);
+ end CLNEVENTS_NEXTNUMB_GET;
+
+ /* Считывание параметров события */
+ procedure CLNEVENTS_GET
+ (
+ NCLNEVENTS in number, -- Рег. номер события
+ COUT out clob -- Данные о записи события
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
+ RCLNEVENTS CLNEVENTS%rowtype; -- Запись события
+ SCRN PKG_STD.TSTRING; -- Наименование каталога
+ STYPE PKG_STD.TSTRING; -- Мнемокод типа события
+ SSTATUS PKG_STD.TSTRING; -- Мнемокод статуса типа события
+ SINIT_PERSON PKG_STD.TSTRING; -- Мнемокод инициатора сотрудника
+ SCLIENT_CLIENT PKG_STD.TSTRING; -- Мнемокод клиента (организации)
+ SCLIENT_PERSON PKG_STD.TSTRING; -- Мнемокод клиента (сотрудника)
+ SSEND_CLIENT PKG_STD.TSTRING; -- Мнемокод направления (организация)
+ SSEND_DIVISION PKG_STD.TSTRING; -- Мнемокод направления (подразделение)
+ SSEND_POST PKG_STD.TSTRING; -- Мнемокод направления (должность)
+ SSEND_PERFORM PKG_STD.TSTRING; -- Мнемокод направления (должность в подразделении)
+ SSEND_PERSON PKG_STD.TSTRING; -- Мнемокод направления (сотрудник)
+ SSEND_STAFFGRP PKG_STD.TSTRING; -- Мнемокод направления (нештатная структура)
+ SSEND_USER_GROUP PKG_STD.TSTRING; -- Мнемокод направления (группа пользователей)
+ SSEND_USER_NAME PKG_STD.TSTRING; -- Мнемокод направления (группа пользователь)
+ SDP_FORMATTED_ID PKG_STD.TSTRING; -- Отформатированный ID свойства документа
+ begin
+ /* Считываем запись события */
+ RCLNEVENTS := UTL_CLNEVENTS_GET(NCOMPANY => NCOMPANY, NCLNEVENTS => NCLNEVENTS);
+ /* Считываем наименование каталога */
+ FIND_ACATALOG_RN(NFLAG_SMART => 0,
+ NCOMPANY => NCOMPANY,
+ NVERSION => null,
+ SUNITCODE => SUNIT_CLNEVENTS,
+ NRN => RCLNEVENTS.CRN,
+ SNAME => SCRN);
+ /* Считываем мнемокоды связанных записей */
+ UTL_CLNEVENTS_JOINS_GET(NCLNEVENTS => RCLNEVENTS.RN,
+ STYPE => STYPE,
+ SSTATUS => SSTATUS,
+ SINIT_PERSON => SINIT_PERSON,
+ SCLIENT_CLIENT => SCLIENT_CLIENT,
+ SCLIENT_PERSON => SCLIENT_PERSON,
+ SSEND_CLIENT => SSEND_CLIENT,
+ SSEND_DIVISION => SSEND_DIVISION,
+ SSEND_POST => SSEND_POST,
+ SSEND_PERFORM => SSEND_PERFORM,
+ SSEND_PERSON => SSEND_PERSON,
+ SSEND_STAFFGRP => SSEND_STAFFGRP,
+ SSEND_USER_GROUP => SSEND_USER_GROUP,
+ SSEND_USER_NAME => SSEND_USER_NAME);
+ /* Формируем данные на выход */
+ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
+ PKG_XFAST.DOWN_NODE(SNAME => 'XDATA');
+ PKG_XFAST.DOWN_NODE(SNAME => 'XEVENT');
+ PKG_XFAST.ATTR(SNAME => 'SCRN', SVALUE => SCRN);
+ PKG_XFAST.ATTR(SNAME => 'SPREF', SVALUE => RCLNEVENTS.EVENT_PREF);
+ PKG_XFAST.ATTR(SNAME => 'SNUMB', SVALUE => RCLNEVENTS.EVENT_NUMB);
+ PKG_XFAST.ATTR(SNAME => 'STYPE', SVALUE => STYPE);
+ PKG_XFAST.ATTR(SNAME => 'SSTATUS', SVALUE => SSTATUS);
+ PKG_XFAST.ATTR(SNAME => 'SDESCRIPTION', SVALUE => RCLNEVENTS.EVENT_DESCR);
+ PKG_XFAST.ATTR(SNAME => 'SCLIENT_CLIENT', SVALUE => SCLIENT_CLIENT);
+ PKG_XFAST.ATTR(SNAME => 'SCLIENT_PERSON', SVALUE => SCLIENT_PERSON);
+ PKG_XFAST.ATTR(SNAME => 'SPLAN_DATE', SVALUE => TO_CHAR(RCLNEVENTS.PLAN_DATE, SDATE_PATTERN_HH_MI));
+ PKG_XFAST.ATTR(SNAME => 'SINIT_PERSON', SVALUE => SINIT_PERSON);
+ PKG_XFAST.ATTR(SNAME => 'SINIT_AUTHID', SVALUE => RCLNEVENTS.INIT_AUTHID);
+ PKG_XFAST.ATTR(SNAME => 'SREASON', SVALUE => RCLNEVENTS.REASON);
+ PKG_XFAST.ATTR(SNAME => 'SSEND_CLIENT', SVALUE => SSEND_CLIENT);
+ PKG_XFAST.ATTR(SNAME => 'SSEND_DIVISION', SVALUE => SSEND_DIVISION);
+ PKG_XFAST.ATTR(SNAME => 'SSEND_POST', SVALUE => SSEND_POST);
+ PKG_XFAST.ATTR(SNAME => 'SSEND_PERFORM', SVALUE => SSEND_PERFORM);
+ PKG_XFAST.ATTR(SNAME => 'SSEND_PERSON', SVALUE => SSEND_PERSON);
+ PKG_XFAST.ATTR(SNAME => 'SSEND_STAFFGRP', SVALUE => SSEND_STAFFGRP);
+ PKG_XFAST.ATTR(SNAME => 'SSEND_USER_GROUP', SVALUE => SSEND_USER_GROUP);
+ PKG_XFAST.ATTR(SNAME => 'SSEND_USER_NAME', SVALUE => SSEND_USER_NAME);
+ /* Цикл добавления свойств документа события */
+ for REC in (select DPV.*,
+ DP.FORMAT,
+ DP.DATA_SUBTYPE
+ from CLEVTPROPS CEP,
+ CLNEVNTYPES CET,
+ DOCS_PROPS_VALS DPV,
+ DOCS_PROPS DP
+ where CEP.PRN = CET.RN
+ and CET.EVNTYPE_CODE = STYPE
+ and CEP.COMPANY = NCOMPANY
+ and CEP.PROPERTY = DPV.DOCS_PROP_RN(+)
+ and CEP.PROPERTY = DP.RN(+)
+ and DPV.UNIT_RN = NCLNEVENTS)
+ loop
+ /* Считываем отформатированный ID свойства документа */
+ SDP_FORMATTED_ID := UTL_DP_FORMATTED_ID_GET(NTYPE => REC.FORMAT, NRN => REC.DOCS_PROP_RN);
+ /* Исходим от типа свойства документа */
+ case
+ /* Строка */
+ when (REC.FORMAT = 0) then
+ PKG_XFAST.ATTR(SNAME => SDP_FORMATTED_ID, SVALUE => REC.STR_VALUE);
+ /* Число */
+ when ((REC.FORMAT = 1) or (REC.FORMAT = 3)) then
+ PKG_XFAST.ATTR(SNAME => SDP_FORMATTED_ID, NVALUE => REC.NUM_VALUE);
+ /* Дата */
+ else
+ PKG_XFAST.ATTR(SNAME => SDP_FORMATTED_ID, DVALUE => REC.DATE_VALUE, IFORMAT => REC.DATA_SUBTYPE);
+ end case;
+ end loop;
+ PKG_XFAST.UP();
+ PKG_XFAST.UP();
+ /* Сериализуем в CLOB */
+ COUT := PKG_XFAST.SERIALIZE_TO_CLOB();
+ PKG_XFAST.EPILOGUE();
+ exception
+ when others then
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end CLNEVENTS_GET;
+
+ /* Инициализация параметров события */
+ procedure CLNEVENTS_INIT
+ (
+ SEVENT_TYPE in varchar2, -- Мнемокод типа события
+ SPREF out varchar2, -- Префикс события
+ SNUMB out varchar2, -- Номер события
+ SINIT_PERSON out varchar2, -- Сотрудник - инициатор
+ SINIT_AUTHNAME out varchar2 -- Пользователь - инициатор
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
+ SOWNER_AGENT_RN PKG_STD.TSTRING; -- Рег. номер работодателя сотрудника
+ STAB_PREF PKG_STD.TSTRING; -- Префикс табельного номера сотрудника
+ STAB_NUMB PKG_STD.TSTRING; -- Таблеьный номер сотрудника
+
+ /* Считывание префикса типа события */
+ function CLNEVNTYPES_PREF_GET
+ (
+ NCOMPANY in number, -- Рег. номер организации
+ SEVENT_TYPE in varchar2 -- Мнемокод типа события
+ ) return varchar2 -- Префикс типа события
+ is
+ SRESULT PKG_STD.TSTRING; -- Префикс типа события
+ begin
+ /* Считываем префикс типа события */
+ begin
+ select T.EVENT_PREF
+ into SRESULT
+ from CLNEVNTYPES T
+ where T.EVNTYPE_CODE = SEVENT_TYPE
+ and T.COMPANY = NCOMPANY;
+ exception
+ when NO_DATA_FOUND then
+ SRESULT := null;
+ end;
+ /* Возвращаем результат */
+ return SRESULT;
+ end CLNEVNTYPES_PREF_GET;
+ begin
+ /* Считываем префикс из параметра */
+ SPREF := GET_OPTIONS_STR(SCODE => 'EventPrefix', NCOMP_VERS => NCOMPANY);
+ /* Если префикс не указан в параметре и задан тип события */
+ if ((SPREF is null) and (SEVENT_TYPE is not null)) then
+ /* Считываем префикс типа события */
+ SPREF := CLNEVNTYPES_PREF_GET(NCOMPANY => NCOMPANY, SEVENT_TYPE => SEVENT_TYPE);
+ end if;
+ /* Если префикс определен */
+ if (SPREF is not null) then
+ /* Считываем номер */
+ SNUMB := GET_CLNEVENTS_NEXTNUMB(NCOMPANY => NCOMPANY, SPREF => SPREF);
+ end if;
+ /* Считываем сотрудника текущего пользователя */
+ FIND_PERSON_AUTHID(SPERSON => SINIT_PERSON,
+ SOWNER_AGENT => SOWNER_AGENT_RN,
+ STAB_PREF => STAB_PREF,
+ STAB_NUMB => STAB_NUMB);
+ /* Считываем имя пользователя */
+ FIND_USERLIST_BY_AUTHID(NFLAG_SMART => 1, SAUTHID => UTILIZER, SNAME => SINIT_AUTHNAME);
+ end CLNEVENTS_INIT;
+
+ /* Добавление значений свойств документа */
+ procedure CLNEVENTS_PROPS_INSERT
+ (
+ NCLNEVENTS in number, -- Рег. номер события
+ SEVENT_TYPE in varchar2, -- Мнемокод типа события
+ CPROPS in clob -- Свойства документа
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
+ NDP_VER PKG_STD.TREF; -- Версия раздела "Свойства документов"
+ RDOC PKG_XPATH.TDOCUMENT; -- Документ XML
+ RNODE_ROOT PKG_XPATH.TNODE; -- Корневой узел
+ RNODE_PROPS PKG_XPATH.TNODE; -- Узел "props"
+ RNODE_PROP PKG_XPATH.TNODE; -- Узел свойства (в формате "_<РЕГ_НОМЕР>")
+ SPROP_ID PKG_STD.TSTRING; -- Код свойства документа из XML (в формате "_<РЕГ_НОМЕР>")
+ SPROP_CODE PKG_STD.TSTRING; -- Мнемокод свойства документа
+ SSTR_VALUE PKG_STD.TSTRING := null; -- Строковое значение
+ NNUM_VALUE PKG_STD.TLNUMBER := null; -- Числовое значение
+ DDATE_VALUE PKG_STD.TLDATE := null; -- Дата
+ begin
+ /* Считываем версию организации */
+ FIND_VERSION_BY_COMPANY(NCOMPANY => NCOMPANY, SUNITCODE => SUNIT_DP, NVERSION => NDP_VER);
+ /* Загрузка XML */
+ RDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => BLOB2CLOB(LBDATA => BASE64_DECODE(LCSRCE => CPROPS),
+ SCHARSET => PKG_CHARSET.CHARSET_UTF_()));
+ /* Разбор XML */
+ RNODE_ROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => RDOC);
+ RNODE_PROPS := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => RNODE_ROOT, SPATTERN => 'props');
+ /* Если найдено событие */
+ if (PKG_DOCS_PROPS_VALS.PROLOGUE(SUNITCODE => SUNIT_CLNEVENTS, NDOCUMENT => NCLNEVENTS)) then
+ /* Цикл по свойствам документа типа события */
+ for REC in (select CEP.RN,
+ CEP.PROPERTY,
+ DP.FORMAT,
+ DP.NUM_WIDTH,
+ DP.NUM_PRECISION,
+ DP.STR_WIDTH
+ from CLEVTPROPS CEP,
+ CLNEVNTYPES CET,
+ DOCS_PROPS DP
+ where CEP.PRN = CET.RN
+ and CET.EVNTYPE_CODE = SEVENT_TYPE
+ and CEP.COMPANY = NCOMPANY
+ and CEP.PROPERTY = DP.RN(+))
+ loop
+ /* Считываем код свойства */
+ SPROP_ID := UTL_DP_FORMATTED_ID_GET(NTYPE => REC.FORMAT, NRN => REC.PROPERTY);
+ SPROP_CODE := GET_DOCS_PROPS_CODE_ID(NFLAG_SMART => 0, NPROP_ID => REC.PROPERTY);
+ /* Если в XML найдено новое значение свойства */
+ if (PKG_XPATH.IS_NULL(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => RNODE_PROPS, SPATTERN => SPROP_ID)) =
+ false) then
+ /* Считываем узел свойства документа */
+ RNODE_PROP := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => RNODE_PROPS, SPATTERN => SPROP_ID);
+ /* Исходим от типа свойства документа */
+ case REC.FORMAT
+ /* Строка */
+ when 0 then
+ SSTR_VALUE := PKG_XPATH.VALUE(RNODE => RNODE_PROP);
+ /* Проверка длины строки */
+ if (LENGTH(SSTR_VALUE) > REC.STR_WIDTH) then
+ P_EXCEPTION(0,
+ 'Недопустимый формат значения для свойства "%s" (ожидалась строка длиной "%s" символов).',
+ SPROP_CODE,
+ TO_CHAR(REC.STR_WIDTH));
+ end if;
+ /* Число */
+ when 1 then
+ NNUM_VALUE := PKG_XPATH.VALUE_NUM(RNODE => RNODE_PROP);
+ /* Проверка длины и точности числа */
+ if ((LENGTH(TO_CHAR(TRUNC(ABS(NNUM_VALUE)))) > (REC.NUM_WIDTH - REC.NUM_PRECISION)) or
+ (ROUND(NNUM_VALUE, REC.NUM_PRECISION) <> NNUM_VALUE)) then
+ P_EXCEPTION(0,
+ 'Недопустимый формат значения для свойства "%s" (ожидалось число размерностью "%s" с точностью "%s").',
+ SPROP_CODE,
+ TO_CHAR(REC.NUM_WIDTH),
+ TO_CHAR(REC.NUM_PRECISION));
+ end if;
+ /* Дата */
+ when 2 then
+ begin
+ DDATE_VALUE := PKG_XPATH.VALUE_DATE(RNODE => RNODE_PROP);
+ exception
+ when others then
+ P_EXCEPTION(0, 'Недопустимый формат даты.');
+ end;
+ /* Время */
+ else
+ NNUM_VALUE := TO_DATE(PKG_XPATH.VALUE(RNODE => RNODE_PROP), 'HH24:MI:SS') -
+ TO_DATE('00:00:00', 'HH24:MI:SS');
+ end case;
+ end if;
+ /* Добавление значения свойства */
+ PKG_DOCS_PROPS_VALS.APPEND(SPROPERTY => SPROP_CODE,
+ SSTR_VALUE => SSTR_VALUE,
+ NNUM_VALUE => NNUM_VALUE,
+ DDATE_VALUE => DDATE_VALUE);
+ /* Очищаем значения */
+ SSTR_VALUE := null;
+ NNUM_VALUE := null;
+ DDATE_VALUE := null;
+ end loop;
+ PKG_DOCS_PROPS_VALS.EPILOGUE;
+ end if;
+ end CLNEVENTS_PROPS_INSERT;
+
+ /* Добавление события */
+ procedure CLNEVENTS_INSERT
+ (
+ SCRN in varchar2, -- Наименование каталога
+ SPREF in varchar2, -- Префикс события
+ SNUMB in varchar2, -- Номер события
+ STYPE in varchar2, -- Тип события
+ SSTATUS in varchar2, -- Статус события
+ SPLAN_DATE in varchar2, -- Дата планируемого начала работ
+ SINIT_PERSON in varchar2, -- Инициатор
+ SCLIENT_CLIENT in varchar2, -- Клиент-инициатор
+ SCLIENT_PERSON in varchar2, -- Сотрудник-инициатор
+ SDESCRIPTION in varchar2, -- Описание
+ SREASON in varchar2, -- Причина
+ CPROPS in clob -- Свойства документа
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
+ NCRN PKG_STD.TREF; -- Рег. номер каталога
+ NCLNEVENTS PKG_STD.TREF; -- Рег. номер события
+ begin
+ /* Считываем рег. номер каталога */
+ FIND_ACATALOG_NAME(NFLAG_SMART => 0,
+ NCOMPANY => NCOMPANY,
+ NVERSION => null,
+ SUNITCODE => SUNIT_CLNEVENTS,
+ SNAME => SCRN,
+ NRN => NCRN);
+ /* Добавляем событие */
+ P_CLNEVENTS_INSERT(NCOMPANY => NCOMPANY,
+ NCRN => NCRN,
+ SEVENT_PREF => SPREF,
+ SEVENT_NUMB => SNUMB,
+ SEVENT_TYPE => STYPE,
+ SEVENT_STAT => SSTATUS,
+ DPLAN_DATE => TO_DATE(SPLAN_DATE, SDATE_PATTERN_HH_MI),
+ SINIT_PERSON => SINIT_PERSON,
+ SCLIENT_CLIENT => SCLIENT_CLIENT,
+ SCLIENT_PERSON => SCLIENT_PERSON,
+ SSEND_CLIENT => null,
+ SSEND_DIVISION => null,
+ SSEND_POST => null,
+ SSEND_PERFORM => null,
+ SSEND_PERSON => null,
+ SSEND_STAFFGRP => null,
+ SSEND_USER_GROUP => null,
+ SSEND_USER_NAME => null,
+ SEVENT_DESCR => SDESCRIPTION,
+ SREASON => SREASON,
+ NRN => NCLNEVENTS);
+ /* Добавляем значения свойств документа */
+ CLNEVENTS_PROPS_INSERT(NCLNEVENTS => NCLNEVENTS, SEVENT_TYPE => STYPE, CPROPS => CPROPS);
+ end CLNEVENTS_INSERT;
+
+ /* Исправление события */
+ procedure CLNEVENTS_UPDATE
+ (
+ NCLNEVENTS in number, -- Рег. номер события
+ SCLIENT_CLIENT in varchar2, -- Клиент-инициатор
+ SCLIENT_PERSON in varchar2, -- Сотрудник-инициатор
+ SDESCRIPTION in varchar2, -- Описание
+ CPROPS in clob -- Свойства
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
+ RCLNEVENTS CLNEVENTS%rowtype; -- Запись события
+ STYPE PKG_STD.TSTRING; -- Мнемокод типа события
+ SSTATUS PKG_STD.TSTRING; -- Мнемокод статуса типа события
+ SINIT_PERSON PKG_STD.TSTRING; -- Мнемокод инициатора сотрудника
+ SCLIENT_CLIENT_TMP PKG_STD.TSTRING; -- Мнемокод клиента (организации) (не требуется)
+ SCLIENT_PERSON_TMP PKG_STD.TSTRING; -- Мнемокод клиента (сотрудника) (не требуется)
+ SSEND_CLIENT PKG_STD.TSTRING; -- Мнемокод направления (организация)
+ SSEND_DIVISION PKG_STD.TSTRING; -- Мнемокод направления (подразделение)
+ SSEND_POST PKG_STD.TSTRING; -- Мнемокод направления (должность)
+ SSEND_PERFORM PKG_STD.TSTRING; -- Мнемокод направления (должность в подразделении)
+ SSEND_PERSON PKG_STD.TSTRING; -- Мнемокод направления (сотрудник)
+ SSEND_STAFFGRP PKG_STD.TSTRING; -- Мнемокод направления (нештатная структура)
+ SSEND_USER_GROUP PKG_STD.TSTRING; -- Мнемокод направления (группа пользователей)
+ SSEND_USER_NAME PKG_STD.TSTRING; -- Мнемокод направления (группа пользователь)
+ NBAN_UPDATE PKG_STD.TNUMBER; -- Признак запрета исправления события в точке маршрута (0 - нет, 1 - да)
+ begin
+ /* Считываем запись события */
+ RCLNEVENTS := UTL_CLNEVENTS_GET(NCOMPANY => NCOMPANY, NCLNEVENTS => NCLNEVENTS);
+ /* Считываем мнемокоды связанных записей */
+ UTL_CLNEVENTS_JOINS_GET(NCLNEVENTS => RCLNEVENTS.RN,
+ STYPE => STYPE,
+ SSTATUS => SSTATUS,
+ SINIT_PERSON => SINIT_PERSON,
+ SCLIENT_CLIENT => SCLIENT_CLIENT_TMP,
+ SCLIENT_PERSON => SCLIENT_PERSON_TMP,
+ SSEND_CLIENT => SSEND_CLIENT,
+ SSEND_DIVISION => SSEND_DIVISION,
+ SSEND_POST => SSEND_POST,
+ SSEND_PERFORM => SSEND_PERFORM,
+ SSEND_PERSON => SSEND_PERSON,
+ SSEND_STAFFGRP => SSEND_STAFFGRP,
+ SSEND_USER_GROUP => SSEND_USER_GROUP,
+ SSEND_USER_NAME => SSEND_USER_NAME);
+ /* Проверка возможности редактирования события в текущем статусе */
+ NBAN_UPDATE := UTL_CLNEVENTS_BAN_UPDATE_GET(NCOMPANY => NCOMPANY, NCLNEVENTS => NCLNEVENTS);
+ /* Если редактирование запрещено */
+ if (NBAN_UPDATE = 1) then
+ /* Формируем исключение */
+ P_EXCEPTION(0, 'События в статусе "%s" запрещено редактировать.', SSTATUS);
+ end if;
+ /* Исправляем событие */
+ P_CLNEVENTS_UPDATE(NCOMPANY => NCOMPANY,
+ NRN => NCLNEVENTS,
+ NREMOTE_ACCESS => null,
+ SCHANGE_DATE => STAMP2S(RCLNEVENTS.CHANGE_DATE),
+ SEVENT_TYPE => STYPE,
+ SEVENT_STAT => SSTATUS,
+ SCLIENT_CLIENT => SCLIENT_CLIENT,
+ SCLIENT_PERSON => SCLIENT_PERSON,
+ SSEND_CLIENT => SSEND_CLIENT,
+ SSEND_DIVISION => SSEND_DIVISION,
+ SSEND_POST => SSEND_POST,
+ SSEND_PERFORM => SSEND_PERFORM,
+ SSEND_PERSON => SSEND_PERSON,
+ SSEND_STAFFGRP => SSEND_STAFFGRP,
+ SSEND_USER_GROUP => SSEND_USER_GROUP,
+ SSEND_USER_NAME => SSEND_USER_NAME,
+ SEVENT_DESCR => SDESCRIPTION);
+ /* Обновляем значения свойств документа */
+ CLNEVENTS_PROPS_INSERT(NCLNEVENTS => NCLNEVENTS, SEVENT_TYPE => STYPE, CPROPS => CPROPS);
+ end CLNEVENTS_UPDATE;
+
+ /* Удаление события */
+ procedure CLNEVENTS_DELETE
+ (
+ NCLNEVENTS in number -- Рег. номер события
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
+ begin
+ /* Удаляем событие */
+ P_CLNEVENTS_DELETE(NCOMPANY => NCOMPANY, NRN => NCLNEVENTS);
+ /* Удаляем значения свойств документа */
+ PKG_DOCS_PROPS_VALS.AFTER_DELETE(SUNITCODE => SUNIT_CLNEVENTS, NDOCUMENT => NCLNEVENTS);
+ end CLNEVENTS_DELETE;
+
+ /* Изменение статуса события */
+ procedure CLNEVENTS_STATE_CHANGE
+ (
+ NIDENT in out number, -- Рег. номер отмеченных записей
+ NSTEP in out number, -- Шаг выполнения (0-прекратить выполнение, 1,2..- номер шага)
+ NEVENT in out number, -- Событие, для отбора исполнителей
+ SEVENT_TYPE in out varchar2, -- Тип события, для отбора точек перехода и исполнителей
+ SEVENT_STAT in out varchar2, -- Cтатус события, текущий - для отбора точек перехода, следующий - для отбора исполнителей
+ SINIT_PERSON in out varchar2, -- Инициатор–сотрудник, для отбора исполнителей
+ SINIT_AUTHNAME in out varchar2, -- Инициатор–пользователь, для отбора исполнителей
+ SCLIENT_CLIENT in out varchar2, -- Клиент–организация, для отбора исполнителей
+ SCLIENT_PERSON in out varchar2, -- Клиент–сотрудник, для отбора исполнителей
+ NPOINT in out number, -- Текущая точка маршрута
+ NPASS in number, -- Точка перехода
+ NSELECT_EXEC out number, -- Признак необходимости выбора исполнителя
+ SEXECUTEMETHOD out varchar2, -- Мнемокод метода
+ SSEND_CLIENT in varchar2, -- Направить – организация
+ SSEND_DIVISION in varchar2, -- Направить – подразделение
+ SSEND_POST in varchar2, -- Направить – должность
+ SSEND_PERFORM in varchar2, -- Направить – должность в подразделении
+ SSEND_PERSON in varchar2, -- Направить – сотрудник
+ SSEND_STAFFGRP in varchar2, -- Направить – нештатная структура
+ SSEND_USER_GROUP in varchar2, -- Направить – группа пользователей
+ SSEND_USER_NAME in varchar2, -- Направить – пользователь
+ NSEND_PREDEFINED_EXEC in number, -- Переадресация
+ NSEND_PREDEFINED_PROC in number, -- Процедура выбора предопределенного исполнителя
+ SNEXT_STAT in varchar2 := null, -- Следующий статус (при переносе события)
+ SNOTE_HEADER in varchar2 := null, -- Заголовок примечания (при наличии)
+ SNOTE in varchar2 := null -- Примечание (при наличии)
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
+ NTMP PKG_STD.TREF; -- Заглушка
+ NSTEP_CURRENT PKG_STD.TREF; -- Номер текущего шага
+ NNOTE_RN PKG_STD.TREF; -- Рег. номер добавленного примечания
+
+ /* Считывание рег. номер точки маршрута */
+ function EVRTPTPASS_NEXT_GET
+ (
+ NCOMPANY in number, -- Рег. номер организации
+ SEVENT_TYPE in varchar2, -- Мнемокод типа события
+ NPOINT in number, -- Рег. номер текущей точки
+ SEVENT_STAT in varchar2, -- Мнемокод текущего статуса события
+ SNEXT_STAT in varchar2 -- Мнемокод следующего статуса события
+ ) return number -- Рег. номер точки перехода
+ is
+ NNEXT_POINT PKG_STD.TREF; -- Рег. номер следующей точки
+ NRESULT PKG_STD.TREF; -- Рег. номер точки перехода
+ begin
+ /* Определение следующей точки маршрута */
+ FIND_EVRPOINTS_BY_STATUS(NFLAG_SMART => 0,
+ NFLAG_OPTION => 0,
+ NCOMPANY => NCOMPANY,
+ SEVENT_TYPE => SEVENT_TYPE,
+ SEVENT_STATUS => SNEXT_STAT,
+ NRN => NNEXT_POINT);
+ /* Считывание рег. номера точки перехода */
+ begin
+ select T.RN
+ into NRESULT
+ from EVRTPTPASS T
+ where T.PRN = NPOINT
+ and T.NEXT_POINT = NNEXT_POINT;
+ exception
+ when others then
+ P_EXCEPTION(0,
+ 'Ошибка считывания точки перехода из статуса "%s" в статус "%s".',
+ SEVENT_STAT,
+ SNEXT_STAT);
+ end;
+ /* Возвращаем результат */
+ return NRESULT;
+ end EVRTPTPASS_NEXT_GET;
+ begin
+ /* Определяем номер текущего шага */
+ NSTEP_CURRENT := NSTEP;
+ /* Если это первый шаг */
+ if (NSTEP_CURRENT = 1) then
+ /* Генерируем идентификатор отмеченных записей */
+ NIDENT := GEN_IDENT();
+ /* Добавляем запись события в отмеченные */
+ P_SELECTLIST_BASE_INSERT(NIDENT => NIDENT,
+ NCOMPANY => NCOMPANY,
+ NDOCUMENT => NEVENT,
+ SUNITCODE => SUNIT_CLNEVENTS,
+ SACTIONCODE => SSELECTLIST_ACTION,
+ NCRN => null,
+ NDOCUMENT1 => null,
+ SUNITCODE1 => null,
+ SACTIONCODE1 => null,
+ NRN => NTMP);
+ end if;
+ /* Изменяем статус события */
+ P_CLNEVENTS_CHANGE_STATE_WEB(NCOMPANY => NCOMPANY,
+ NIDENT => NIDENT,
+ NSTEP => NSTEP,
+ NEVENT => NEVENT,
+ SEVENT_TYPE => SEVENT_TYPE,
+ SEVENT_STAT => SEVENT_STAT,
+ SINIT_PERSON => SINIT_PERSON,
+ SINIT_AUTHNAME => SINIT_AUTHNAME,
+ SCLIENT_CLIENT => SCLIENT_CLIENT,
+ SCLIENT_PERSON => SCLIENT_PERSON,
+ NPOINT => NPOINT,
+ NPASS => NPASS,
+ NSELECT_EXEC => NSELECT_EXEC,
+ SEXECUTEMETHOD => SEXECUTEMETHOD,
+ SSEND_CLIENT => SSEND_CLIENT,
+ SSEND_DIVISION => SSEND_DIVISION,
+ SSEND_POST => SSEND_POST,
+ SSEND_PERFORM => SSEND_PERFORM,
+ SSEND_PERSON => SSEND_PERSON,
+ SSEND_STAFFGRP => SSEND_STAFFGRP,
+ SSEND_USER_GROUP => SSEND_USER_GROUP,
+ SSEND_USER_NAME => SSEND_USER_NAME,
+ NSEND_PREDEFINED_EXEC => NSEND_PREDEFINED_EXEC,
+ NSEND_PREDEFINED_PROC => NSEND_PREDEFINED_PROC);
+ /* Если это первый шаг и уже известен следующий статус */
+ if ((NSTEP_CURRENT = 1) and (SNEXT_STAT is not null)) then
+ /* Сразу выполняем следующее действие */
+ P_CLNEVENTS_CHANGE_STATE_WEB(NCOMPANY => NCOMPANY,
+ NIDENT => NIDENT,
+ NSTEP => NSTEP,
+ NEVENT => NEVENT,
+ SEVENT_TYPE => SEVENT_TYPE,
+ SEVENT_STAT => SEVENT_STAT,
+ SINIT_PERSON => SINIT_PERSON,
+ SINIT_AUTHNAME => SINIT_AUTHNAME,
+ SCLIENT_CLIENT => SCLIENT_CLIENT,
+ SCLIENT_PERSON => SCLIENT_PERSON,
+ NPOINT => NPOINT,
+ NPASS => EVRTPTPASS_NEXT_GET(NCOMPANY => NCOMPANY,
+ SEVENT_TYPE => SEVENT_TYPE,
+ NPOINT => NPOINT,
+ SEVENT_STAT => SEVENT_STAT,
+ SNEXT_STAT => SNEXT_STAT),
+ NSELECT_EXEC => NSELECT_EXEC,
+ SEXECUTEMETHOD => SEXECUTEMETHOD,
+ SSEND_CLIENT => SSEND_CLIENT,
+ SSEND_DIVISION => SSEND_DIVISION,
+ SSEND_POST => SSEND_POST,
+ SSEND_PERFORM => SSEND_PERFORM,
+ SSEND_PERSON => SSEND_PERSON,
+ SSEND_STAFFGRP => SSEND_STAFFGRP,
+ SSEND_USER_GROUP => SSEND_USER_GROUP,
+ SSEND_USER_NAME => SSEND_USER_NAME,
+ NSEND_PREDEFINED_EXEC => NSEND_PREDEFINED_EXEC,
+ NSEND_PREDEFINED_PROC => NSEND_PREDEFINED_PROC);
+ /* Указываем следующий статус */
+ SEVENT_STAT := SNEXT_STAT;
+ end if;
+ /* Если это последний шаг */
+ if (NSTEP_CURRENT = 3) then
+ /* Если добавлено примечание */
+ if (SNOTE is not null) then
+ P_CLNEVNOTES_INSERT(NCOMPANY => NCOMPANY,
+ NPRN => NEVENT,
+ SNOTE_HEADER => SNOTE_HEADER,
+ SNOTE => SNOTE,
+ NRN => NNOTE_RN);
+ end if;
+ /* Очищаем отмеченные записи */
+ P_SELECTLIST_CLEAR(NIDENT => NIDENT);
+ end if;
+ exception
+ when others then
+ /* Если список отмеченных есть */
+ if (NIDENT is not null) then
+ /* Удаляем в автономной транзакции */
+ P_SELECTLIST_CLEAR_AT(NIDENT => NIDENT);
+ end if;
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end CLNEVENTS_STATE_CHANGE;
+
+ /* Переадресация события */
+ procedure CLNEVENTS_SEND
+ (
+ NIDENT in out number, -- Рег. номер отмеченных записей
+ NSTEP in out number, -- Шаг выполнения
+ NEVENT in out number, -- Событие, для отбора исполнителей
+ SEVENT_TYPE in out varchar2, -- Тип события, для отбора точек перехода и исполнителей
+ SEVENT_STAT in out varchar2, -- Текущий статус события, для отбора исполнителей
+ SINIT_PERSON in out varchar2, -- Инициатор–сотрудник, для отбора исполнителей
+ SINIT_AUTHNAME in out varchar2, -- Инициатор–пользователь, для отбора исполнителей
+ SCLIENT_CLIENT in out varchar2, -- Клиент–организация, для отбора исполнителей
+ SCLIENT_PERSON in out varchar2, -- Клиент–сотрудник, для отбора исполнителей
+ SEXECUTEMETHOD out varchar2, -- Мнемокод метода
+ SSEND_CLIENT in varchar2, -- Направить – организация
+ SSEND_DIVISION in varchar2, -- Направить – подразделение
+ SSEND_POST in varchar2, -- Направить – должность
+ SSEND_PERFORM in varchar2, -- Направить – должность в подразделении
+ SSEND_PERSON in out varchar2, -- Направить – сотрудник
+ SSEND_STAFFGRP in varchar2, -- Направить – нештатная структура
+ SSEND_USER_GROUP in varchar2, -- Направить – группа пользователей
+ SSEND_USER_NAME in out varchar2, -- Направить – пользователь
+ NSEND_PREDEFINED_EXEC in number, -- Переадресация
+ NSEND_PREDEFINED_PROC in number, -- Процедура выбора предопределенного исполнителя
+ SNOTE_HEADER in varchar2 := null, -- Заголовок примечания (при наличии)
+ SNOTE in varchar2 := null -- Примечание (при наличии)
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
+ NTMP PKG_STD.TREF; -- Заглушка
+ NSTEP_CURRENT PKG_STD.TREF; -- Номер текущего шага
+ NNOTE_RN PKG_STD.TREF; -- Рег. номер добавленного примечания
+ begin
+ /* Определяем номер текущего шага */
+ NSTEP_CURRENT := NSTEP;
+ /* Если это первый шаг */
+ if (NSTEP_CURRENT = 1) then
+ /* Генерируем идентификатор отмеченных записей */
+ NIDENT := GEN_IDENT();
+ /* Добавляем запись события в отмеченные */
+ P_SELECTLIST_BASE_INSERT(NIDENT => NIDENT,
+ NCOMPANY => NCOMPANY,
+ NDOCUMENT => NEVENT,
+ SUNITCODE => SUNIT_CLNEVENTS,
+ SACTIONCODE => SSELECTLIST_ACTION,
+ NCRN => null,
+ NDOCUMENT1 => null,
+ SUNITCODE1 => null,
+ SACTIONCODE1 => null,
+ NRN => NTMP);
+ end if;
+ /* Выполняем переадресацию события */
+ P_CLNEVENTS_SEND_WEB(NCOMPANY => NCOMPANY,
+ NIDENT => NIDENT,
+ NSTEP => NSTEP,
+ NEVENT => NEVENT,
+ SEVENT_TYPE => SEVENT_TYPE,
+ SEVENT_STAT => SEVENT_STAT,
+ SINIT_PERSON => SINIT_PERSON,
+ SINIT_AUTHNAME => SINIT_AUTHNAME,
+ SCLIENT_CLIENT => SCLIENT_CLIENT,
+ SCLIENT_PERSON => SCLIENT_PERSON,
+ SEXECUTEMETHOD => SEXECUTEMETHOD,
+ SSEND_CLIENT => SSEND_CLIENT,
+ SSEND_DIVISION => SSEND_DIVISION,
+ SSEND_POST => SSEND_POST,
+ SSEND_PERFORM => SSEND_PERFORM,
+ SSEND_PERSON => SSEND_PERSON,
+ SSEND_STAFFGRP => SSEND_STAFFGRP,
+ SSEND_USER_GROUP => SSEND_USER_GROUP,
+ SSEND_USER_NAME => SSEND_USER_NAME,
+ NSEND_PREDEFINED_EXEC => NSEND_PREDEFINED_EXEC,
+ NSEND_PREDEFINED_PROC => NSEND_PREDEFINED_PROC);
+ /* Если это последний шаг */
+ if (NSTEP_CURRENT = 2) then
+ /* Если добавлено примечание */
+ if (SNOTE is not null) then
+ P_CLNEVNOTES_INSERT(NCOMPANY => NCOMPANY,
+ NPRN => NEVENT,
+ SNOTE_HEADER => SNOTE_HEADER,
+ SNOTE => SNOTE,
+ NRN => NNOTE_RN);
+ end if;
+ /* Очищаем отмеченные записи */
+ P_SELECTLIST_CLEAR(NIDENT => NIDENT);
+ end if;
+ exception
+ when others then
+ /* Если список отмеченных есть */
+ if (NIDENT is not null) then
+ /* Удаляем в автономной транзакции */
+ P_SELECTLIST_CLEAR_AT(NIDENT => NIDENT);
+ end if;
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end CLNEVENTS_SEND;
+
+ /* Возврат в предыдущую точку события */
+ procedure CLNEVENTS_RETURN
+ (
+ NCLNEVENTS in number -- Рег. номер события
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
+ begin
+ /* Возвращаем событие */
+ P_CLNEVENTS_RETURN_WEB(NCOMPANY => NCOMPANY, NIDENT => null, NEVENT => NCLNEVENTS);
+ end CLNEVENTS_RETURN;
+
+ /* Считывание маршрутов и исполнителей события */
+ procedure CLNEVENTS_GET_INFO_BY_CODE
+ (
+ SEVNTYPE_CODE in varchar2, -- Мнемокод типа события
+ COUT out clob -- XML с параметрами фильтра по умолчанию
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
+ SSRC PKG_STD.TSTRING; -- Наименование точки маршрута
+ SDEST PKG_STD.TSTRING; -- Наименование точки перехода
+ begin
+ /* Формируем XML */
+ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
+ PKG_XFAST.DOWN_NODE(SNAME => 'DATA');
+ /* Цикл по точкам маршрута и точкам перехода */
+ for REC in (select CST1.EVNSTAT_CODE SSRC,
+ CST2.EVNSTAT_CODE SDEST
+ from EVRTPTPASS EPS,
+ EVRTPOINTS EP1,
+ CLNEVNTYPSTS CT1,
+ CLNEVNSTATS CST1,
+ EVRTPOINTS EP2,
+ CLNEVNTYPSTS CT2,
+ CLNEVNSTATS CST2
+ where EPS.PRN in (select EP.RN
+ from EVROUTES ER,
+ CLNEVNTYPES C,
+ EVRTPOINTS EP
+ where ER.COMPANY = NCOMPANY
+ and ER.EVENT_TYPE = C.RN
+ and C.EVNTYPE_CODE = SEVNTYPE_CODE
+ and EP.PRN = ER.RN)
+ and EPS.FORCE_ONLY = 0
+ and EPS.PRN = EP1.RN
+ and EP1.EVENT_STATUS = CT1.RN
+ and CT1.EVENT_STATUS = CST1.RN
+ and EPS.NEXT_POINT = EP2.RN
+ and EP2.EVENT_STATUS = CT2.RN
+ and CT2.EVENT_STATUS = CST2.RN)
+ loop
+ /* Добавляем информацию о маршруте */
+ PKG_XFAST.DOWN_NODE(SNAME => 'XEVROUTES');
+ /* Начало перехода */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SSOURCE');
+ PKG_XFAST.VALUE(SVALUE => REC.SSRC);
+ PKG_XFAST.UP();
+ /* Конец перехода */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SDESTINATION');
+ PKG_XFAST.VALUE(SVALUE => REC.SDEST);
+ PKG_XFAST.UP();
+ PKG_XFAST.UP();
+ end loop;
+ /* Цикл по точкам маршрута */
+ for REC in (select CST.EVNSTAT_CODE,
+ CST.EVNSTAT_NAME,
+ EP.COMMENTARY,
+ EP.ADDNOTE_ONCHST,
+ EP.ADDNOTE_ONSEND,
+ EP.BAN_UPDATE
+ from EVROUTES ER,
+ CLNEVNTYPES C,
+ EVRTPOINTS EP,
+ CLNEVNTYPSTS CT,
+ CLNEVNSTATS CST
+ where ER.COMPANY = NCOMPANY
+ and ER.EVENT_TYPE = C.RN
+ and C.EVNTYPE_CODE = SEVNTYPE_CODE
+ and EP.PRN = ER.RN
+ and EP.EVENT_STATUS = CT.RN
+ and CT.EVENT_STATUS = CST.RN)
+ loop
+ /* Добавляем информацию о точке маршрута */
+ PKG_XFAST.DOWN_NODE(SNAME => 'XEVPOINTS');
+ /* Мнемокод точки маршрута */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SEVPOINT');
+ PKG_XFAST.VALUE(SVALUE => REC.EVNSTAT_CODE);
+ PKG_XFAST.UP();
+ /* Описание в точке маршрута */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SEVPOINT_DESCR');
+ PKG_XFAST.VALUE(SVALUE => COALESCE(REC.COMMENTARY, REC.EVNSTAT_NAME));
+ PKG_XFAST.UP();
+ /* Признак добавления примечания при переходе в точку маршрута */
+ PKG_XFAST.DOWN_NODE(SNAME => 'ADDNOTE_ONCHST');
+ PKG_XFAST.VALUE(BVALUE => INT2BOOL(REC.ADDNOTE_ONCHST));
+ PKG_XFAST.UP();
+ /* Признак добавления примечания при перенаправлении в точке маршрута */
+ PKG_XFAST.DOWN_NODE(SNAME => 'ADDNOTE_ONSEND');
+ PKG_XFAST.VALUE(BVALUE => INT2BOOL(REC.ADDNOTE_ONSEND));
+ PKG_XFAST.UP();
+ /* Признак запрета исправления события в точке маршрута */
+ PKG_XFAST.DOWN_NODE(SNAME => 'BAN_UPDATE');
+ PKG_XFAST.VALUE(BVALUE => INT2BOOL(REC.BAN_UPDATE));
+ PKG_XFAST.UP();
+ PKG_XFAST.UP();
+ end loop;
+ /* Цикл по типам заголовков примечаний */
+ for REC in (select NT.NAME
+ from CLNEVNTYPES ET,
+ CLNEVNTYPENOTES TN,
+ CLNEVNTNOTETYPES NT
+ where ET.COMPANY = NCOMPANY
+ and ET.EVNTYPE_CODE = SEVNTYPE_CODE
+ and TN.PRN = ET.RN
+ and NT.RN = TN.NOTE_TYPE)
+ loop
+ /* Добавляем информацию о заголовке примечания */
+ PKG_XFAST.DOWN_NODE(SNAME => 'XNOTETYPES');
+ /* Наименование типа заголовка примечания */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SNAME');
+ PKG_XFAST.VALUE(SVALUE => REC.NAME);
+ PKG_XFAST.UP();
+ PKG_XFAST.UP();
+ end loop;
+ PKG_XFAST.UP();
+ /* Сериализуем в CLOB */
+ COUT := PKG_XFAST.SERIALIZE_TO_CLOB();
+ PKG_XFAST.EPILOGUE();
+ end CLNEVENTS_GET_INFO_BY_CODE;
+
+ /* Считывание учётных документов события */
+ procedure CLNEVENTS_DOCLINKS_GET
+ (
+ SEVNTYPE_CODE in varchar2, -- Мнемокод типа события
+ COUT out clob -- XML с параметрами фильтра по умолчанию
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
+ begin
+ /* Формируем XML */
+ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
+ PKG_XFAST.DOWN_NODE(SNAME => 'DATA');
+ /* Цикл по учётным документам */
+ for REC in (select C.LINKED_RN NRN,
+ C.LINKED_UNIT SUNIT
+ from CLNEVENTS C,
+ CLNEVNTYPES CT
+ where C.COMPANY = NCOMPANY
+ and C.EVENT_TYPE = CT.RN
+ and CT.EVNTYPE_CODE = SEVNTYPE_CODE
+ and C.LINKED_RN is not null)
+ loop
+ PKG_XFAST.DOWN_NODE(SNAME => 'XDOCLINKS');
+ /* Идентификатор */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NRN');
+ PKG_XFAST.VALUE(NVALUE => REC.NRN);
+ PKG_XFAST.UP();
+ /* Описатель документа */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SDESCR');
+ PKG_XFAST.VALUE(SVALUE => UTL_UNITLIST_DESCR_GET(SUNIT_CODE => REC.SUNIT, NDOCUMENT => REC.NRN));
+ PKG_XFAST.UP();
+ PKG_XFAST.UP();
+ end loop;
+ PKG_XFAST.UP();
+ /* Сериализуем в CLOB */
+ COUT := PKG_XFAST.SERIALIZE_TO_CLOB();
+ PKG_XFAST.EPILOGUE();
+ end CLNEVENTS_DOCLINKS_GET;
+
+ /* Считывание списка рег. номеров дочерних каталогов (включая основной) */
+ procedure CLNEVENTS_SUBCATALOGS_GET
+ (
+ SCRN_NAME in varchar2, -- Наименование каталога
+ NSUBCAT in number := 0, -- Признак включения подкаталогов (0 - нет, 1 - да)
+ SRESULT out varchar2 -- Список рег. номеров каталогов
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
+ NCRN_MAIN PKG_STD.TREF; -- Рег. номер основного каталога
+ begin
+ /* Считываем рег. номер основого каталога */
+ FIND_ACATALOG_NAME(NFLAG_SMART => 0,
+ NCOMPANY => NCOMPANY,
+ NVERSION => null,
+ SUNITCODE => SUNIT_CLNEVENTS,
+ SNAME => SCRN_NAME,
+ NRN => NCRN_MAIN);
+ /* Если требуется включить подкаталоги */
+ if (NSUBCAT = 1) then
+ /* Цикл по иерерахии каталогов */
+ for REC in (select A.RN from ACATALOG A start with A.RN = NCRN_MAIN connect by prior A.RN = A.CRN)
+ loop
+ SRESULT := SRESULT || REC.RN || ';';
+ end loop;
+ else
+ /* Указываем только основной каталог */
+ SRESULT := TO_CHAR(NCRN_MAIN);
+ end if;
+ /* Удаляем лишний ";" */
+ SRESULT := RTRIM(SRESULT, ';');
+ end CLNEVENTS_SUBCATALOGS_GET;
+
+ /* Формирование условий отбора фильтра */
+ procedure CLNEVENTS_COND
+ is
+ begin
+ /* Установка главной таблицы */
+ PKG_COND_BROKER.SET_TABLE(STABLE_NAME => 'CLNEVENTS');
+ /* Формирование условий отбора */
+ /* Условие по состоянию события */
+ PKG_COND_BROKER.ADD_CONDITION_BETWEEN(SCOLUMN_NAME => 'CLOSED',
+ SCONDITION_NAME_FROM => 'BYEV_CLOSED_From',
+ SCONDITION_NAME_TO => 'BYEV_CLOSED_To');
+ /* Условие по типу события */
+ PKG_COND_BROKER.ADD_CONDITION_CODE(SCOLUMN_NAME => 'EVNTYPE_CODE',
+ SCONDITION_NAME => 'BYEV_TYPE',
+ SJOINS => 'EVENT_TYPE <- RN;CLNEVNTYPES');
+ /* Условие по каталогу события */
+ PKG_COND_BROKER.ADD_CONDITION_ENUM(SCOLUMN_NAME => 'CRN', SCONDITION_NAME => 'BYEV_CRN');
+ /* Условие по исполнителю */
+ PKG_COND_BROKER.ADD_CONDITION_CODE(SCOLUMN_NAME => 'AGNABBR',
+ SCONDITION_NAME => 'BYEV_S_PERSON',
+ SJOINS => 'SEND_PERSON <- RN;CLNPERSONS;PERS_AGENT <- RN;AGNLIST');
+ /* Условие по подразделению */
+ PKG_COND_BROKER.ADD_CONDITION_CODE(SCOLUMN_NAME => 'CODE',
+ SCONDITION_NAME => 'BYEV_S_DIVISION',
+ SJOINS => 'SEND_DIVISION <- RN;INS_DEPARTMENT');
+ /* Условие по группе пользователей */
+ PKG_COND_BROKER.ADD_CONDITION_CODE(SCOLUMN_NAME => 'CODE',
+ SCONDITION_NAME => 'BYEV_S_USER_GROUP',
+ SJOINS => 'SEND_USER_GROUP <- RN;USERGRP');
+ /* Условие по учётному документу */
+ PKG_COND_BROKER.ADD_CONDITION_COMPARE(SCOLUMN_NAME => 'LINKED_RN',
+ SOPERATION => '=',
+ SCONDITION_NAME => 'BYEV_LINKED_RN');
+ end CLNEVENTS_COND;
+
+ /* Считывание настройки раздела "События" для пользователя */
+ procedure CLNEVENTS_DP_RULES_GET
+ (
+ COUT out clob -- XML с настройкой раздела
+ )
+ is
+ SAUTHID PKG_STD.TSTRING := PKG_SESSION.GET_UTILIZER(); -- Пользователь
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
+ BXML blob; -- XML профиля пользователя для раздела "События"
+ RDOC PKG_XPATH.TDOCUMENT; -- Документ XML
+ RNODE_ROOT PKG_XPATH.TNODE; -- Корневой узел
+ RNODE_MAIN PKG_XPATH.TNODE; -- Основной узел
+ RNODE_GRIDFRAME PKG_XPATH.TNODE; -- Узел "gridFrame"
+ RNODE_DECOR_RULES PKG_XPATH.TNODE; -- Узел "decorationRules"
+ RNODE_RULE PKG_XPATH.TNODE; -- Узел "rule"
+ RNODE_RULE_LIST PKG_XPATH.TNODES; -- Список узлов "rule"
+ SFIELD PKG_STD.TSTRING; -- Код свойства документа (в формате "DP_<РЕГ_НОМЕР>")
+ SCOLOR PKG_STD.TSTRING; -- Код цвета
+ SDATA_TYPE PKG_STD.TSTRING; -- Тип данных
+ SDP_NAME PKG_STD.TSTRING; -- Наименование свойства документа
+ NFROM_VALUE PKG_STD.TNUMBER; -- Значение с (number)
+ NTO_VALUE PKG_STD.TNUMBER; -- Значение по (number)
+ SFROM_VALUE PKG_STD.TSTRING; -- Значение с (string)
+ STO_VALUE PKG_STD.TSTRING; -- Значение по (string)
+ DFROM_VALUE PKG_STD.TLDATE; -- Значение с (date)
+ DTO_VALUE PKG_STD.TLDATE; -- Значение по (date)
+
+ /* Считывание наименования свойства документа */
+ function DOCS_PROPS_NAME_GET
+ (
+ SFIELD in varchar2 -- Код свойства документа (в формате "DP_<РЕГ_НОМЕР>")
+ ) return varchar2 -- Наименование свойства документа
+ is
+ NRN PKG_STD.TREF; -- Рег. номер свойства документа
+ SRESULT PKG_STD.TSTRING; -- Наименование доп. свойства
+ begin
+ /* Определяем рег. номер свойства документа по формату "DP_<РЕГ_НОМЕР>" */
+ NRN := TO_NUMBER(SUBSTR(SFIELD, INSTR(SFIELD, '_') + 1));
+ /* Считываем наименование из записи свойства документа */
+ begin
+ select T.NAME into SRESULT from DOCS_PROPS T where T.RN = NRN;
+ exception
+ when NO_DATA_FOUND then
+ P_EXCEPTION(0,
+ 'Ошибка считывания записи свойства документа с рег. номером (%s).');
+ when others then
+ P_EXCEPTION(0,
+ 'Ошибка считывания наименования свойства документа с рег. номером (%s).');
+ end;
+ /* Возвращаем результат */
+ return SRESULT;
+ end DOCS_PROPS_NAME_GET;
+
+ /* Считывание профиля пользователя для раздела "События" */
+ function USERPROFILES_GET
+ (
+ NCOMPANY in number, -- Рег. номер организации
+ SAUTHID in varchar2 -- Мнемокод пользователя
+ ) return blob -- Профиль пользователя для раздела "События"
+ is
+ BRESULT blob; -- Профиль пользователя для раздела "События"
+ begin
+ /* Считываем профиль пользователя для раздела "События" */
+ begin
+ select U.USERDATA
+ into BRESULT
+ from USERPROFILES U
+ where U.COMPANY = NCOMPANY
+ and U.AUTHID = SAUTHID
+ and U.REC_TYPE = 1
+ and U.UNITMODE = 0
+ and U.UNITCODE = SUNIT_CLNEVENTS;
+ exception
+ when others then
+ P_EXCEPTION(0,
+ 'Профиль пользователя "%s" для раздела "События" не найден.',
+ SAUTHID);
+ end;
+ /* Возвращаем результат */
+ return BRESULT;
+ end USERPROFILES_GET;
+ begin
+ /* Считываем профиль пользователя для раздела "События" */
+ BXML := USERPROFILES_GET(NCOMPANY => NCOMPANY, SAUTHID => SAUTHID);
+ begin
+ /* Формируем XML данных */
+ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
+ PKG_XFAST.DOWN_NODE(SNAME => 'DATA');
+ /* Загрузка XML */
+ RDOC := PKG_XPATH.PARSE_FROM_BLOB(LBXML => BXML);
+ RNODE_ROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => RDOC);
+ /* Обработка XML */
+ RNODE_MAIN := PKG_XPATH.FIRST_NODE(RPARENT_NODE => RNODE_ROOT);
+ RNODE_GRIDFRAME := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => RNODE_MAIN, SPATTERN => 'gridFrame');
+ RNODE_DECOR_RULES := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => RNODE_GRIDFRAME, SPATTERN => 'decorationRules');
+ RNODE_RULE_LIST := PKG_XPATH.LIST_NODES(RPARENT_NODE => RNODE_DECOR_RULES, SPATTERN => 'rule');
+ /* Цикл по узлам внутри узла rule */
+ for I in 1 .. PKG_XPATH.COUNT_NODES(RNODES => RNODE_RULE_LIST)
+ loop
+ /* Считываем текущее правило */
+ RNODE_RULE := PKG_XPATH.ITEM_NODE(RNODES => RNODE_RULE_LIST, INUMBER => I);
+ /* Атрибуты */
+ SFIELD := PKG_XPATH.ATTRIBUTE(RNODE => RNODE_RULE, SNAME => 'field');
+ SCOLOR := PKG_XPATH.ATTRIBUTE(RNODE => RNODE_RULE, SNAME => 'color');
+ SDATA_TYPE := PKG_XPATH.ATTRIBUTE(RNODE => RNODE_RULE, SNAME => 'dataType');
+ PKG_XFAST.DOWN_NODE(SNAME => 'XRULES');
+ /* Код правила */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SFIELD');
+ PKG_XFAST.VALUE(SVALUE => SFIELD);
+ PKG_XFAST.UP();
+ /* Считываем наименование свойства документа */
+ SDP_NAME := DOCS_PROPS_NAME_GET(SFIELD => SFIELD);
+ /* Наименование доп. свойства */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SDP_NAME');
+ PKG_XFAST.VALUE(SVALUE => SDP_NAME);
+ PKG_XFAST.UP();
+ /* Код цвета */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SCOLOR');
+ PKG_XFAST.VALUE(SVALUE => SCOLOR);
+ PKG_XFAST.UP();
+ /* Тип значения */
+ PKG_XFAST.DOWN_NODE(SNAME => 'STYPE');
+ PKG_XFAST.VALUE(SVALUE => SDATA_TYPE);
+ PKG_XFAST.UP();
+ /* Исходим от типа данных */
+ case SDATA_TYPE
+ /* Число */
+ when 'number' then
+ NFROM_VALUE := PKG_XPATH.VALUE_NUM(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => RNODE_RULE,
+ SPATTERN => 'fromValue'));
+ NTO_VALUE := PKG_XPATH.VALUE_NUM(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => RNODE_RULE,
+ SPATTERN => 'toValue'));
+ /* Число с */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NFROM');
+ PKG_XFAST.VALUE(NVALUE => NFROM_VALUE);
+ PKG_XFAST.UP();
+ /* Число по */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NTO');
+ PKG_XFAST.VALUE(NVALUE => NTO_VALUE);
+ PKG_XFAST.UP();
+ /* Строка */
+ when 'string' then
+ SFROM_VALUE := PKG_XPATH.VALUE(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => RNODE_RULE,
+ SPATTERN => 'fromValue'));
+ STO_VALUE := PKG_XPATH.VALUE(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => RNODE_RULE,
+ SPATTERN => 'toValue'));
+ /* Строка с */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SFROM');
+ PKG_XFAST.VALUE(SVALUE => SFROM_VALUE);
+ PKG_XFAST.UP();
+ /* Строка по */
+ PKG_XFAST.DOWN_NODE(SNAME => 'STO');
+ PKG_XFAST.VALUE(SVALUE => STO_VALUE);
+ PKG_XFAST.UP();
+ /* Дата */
+ else
+ DFROM_VALUE := PKG_XPATH.VALUE_DATE(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => RNODE_RULE,
+ SPATTERN => 'fromValue'));
+ DTO_VALUE := PKG_XPATH.VALUE_DATE(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => RNODE_RULE,
+ SPATTERN => 'toValue'));
+ /* Дата с */
+ PKG_XFAST.DOWN_NODE(SNAME => 'DFROM');
+ PKG_XFAST.VALUE(DVALUE => DFROM_VALUE);
+ PKG_XFAST.UP();
+ /* Дата по */
+ PKG_XFAST.DOWN_NODE(SNAME => 'DTO');
+ PKG_XFAST.VALUE(DVALUE => DTO_VALUE);
+ PKG_XFAST.UP();
+ end case;
+ PKG_XFAST.UP();
+ end loop;
+ PKG_XPATH.FREE(RDOCUMENT => RDOC);
+ PKG_XFAST.UP();
+ /* Сериализуем в CLOB */
+ COUT := PKG_XFAST.SERIALIZE_TO_CLOB();
+ PKG_XFAST.EPILOGUE();
+ exception
+ when others then
+ PKG_XPATH.FREE(RDOCUMENT => RDOC);
+ PKG_XFAST.EPILOGUE();
+ end;
+ end CLNEVENTS_DP_RULES_GET;
+
+ /* Считывание свойств раздела "События" */
+ procedure CLNEVENTS_PROPS_GET
+ (
+ SEVNTYPE_CODE in varchar2, -- Мнемокод типа события
+ COUT out clob -- XML со свойствами раздела
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
+ begin
+ /* Формируем XML данных */
+ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
+ PKG_XFAST.DOWN_NODE(SNAME => 'DATA');
+ /* Цикл по свойствам документа */
+ for REC in (select CEP.PROPERTY,
+ CEP.PROPERTY_LINK,
+ DPL.NAME,
+ DPL.READONLY,
+ DPL.CHECK_VALUE,
+ DPL.CHECK_UNIQUE,
+ DPL.REQUIRE,
+ DPL.DUPLICATE_VALUE,
+ DPL.ACCESS_MODE,
+ DPL.SHOW_IN_GRID,
+ DPL.DEFAULT_STR,
+ DPL.DEFAULT_NUM,
+ DPL.DEFAULT_DATE,
+ DP.ENTRY_TYPE,
+ DP.FORMAT,
+ DP.DATA_SUBTYPE,
+ DP.NUM_WIDTH,
+ DP.NUM_PRECISION,
+ DP.STR_WIDTH,
+ DP.UNITCODE,
+ DP.PARAM_RN,
+ UP.IN_CODE,
+ UP.OUT_CODE,
+ DP.SHOW_METHOD_RN,
+ UM.METHOD_CODE,
+ DP.EXTRA_DICT_RN,
+ DP.INIT_RN
+ from CLEVTPROPS CEP,
+ CLNEVNTYPES CET,
+ DOCS_PROPS DP,
+ DOCS_PROPS_LINKS DPL,
+ UNITPARAMS UP,
+ UNIT_SHOWMETHODS UM
+ where CEP.PRN = CET.RN
+ and CET.EVNTYPE_CODE = SEVNTYPE_CODE
+ and CEP.COMPANY = NCOMPANY
+ and CEP.PROPERTY = DP.RN(+)
+ and CEP.PROPERTY_LINK = DPL.RN(+)
+ and (DP.PARAM_RN = UP.RN(+) and DP.UNITCODE = UP.UNITCODE(+))
+ and (DP.SHOW_METHOD_RN = UM.RN(+) and DP.UNITCODE = UM.UNITCODE(+))
+ order by DPL.POSITION)
+ loop
+ PKG_XFAST.DOWN_NODE(SNAME => 'XPROPS');
+ /* Рег. номер свойства */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NRN');
+ PKG_XFAST.VALUE(NVALUE => REC.PROPERTY);
+ PKG_XFAST.UP();
+ /* Наименование свойства */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SNAME');
+ PKG_XFAST.VALUE(SVALUE => REC.NAME);
+ PKG_XFAST.UP();
+ /* Признак "только для чтения" */
+ PKG_XFAST.DOWN_NODE(SNAME => 'BREADONLY');
+ PKG_XFAST.VALUE(BVALUE => INT2BOOL(REC.READONLY));
+ PKG_XFAST.UP();
+ /* Признак "проверка/синхронизация вводимого значения" */
+ PKG_XFAST.DOWN_NODE(SNAME => 'CHECK_VALUE');
+ PKG_XFAST.VALUE(BVALUE => INT2BOOL(REC.CHECK_VALUE));
+ PKG_XFAST.UP();
+ /* Признак "проверка вводимого значения на уникальность" */
+ PKG_XFAST.DOWN_NODE(SNAME => 'CHECK_UNIQUE');
+ PKG_XFAST.VALUE(BVALUE => INT2BOOL(REC.CHECK_UNIQUE));
+ PKG_XFAST.UP();
+ /* Признак "обязательность к заполнению" */
+ PKG_XFAST.DOWN_NODE(SNAME => 'BREQUIRE');
+ PKG_XFAST.VALUE(BVALUE => INT2BOOL(REC.REQUIRE));
+ PKG_XFAST.UP();
+ /* Признак размножения */
+ PKG_XFAST.DOWN_NODE(SNAME => 'DUPLICATE_VALUE');
+ PKG_XFAST.VALUE(BVALUE => INT2BOOL(REC.DUPLICATE_VALUE));
+ PKG_XFAST.UP();
+ /* Уровень доступа к значениям свойства */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NACCESS_MODE');
+ PKG_XFAST.VALUE(NVALUE => REC.ACCESS_MODE);
+ PKG_XFAST.UP();
+ /* Признак "показывать значение свойства в гриде раздела" */
+ PKG_XFAST.DOWN_NODE(SNAME => 'BSHOW_IN_GRID');
+ PKG_XFAST.VALUE(BVALUE => INT2BOOL(REC.SHOW_IN_GRID));
+ PKG_XFAST.UP();
+ /* Строка "по умолчанию" */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SDEFAULT_STR');
+ PKG_XFAST.VALUE(SVALUE => REC.DEFAULT_STR);
+ PKG_XFAST.UP();
+ /* Число "по умолчанию" */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NDEFAULT_NUM');
+ PKG_XFAST.VALUE(SVALUE => REC.DEFAULT_NUM);
+ PKG_XFAST.UP();
+ /* Дата "по умолчанию" */
+ PKG_XFAST.DOWN_NODE(SNAME => 'DDEFAULT_DATE');
+ PKG_XFAST.VALUE(DVALUE => REC.DEFAULT_DATE);
+ PKG_XFAST.UP();
+ /* Способ формирования */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NENTRY_TYPE');
+ PKG_XFAST.VALUE(NVALUE => REC.ENTRY_TYPE);
+ PKG_XFAST.UP();
+ /* Формат данных */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NFORMAT');
+ PKG_XFAST.VALUE(NVALUE => REC.FORMAT);
+ PKG_XFAST.UP();
+ /* Подтип */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NDATA_SUBTYPE');
+ PKG_XFAST.VALUE(NVALUE => REC.DATA_SUBTYPE);
+ PKG_XFAST.UP();
+ /* Длина числа */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NNUM_WIDTH');
+ PKG_XFAST.VALUE(NVALUE => REC.NUM_WIDTH);
+ PKG_XFAST.UP();
+ /* Точность числа */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NNUM_PRECISION');
+ PKG_XFAST.VALUE(NVALUE => REC.NUM_PRECISION);
+ PKG_XFAST.UP();
+ /* Длина строки */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NSTR_WIDTH');
+ PKG_XFAST.VALUE(NVALUE => REC.STR_WIDTH);
+ PKG_XFAST.UP();
+ /* Код раздела */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SUNITCODE');
+ PKG_XFAST.VALUE(SVALUE => REC.UNITCODE);
+ PKG_XFAST.UP();
+ /* Рег. номер параметра раздела */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NPARAM_RN');
+ PKG_XFAST.VALUE(NVALUE => REC.PARAM_RN);
+ PKG_XFAST.UP();
+ /* Код параметра раздела (входящий) */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SPARAM_IN_CODE');
+ PKG_XFAST.VALUE(SVALUE => REC.IN_CODE);
+ PKG_XFAST.UP();
+ /* Код параметра раздела (исходящий) */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SPARAM_OUT_CODE');
+ PKG_XFAST.VALUE(SVALUE => REC.OUT_CODE);
+ PKG_XFAST.UP();
+ /* Рег. номер метода вызова */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NSHOW_METHOD_RN');
+ PKG_XFAST.VALUE(NVALUE => REC.SHOW_METHOD_RN);
+ PKG_XFAST.UP();
+ /* Код метода вызова */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SMETHOD_CODE');
+ PKG_XFAST.VALUE(SVALUE => REC.METHOD_CODE);
+ PKG_XFAST.UP();
+ /* Рег. номер дополнительного словаря */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NEXTRA_DICT_RN');
+ PKG_XFAST.VALUE(NVALUE => REC.EXTRA_DICT_RN);
+ PKG_XFAST.UP();
+ /* Рег. номер свойства инициализации */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NINIT_RN');
+ PKG_XFAST.VALUE(NVALUE => REC.INIT_RN);
+ PKG_XFAST.UP();
+ /* Отформатированный рег. номер (как в доп. свойствах задачи) */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SFORMATTED_ID');
+ PKG_XFAST.VALUE(SVALUE => UTL_DP_FORMATTED_ID_GET(NTYPE => REC.FORMAT, NRN => REC.PROPERTY));
+ PKG_XFAST.UP();
+ PKG_XFAST.UP();
+ end loop;
+ PKG_XFAST.UP();
+ /* Сериализуем в CLOB */
+ COUT := PKG_XFAST.SERIALIZE_TO_CLOB();
+ PKG_XFAST.EPILOGUE();
+ end CLNEVENTS_PROPS_GET;
+
+ /* Загрузка статусов типа события */
+ procedure CLNEVNSTATS_LOAD
+ (
+ SCLNEVNTYPES in varchar2, -- Мнемокод типа события
+ COUT out clob -- Статусы типа события
+ )
+ is
+ NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
+ NINDEX PKG_STD.TREF := 0; -- Индекс статуса
+ begin
+ /* Начинаем формирование XML */
+ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
+ /* Открываем статусы */
+ PKG_XFAST.DOWN_NODE(SNAME => 'XCLNEVNSTATS');
+ /* Считываем статусы типа события */
+ for REC in (select S.RN,
+ S.EVNSTAT_CODE,
+ S.EVNSTAT_NAME,
+ (select EP.COMMENTARY
+ from EVROUTES ER,
+ EVRTPOINTS EP
+ where ER.EVENT_TYPE = T.RN
+ and EP.PRN = ER.RN
+ and EP.EVENT_STATUS = TS.RN
+ and EP.COMMENTARY is not null
+ and rownum = 1) COMMENTARY
+ from CLNEVNTYPES T,
+ CLNEVNTYPSTS TS,
+ CLNEVNSTATS S
+ where T.COMPANY = NCOMPANY
+ and T.EVNTYPE_CODE = SCLNEVNTYPES
+ and TS.PRN = T.RN
+ and TS.EVENT_STATUS = S.RN
+ order by TS.DEFAULT_STATUS desc,
+ S.EVNSTAT_CODE)
+ loop
+ /* Открываем статус */
+ PKG_XFAST.DOWN_NODE(SNAME => 'XSTATUS');
+ /* Индекс статуса */
+ PKG_XFAST.DOWN_NODE(SNAME => 'ID');
+ PKG_XFAST.VALUE(NVALUE => NINDEX);
+ PKG_XFAST.UP();
+ /* Рег. номер статуса */
+ PKG_XFAST.DOWN_NODE(SNAME => 'NRN');
+ PKG_XFAST.VALUE(NVALUE => REC.RN);
+ PKG_XFAST.UP();
+ /* Мнемокод статуса */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SEVNSTAT_CODE');
+ PKG_XFAST.VALUE(SVALUE => REC.EVNSTAT_CODE);
+ PKG_XFAST.UP();
+ /* Наименование статуса */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SEVNSTAT_NAME');
+ PKG_XFAST.VALUE(SVALUE => REC.EVNSTAT_NAME);
+ PKG_XFAST.UP();
+ /* Описание в точке маршрута */
+ PKG_XFAST.DOWN_NODE(SNAME => 'SEVPOINT_DESCR');
+ PKG_XFAST.VALUE(SVALUE => COALESCE(REC.COMMENTARY, REC.EVNSTAT_NAME));
+ PKG_XFAST.UP();
+ /* Закрываем статус */
+ PKG_XFAST.UP();
+ /* Увеличиваем индекс статуса */
+ NINDEX := NINDEX + 1;
+ end loop;
+ /* Закрываем статусы */
+ PKG_XFAST.UP();
+ /* Сериализуем */
+ COUT := PKG_XFAST.SERIALIZE_TO_CLOB();
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ exception
+ when others then
+ /* Завершаем формирование XML */
+ PKG_XFAST.EPILOGUE();
+ /* Вернем ошибку */
+ PKG_STATE.DIAGNOSTICS_STACKED();
+ P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
+ end CLNEVNSTATS_LOAD;
+
+end PKG_P8PANELS_CLNTTSKBRD;
+/