/* Парус 8 - Панели мониторинга - Редактор настройки регламентированного отчёта Пользовательские хуки */ //--------------------- //Подключение библиотек //--------------------- import { useState, useContext, useEffect, useCallback } from "react"; //Классы React import { ApplicationСtx } from "../../context/application"; //Контекст приложения import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений import { object2Base64XML, deepCopyObject } from "../../core/utils"; //Вспомогательные функции import { EVENT_STATES } from "./components/filter_dialog"; //Перечисление состояний события //--------- //Константы //--------- //Цвета статусов export const COLORS = [ "mediumSlateBlue", "lightSalmon", "fireBrick", "orange", "gold", "limeGreen", "yellowGreen", "mediumAquaMarine", "paleTurquoise", "steelBlue", "skyBlue", "tan" ]; //--------------------------------------------- //Вспомогательные функции форматирования данных //--------------------------------------------- //Формирование случайного цвета const randomColor = index => { const hue = index * 137.508; return hslToRgba(hue, 50, 70); }; //Цвет из hsl формата в rgba формат const hslToRgba = (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)`; }; //Формирование массива из 0, 1 и 1< элементов export const arrayFormer = arr => { return arr ? (arr.length ? arr : [arr]) : []; }; //----------- //Тело модуля //----------- //Хук основных данных const useTasks = () => { //Состояние открытия формы события const [taskFormOpen, setTaskFormOpen] = useState(false); //Состояние изменения настройки статуса const [cardSettings, setCardSettings] = useState({ isOpen: false, settings: {} }); //Состояние событий const [tasks, setTasks] = useState({ groupsLoaded: false, tasksLoaded: false, orders: [], filters: { isOpen: true, isSetByUser: false, needSave: false, values: { evState: EVENT_STATES[1], type: "", catalog: "", crn: "", wSubcatalogs: false, sendPerson: "", sendDivision: "", sendUsrGrp: "", docLink: "" }, fArray: [ { name: "NCLOSED", from: 0, to: 1 }, { name: "SEVTYPE_CODE", from: "", to: "" }, { name: "NCRN", from: "", to: "" }, { name: "SSEND_PERSON", from: "", to: "" }, { name: "SSEND_DIVISION", from: "", to: "" }, { name: "SSEND_USRGRP", from: "", to: "" }, { name: "NLINKED_RN", from: "", to: "" } ] }, extraData: { typeLoaded: "", evRoutes: [], evPoints: [], noteTypes: [], docLinks: [], accounts: [] }, rows: [], statuses: [], openCardForm: false, reload: true }); //Подключение к контексту взаимодействия с сервером const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); //Подключение к контексту приложения const { pOnlineShowDictionary } = useContext(ApplicationСtx); //Инициализация параметров события const initTask = (id, gp, task) => { //Добавление дополнительных свойств let newDocProps = {}; Object.keys(task) .filter(k => k.includes("DP_")) .map(dp => (newDocProps = { ...newDocProps, [dp]: task[dp] })); return { id: id, name: task.SPREF_NUMB, category: gp, nrn: task.NRN, scrn: "", sprefix: task.SEVPREF, snumber: task.SEVNUMB, stype: task.SEVTYPE_CODE, sstatus: task.SEVSTAT_NAME, sdescription: task.SEVDESCR, sclnt_clnclients: "", sclnt_clnperson: "", dchange_date: task.DCHANGE_DATE, dstart_date: task.DREG_DATE, dexpire_date: task.DEXPIRE_DATE, dplan_date: task.DPLAN_DATE, sinit_clnperson: task.SINIT_PERSON, sinit_user: "", sinit_reason: "", //SEND_CLIENT sto_company: "", //SEND_DIVISION sto_department: task.SSEND_DIVISION, //SEND_POST sto_clnpost: "", //SEND_PERFORM sto_clnpsdep: "", //SEND_PERSON sto_clnperson: task.SSEND_PERSON, //SEND_STAFFGRP sto_fcstaffgrp: "", //SEND_USER_AUTHID sto_user: "", //SEND_USER_GROUP sto_usergrp: task.SSEND_USRGRP, sSender: task.SSENDER, scurrent_user: "", slinked_unit: task.SLINKED_UNIT, nlinked_rn: task.NLINKED_RN, docProps: newDocProps }; }; //При открытии диалога фильтра const handleFilterClick = () => setFilterOpen(true); //При изменении фильтра в диалоге const handleFilterOk = filter => { setFilterValues(filter); setFilterOpen(false); }; //При закрытии диалога фильтра const handleFilterCancel = () => setFilterOpen(false); //Установить значение фильтра const setFilterValues = (values, ns = true) => { //Считываем массив фильтров let filterArr = tasks.filters.fArray.slice(); //Состояние if (values.evState) { if (values.evState === EVENT_STATES[0]) { filterArr.find(f => f.name === "NCLOSED").from = 0; filterArr.find(f => f.name === "NCLOSED").to = 1; } else if (values.evState === EVENT_STATES[1]) { filterArr.find(f => f.name === "NCLOSED").from = 0; filterArr.find(f => f.name === "NCLOSED").to = 0; } else if (values.evState === EVENT_STATES[2]) { filterArr.find(f => f.name === "NCLOSED").from = 1; filterArr.find(f => f.name === "NCLOSED").to = 1; } } //Тип filterArr.find(f => f.name === "SEVTYPE_CODE").from = values.type ? values.type : null; //Каталог filterArr.find(f => f.name === "NCRN").from = values.crn ? values.crn : null; //Исполнитель filterArr.find(f => f.name === "SSEND_PERSON").from = values.sendPerson ? values.sendPerson : null; //Подразделение filterArr.find(f => f.name === "SSEND_DIVISION").from = values.sendDivision ? values.sendDivision : null; //Группа пользователей filterArr.find(f => f.name === "SSEND_USRGRP").from = values.sendUsrGrp ? values.sendUsrGrp : null; //Учётный документ filterArr.find(f => f.name === "NLINKED_RN").from = values.docLink ? values.docLink : null; //Устанавливаем фильтры setTasks(pv => ({ ...pv, filters: { ...pv.filters, isSetByUser: true, needSave: ns, values: { ...values }, fArray: [...filterArr] }, reload: true })); }; //Загрузка значений фильтра из локального хранилища браузера const loadLocalFilter = useCallback(async () => { let vs = { ...tasks.filters.values }; if (localStorage.getItem("type")) { Object.keys(vs).map(function (k) { if (k === "wSubcatalogs") vs[k] = localStorage.getItem(k) === "true"; else k !== "docLink" ? (vs[k] = localStorage.getItem(k)) : null; }); setFilterValues(vs, false); setFilterOpen(false); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); //При закрытии панели useEffect(() => { tasks.filters.needSave ? window.addEventListener("beforeunload", function () { Object.keys(tasks.filters.values).map(function (k) { k !== "docLink" ? localStorage.setItem(k, tasks.filters.values[k] ? tasks.filters.values[k] : "") : null; }); }) : null; }, [tasks.filters.needSave, tasks.filters.values]); //При отсутствии пользовательских настроек фильтра useEffect(() => { if (!tasks.filters.isSetByUser) setFilterOpen(true); }, [tasks.filters.isSetByUser]); //При подключении к странице useEffect(() => { localStorage.length > 0 ? loadLocalFilter() : null; }, [loadLocalFilter]); //Показать/скрыть фильтр const setFilterOpen = isOpen => { setTasks(pv => ({ ...pv, filters: { ...pv.filters, isOpen } })); }; //Открытие настройки статуса const handleCardSettingsClick = curSettings => { setCardSettings({ isOpen: true, settings: { ...curSettings } }); }; //Закрытие настройки статуса const handleCardSettingsCancel = () => setCardSettings(pv => ({ ...pv, isOpen: false })); //Применение настройки статуса const handleCardSettingsOk = settings => { //Считываем статусы let cloneS = tasks.statuses.slice(); //Изменяем статус у выбранного cloneS[tasks.statuses.findIndex(x => x.id === settings.id)] = { ...settings }; setTasks(pv => ({ ...pv, statuses: cloneS })); setCardSettings({ isOpen: false, settings: {} }); }; //При изменении сортировки const handleOrderChanged = useCallback( columnName => { let newOrders = deepCopyObject(tasks.orders); const colOrder = newOrders.find(o => o.name == columnName); const newDirection = colOrder?.direction == "ASC" ? "DESC" : colOrder?.direction == "DESC" ? null : "ASC"; if (newDirection == null && colOrder) newOrders.splice(newOrders.indexOf(colOrder), 1); if (newDirection != null && !colOrder) newOrders.push({ name: columnName, direction: newDirection }); if (newDirection != null && colOrder) colOrder.direction = newDirection; setTasks(pv => ({ ...pv, orders: newOrders, reload: true })); }, [tasks.orders] ); //Изменение статуса события (переносом) const handleStateChange = useCallback( async (nEvent, sNextStat, note) => { try { //Выполняем инициализацию параметров const firstStep = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE", args: { NSTEP: 1, NEVENT: nEvent, SNEXT_STAT: sNextStat } }); if (firstStep) { //Если требуется выбрать получателя if (firstStep.NSELECT_EXEC === 1) { //Открываем раздел "Маршруты событий (исполнители в точках)" для выбора исполнителя pOnlineShowDictionary({ unitCode: "EventRoutesPointExecuters", showMethod: "executers", inputParameters: [ { name: "in_IDENT", value: firstStep.NIDENT }, { name: "in_EVENT", value: nEvent }, { 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: async send => { //Общие аргументы const mainArgs = { NIDENT: firstStep.NIDENT, NSTEP: 3, NEVENT: nEvent, SEVENT_STAT: firstStep.SEVENT_STAT, SSEND_CLIENT: send.outParameters.out_CLIENT_CODE, SSEND_DIVISION: send.outParameters.out_DIVISION_CODE, SSEND_POST: send.outParameters.out_POST_CODE, SSEND_PERFORM: send.outParameters.out_POST_IN_DIV_CODE, SSEND_PERSON: send.outParameters.out_PERSON_CODE, SSEND_STAFFGRP: send.outParameters.out_STAFFGRP_CODE, SSEND_USER_GROUP: send.outParameters.out_USER_GROUP_CODE, SSEND_USER_NAME: send.outParameters.out_USER_NAME, NSEND_PREDEFINED_EXEC: send.outParameters.out_PREDEFINED_EXEC, NSEND_PREDEFINED_PROC: send.outParameters.out_PREDEFINED_PROC }; //Выполняем переход к выбранной точке с исполнителем await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE", args: note ? { ...mainArgs, SNOTE_HEADER: note.header, SNOTE: note.text } : mainArgs }); //Необходимо обновить данные setTasks(pv => ({ ...pv, reload: true })); } }); } else { //Общие аргументы const mainArgs = { NIDENT: firstStep.NIDENT, NSTEP: 3, NEVENT: nEvent, SEVENT_STAT: firstStep.SEVENT_STAT }; //Выполняем переход к выбранной точке с предопределенным исполнителем await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE", args: note ? { ...mainArgs, ...{ SNOTE_HEADER: note.header, SNOTE: note.text } } : mainArgs }); //Необходимо обновить данные setTasks(pv => ({ ...pv, reload: true })); } } } catch (e) { //Необходимо обновить данные setTasks(pv => ({ ...pv, reload: true })); } }, [executeStored, pOnlineShowDictionary] ); //При необходимости обновить события const handleReload = useCallback(() => { setTasks(pv => ({ ...pv, reload: true })); }, []); //Взаимодействие с событием (через перенос) const onDragEnd = useCallback( (result, eventPoints, openNoteDialog) => { //Определяем нужные параметры const { source, destination } = result; //Если путь не указан if (!destination) { return; } //Если происходит изменение статуса if (destination.droppableId !== source.droppableId) { //Считываем строку, у которой изменяется статус let row = tasks.rows.find(f => f.id === parseInt(result.draggableId)); //Формируем события с учетом изменения let rows = tasks.rows.map(task => task.id === parseInt(result.draggableId) ? { ...task, category: parseInt(result.destination.droppableId) } : task ); //Мнемокод точки назначения const destCode = tasks.statuses.find(s => s.id == destination.droppableId).code; //Получение настройки точки назначения const pointSettings = eventPoints.find(ep => ep.point === destCode); //Если необходимо примечание при переходе if (pointSettings.addNoteOnChst) { //Изменяем статус события с добавлением примечания openNoteDialog(n => { setTasks(pv => ({ ...pv, rows: [...rows] })); handleStateChange(row.nrn, destCode, n); }); } //Изменяем статус события else { //Переинициализируем строки с учетом изменений (для визуального отображения) setTasks(pv => ({ ...pv, rows: [...rows] })); handleStateChange(row.nrn, destCode); } } }, [handleStateChange, tasks.rows, tasks.statuses] ); //Получение учётных документов const getDocLinks = useCallback( async (type = tasks.filters.values.type) => { const data = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DOCLINKS", args: { SEVNTYPE_CODE: type }, respArg: "COUT" }); //Инициализируем учётные документы let newDocLinks = []; //Если найдены учётные документы if (data.XDOCLINKS) { arrayFormer(data.XDOCLINKS).map(d => { newDocLinks.push({ id: d.NRN, descr: d.SDESCR }); }); } //Возвращаем сформированные учётные документы return newDocLinks; }, [executeStored, tasks.filters.values.type] ); useEffect(() => { //Считывание данных с учетом фильтрации let getTasks = async () => { const ds = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DATASET", args: { CFILTERS: { VALUE: object2Base64XML(tasks.filters.fArray, { arrayNodeName: "filters" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, CORDERS: { VALUE: object2Base64XML(tasks.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, NINCLUDE_DEF: tasks.tasksLoaded ? 0 : 1 }, respArg: "COUT" }); //Инициализируем статусы и события let newGroups = []; let newRows = []; //Если статусы есть if (ds.XDATA_GRID.groups) { //Формируем структуру статусов arrayFormer(ds.XDATA_GRID.groups).map((group, i) => { newGroups.push({ id: i, code: group.name, name: group.caption, color: randomColor(i + 1) }); }); //Если есть события if (ds.XDATA_GRID.rows) { //Формируем структуру событий arrayFormer(ds.XDATA_GRID.rows).map((task, i) => { newRows.push(initTask(i, newGroups.find(x => x.name === task.groupName).id, task)); }); } } //Возвращаем информацию return { statuses: [...newGroups], rows: [...newRows] }; }; //Считывание вспомогательных данных let getEventData = async () => { const data = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_INFO_BY_CODE", args: { SEVNTYPE_CODE: tasks.filters.values.type }, respArg: "COUT" }); //Инициализируем маршруты событий let newRoutes = data.XEVROUTES ? arrayFormer(data.XEVROUTES).reduce((prev, cur) => { prev.push({ src: cur.SSOURCE, dest: cur.SDESTINATION }); return prev; }, []) : []; //Инициализируем точки событий let newPoints = data.XEVPOINTS ? arrayFormer(data.XEVPOINTS).reduce((prev, cur) => { prev.push({ point: cur.SEVPOINT, pointDescr: cur.SEVPOINT_DESCR, addNoteOnChst: cur.ADDNOTE_ONCHST, addNoteOnSend: cur.ADDNOTE_ONSEND, banUpdate: cur.BAN_UPDATE }); return prev; }, []) : []; //Инициализируем типы заголовков примечаний let newNoteTypes = data.XNOTETYPES ? arrayFormer(data.XNOTETYPES).reduce((prev, cur) => { prev.push(cur.SNAME); return prev; }, []) : []; //Инициализируем пользователей let newAccounts = data.XACCOUNTS ? arrayFormer(data.XACCOUNTS).reduce((prev, cur) => { prev.push({ agnAbbr: cur.SAGNABBR, image: cur.BIMAGE }); return prev; }, []) : []; //Загружаем учётные документы let docLinks = await getDocLinks(tasks.filters.values.type); //Возвращаем результат return { typeLoaded: tasks.filters.values.type, evRoutes: [...newRoutes], evPoints: [...newPoints], noteTypes: [...newNoteTypes], docLinks: [...docLinks], accounts: [...newAccounts] }; }; //Считывание данных let getData = async () => { //Инициализируем информацию о типе событии let eventData = { ...tasks.extraData }; //Если необходимо обновить информацию о типе события if (!tasks.extraData.typeLoaded || tasks.filters.values.type !== tasks.extraData.typeLoaded) { //Загружаем информацию о типе события eventData = await getEventData(); } //Считываем информацию о задачах let eventTasks = await getTasks(); //Добавление описания точки маршрута eventTasks.statuses.map(s => (s["pointDescr"] = eventData.evPoints.find(ep => ep.point === s.code).pointDescr)); //Загружаем данные setTasks(pv => ({ ...pv, groupsLoaded: true, tasksLoaded: true, statuses: eventTasks.statuses, rows: eventTasks.rows, extraData: eventData, reload: false })); }; //Если необходимо загрузить данные и указан тип событий if (tasks.reload && tasks.filters.values.type) { //Загружаем данные getData(); } }, [ tasks.reload, tasks.filters.values.type, tasks.orders, executeStored, SERV_DATA_TYPE_CLOB, tasks.filters.fArray, tasks.tasksLoaded, tasks.extraData, getDocLinks ]); return [ tasks, taskFormOpen, setTaskFormOpen, cardSettings, handleFilterOk, handleFilterCancel, handleFilterClick, handleCardSettingsClick, handleCardSettingsOk, handleCardSettingsCancel, handleReload, onDragEnd, handleOrderChanged, getDocLinks ]; }; //Хук для получения пользовательских настроек разметки const useColorRules = () => { //Собственное состояние const [clrRules, setClrRules] = useState({ loaded: false, rules: [] }); //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); useEffect(() => { let getClrRules = async () => { //Получаем массив пользовательских настроек const data = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DP_RULES_GET", respArg: "COUT" }); //Инициализируем let newClrRules = []; if (data) { //Формируем структуру настройки arrayFormer(data.XRULES).map((cr, i) => { let fromV; let toV; if (cr.STYPE === "number") { fromV = cr.NFROM; toV = cr.NTO; } else if (cr.STYPE === "string") { fromV = cr.SFROM; toV = cr.STO; } else { fromV = cr.DFROM; toV = cr.DTO; } newClrRules.push({ id: i, fieldCode: cr.SFIELD, propName: cr.SDP_NAME, color: cr.SCOLOR, vType: cr.STYPE, from: fromV, to: toV }); }); setClrRules({ loaded: true, rules: [...newClrRules] }); } }; if (!clrRules.loaded) getClrRules(); }, [clrRules.loaded, executeStored]); return [clrRules]; }; //Хук для получения свойств раздела "События" 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 }, respArg: "COUT" }); //Инициализируем let newDocProps = []; if (data) { //Формируем структуру настройки arrayFormer(data.XPROPS).map((dp, i) => { newDocProps.push({ id: i, rn: dp.NRN, name: dp.SNAME, readonly: dp.READONLY, checkValue: dp.CHECK_VALUE, checkUnique: dp.CHECK_UNIQUE, require: dp.REQUIRE, duplicateValue: dp.DUPLICATE_VALUE, accessMode: dp.NACCESS_MODE, showInGrid: dp.SHOW_IN_GRID, defaultStr: dp.SDEFAULT_STR, defaultNum: dp.NDEFAULT_NUM, defaultDate: dp.DDEFAULT_DATE, entryType: dp.NENTRY_TYPE, format: dp.NFORMAT, dataSubtype: dp.NDATA_SUBTYPE, numWidth: dp.NNUM_WIDTH, numPrecision: dp.NNUM_PRECISION, strWidth: dp.NSTR_WIDTH, unitcode: dp.SUNITCODE, paramRn: dp.NPARAM_RN, paramIn: dp.SPARAM_IN_CODE, paramOut: dp.SPARAM_OUT_CODE, showMethodRn: dp.NSHOW_METHOD_RN, showMethodCode: dp.SMETHOD_CODE, extraDictRn: dp.NEXTRA_DICT_RN, initRn: dp.NINIT_RN }); }); setDocsProps({ loaded: true, props: [...newDocProps] }); } }; if (!docProps.loaded) getDocsProps(); }, [docProps.loaded, executeStored, taskType]); return [docProps]; }; //Хук для события const useClientEvent = (taskRn, taskType = "", taskStatus = "") => { //Собственное состояние const [task, setTask] = useState({ init: true, nrn: taskRn, scrn: "", sprefix: "", snumber: "", stype: taskType, sstatus: taskStatus, sdescription: "", sclnt_clnclients: "", sclnt_clnperson: "", dstart_date: "", sinit_clnperson: "", sinit_user: "", sinit_reason: "", sto_company: "", sto_department: "", sto_clnpost: "", sto_clnpsdep: "", sto_clnperson: "", sto_fcstaffgrp: "", sto_user: "", sto_usergrp: "", scurrent_user: "", isUpdate: false, insertDisabled: true, updateDisabled: true, docProps: {} }); //Подключение к контексту взаимодействия с сервером const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); //Подключение к контексту приложения const { pOnlineShowDictionary } = useContext(ApplicationСtx); const initEventType = useCallback(async () => { //Считываем параметры исходя из типа события const data = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVNTYPES_INIT", args: { SEVENT_TYPE: task.stype, SCURRENT_PREF: task.sprefix }, tagValueProcessor: () => undefined }); if (data) { setTask(pv => ({ ...pv, sprefix: data.SPREF, snumber: data.SNUMB })); } }, [task.sprefix, task.stype, executeStored]); //Отображение раздела "Клиенты" const handleClientClientsOpen = useCallback(async () => { pOnlineShowDictionary({ unitCode: "ClientClients", showMethod: "main", inputParameters: [{ name: "in_CLIENT_CODE", value: task.sclnt_clnclients }], callBack: res => { res.success ? setTask(pv => ({ ...pv, sclnt_clnclients: res.outParameters.out_CLIENT_CODE, sclnt_clnperson: "" })) : null; } }); }, [pOnlineShowDictionary, task.sclnt_clnclients]); //Отображение раздела "Сотрудники" const handleClientPersonOpen = useCallback( //Тип открытия (0 - для клиента, 1 - для инициатора) async (nType = 0) => { pOnlineShowDictionary({ unitCode: "ClientPersons", showMethod: "main", inputParameters: [{ name: "in_CODE", value: nType === 0 ? task.sclnt_clnperson : task.sinit_clnperson }], callBack: res => { if (res.success) { if (nType === 0) { setTask(pv => ({ ...pv, sclnt_clnperson: res.outParameters.out_CODE, sclnt_clnclients: "" })); } else { setTask(pv => ({ ...pv, sinit_clnperson: res.outParameters.out_CODE })); } } } }); }, [pOnlineShowDictionary, task.sclnt_clnperson, task.sinit_clnperson] ); //Отображение раздела "Каталоги" для событий const handleCrnOpen = useCallback(async () => { pOnlineShowDictionary({ unitCode: "CatalogTree", showMethod: "main", inputParameters: [ { name: "in_DOCNAME", value: "ClientEvents" }, { name: "in_NAME", value: task.scrn } ], callBack: res => { res.success ? setTask(pv => ({ ...pv, scrn: res.outParameters.out_NAME })) : null; } }); }, [pOnlineShowDictionary, task.scrn]); //Считывание следующего номера события const getEventNextNumb = 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, task.sprefix]); //Добавление события const insertEvent = useCallback( 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.dplan_date, // ? dayjs(task.dplan_date).format("DD.MM.YYYY HH:mm") : null, SINIT_PERSON: task.sinit_clnperson, SCLIENT_CLIENT: task.sclnt_clnclients, SCLIENT_PERSON: task.sclnt_clnperson, SDESCRIPTION: task.sdescription, SREASON: task.sinit_reason, 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(); }, [ executeStored, task.scrn, task.sprefix, task.snumber, task.stype, task.sstatus, task.dplan_date, task.sinit_clnperson, task.sclnt_clnclients, task.sclnt_clnperson, task.sdescription, task.sinit_reason, task.docProps, SERV_DATA_TYPE_CLOB ] ); //Исправление события const updateEvent = useCallback( async callBack => { await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_UPDATE", args: { NCLNEVENTS: task.nrn, SCLIENT_CLIENT: task.sclnt_clnclients, SCLIENT_PERSON: task.sclnt_clnperson, 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(); }, [SERV_DATA_TYPE_CLOB, executeStored, task.docProps, task.nrn, task.sclnt_clnclients, task.sclnt_clnperson, task.sdescription] ); 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 newDocProps = {}; Object.keys(data.XEVENT) .filter(k => k.includes("DP_")) .map(dp => (newDocProps = { ...newDocProps, [dp]: data.XEVENT[dp] })); 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, sclnt_clnclients: data.XEVENT.SCLIENT_CLIENT, sclnt_clnperson: data.XEVENT.SCLIENT_PERSON, dplan_date: data.XEVENT.SPLAN_DATE, sinit_clnperson: data.XEVENT.SINIT_PERSON, sinit_user: data.XEVENT.SINIT_AUTHID, sinit_reason: data.XEVENT.SREASON, sto_company: data.XEVENT.SSEND_CLIENT, sto_department: data.XEVENT.SSEND_DIVISION, sto_clnpost: data.XEVENT.SSEND_POST, sto_clnpsdep: data.XEVENT.SSEND_PERFORM, sto_clnperson: data.XEVENT.SSEND_PERSON, sto_fcstaffgrp: data.XEVENT.SSEND_STAFFGRP, sto_user: data.XEVENT.SSEND_USER_NAME, sto_usergrp: data.XEVENT.SSEND_USER_GROUP, scurrent_user: data.XEVENT.SINIT_AUTHID, isUpdate: true, init: false, docProps: newDocProps })); }; //Инициализация параметров события readEvent(); } else { //Считывание изначальных параметров события const initEvent = async () => { const data = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_INIT", args: {} }); if (data) { setTask(pv => ({ ...pv, sprefix: data.SPREF, snumber: data.SNUMB, scurrent_user: data.SINIT_AUTHNAME, sinit_clnperson: data.SINIT_PERSON, sinit_user: !data.SINIT_PERSON ? data.SINIT_AUTHNAME : "", init: false })); } }; //Инициализация изначальных параметров события initEvent(); initEventType(); } } if (!task.init) { setTask(pv => ({ ...pv, sinit_user: !task.sinit_clnperson ? task.scurrent_user : "" })); } }, [executeStored, task.init, task.nrn, task.stype, task.scurrent_user, task.sinit_clnperson, taskRn, initEventType]); //Проверка доступности действия useEffect(() => { setTask(pv => ({ ...pv, insertDisabled: !task.scrn || !task.sprefix || !task.snumber || !task.stype || !task.sstatus || !task.sdescription || (!task.sinit_clnperson && !task.sinit_user), updateDisabled: !task.sdescription })); }, [task.scrn, task.sdescription, task.sinit_clnperson, task.sinit_user, task.snumber, task.sprefix, task.sstatus, task.stype]); return [task, setTask, insertEvent, updateEvent, handleClientClientsOpen, handleClientPersonOpen, handleCrnOpen, getEventNextNumb]; }; //Хук дополнительныч настроек const useSettings = statuses => { //Собственное состояние const [settings, setSettings] = useState({ open: false, statusesSort: { sorted: false, attr: localStorage.getItem("settingsSortAttr") ? localStorage.getItem("settingsSortAttr") : "name", dest: localStorage.getItem("settingsSortDest") ? localStorage.getItem("settingsSortDest") : "asc", statuses: [] }, colorRule: localStorage.getItem("settingsColorRule") ? JSON.parse(localStorage.getItem("settingsColorRule")) : {} }); //При открытии диалога дополнительных настроек const settingsOpen = () => setSettings(pv => ({ ...pv, open: true })); //При закрытии диалога дополнительных настроек const settingsClose = () => setSettings(pv => ({ ...pv, open: false })); //Изменение состояния после сортировки const afterSort = statuses => setSettings(pv => ({ ...pv, statusesSort: { ...pv.statusesSort, sorted: true, statuses: statuses } })); //При закрытии диалога дополнительных настроек по кнопке ОК const handleSettingsOk = s => { setSettings({ ...s, open: false, statusesSort: { ...s.statusesSort, sorted: false } }); }; //При получении новых настроек сортировки useEffect(() => { //Подгрузкка новых статусов if (statuses.length > 0 && statuses.toString() !== settings.statusesSort.statuses.toString() && settings.statusesSort.sorted) setSettings(pv => ({ ...pv, statusesSort: { ...pv.statusesSort, sorted: false } })); //Сортировка if (statuses.length > 0 && !settings.statusesSort.sorted) { const attr = settings.statusesSort.attr; const d = settings.statusesSort.dest; let s = statuses; s.sort((a, b) => (d === "asc" ? a[attr].localeCompare(b[attr]) : b[attr].localeCompare(a[attr]))); afterSort(s); } }, [settings.statusesSort.attr, settings.statusesSort.dest, settings.statusesSort.sorted, settings.statusesSort.statuses, statuses]); //Сохранение при закрытии панели useEffect(() => { window.addEventListener("beforeunload", function () { localStorage.setItem("settingsSortAttr", settings.statusesSort.attr); localStorage.setItem("settingsSortDest", settings.statusesSort.dest); localStorage.setItem("settingsColorRule", JSON.stringify(settings.colorRule)); }); }, [settings.colorRule, settings.statusesSort.attr, settings.statusesSort.dest]); return [settings, settingsOpen, settingsClose, handleSettingsOk]; }; //Хук карточки события const useTaskCard = () => { //Собственное состояние const [taskCard, setTaskCard] = useState({ openEdit: false }); //Состояние действий const [cardActions, setCardActions] = useState({ anchorMenuMethods: null, openMethods: false }); //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); //Подключение к контексту сообщений const { showMsgWarn } = useContext(MessagingСtx); //Подключение к контексту приложения const { pOnlineShowDictionary } = useContext(ApplicationСtx); //Удаление контрагента const deleteTask = useCallback( async (nEvent, handleReload) => { await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DELETE", args: { NCLNEVENTS: nEvent } }); //Если требуется перезагрузить данные if (handleReload) { handleReload(); } }, [executeStored] ); //Возврат в предыдущую точку события const returnTask = useCallback( async (nEvent, handleReload) => { await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_RETURN", args: { NCLNEVENTS: nEvent } }); //Если требуется перезагрузить данные if (handleReload) { handleReload(); } }, [executeStored] ); //По нажатию на открытие меню действий const handleMethodsMenuButtonClick = event => { setCardActions(pv => ({ ...pv, anchorMenuMethods: event.currentTarget, openMethods: true })); }; //При закрытии меню const handleMethodsMenuClose = () => { setCardActions(pv => ({ ...pv, anchorMenuMethods: null, openMethods: false })); }; //По нажатия действия "Редактировать" const handleTaskEdit = () => { setTaskCard(pv => ({ ...pv, openEdit: true })); }; //По нажатия действия "Редактировать в разделе" const handleTaskEditClient = useCallback( async nEvent => { const data = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.SELECT_CLNEVENT", args: { NRN: nEvent } }); if (data.NIDENT) { pOnlineShowDictionary({ unitCode: "ClientEvents", inputParameters: [{ name: "in_Ident", value: data.NIDENT }] }); } }, [executeStored, pOnlineShowDictionary] ); //По нажатию действия "Удалить" const handleTaskDelete = (nEvent, handleReload) => { showMsgWarn("Удалить событие?", () => deleteTask(nEvent, handleReload)); }; //По нажатию действия "Выполнить возврат" const handleTaskReturn = (nEvent, handleReload) => { showMsgWarn("Выполнить возврат события в предыдущую точку?", () => returnTask(nEvent, handleReload)); }; //По нажатию действия "Примечания" const handleEventNotesOpen = useCallback( async nEvent => { pOnlineShowDictionary({ unitCode: "ClientEventsNotes", showMethod: "main", inputParameters: [{ name: "in_PRN", value: nEvent }] }); }, [pOnlineShowDictionary] ); //По нажатию действия "Присоединенные документы" const handleFileLinksOpen = useCallback( async nEvent => { pOnlineShowDictionary({ unitCode: "FileLinks", showMethod: "main_link", inputParameters: [ { name: "in_PRN", value: nEvent }, { name: "in_UNITCODE", value: "ClientEvents" } ] }); }, [pOnlineShowDictionary] ); //По нажатию действия "Перейти" const handleStateChange = useCallback( async (nEvent, handleReload, evPoints, handleNote) => { //Выполняем инициализацию параметров const firstStep = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE", args: { NSTEP: 1, NEVENT: nEvent } }); if (firstStep) { //Открываем раздел "Маршруты событий (точки перехода)" для выбора следующей точки pOnlineShowDictionary({ unitCode: "EventRoutesPointsPasses", showMethod: "main_passes", inputParameters: [ { name: "in_ENVTYPE_CODE", value: firstStep.SEVENT_TYPE }, { name: "in_ENVSTAT_CODE", value: firstStep.SEVENT_STAT }, { name: "in_POINT", value: firstStep.NPOINT } ], callBack: async point => { //Выполняем проверку необходимости выбора исполнителя const secondStep = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE", args: { NIDENT: firstStep.NIDENT, NSTEP: 2, NPASS: point.outParameters.out_RN } }); const pointSettings = evPoints.find(ep => ep.point === point.outParameters.out_NEXT_POINT); if (secondStep) { //Если требуется выбрать получателя if (secondStep.NSELECT_EXEC === 1) { //Открываем раздел "Маршруты событий (исполнители в точках)" для выбора исполнителя pOnlineShowDictionary({ unitCode: "EventRoutesPointExecuters", showMethod: "executers", inputParameters: [ { name: "in_IDENT", value: firstStep.NIDENT }, { name: "in_EVENT", value: nEvent }, { 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: async send => { //Общие аргументы const mainArgs = { NIDENT: firstStep.NIDENT, NSTEP: 3, NEVENT: nEvent, SEVENT_STAT: point.outParameters.out_NEXT_POINT, SSEND_CLIENT: send.outParameters.out_CLIENT_CODE, SSEND_DIVISION: send.outParameters.out_DIVISION_CODE, SSEND_POST: send.outParameters.out_POST_CODE, SSEND_PERFORM: send.outParameters.out_POST_IN_DIV_CODE, SSEND_PERSON: send.outParameters.out_PERSON_CODE, SSEND_STAFFGRP: send.outParameters.out_STAFFGRP_CODE, SSEND_USER_GROUP: send.outParameters.out_USER_GROUP_CODE, SSEND_USER_NAME: send.outParameters.out_USER_NAME, NSEND_PREDEFINED_EXEC: send.outParameters.out_PREDEFINED_EXEC, NSEND_PREDEFINED_PROC: send.outParameters.out_PREDEFINED_PROC }; //Выполняем переход к выбранной точке с исполнителем pointSettings.addNoteOnChst ? handleNote(async n => { //Если требуется примечание await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE", args: { ...mainArgs, ...{ SNOTE_HEADER: n.header, SNOTE: n.text } } }); //Если требуется перезагрузить данные if (handleReload) { handleReload(); } }) : await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE", args: mainArgs }); //Если требуется перезагрузить данные if (handleReload && !pointSettings.addNoteOnChst) { handleReload(); } } }); } else { //Общие аргументы const mainArgs = { NIDENT: firstStep.NIDENT, NSTEP: 3, NEVENT: nEvent, SEVENT_STAT: point.outParameters.out_NEXT_POINT }; //Выполняем переход к выбранной точке с предопределенным исполнителем pointSettings.addNoteOnChst ? handleNote(async n => { //Если требуется примечание await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE", args: { ...mainArgs, ...{ SNOTE_HEADER: n.header, SNOTE: n.text } } }); //Если требуется перезагрузить данные if (handleReload) { handleReload(); } }) : await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE", args: mainArgs }); //Если требуется перезагрузить данные if (handleReload && !pointSettings.addNoteOnChst) { handleReload(); } } } } }); } }, // eslint-disable-next-line react-hooks/exhaustive-deps [executeStored, pOnlineShowDictionary] ); //Изменение статуса события const handleSend = useCallback( async (nEvent, handleReload, note = null) => { //Выполняем инициализацию параметров const firstStep = await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SEND", args: { NSTEP: 1, NEVENT: nEvent } }); if (firstStep) { //Открываем раздел "Маршруты событий (исполнители в точках)" для выбора исполнителя pOnlineShowDictionary({ unitCode: "EventRoutesPointExecuters", showMethod: "executers", 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: async send => { //Выполняем проверку необходимости выбора исполнителя await executeStored({ stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SEND", args: { NIDENT: firstStep.NIDENT, NSTEP: 2, NEVENT: nEvent, SSEND_CLIENT: send.outParameters.out_CLIENT_CODE, SSEND_DIVISION: send.outParameters.out_DIVISION_CODE, SSEND_POST: send.outParameters.out_POST_CODE, SSEND_PERFORM: send.outParameters.out_POST_IN_DIV_CODE, SSEND_PERSON: send.outParameters.out_PERSON_CODE, SSEND_STAFFGRP: send.outParameters.out_STAFFGRP_CODE, SSEND_USER_GROUP: send.outParameters.out_USER_GROUP_CODE, SSEND_USER_NAME: send.outParameters.out_USER_NAME, NSEND_PREDEFINED_EXEC: send.outParameters.out_PREDEFINED_EXEC, NSEND_PREDEFINED_PROC: send.outParameters.out_PREDEFINED_PROC, SNOTE_HEADER: note.text ? note.header : null, SNOTE: note.text ? note.text : null } }); //Если требуется перезагрузить данные if (handleReload) { handleReload(); } } }); } }, [executeStored, pOnlineShowDictionary] ); //Формируем меню показателей const menuItems = [ { method: "EDIT", name: "Исправить", icon: "edit", visible: false, delimiter: false, needReload: false, func: handleTaskEdit }, { method: "EDIT_CLIENT", name: "Исправить в разделе", icon: "edit_note", visible: true, delimiter: false, needReload: false, func: handleTaskEditClient }, { method: "DELETE", name: "Удалить", icon: "delete", visible: true, delimiter: true, needReload: true, func: handleTaskDelete }, { method: "TASK_STATE_CHANGE", name: "Перейти", icon: "turn_right", visible: true, delimiter: false, needReload: true, func: handleStateChange }, { method: "TASK_RETURN", name: "Выполнить возврат", icon: "turn_left", visible: true, delimiter: false, needReload: true, func: handleTaskReturn }, { method: "TASK_SEND", name: "Направить", icon: "send", visible: true, delimiter: true, needReload: true, func: handleSend }, { method: "NOTES", name: "Примечания", icon: "event_note", visible: true, delimiter: true, needReload: false, func: handleEventNotesOpen }, { method: "FILE_LINKS", name: "Присоединенные документы", icon: "attach_file", visible: true, delimiter: false, needReload: false, func: handleFileLinksOpen } ]; return [taskCard, setTaskCard, cardActions, handleMethodsMenuButtonClick, handleMethodsMenuClose, menuItems]; }; //Хук для сортировок const useOrders = () => { //Состояние меню сортировки 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 [menuOrders, handleOrdersMenuButtonClick, handleOrdersMenuClose]; }; export { useTasks, useSettings, useColorRules, useDocsProps, useClientEvent, useTaskCard, useOrders };