create or replace package PKG_P8PANELS_VISUAL as

  /* Константы - типы данных */
  SDATA_TYPE_STR            constant PKG_STD.TSTRING := 'STR';  -- Тип данных "строка"
  SDATA_TYPE_NUMB           constant PKG_STD.TSTRING := 'NUMB'; -- Тип данных "число"
  SDATA_TYPE_DATE           constant PKG_STD.TSTRING := 'DATE'; -- Тип данных "дата"
  
  /* Константы - направление сортировки */
  SORDER_DIRECTION_ASC      constant PKG_STD.TSTRING := 'ASC';  -- По возрастанию
  SORDER_DIRECTION_DESC     constant PKG_STD.TSTRING := 'DESC'; -- По убыванию
  
  /* Константы - масштаб диаграммы Ганта */
  NGANTT_ZOOM_QUARTER_DAY   constant PKG_STD.TNUMBER := 0; -- Четверть дня
  NGANTT_ZOOM_HALF_DAY      constant PKG_STD.TNUMBER := 1; -- Пол дня
  NGANTT_ZOOM_DAY           constant PKG_STD.TNUMBER := 2; -- День
  NGANTT_ZOOM_WEEK          constant PKG_STD.TNUMBER := 3; -- Неделя
  NGANTT_ZOOM_MONTH         constant PKG_STD.TNUMBER := 4; -- Месяц
  
  /* Константы - тип графика */
  SCHART_TYPE_BAR           constant PKG_STD.TSTRING := 'bar';
  SCHART_TYPE_LINE          constant PKG_STD.TSTRING := 'line';
  SCHART_TYPE_PIE           constant PKG_STD.TSTRING := 'pie';
  SCHART_TYPE_DOUGHNUT      constant PKG_STD.TSTRING := 'doughnut';

  /* Константы - расположение легенды графика */  
  SCHART_LGND_POS_LEFT      constant PKG_STD.TSTRING := 'left';
  SCHART_LGND_POS_RIGHT     constant PKG_STD.TSTRING := 'right';
  SCHART_LGND_POS_TOP       constant PKG_STD.TSTRING := 'top';
  SCHART_LGND_POS_BOTTOM    constant PKG_STD.TSTRING := 'bottom';

  /* Типы данных - значение колонки таблицы данных */
  type TCOL_VAL is record
  (
    SVALUE                  PKG_STD.TLSTRING, -- Значение (строка)
    NVALUE                  PKG_STD.TNUMBER,  -- Значение (число)
    DVALUE                  PKG_STD.TLDATE    -- Значение (дата)
  );
  
  /* Типы данных - коллекция значений колонки таблицы данных */
  type TCOL_VALS is table of TCOL_VAL;
 
  /* Типы данных - описатель колонки таблицы данных */
  type TCOL_DEF is record
  (
    SNAME                   PKG_STD.TSTRING, -- Наименование
    SCAPTION                PKG_STD.TSTRING, -- Заголовок
    SDATA_TYPE              PKG_STD.TSTRING, -- Тип данных (см. константы SDATA_TYPE_*)
    SCOND_FROM              PKG_STD.TSTRING, -- Наименование нижней границы условия отбора
    SCOND_TO                PKG_STD.TSTRING, -- Наименование верхней границы условия отбора
    BVISIBLE                boolean,         -- Разрешить отображение
    BORDER                  boolean,         -- Разрешить сортировку
    BFILTER                 boolean,         -- Разрешить отбор
    RCOL_VALS               TCOL_VALS        -- Предопределённые значения
  );
  
  /* Типы данных - коллекция описателей колонок таблицы данных */
  type TCOL_DEFS is table of TCOL_DEF;
  
  /* Типы данных - колонка */
  type TCOL is record
  (
    SNAME                   PKG_STD.TSTRING, -- Наименование
    RCOL_VAL                TCOL_VAL         -- Значение
  );

  /* Типы данных - коллекция колонок */
  type TCOLS is table of TCOL;
  
  /* Типы данных - строка */
  type TROW is record
  (
    RCOLS                   TCOLS       -- Колонки
  );
  
  /* Типы данных - коллекция строк */
  type TROWS is table of TROW;
  
  /* Типы данных - таблица данных */
  type TDATA_GRID is record
  (
    RCOL_DEFS               TCOL_DEFS,  -- Описание колонок
    RROWS                   TROWS       -- Данные строк
  );
  
  /* Типы данных - фильтр */
  type TFILTER is record
  (
    SNAME                   PKG_STD.TSTRING, -- Наименование
    SFROM                   PKG_STD.TSTRING, -- Значение "с"
    STO                     PKG_STD.TSTRING  -- Значение "по"
  );
  
  /* Типы данных - коллекция фильтров */
  type TFILTERS is table of TFILTER;
  
  /* Типы данных - сортировка */
  type TORDER is record
  (
    SNAME                   PKG_STD.TSTRING, -- Наименование
    SDIRECTION              PKG_STD.TSTRING  -- Направление (см. константы SORDER_DIRECTION_*)
  );
  
  /* Типы данных - коллекция сортировок */
  type TORDERS is table of TORDER;
  
  /* Типы данных - описание атрибута задачи для диаграммы Ганта */
  type TGANTT_TASK_ATTR is record
  (
    SNAME                   PKG_STD.TSTRING, -- Наименование
    SCAPTION                PKG_STD.TSTRING  -- Заголовок
  );
  
  /* Типы данных - коллекция описаний атрибутов задачи для диаграммы Ганта */
  type TGANTT_TASK_ATTRS is table of TGANTT_TASK_ATTR;

  /* Типы данных - значение атрибута задачи для диаграммы Ганта */
  type TGANTT_TASK_ATTR_VAL is record
  (
    SNAME                   PKG_STD.TSTRING, -- Наименование
    SVALUE                  PKG_STD.TSTRING  -- Значение
  );
  
  /* Типы данных - коллекция значений атрибутов задачи для диаграммы Ганта */
  type TGANTT_TASK_ATTR_VALS is table of TGANTT_TASK_ATTR_VAL;
  
  /* Типы данных - коллекция ссылок на предшествующие задачи для диаграммы Ганта */
  type TGANTT_TASK_DEPENDENCIES is table of PKG_STD.TREF;
  
  /* Тип данных - задача для диаграммы Ганта */
  type TGANTT_TASK is record
  (
    NRN                     PKG_STD.TREF,                    -- Рег. номер
    SNUMB                   PKG_STD.TSTRING,                 -- Номер
    SCAPTION                PKG_STD.TSTRING,                 -- Заголовок
    SNAME                   PKG_STD.TSTRING,                 -- Наименование
    DSTART                  PKG_STD.TLDATE,                  -- Дата начала
    DEND                    PKG_STD.TLDATE,                  -- Дата окончания
    NPROGRESS               PKG_STD.TNUMBER := null,         -- Прогресс (% готовности) задачи (null - не определен)
    SBG_COLOR               PKG_STD.TSTRING := null,         -- Цвет заливки задачи (null - использовать цвет по умолчанию из стилей, формат - HTML-цвет, #RRGGBBAA)
    STEXT_COLOR             PKG_STD.TSTRING := null,         -- Цвет текста заголовка задачи (null - использовать цвет по умолчанию из стилей, формат - HTML-цвет, #RRGGBBAA)
    BREAD_ONLY              boolean := null,                 -- Сроки и прогресс задачи только для чтения (null - как указано в описании диаграммы)
    BREAD_ONLY_DATES        boolean := null,                 -- Сроки задачи только для чтения (null - как указано в описании диаграммы)
    BREAD_ONLY_PROGRESS     boolean := null,                 -- Прогресс задачи только для чтения (null - как указано в описании диаграммы)
    RATTR_VALS              TGANTT_TASK_ATTR_VALS := null,   -- Значения дополнительных атрбутов (null - дополнительные атрибуты не определены)
    RDEPENDENCIES           TGANTT_TASK_DEPENDENCIES := null -- Список предшествующих задач
  );
  
  /* Тип данных - коллекция задач диаграммы Ганта */
  type TGANTT_TASKS is table of TGANTT_TASK;
  
  /* Тип данных - описание цвета задач диаграммы Ганта */
  type TGANTT_TASK_COLOR is record
  (
    SBG_COLOR               PKG_STD.TSTRING := null, -- Цвет заливки задачи (формат - HTML-цвет, #RRGGBBAA)
    STEXT_COLOR             PKG_STD.TSTRING := null, -- Цвет текста заголовка задачи (формат - HTML-цвет, #RRGGBBAA)
    SDESC                   PKG_STD.TSTRING          -- Описание
  );
  
  /* Тип данных - коллекция описаний цветов задач диаграммы Ганта */
  type TGANTT_TASK_COLORS is table of TGANTT_TASK_COLOR;
  
  /* Типы данных - диаграмма Ганта */
  type TGANTT is record
  (
    STITLE                  PKG_STD.TSTRING := null,             -- Заголовок (null - не отображать)
    NZOOM                   PKG_STD.TNUMBER := NGANTT_ZOOM_WEEK, -- Текущий масштаб (см. константы NGANTT_ZOOM_*)
    BZOOM_BAR               boolean := true,                     -- Обображать панель масштабирования
    BREAD_ONLY              boolean := false,                    -- Сроки и прогресс задач только для чтения
    BREAD_ONLY_DATES        boolean := false,                    -- Сроки задач только для чтения
    BREAD_ONLY_PROGRESS     boolean := false,                    -- Прогресс задач только для чтения
    RTASK_ATTRS             TGANTT_TASK_ATTRS,                   -- Описание атрибутов карточки задачи
    RTASK_COLORS            TGANTT_TASK_COLORS,                  -- Описание цветов задач
    RTASKS                  TGANTT_TASKS                         -- Список задач
  );
  
  /* Типы данных - значение атрибута элемента данных графика */
  type TCHART_DATASET_ITEM_ATTR_VAL is record
  (
    SNAME                   PKG_STD.TSTRING, -- Наименование
    SVALUE                  PKG_STD.TSTRING  -- Значение
  );
  
  /* Типы данных - коллекция значений атрибутов элемента данных графика */
  type TCHART_DATASET_ITEM_ATTR_VALS is table of TCHART_DATASET_ITEM_ATTR_VAL;  
  
  /* Тип данных - элемент данных графика */
  type TCHART_DATASET_ITEM is record
  (
    NVALUE                  PKG_STD.TNUMBER,                      -- Значение элемента данных, отображаемое на графике
    RATTR_VALS              TCHART_DATASET_ITEM_ATTR_VALS := null -- Значения дополнительных атрбутов (null - дополнительные атрибуты не определены)
  );
  
  /* Тип данных - коллекция элементов данных */
  type TCHART_DATASET_ITEMS is table of TCHART_DATASET_ITEM;
  
  /* Тип данных - набор данных графика */
  type TCHART_DATASET is record
  (
    SCAPTION                PKG_STD.TSTRING,         -- Заголовок
    SBORDER_COLOR           PKG_STD.TSTRING := null, -- Цвет границы элемента данных на графике (null - использовать цвет по умолчанию из стилей, формат - HTML-цвет, #RRGGBBAA)
    SBG_COLOR               PKG_STD.TSTRING := null, -- Цвет заливки элемента данных на графике (null - использовать цвет по умолчанию из стилей, формат - HTML-цвет, #RRGGBBAA)
    RITEMS                  TCHART_DATASET_ITEMS     -- Элементы данных
  );
  
  /* Тип данных - коллекция наборов данных графика */
  type TCHART_DATASETS is table of TCHART_DATASET;
  
  /* Тип данных - коллекция меток данных графика */
  type TCHART_LABELS is table of PKG_STD.TSTRING;
  
  /* Типы данных - график */
  type TCHART is record
  (
    STYPE                   PKG_STD.TSTRING,         -- Тип (см. константы SCHART_TYPE_*)
    STITLE                  PKG_STD.TSTRING := null, -- Заголовок (null - не отображать)
    SLGND_POS               PKG_STD.TSTRING := null, -- Расположение легенды (null - не отображать, см. константы SCHART_LGND_POS_*)
    RLABELS                 TCHART_LABELS,           -- Метки значений
    RDATASETS               TCHART_DATASETS          -- Наборы данных
  );
  
  /* Расчет диапаона выдаваемых записей */
  procedure UTL_ROWS_LIMITS_CALC
  (
    NPAGE_NUMBER            in number,  -- Номер страницы (игнорируется при NPAGE_SIZE=0)
    NPAGE_SIZE              in number,  -- Количество записей на странице (0 - все)
    NROW_FROM               out number, -- Нижняя граница диапазона
    NROW_TO                 out number  -- Верхняя граница диапазона
  );
  
  /* Формирование наименования условия отбора для нижней границы */
  function UTL_COND_NAME_MAKE_FROM
  (
    SNAME                   in varchar2 -- Наименование колонки
  ) return                  varchar2;   -- Результат
  
  /* Формирование наименования условия отбора для верхней границы */
  function UTL_COND_NAME_MAKE_TO
  (
    SNAME                   in varchar2 -- Наименование колонки
  ) return                  varchar2;   -- Результат
  
  /* Добавление значения в коллекцию */
  procedure TCOL_VALS_ADD
  (
    RCOL_VALS               in out nocopy TCOL_VALS, -- Коллекция значений
    SVALUE                  in varchar2 := null,     -- Значение (строка)
    NVALUE                  in number := null,       -- Значение (число)
    DVALUE                  in date := null,         -- Значение (дата)
    BCLEAR                  in boolean := false      -- Флаг очистки коллекции (false - не очищать, true - очистить коллекцию перед добавлением)
  );
  
  /* Формирование строки */
  function TROW_MAKE
  return                    TROW;       -- Результат работы
  
  /* Добавление колонки к строке */
  procedure TROW_ADD_COL
  (
    RROW                    in out nocopy TROW,  -- Строка
    SNAME                   in varchar2,         -- Наименование колонки
    SVALUE                  in varchar2 := null, -- Значение (строка)
    NVALUE                  in number := null,   -- Значение (число)
    DVALUE                  in date := null,     -- Значение (дата)
    BCLEAR                  in boolean := false  -- Флаг очистки коллекции (false - не очищать, true - очистить коллекцию перед добавлением)
  );
  
  /* Добавление строковой колонки к строке из курсора динамического запроса */
  procedure TROW_ADD_CUR_COLS
  (
    RROW                    in out nocopy TROW, -- Строка
    SNAME                   in varchar2,        -- Наименование колонки
    ICURSOR                 in integer,         -- Курсор
    NPOSITION               in number,          -- Номер колонки в курсоре
    BCLEAR                  in boolean := false -- Флаг очистки коллекции (false - не очищать, true - очистить коллекцию перед добавлением)
  );
  
  /* Добавление числовой колонки к строке из курсора динамического запроса */
  procedure TROW_ADD_CUR_COLN
  (
    RROW                    in out nocopy TROW, -- Строка
    SNAME                   in varchar2,        -- Наименование колонки
    ICURSOR                 in integer,         -- Курсор
    NPOSITION               in number,          -- Номер колонки в курсоре
    BCLEAR                  in boolean := false -- Флаг очистки коллекции (false - не очищать, true - очистить коллекцию перед добавлением)
  );

  /* Добавление колонки типа "дата" к строке из курсора динамического запроса */
  procedure TROW_ADD_CUR_COLD
  (
    RROW                    in out nocopy TROW, -- Строка
    SNAME                   in varchar2,        -- Наименование колонки
    ICURSOR                 in integer,         -- Курсор
    NPOSITION               in number,          -- Номер колонки в курсоре
    BCLEAR                  in boolean := false -- Флаг очистки коллекции (false - не очищать, true - очистить коллекцию перед добавлением)
  );
  
  /* Формирование таблицы данных */
  function TDATA_GRID_MAKE
  return                    TDATA_GRID; -- Результат работы
  
  /* Поиск описания колонки в таблице данных по наименованию */
  function TDATA_GRID_FIND_COL_DEF
  (
    RDATA_GRID              in TDATA_GRID, -- Описание таблицы данных
    SNAME                   in varchar2    -- Наименование колонки
  ) return                  TCOL_DEF;      -- Найденное описание (null - если не нашли)
  
  /* Добавление описания колонки к таблице данных */
  procedure TDATA_GRID_ADD_COL_DEF
  (
    RDATA_GRID              in out nocopy TDATA_GRID,      -- Описание таблицы данных
    SNAME                   in varchar2,                   -- Наименование колонки
    SCAPTION                in varchar2,                   -- Заголовок колонки
    SDATA_TYPE              in varchar2 := SDATA_TYPE_STR, -- Тип данных колонки (см. константы SDATA_TYPE_*)
    SCOND_FROM              in varchar2 := null,           -- Наименование нижней границы условия отбора (null - используется UTL_COND_NAME_MAKE_FROM)
    SCOND_TO                in varchar2 := null,           -- Наименование верхней границы условия отбора (null - используется UTL_COND_NAME_MAKE_TO)
    BVISIBLE                in boolean := true,            -- Разрешить отображение
    BORDER                  in boolean := false,           -- Разрешить сортировку по колонке
    BFILTER                 in boolean := false,           -- Разрешить отбор по колонке
    RCOL_VALS               in TCOL_VALS := null,          -- Предопределённые значения колонки
    BCLEAR                  in boolean := false            -- Флаг очистки коллекции описаний колонок таблицы данных (false - не очищать, true - очистить коллекцию перед добавлением)
  );
  
  /* Добавление описания колонки к таблице данных */
  procedure TDATA_GRID_ADD_ROW
  (
    RDATA_GRID              in out nocopy TDATA_GRID, -- Описание таблицы данных
    RROW                    in TROW,                  -- Строка
    BCLEAR                  in boolean := false       -- Флаг очистки коллекции строк таблицы данных (false - не очищать, true - очистить коллекцию перед добавлением)
  );

  /* Сериализация таблицы данных */
  function TDATA_GRID_TO_XML
  (
    RDATA_GRID              in TDATA_GRID, -- Описание таблицы данных
    NINCLUDE_DEF            in number := 1 -- Включить описание колонок (0 - нет, 1 - да)
  ) return                  clob;          -- XML-описание

  
  /* Конвертация значений фильтра в число */
  procedure TFILTER_TO_NUMBER
  (
    RFILTER                 in TFILTER, -- Фильтр
    NFROM                   out number, -- Значение нижней границы диапазона
    NTO                     out number  -- Значение верхней границы диапазона
  );

  /* Конвертация значений фильтра в дату */
  procedure TFILTER_TO_DATE
  (
    RFILTER                 in TFILTER, -- Фильтр
    DFROM                   out date,   -- Значение нижней границы диапазона
    DTO                     out date    -- Значение верхней границы диапазона
  );
  
  /* Поиск фильтра в коллекции */
  function TFILTERS_FIND
  (
    RFILTERS                in TFILTERS, -- Коллекция фильтров
    SNAME                   in varchar2  -- Наименование
  ) return                  TFILTER;     -- Найденный фильтр (null - если не нашли)
  
  /* Десериализация фильтров */
  function TFILTERS_FROM_XML
  (
    CFILTERS                in clob     -- Сериализованное представление фильтров (BASE64(<filters><name>ИМЯ</name><from>ЗНАЧЕНИЕ</from><to>ЗНАЧЕНИЕ</to></filters>...))
  ) return                  TFILTERS;   -- Результат работы

  /* Применение параметров фильтрации в запросе */
  procedure TFILTERS_SET_QUERY
  (
    NIDENT                  in number,         -- Идентификатор отбора
    NCOMPANY                in number,         -- Рег. номер организации
    NPARENT                 in number := null, -- Рег. номер родителя
    SUNIT                   in varchar2,       -- Код раздела
    SPROCEDURE              in varchar2,       -- Наименование серверной процедуры отбора
    RDATA_GRID              in TDATA_GRID,     -- Описание таблицы данных
    RFILTERS                in TFILTERS        -- Коллекция фильтров
  );
  
  /* Десериализация сортировок */
  function TORDERS_FROM_XML
  (
    CORDERS                 in clob     -- Сериализованное представление сотрировок (BASE64(<orders><name>ИМЯ</name><direction>ASC/DESC</direction></orders>...))
  ) return                  TORDERS;    -- Результат работы

  /* Применение параметров сортировки в запросе */
  procedure TORDERS_SET_QUERY
  (
    RDATA_GRID              in TDATA_GRID,     -- Описание таблицы
    RORDERS                 in TORDERS,        -- Коллекция сортировок
    SPATTERN                in varchar2,       -- Шаблон для подстановки условий отбора в запрос
    CSQL                    in out nocopy clob -- Буфер запроса
  );
  
  /* Формирование задачи для диаграммы Ганта */
  function TGANTT_TASK_MAKE
  (
    NRN                     in number,           -- Рег. номер
    SNUMB                   in varchar2,         -- Номер
    SCAPTION                in varchar2,         -- Заголовок
    SNAME                   in varchar2,         -- Наименование    
    DSTART                  in date,             -- Дата начала
    DEND                    in date,             -- Дата окончания
    NPROGRESS               in number := null,   -- Прогресс (% готовности) задачи (null - не определен)
    SBG_COLOR               in varchar2 := null, -- Цвет заливки задачи (null - использовать цвет по умолчанию из стилей, формат - HTML-цвет, #RRGGBBAA)
    STEXT_COLOR             in varchar2 := null, -- Цвет текста заголовка задачи (null - использовать цвет по умолчанию из стилей, формат - HTML-цвет, #RRGGBBAA)
    BREAD_ONLY              in boolean := null,  -- Сроки и прогресс задачи только для чтения (null - как указано в описании диаграммы)
    BREAD_ONLY_DATES        in boolean := null,  -- Сроки задачи только для чтения (null - как указано в описании диаграммы)
    BREAD_ONLY_PROGRESS     in boolean := null   -- Прогресс задачи только для чтения (null - как указано в описании диаграммы)
  ) return                  TGANTT_TASK;         -- Результат работы
  
  /* Добавление значения атрибута к задаче диаграммы Ганта */
  procedure TGANTT_TASK_ADD_ATTR_VAL
  (
    RGANTT                  in TGANTT,                 -- Описание диаграммы
    RTASK                   in out nocopy TGANTT_TASK, -- Описание задачи
    SNAME                   in varchar2,               -- Наименование
    SVALUE                  in varchar2,               -- Значение
    BCLEAR                  in boolean := false        -- Флаг очистки коллекции значений атрибутов (false - не очищать, true - очистить коллекцию перед добавлением)
  );
  
  /* Добавление предшествующей задачи к задаче диаграммы Ганта */
  procedure TGANTT_TASK_ADD_DEPENDENCY
  (
    RTASK                   in out nocopy TGANTT_TASK, -- Описание задачи
    NDEPENDENCY             in number,                 -- Рег. номер предшествующей задачи
    BCLEAR                  in boolean := false        -- Флаг очистки коллекции предшествущих задач (false - не очищать, true - очистить коллекцию перед добавлением)
  );
  
  /* Формирование диаграммы Ганта */
  function TGANTT_MAKE
  (
    STITLE                  in varchar2 := null,           -- Заголовок (null - не отображать)
    NZOOM                   in number := NGANTT_ZOOM_WEEK, -- Текущий масштаб (см. константы NGANTT_ZOOM_*)
    BZOOM_BAR               in boolean := true,            -- Обображать панель масштабирования
    BREAD_ONLY              in boolean := false,           -- Сроки и прогресс задач только для чтения
    BREAD_ONLY_DATES        in boolean := false,           -- Сроки задач только для чтения
    BREAD_ONLY_PROGRESS     in boolean := false            -- Прогресс задач только для чтения
  ) return                  TGANTT;                        -- Результат работы
  
  /* Добавление описания атрибута карточки задачи диаграммы Ганта */
  procedure TGANTT_ADD_TASK_ATTR
  (
    RGANTT                  in out nocopy TGANTT, -- Описание диаграммы Ганта
    SNAME                   in varchar2,          -- Наименование
    SCAPTION                in varchar2,          -- Заголовок
    BCLEAR                  in boolean := false   -- Флаг очистки коллекции атрибутов (false - не очищать, true - очистить коллекцию перед добавлением)
  );

  /* Добавление описания цвета задачи диаграммы Ганта */
  procedure TGANTT_ADD_TASK_COLOR
  (
    RGANTT                  in out nocopy TGANTT, -- Описание диаграммы Ганта
    SBG_COLOR               in varchar2 := null,  -- Цвет заливки задачи (формат - HTML-цвет, #RRGGBBAA)
    STEXT_COLOR             in varchar2 := null,  -- Цвет текста заголовка задачи (формат - HTML-цвет, #RRGGBBAA)
    SDESC                   in varchar2,          -- Описание
    BCLEAR                  in boolean := false   -- Флаг очистки коллекции цветов (false - не очищать, true - очистить коллекцию перед добавлением)
  );

  /* Добавление задачи к диаграмме Ганта */
  procedure TGANTT_ADD_TASK
  (
    RGANTT                  in out nocopy TGANTT, -- Описание диаграммы Ганта
    RTASK                   in TGANTT_TASK,       -- Задача
    BCLEAR                  in boolean := false   -- Флаг очистки коллекции задач диаграммы (false - не очищать, true - очистить коллекцию перед добавлением)
  );
  
  /* Сериализация диаграммы Ганта */
  function TGANTT_TO_XML
  (
    RGANTT                  in TGANTT,     -- Описание диаграммы Ганта
    NINCLUDE_DEF            in number := 1 -- Включить описание заголовка (0 - нет, 1 - да)
  ) return                  clob;          -- XML-описание

  /* Добавление дополнительного атрибута элемента данных графика */
  procedure TCHART_DATASET_ITM_ATTR_VL_ADD
  (
    RATTR_VALS              in out nocopy TCHART_DATASET_ITEM_ATTR_VALS, -- Коллекция дополнительных атрибутов элемента данных графика
    SNAME                   PKG_STD.TSTRING,                             -- Наименование
    SVALUE                  PKG_STD.TSTRING,                             -- Значение
    BCLEAR                  in boolean := false                          -- Флаг очистки коллекции дополнительных атрибутов элемента данных (false - не очищать, true - очистить коллекцию перед добавлением)
  );

  /* Формирование набора данных графика */
  function TCHART_DATASET_MAKE
  (
    SCAPTION                in varchar2,         -- Заголовок
    SBORDER_COLOR           in varchar2 := null, -- Цвет границы элемента данных на графике (null - использовать цвет по умолчанию из стилей, формат - HTML-цвет, #RRGGBBAA)
    SBG_COLOR               in varchar2 := null  -- Цвет заливки элемента данных на графике (null - использовать цвет по умолчанию из стилей, формат - HTML-цвет, #RRGGBBAA)
  ) return                  TCHART_DATASET;      -- Результат работы

  /* Добавление элемента в набор данных графика */
  procedure TCHART_DATASET_ADD_ITEM
  (
    RDATASET                in out nocopy TCHART_DATASET,             -- Описание набора данных графика
    NVALUE                  in number,                                -- Значение элемента данных, отображаемое на графике
    RATTR_VALS              in TCHART_DATASET_ITEM_ATTR_VALS := null, -- Значения дополнительных атрбутов (null - дополнительные атрибуты не определены)
    BCLEAR                  in boolean := false                       -- Флаг очистки коллекции элементов набора данных (false - не очищать, true - очистить коллекцию перед добавлением)
  );

  /* Формирование графика */
  function TCHART_MAKE
  (
    STYPE                   in varchar2,         -- Тип (см. константы SCHART_TYPE_*)
    STITLE                  in varchar2 := null, -- Заголовок (null - не отображать)
    SLGND_POS               in varchar2 := null  -- Расположение легенды (null - не отображать, см. константы SCHART_LGND_POS_*)
  ) return                  TCHART;              -- Результат работы

  /* Добавление метки значения графика */
  procedure TCHART_ADD_LABEL
  (
    RCHART                  in out nocopy TCHART, -- Описание графика
    SLABEL                  in varchar2,          -- Метка значения
    BCLEAR                  in boolean := false   -- Флаг очистки коллекции меток (false - не очищать, true - очистить коллекцию перед добавлением)
  );

  /* Добавление набора данных графика */
  procedure TCHART_ADD_DATASET
  (
    RCHART                  in out nocopy TCHART, -- Описание графика
    RDATASET                in TCHART_DATASET,    -- Набор данных
    BCLEAR                  in boolean := false   -- Флаг очистки коллекции наборов данных (false - не очищать, true - очистить коллекцию перед добавлением)
  );

  /* Сериализация графика */
  function TCHART_TO_XML
  (
    RCHART                  in TCHART,     -- Описание графика
    NINCLUDE_DEF            in number := 1 -- Включить описание заголовка (0 - нет, 1 - да)
  ) return                  clob;          -- XML-описание

end PKG_P8PANELS_VISUAL;
/
create or replace package body PKG_P8PANELS_VISUAL as
/*
TODO: owner="root" created="18.10.2023"
text="Формат data_grid и gant как в chart"
*/
  /* Константы - тэги запросов */
  SRQ_TAG_XROOT               constant PKG_STD.TSTRING := 'XROOT';     -- Тэг для корня данных запроса
  SRQ_TAG_XFILTERS            constant PKG_STD.TSTRING := 'filters';   -- Тэг для строк данных
  SRQ_TAG_XORDERS             constant PKG_STD.TSTRING := 'orders';    -- Тэг для описания колонок
  SRQ_TAG_SNAME               constant PKG_STD.TSTRING := 'name';      -- Тэг для наименования
  SRQ_TAG_SDIRECTION          constant PKG_STD.TSTRING := 'direction'; -- Тэг для направления
  SRQ_TAG_SFROM               constant PKG_STD.TSTRING := 'from';      -- Тэг для значения "с"
  SRQ_TAG_STO                 constant PKG_STD.TSTRING := 'to';        -- Тэг для значения "по"

  /* Константы - тэги ответов */
  SRESP_TAG_XDATA             constant PKG_STD.TSTRING := 'XDATA';           -- Тэг для корня описания данных
  SRESP_TAG_XROWS             constant PKG_STD.TSTRING := 'XROWS';           -- Тэг для строк данных
  SRESP_TAG_XCOLUMNS_DEF      constant PKG_STD.TSTRING := 'XCOLUMNS_DEF';    -- Тэг для описания колонок
  SRESP_TAG_XGANTT_DEF        constant PKG_STD.TSTRING := 'XGANTT_DEF';      -- Тэг для описания заголовка диаграммы Ганта
  SRESP_TAG_XGANTT_TASKS      constant PKG_STD.TSTRING := 'XGANTT_TASKS';    -- Тэг для описания коллекции задач диаграммы Ганта
  SRESP_TAG_XCHART            constant PKG_STD.TSTRING := 'XCHART';          -- Тэг для описания графика
  
  /* Константы - атрибуты ответов (универсальные) */
  SRESP_ATTR_NAME             constant PKG_STD.TSTRING := 'name';     -- Атрибут для наименования
  SRESP_ATTR_CAPTION          constant PKG_STD.TSTRING := 'caption';  -- Атрибут для подписи
  SRESP_ATTR_DATA_TYPE        constant PKG_STD.TSTRING := 'dataType'; -- Атрибут для типа данных
  SRESP_ATTR_VISIBLE          constant PKG_STD.TSTRING := 'visible';  -- Атрибут для флага видимости
  SRESP_ATTR_TITLE            constant PKG_STD.TSTRING := 'title';    -- Атрибут для заголовка
  SRESP_ATTR_ZOOM             constant PKG_STD.TSTRING := 'zoom';     -- Атрибут для масштаба
  SRESP_ATTR_ID               constant PKG_STD.TSTRING := 'id';       -- Атрибут для идентификатора
  SRESP_ATTR_START            constant PKG_STD.TSTRING := 'start';    -- Атрибут для даты начала
  SRESP_ATTR_END              constant PKG_STD.TSTRING := 'end';      -- Атрибут для даты окончания
  SRESP_ATTR_RN               constant PKG_STD.TSTRING := 'rn';       -- Атрибут для рег. номера
  SRESP_ATTR_NUMB             constant PKG_STD.TSTRING := 'numb';     -- Атрибут для номера
  SRESP_ATTR_FULL_NAME        constant PKG_STD.TSTRING := 'fullName'; -- Атрибут для полного наименования
  SRESP_ATTR_DESC             constant PKG_STD.TSTRING := 'desc';     -- Атрибут для описания
  SRESP_ATTR_TYPE             constant PKG_STD.TSTRING := 'type';     -- Атрибут для типа

  /* Константы - атрибуты ответов (таблица данных) */
  SRESP_ATTR_DT_ORDER         constant PKG_STD.TSTRING := 'order';  -- Атрибут для флага сортировки
  SRESP_ATTR_DT_FILTER        constant PKG_STD.TSTRING := 'filter'; -- Атрибут для флага отбора
  SRESP_ATTR_DT_COLUMN_VALUES constant PKG_STD.TSTRING := 'values'; -- Атрибут для предопределённых значений

  /* Константы - атрибуты ответов (диаграмма Ганта) */  
  SRESP_ATTR_GANTT_ZOOM_BAR   constant PKG_STD.TSTRING := 'zoomBar';          -- Атрибут для флага отображения панели масштаба диаграммы Ганта
  SRESP_ATTR_TASK_PROGRESS    constant PKG_STD.TSTRING := 'progress';         -- Атрибут для прогресса задачи
  SRESP_ATTR_TASK_DEPS        constant PKG_STD.TSTRING := 'dependencies';     -- Атрибут для зависимостей задачи
  SRESP_ATTR_TASK_RO          constant PKG_STD.TSTRING := 'readOnly';         -- Атрибут для флага задачи "только для чтения"
  SRESP_ATTR_TASK_RO_PRGRS    constant PKG_STD.TSTRING := 'readOnlyProgress'; -- Атрибут для флага задачи "прогресс только для чтения"
  SRESP_ATTR_TASK_RO_DATES    constant PKG_STD.TSTRING := 'readOnlyDates';    -- Атрибут для флага задачи "даты только для чтения"
  SRESP_ATTR_TASK_BG_COLOR    constant PKG_STD.TSTRING := 'bgColor';          -- Атрибут для цвета заголовка задачи
  SRESP_ATTR_TASK_TEXT_COLOR  constant PKG_STD.TSTRING := 'textColor';        -- Атрибут для цвета текста задачи
  SRESP_ATTR_TASK_ATTRIBUTES  constant PKG_STD.TSTRING := 'taskAttributes';   -- Атрибут для коллекции атрибутов задачи
  SRESP_ATTR_TASK_COLORS      constant PKG_STD.TSTRING := 'taskColors';       -- Атрибут для коллекции цветов задачи

  /* Константы - атрибуты ответов (графики) */  
  SRESP_ATTR_CHART_LGND_POS   constant PKG_STD.TSTRING := 'legendPosition';  -- Атрибут для места размешения легенды графика
  SRESP_ATTR_CHART_LABELS     constant PKG_STD.TSTRING := 'labels';          -- Атрибут для меток графика
  SRESP_ATTR_CHART_DATASETS   constant PKG_STD.TSTRING := 'datasets';        -- Атрибут для наборов данных графика
  SRESP_ATTR_CHART_DS_LABEL   constant PKG_STD.TSTRING := 'label';           -- Атрибут для метки набора данных графика
  SRESP_ATTR_CHART_DS_BR_CLR  constant PKG_STD.TSTRING := 'borderColor';     -- Атрибут для цвета границы элемента набора данных графика
  SRESP_ATTR_CHART_DS_BG_CLR  constant PKG_STD.TSTRING := 'backgroundColor'; -- Атрибут для цвета заливки элемента набора данных графика
  SRESP_ATTR_CHART_DS_DATA    constant PKG_STD.TSTRING := 'data';            -- Атрибут для коллекции значений элементов набора данных
  SRESP_ATTR_CHART_DS_ITEMS   constant PKG_STD.TSTRING := 'items';           -- Атрибут для коллекции элементов набора данных
  SRESP_ATTR_CHART_DS_I_VAL   constant PKG_STD.TSTRING := 'value';           -- Атрибут для значения элемента набора данных
  
  /* Константы - параметры условий отбора */
  SCOND_FROM_POSTFIX          constant PKG_STD.TSTRING := 'From'; -- Постфикс наименования нижней границы условия отбора
  SCOND_TO_POSTFIX            constant PKG_STD.TSTRING := 'To';   -- Постфикс наименования верхней границы условия отбора

  /* Расчет диапаона выдаваемых записей */
  procedure UTL_ROWS_LIMITS_CALC
  (
    NPAGE_NUMBER            in number,  -- Номер страницы (игнорируется при NPAGE_SIZE=0)
    NPAGE_SIZE              in number,  -- Количество записей на странице (0 - все)
    NROW_FROM               out number, -- Нижняя граница диапазона
    NROW_TO                 out number  -- Верхняя граница диапазона
  )
  is
  begin
    if (COALESCE(NPAGE_SIZE, 0) <= 0)
    then
      NROW_FROM := 1;
      NROW_TO   := 1000000000;
    else
      NROW_FROM := COALESCE(NPAGE_NUMBER, 1) * NPAGE_SIZE - NPAGE_SIZE + 1;
      NROW_TO   := COALESCE(NPAGE_NUMBER, 1) * NPAGE_SIZE;
    end if;
  end UTL_ROWS_LIMITS_CALC;
  
  /* Формирование наименования условия отбора для нижней границы */
  function UTL_COND_NAME_MAKE_FROM
  (
    SNAME                   in varchar2 -- Наименование колонки
  ) return                  varchar2    -- Результат
  is
  begin
    return SNAME || SCOND_FROM_POSTFIX;
  end UTL_COND_NAME_MAKE_FROM;

  /* Формирование наименования условия отбора для верхней границы */
  function UTL_COND_NAME_MAKE_TO
  (
    SNAME                   in varchar2 -- Наименование колонки
  ) return                  varchar2    -- Результат
  is
  begin
    return SNAME || SCOND_TO_POSTFIX;
  end UTL_COND_NAME_MAKE_TO;
  
  /* Формирование значения */
  function TCOL_VAL_MAKE
  (
    SVALUE                  in varchar2, -- Значение (строка)
    NVALUE                  in number,   -- Значение (число)
    DVALUE                  in date      -- Значение (дата)
  ) return                  TCOL_VAL     -- Результат работы
  is
    RRES                    TCOL_VAL;    -- Буфер для результата
  begin
    /* Формируем объект */
    RRES.SVALUE := SVALUE;
    RRES.NVALUE := NVALUE;
    RRES.DVALUE := DVALUE;
    /* Возвращаем результат */
    return RRES;
  end TCOL_VAL_MAKE;

  /* Добавление значения в коллекцию */
  procedure TCOL_VALS_ADD
  (
    RCOL_VALS               in out nocopy TCOL_VALS, -- Коллекция значений
    SVALUE                  in varchar2 := null,     -- Значение (строка)
    NVALUE                  in number := null,       -- Значение (число)
    DVALUE                  in date := null,         -- Значение (дата)
    BCLEAR                  in boolean := false      -- Флаг очистки коллекции (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Инициализируем коллекцию если необходимо */
    if ((RCOL_VALS is null) or (BCLEAR)) then
      RCOL_VALS := TCOL_VALS();
    end if;
    /* Добавляем элемент */
    RCOL_VALS.EXTEND();
    RCOL_VALS(RCOL_VALS.LAST) := TCOL_VAL_MAKE(SVALUE => SVALUE, NVALUE => NVALUE, DVALUE => DVALUE);
  end TCOL_VALS_ADD;
  
  /* Формирование описания колонки */
  function TCOL_DEF_MAKE
  (
    SNAME                   in varchar2,                   -- Наименование
    SCAPTION                in varchar2,                   -- Заголовок
    SDATA_TYPE              in varchar2 := SDATA_TYPE_STR, -- Тип данных (см. константы SDATA_TYPE_*)
    SCOND_FROM              in varchar2 := null,           -- Наименование нижней границы условия отбора (null - используется UTL_COND_NAME_MAKE_FROM)
    SCOND_TO                in varchar2 := null,           -- Наименование верхней границы условия отбора (null - используется UTL_COND_NAME_MAKE_TO)
    BVISIBLE                in boolean := true,            -- Разрешить отображение
    BORDER                  in boolean := false,           -- Разрешить сортировку
    BFILTER                 in boolean := false,           -- Разрешить отбор
    RCOL_VALS               in TCOL_VALS := null           -- Предопределённые значения
  ) return                  TCOL_DEF                       -- Результат работы
  is
    RRES                    TCOL_DEF;                      -- Буфер для результата
  begin
    /* Формируем объект */
    RRES.SNAME      := SNAME;
    RRES.SCAPTION   := SCAPTION;
    RRES.SDATA_TYPE := COALESCE(SDATA_TYPE, SDATA_TYPE_STR);
    RRES.SCOND_FROM := COALESCE(SCOND_FROM, UTL_COND_NAME_MAKE_FROM(SNAME => SNAME));
    RRES.SCOND_TO   := COALESCE(SCOND_TO, UTL_COND_NAME_MAKE_TO(SNAME => SNAME));
    RRES.BVISIBLE   := COALESCE(BVISIBLE, true);
    RRES.BORDER     := COALESCE(BORDER, false);
    RRES.BFILTER    := COALESCE(BFILTER, false);
    RRES.RCOL_VALS  := COALESCE(RCOL_VALS, TCOL_VALS());
    /* Возвращаем результат */
    return RRES;
  end TCOL_DEF_MAKE;
  
  /* Добавление описания колонки в коллекцию */
  procedure TCOL_DEFS_ADD
  (
    RCOL_DEFS               in out nocopy TCOL_DEFS,       -- Коллекция описаний колонок
    SNAME                   in varchar2,                   -- Наименование
    SCAPTION                in varchar2,                   -- Заголовок
    SDATA_TYPE              in varchar2 := SDATA_TYPE_STR, -- Тип данных (см. константы SDATA_TYPE_*)
    SCOND_FROM              in varchar2 := null,           -- Наименование нижней границы условия отбора (null - используется UTL_COND_NAME_MAKE_FROM)
    SCOND_TO                in varchar2 := null,           -- Наименование верхней границы условия отбора (null - используется UTL_COND_NAME_MAKE_TO)
    BVISIBLE                in boolean := true,            -- Разрешить отображение
    BORDER                  in boolean := false,           -- Разрешить сортировку
    BFILTER                 in boolean := false,           -- Разрешить отбор
    RCOL_VALS               in TCOL_VALS := null,          -- Предопределённые значения
    BCLEAR                  in boolean := false            -- Флаг очистки коллекции (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Инициализируем коллекцию если необходимо */
    if ((RCOL_DEFS is null) or (BCLEAR)) then
      RCOL_DEFS := TCOL_DEFS();
    end if;
    /* Добавляем элемент */
    RCOL_DEFS.EXTEND();
    RCOL_DEFS(RCOL_DEFS.LAST) := TCOL_DEF_MAKE(SNAME      => SNAME,
                                               SCAPTION   => SCAPTION,
                                               SDATA_TYPE => SDATA_TYPE,
                                               SCOND_FROM => SCOND_FROM,
                                               SCOND_TO   => SCOND_TO,
                                               BVISIBLE   => BVISIBLE,
                                               BORDER     => BORDER,
                                               BFILTER    => BFILTER,
                                               RCOL_VALS  => RCOL_VALS);
  end TCOL_DEFS_ADD;
  
  /* Поиск описания колонки по наименованию */
  function TCOL_DEFS_FIND
  (
    RCOL_DEFS               in TCOL_DEFS, -- Описание колонок таблицы данных
    SNAME                   in varchar2   -- Наименование
  ) return                  TCOL_DEF      -- Найденное описание (null - если не нашли)
  is
  begin
    /* Обходим колонки из коллекции описаний */
    if ((RCOL_DEFS is not null) and (RCOL_DEFS.COUNT > 0)) then
      for I in RCOL_DEFS.FIRST .. RCOL_DEFS.LAST
      loop
        if (RCOL_DEFS(I).SNAME = SNAME) then
          return RCOL_DEFS(I);
        end if;
      end loop;
    end if;
    /* Ничего не нашли */
    return null;
  end TCOL_DEFS_FIND;
  
  /* Сериализация описания колонки таблицы данных */
  procedure TCOL_DEFS_TO_XML
  (
    RCOL_DEFS               in TCOL_DEFS -- Описание колонок таблицы данных
  )
  is    
  begin
    /* Обходим колонки из коллекции */
    if ((RCOL_DEFS is not null) and (RCOL_DEFS.COUNT > 0)) then
      for I in RCOL_DEFS.FIRST .. RCOL_DEFS.LAST
      loop
        /* Открываем описание колонки */
        PKG_XFAST.DOWN_NODE(SNAME => SRESP_TAG_XCOLUMNS_DEF);
        /* Атрибуты колонки */
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_NAME, SVALUE => RCOL_DEFS(I).SNAME);
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_CAPTION, SVALUE => RCOL_DEFS(I).SCAPTION);
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_DATA_TYPE, SVALUE => RCOL_DEFS(I).SDATA_TYPE);
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_VISIBLE, BVALUE => RCOL_DEFS(I).BVISIBLE);
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_DT_ORDER, BVALUE => RCOL_DEFS(I).BORDER);
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_DT_FILTER, BVALUE => RCOL_DEFS(I).BFILTER);
        /* Предопределённые значения */
        if (RCOL_DEFS(I).RCOL_VALS is not null) and (RCOL_DEFS(I).RCOL_VALS.COUNT > 0) then
          for V in RCOL_DEFS(I).RCOL_VALS.FIRST .. RCOL_DEFS(I).RCOL_VALS.LAST
          loop
            /* Открываем описание предопределённого значения */
            PKG_XFAST.DOWN_NODE(SNAME => SRESP_ATTR_DT_COLUMN_VALUES);
            /* Значение */
            case RCOL_DEFS(I).SDATA_TYPE
              when SDATA_TYPE_STR then
                PKG_XFAST.VALUE(SVALUE => RCOL_DEFS(I).RCOL_VALS(V).SVALUE);
              when SDATA_TYPE_NUMB then
                PKG_XFAST.VALUE(NVALUE => RCOL_DEFS(I).RCOL_VALS(V).NVALUE);
              when SDATA_TYPE_DATE then
                PKG_XFAST.VALUE(DVALUE => RCOL_DEFS(I).RCOL_VALS(V).DVALUE);
              else
                P_EXCEPTION(0,
                            'Описание колонки "%s" таблицы данных содержит неподдерживаемый тип данных ("%s").',
                            COALESCE(RCOL_DEFS(I).SNAME, '<НЕ ОПРЕДЕЛЕНА>'),
                            COALESCE(RCOL_DEFS(I).SDATA_TYPE, '<НЕ ОПРЕДЕЛЁН>'));
            end case;
            /* Закрываем описание предопределённого значения */
            PKG_XFAST.UP();          
          end loop;
        end if;
        /* Закрываем описание колонки */
        PKG_XFAST.UP();
      end loop;
    end if;
  end TCOL_DEFS_TO_XML;
  
  /* Формирование колонки */
  function TCOL_MAKE
  (
    SNAME                   in varchar2,         -- Наименование колонки
    SVALUE                  in varchar2 := null, -- Значение (строка)
    NVALUE                  in number := null,   -- Значение (число)
    DVALUE                  in date := null      -- Значение (дата)
  ) return                  TCOL                 -- Результат работы
  is
    RRES                    TCOL;                -- Буфер для результата
  begin
    /* Формируем объект */
    RRES.SNAME    := SNAME;
    RRES.RCOL_VAL := TCOL_VAL_MAKE(SVALUE => SVALUE, NVALUE => NVALUE, DVALUE => DVALUE);
    /* Возвращаем результат */
    return RRES;
  end TCOL_MAKE;
  
  /* Добавление колонки в коллекцию */
  procedure TCOLS_ADD
  (
    RCOLS                   in out nocopy TCOLS, -- Коллекция колонок
    SNAME                   in varchar2,         -- Наименование колонки
    SVALUE                  in varchar2 := null, -- Значение (строка)
    NVALUE                  in number := null,   -- Значение (число)
    DVALUE                  in date := null,     -- Значение (дата)
    BCLEAR                  in boolean := false  -- Флаг очистки коллекции (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Инициализируем коллекцию если необходимо */
    if ((RCOLS is null) or (BCLEAR)) then
      RCOLS := TCOLS();
    end if;
    /* Добавляем элемент */
    RCOLS.EXTEND();
    RCOLS(RCOLS.LAST) := TCOL_MAKE(SNAME => SNAME, SVALUE => SVALUE, NVALUE => NVALUE, DVALUE => DVALUE);
  end TCOLS_ADD;
  
  /* Формирование строки */
  function TROW_MAKE
  return                    TROW        -- Результат работы
  is
    RRES                    TROW;       -- Буфер для результата
  begin
    /* Формируем объект */
    RRES.RCOLS := TCOLS();
    /* Возвращаем результат */
    return RRES;
  end TROW_MAKE;
  
  /* Добавление колонки к строке */
  procedure TROW_ADD_COL
  (
    RROW                    in out nocopy TROW,  -- Строка
    SNAME                   in varchar2,         -- Наименование колонки
    SVALUE                  in varchar2 := null, -- Значение (строка)
    NVALUE                  in number := null,   -- Значение (число)
    DVALUE                  in date := null,     -- Значение (дата)
    BCLEAR                  in boolean := false  -- Флаг очистки коллекции (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Сформируем колонку и добавим её к коллекции колонок строки */
    TCOLS_ADD(RCOLS => RROW.RCOLS, SNAME => SNAME, SVALUE => SVALUE, NVALUE => NVALUE, DVALUE => DVALUE, BCLEAR => BCLEAR);
  end TROW_ADD_COL;
  
  /* Добавление строковой колонки к строке из курсора динамического запроса */
  procedure TROW_ADD_CUR_COLS
  (
    RROW                    in out nocopy TROW, -- Строка
    SNAME                   in varchar2,        -- Наименование колонки
    ICURSOR                 in integer,         -- Курсор
    NPOSITION               in number,          -- Номер колонки в курсоре
    BCLEAR                  in boolean := false -- Флаг очистки коллекции (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
    SVALUE                  PKG_STD.TLSTRING;   -- Буфер для значения курсора
  begin
    /* Читаем данные из курсора */
    PKG_SQL_DML.COLUMN_VALUE_STR(ICURSOR => ICURSOR, IPOSITION => NPOSITION, SVALUE => SVALUE);
    /* Сформируем колонку и добавим её к коллекции колонок строки */
    TCOLS_ADD(RCOLS => RROW.RCOLS, SNAME => SNAME, SVALUE => SVALUE, NVALUE => null, DVALUE => null, BCLEAR => BCLEAR);
  end TROW_ADD_CUR_COLS;

  /* Добавление числовой колонки к строке из курсора динамического запроса */
  procedure TROW_ADD_CUR_COLN
  (
    RROW                    in out nocopy TROW, -- Строка
    SNAME                   in varchar2,        -- Наименование колонки
    ICURSOR                 in integer,         -- Курсор
    NPOSITION               in number,          -- Номер колонки в курсоре
    BCLEAR                  in boolean := false -- Флаг очистки коллекции (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
    NVALUE                  PKG_STD.TNUMBER;    -- Буфер для значения курсора
  begin
    /* Читаем данные из курсора */
    PKG_SQL_DML.COLUMN_VALUE_NUM(ICURSOR => ICURSOR, IPOSITION => NPOSITION, NVALUE => NVALUE);
    /* Сформируем колонку и добавим её к коллекции колонок строки */
    TCOLS_ADD(RCOLS => RROW.RCOLS, SNAME => SNAME, SVALUE => null, NVALUE => NVALUE, DVALUE => null, BCLEAR => BCLEAR);
  end TROW_ADD_CUR_COLN;
  
  /* Добавление колонки типа "дата" к строке из курсора динамического запроса */
  procedure TROW_ADD_CUR_COLD
  (
    RROW                    in out nocopy TROW, -- Строка
    SNAME                   in varchar2,        -- Наименование колонки
    ICURSOR                 in integer,         -- Курсор
    NPOSITION               in number,          -- Номер колонки в курсоре
    BCLEAR                  in boolean := false -- Флаг очистки коллекции (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
    DVALUE                  PKG_STD.TLDATE;     -- Буфер для значения курсора
  begin
    /* Читаем данные из курсора */
    PKG_SQL_DML.COLUMN_VALUE_DATE(ICURSOR => ICURSOR, IPOSITION => NPOSITION, DVALUE => DVALUE);
    /* Сформируем колонку и добавим её к коллекции колонок строки */
    TCOLS_ADD(RCOLS => RROW.RCOLS, SNAME => SNAME, SVALUE => null, NVALUE => null, DVALUE => DVALUE, BCLEAR => BCLEAR);
  end TROW_ADD_CUR_COLD;
  
  /* Сериализация строки данных таблицы данных */
  procedure TROWS_TO_XML
  (
    RCOL_DEFS               in TCOL_DEFS, -- Описание колонок таблицы данных
    RROWS                   in TROWS      -- Строки таблицы данных
  )
  is  
    RCOL_DEF                TCOL_DEF;     -- Описание текущей сериализуемой колонки
  begin
    /* Обходим строки из коллекции */
    if ((RROWS is not null) and (RROWS.COUNT > 0)) then
      for I in RROWS.FIRST .. RROWS.LAST
      loop
        /* Открываем строку */
        PKG_XFAST.DOWN_NODE(SNAME => SRESP_TAG_XROWS);
        /* Обходим колонки строки */
        if ((RROWS(I).RCOLS is not null) and (RROWS(I).RCOLS.COUNT > 0)) then
          for J in RROWS(I).RCOLS.FIRST .. RROWS(I).RCOLS.LAST
          loop
            /* Найдём описание колонки */
            RCOL_DEF := TCOL_DEFS_FIND(RCOL_DEFS => RCOL_DEFS, SNAME => RROWS(I).RCOLS(J).SNAME);
            if (RCOL_DEF.SNAME is null) then
              P_EXCEPTION(0,
                          'Описание колонки "%s" таблицы данных не определено.',
                          RROWS(I).RCOLS(J).SNAME);
            end if;
            /* Добавлением значение колонки как атрибут строки */
            case RCOL_DEF.SDATA_TYPE
              when SDATA_TYPE_STR then
                PKG_XFAST.ATTR(SNAME => RROWS(I).RCOLS(J).SNAME, SVALUE => RROWS(I).RCOLS(J).RCOL_VAL.SVALUE);
              when SDATA_TYPE_NUMB then
                PKG_XFAST.ATTR(SNAME => RROWS(I).RCOLS(J).SNAME, NVALUE => RROWS(I).RCOLS(J).RCOL_VAL.NVALUE);
              when SDATA_TYPE_DATE then
                PKG_XFAST.ATTR(SNAME => RROWS(I).RCOLS(J).SNAME, DVALUE => RROWS(I).RCOLS(J).RCOL_VAL.DVALUE);
              else
                P_EXCEPTION(0,
                            'Описание колонки "%s" таблицы данных содержит неподдерживаемый тип данных ("%s").',
                            RCOL_DEFS(I).SNAME,
                            COALESCE(RCOL_DEFS(I).SDATA_TYPE, '<НЕ ОПРЕДЕЛЁН>'));
            end case;
          end loop;
        end if;
        /* Закрываем строку */
        PKG_XFAST.UP();
      end loop;
    end if;
  end TROWS_TO_XML;
  
  /* Формирование таблицы данных */
  function TDATA_GRID_MAKE
  return                    TDATA_GRID  -- Результат работы
  is
    RRES                    TDATA_GRID; -- Буфер для результата
  begin
    /* Формируем объект */
    RRES.RCOL_DEFS := TCOL_DEFS();
    RRES.RROWS     := TROWS();
    /* Возвращаем результат */
    return RRES;
  end TDATA_GRID_MAKE;
  
  /* Поиск описания колонки в таблице данных по наименованию */
  function TDATA_GRID_FIND_COL_DEF
  (
    RDATA_GRID              in TDATA_GRID, -- Описание таблицы данных
    SNAME                   in varchar2    -- Наименование колонки
  ) return                  TCOL_DEF       -- Найденное описание (null - если не нашли)
  is
  begin
    return TCOL_DEFS_FIND(RCOL_DEFS => RDATA_GRID.RCOL_DEFS, SNAME => SNAME);
  end TDATA_GRID_FIND_COL_DEF;
  
  /* Добавление описания колонки к таблице данных */
  procedure TDATA_GRID_ADD_COL_DEF
  (
    RDATA_GRID              in out nocopy TDATA_GRID,      -- Описание таблицы данных
    SNAME                   in varchar2,                   -- Наименование колонки
    SCAPTION                in varchar2,                   -- Заголовок колонки
    SDATA_TYPE              in varchar2 := SDATA_TYPE_STR, -- Тип данных колонки (см. константы SDATA_TYPE_*)
    SCOND_FROM              in varchar2 := null,           -- Наименование нижней границы условия отбора (null - используется UTL_COND_NAME_MAKE_FROM)
    SCOND_TO                in varchar2 := null,           -- Наименование верхней границы условия отбора (null - используется UTL_COND_NAME_MAKE_TO)
    BVISIBLE                in boolean := true,            -- Разрешить отображение
    BORDER                  in boolean := false,           -- Разрешить сортировку по колонке
    BFILTER                 in boolean := false,           -- Разрешить отбор по колонке
    RCOL_VALS               in TCOL_VALS := null,          -- Предопределённые значения колонки
    BCLEAR                  in boolean := false            -- Флаг очистки коллекции описаний колонок таблицы данных (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Формируем описание и добавляем в коллекцию таблицы данных */
    TCOL_DEFS_ADD(RCOL_DEFS  => RDATA_GRID.RCOL_DEFS,
                  SNAME      => SNAME,
                  SCAPTION   => SCAPTION,
                  SDATA_TYPE => SDATA_TYPE,
                  SCOND_FROM => SCOND_FROM,
                  SCOND_TO   => SCOND_TO,
                  BVISIBLE   => BVISIBLE,
                  BORDER     => BORDER,
                  BFILTER    => BFILTER,
                  RCOL_VALS  => RCOL_VALS,
                  BCLEAR     => BCLEAR);
  end TDATA_GRID_ADD_COL_DEF;
  
  /* Добавление описания колонки к таблице данных */
  procedure TDATA_GRID_ADD_ROW
  (
    RDATA_GRID              in out nocopy TDATA_GRID, -- Описание таблицы данных
    RROW                    in TROW,                  -- Строка
    BCLEAR                  in boolean := false       -- Флаг очистки коллекции строк таблицы данных (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Инициализируем коллекцию если необходимо */
    if ((RDATA_GRID.RROWS is null) or (BCLEAR)) then
      RDATA_GRID.RROWS := TROWS();
    end if;
    /* Добавляем элемент */
    RDATA_GRID.RROWS.EXTEND();
    RDATA_GRID.RROWS(RDATA_GRID.RROWS.LAST) := RROW;
  end TDATA_GRID_ADD_ROW;
  
  /* Сериализация таблицы данных */
  function TDATA_GRID_TO_XML
  (
    RDATA_GRID              in TDATA_GRID, -- Описание таблицы данных
    NINCLUDE_DEF            in number := 1 -- Включить описание колонок (0 - нет, 1 - да)
  ) return                  clob           -- XML-описание
  is
    CRES                    clob;          -- Буфер для результата
  begin
    /* Начинаем формирование XML */
    PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
    /* Открываем корень */
    PKG_XFAST.DOWN_NODE(SNAME => SRESP_TAG_XDATA);
    /* Если необходимо включить описание колонок */
    if (NINCLUDE_DEF = 1) then
      TCOL_DEFS_TO_XML(RCOL_DEFS => RDATA_GRID.RCOL_DEFS);
    end if;
    /* Формируем описание строк */
    TROWS_TO_XML(RCOL_DEFS => RDATA_GRID.RCOL_DEFS, RROWS => RDATA_GRID.RROWS);
    /* Закрываем корень */
    PKG_XFAST.UP();
    /* Сериализуем */
    CRES := PKG_XFAST.SERIALIZE_TO_CLOB();
    /* Завершаем формирование XML */
    PKG_XFAST.EPILOGUE();
    /* Возвращаем полученное */
    return CRES;
  exception
    when others then
      /* Завершаем формирование XML */
      PKG_XFAST.EPILOGUE();
      /* Вернем ошибку */
      PKG_STATE.DIAGNOSTICS_STACKED();
      P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
  end TDATA_GRID_TO_XML;
  
  /* Конвертация значений фильтра в число */
  procedure TFILTER_TO_NUMBER
  (
    RFILTER                 in TFILTER, -- Фильтр
    NFROM                   out number, -- Значение нижней границы диапазона
    NTO                     out number  -- Значение верхней границы диапазона
  )
  is
  begin
    /* Нижняя граница диапазона */
    if (RFILTER.SFROM is not null) then
      begin
        NFROM := PKG_P8PANELS_BASE.UTL_S2N(SVALUE => RFILTER.SFROM);
      exception
        when others then
          P_EXCEPTION(0,
                      'Неверный формат числа (%s) в указанной нижней границе диапазона фильтра.',
                      RFILTER.SFROM);
      end;
    end if;
    /* Верхняя граница диапазона */    
    if (RFILTER.STO is not null) then
      begin
        NTO := PKG_P8PANELS_BASE.UTL_S2N(SVALUE => RFILTER.STO);
      exception
        when others then
          P_EXCEPTION(0,
                      'Неверный формат числа (%s) в указанной верхней границе диапазона фильтра.',
                      RFILTER.STO);
      end;
    end if;
  end TFILTER_TO_NUMBER;
  
  /* Конвертация значений фильтра в дату */
  procedure TFILTER_TO_DATE
  (
    RFILTER                 in TFILTER, -- Фильтр
    DFROM                   out date,   -- Значение нижней границы диапазона
    DTO                     out date    -- Значение верхней границы диапазона
  )
  is
  begin
    /* Нижняя граница диапазона */
    if (RFILTER.SFROM is not null) then
      begin
        DFROM := PKG_P8PANELS_BASE.UTL_S2D(SVALUE => RFILTER.SFROM);
      exception
        when others then
          P_EXCEPTION(0,
                      'Неверный формат даты (%s) в указанной нижней границе диапазона фильтра.',
                      RFILTER.SFROM);
      end;
    end if;
    /* Верхняя граница диапазона */
    if (RFILTER.STO is not null) then
      begin
        DTO := PKG_P8PANELS_BASE.UTL_S2D(SVALUE => RFILTER.STO);
      exception
        when others then
          P_EXCEPTION(0,
                      'Неверный формат даты (%s) в указанной верхней границе диапазона фильтра.',
                      RFILTER.STO);
      end;
    end if;
  end TFILTER_TO_DATE;

  /* Формирование фильтра */
  function TFILTER_MAKE
  (
    SNAME                   in varchar2, -- Наименование
    SFROM                   in varchar2, -- Значение "с"
    STO                     in varchar2  -- Значение "по"
  ) return                  TFILTER      -- Результат работы
  is
    RRES                    TFILTER;    -- Буфер для результата
  begin
    /* Формируем объект */
    RRES.SNAME := SNAME;
    RRES.SFROM := SFROM;
    RRES.STO   := STO;
    /* Возвращаем результат */
    return RRES;
  end TFILTER_MAKE;
  
  /* Поиск фильтра в коллекции */
  function TFILTERS_FIND
  (
    RFILTERS                in TFILTERS, -- Коллекция фильтров
    SNAME                   in varchar2  -- Наименование
  ) return                  TFILTER      -- Найденный фильтр (null - если не нашли)
  is
  begin
    /* Обходим фильтры из коллекции */
    if ((RFILTERS is not null) and (RFILTERS.COUNT > 0)) then
      for I in RFILTERS.FIRST .. RFILTERS.LAST
      loop
        if (RFILTERS(I).SNAME = SNAME) then
          return RFILTERS(I);
        end if;
      end loop;
    end if;
    /* Ничего не нашли */
    return null;
  end TFILTERS_FIND;
  
  /* Десериализация фильтров */
  function TFILTERS_FROM_XML
  (
    CFILTERS                in clob              -- Сериализованное представление фильтров (BASE64(<filters><name>ИМЯ</name><from>ЗНАЧЕНИЕ</from><to>ЗНАЧЕНИЕ</to></filters>...))
  ) return                  TFILTERS             -- Результат работы
  is
    RFILTERS                TFILTERS;            -- Буфер для результата работы
    XDOC                    PKG_XPATH.TDOCUMENT; -- Документ XML
    XROOT                   PKG_XPATH.TNODE;     -- Корень документа XML
    XNODE                   PKG_XPATH.TNODE;     -- Буфер узла документа
    XNODES                  PKG_XPATH.TNODES;    -- Буфер коллекции узлов документа
  begin
    /* Вернём выходную коллекцию */
    RFILTERS := TFILTERS();
    /* Разбираем XML */
    XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => '<' || SRQ_TAG_XROOT || '>' ||
                                               BLOB2CLOB(LBDATA   => BASE64_DECODE(LCSRCE => CFILTERS),
                                                         SCHARSET => PKG_CHARSET.CHARSET_UTF_()) || '</' ||
                                               SRQ_TAG_XROOT || '>');
    /* Считываем корневой узел */
    XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC);
    /* Считывание списка записей */
    XNODES := PKG_XPATH.LIST_NODES(RPARENT_NODE => XROOT, SPATTERN => '/' || SRQ_TAG_XROOT || '/' || SRQ_TAG_XFILTERS);
    /* Цикл по списку записией */
    for I in 1 .. PKG_XPATH.COUNT_NODES(RNODES => XNODES)
    loop
      /* Считаем элемент по его номеру */
      XNODE := PKG_XPATH.ITEM_NODE(RNODES => XNODES, INUMBER => I);
      /* Добавим его в коллекцию */
      RFILTERS.EXTEND();
      RFILTERS(RFILTERS.LAST) := TFILTER_MAKE(SNAME => PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => SRQ_TAG_SNAME),
                                              SFROM => PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => SRQ_TAG_SFROM),
                                              STO   => PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => SRQ_TAG_STO));
    end loop;
    /* Освободим документ */
    PKG_XPATH.FREE(RDOCUMENT => XDOC);
    /* Вернём результат */
    return RFILTERS;
  exception
    when others then
      /* Освободим документ */
      PKG_XPATH.FREE(RDOCUMENT => XDOC);
      /* Вернем ошибку */
      PKG_STATE.DIAGNOSTICS_STACKED();
      P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
  end TFILTERS_FROM_XML;
  
  /* Применение параметров фильтрации в запросе */
  procedure TFILTERS_SET_QUERY
  (
    NIDENT                  in number,         -- Идентификатор отбора
    NCOMPANY                in number,         -- Рег. номер организации
    NPARENT                 in number := null, -- Рег. номер родителя
    SUNIT                   in varchar2,       -- Код раздела
    SPROCEDURE              in varchar2,       -- Наименование серверной процедуры отбора
    RDATA_GRID              in TDATA_GRID,     -- Описание таблицы данных
    RFILTERS                in TFILTERS        -- Коллекция фильтров
  )
  is
    RCOL_DEF                TCOL_DEF;          -- Описание текущей фильтруемой колонки
    BENUM                   boolean;           -- Флаг начиличия перечисляемых значений
    NFROM                   PKG_STD.TNUMBER;   -- Буфер для верхней границы диапазона отбора чисел
    NTO                     PKG_STD.TNUMBER;   -- Буфер для нижней границы диапазона отбора чисел
    DFROM                   PKG_STD.TLDATE;    -- Буфер для верхней границы диапазона отбора дат
    DTO                     PKG_STD.TLDATE;    -- Буфер для нижней границы диапазона отбора дат
  begin
    /* Формирование условий отбора - Пролог */
    PKG_COND_BROKER.PROLOGUE(IMODE => PKG_COND_BROKER.MODE_SMART_, NIDENT => NIDENT);
    /* Формирование условий отбора - Установка процедуры серверного отбора */
    PKG_COND_BROKER.SET_PROCEDURE(SPROCEDURE_NAME => SPROCEDURE);
    /* Формирование условий отбора - Установка раздела */
    PKG_COND_BROKER.SET_UNIT(SUNITCODE => SUNIT);
    /* Формирование условий отбора - Установка организации */
    PKG_COND_BROKER.SET_COMPANY(NCOMPANY => NCOMPANY);
    /* Формирование условий отбора - Установка родителя */
    if (NPARENT is not null) then
      PKG_COND_BROKER.SET_PARENT(NPARENT => NPARENT);
    end if;
    /* Обходим фильтр, если задан */
    if ((RFILTERS is not null) and (RFILTERS.COUNT > 0)) then
      for I in RFILTERS.FIRST .. RFILTERS.LAST
      loop
        /* Найдем фильтруемую колонку в описании */
        RCOL_DEF := TCOL_DEFS_FIND(RCOL_DEFS => RDATA_GRID.RCOL_DEFS, SNAME => RFILTERS(I).SNAME);
        if (RCOL_DEF.SNAME is not null) then
          /* Определимся с наличием перечисляемых значений */
          if ((RCOL_DEF.RCOL_VALS is not null) and (RCOL_DEF.RCOL_VALS.COUNT > 0)) then
            BENUM := true;
          else
            BENUM := false;
          end if;
          /* Установим для неё условие отобра согласно типу данных */
          case RCOL_DEF.SDATA_TYPE
            when SDATA_TYPE_STR then
              begin
                if (BENUM) then
                  PKG_COND_BROKER.SET_CONDITION_ESTR(SCONDITION_NAME   => RCOL_DEF.SCOND_FROM,
                                                     SCONDITION_ESTR   => RFILTERS(I).SFROM,
                                                     ICASE_INSENSITIVE => 1);
                else
                  PKG_COND_BROKER.SET_CONDITION_STR(SCONDITION_NAME   => RCOL_DEF.SCOND_FROM,
                                                    SCONDITION_VALUE  => RFILTERS(I).SFROM,
                                                    ICASE_INSENSITIVE => 1);
                end if;
              end;
            when SDATA_TYPE_NUMB then
              begin
                if (BENUM) then
                  PKG_COND_BROKER.SET_CONDITION_ENUM(SCONDITION_NAME => RCOL_DEF.SCOND_FROM,
                                                     SCONDITION_ENUM => RFILTERS(I).SFROM);
                else
                  TFILTER_TO_NUMBER(RFILTER => RFILTERS(I), NFROM => NFROM, NTO => NTO);
                  if (NFROM is not null) then
                    PKG_COND_BROKER.SET_CONDITION_NUM(SCONDITION_NAME  => RCOL_DEF.SCOND_FROM,
                                                      NCONDITION_VALUE => NFROM);
                  end if;
                  if (NTO is not null) then
                    PKG_COND_BROKER.SET_CONDITION_NUM(SCONDITION_NAME => RCOL_DEF.SCOND_TO, NCONDITION_VALUE => NTO);
                  end if;
                end if;
              end;
            when SDATA_TYPE_DATE then
              begin
                if (BENUM) then                  
                  PKG_COND_BROKER.SET_CONDITION_EDATE(SCONDITION_NAME => RCOL_DEF.SCOND_FROM,
                                                      SCONDITION_EDATE => RFILTERS(I).SFROM);
                else
                  TFILTER_TO_DATE(RFILTER => RFILTERS(I), DFROM => DFROM, DTO => DTO);
                  if (DFROM is not null) then
                    PKG_COND_BROKER.SET_CONDITION_DATE(SCONDITION_NAME  => RCOL_DEF.SCOND_FROM,
                                                       DCONDITION_VALUE => DFROM);
                  end if;
                  if (DTO is not null) then
                    PKG_COND_BROKER.SET_CONDITION_DATE(SCONDITION_NAME => RCOL_DEF.SCOND_TO, DCONDITION_VALUE => DTO);
                  end if;
                end if;
              end;
            else
              P_EXCEPTION(0,
                          'Описание колонки "%s" таблицы данных содержит неподдерживаемый тип данных ("%s").',
                          RCOL_DEF.SNAME,
                          COALESCE(RCOL_DEF.SDATA_TYPE, '<НЕ ОПРЕДЕЛЁН>'));
          end case;
        end if;
      end loop;
    end if;
    /* Формирование условий отбора - Эпилог */
    PKG_COND_BROKER.EPILOGUE();
  end TFILTERS_SET_QUERY;
  
  /* Формирование сортировки */
  function TORDER_MAKE
  (
    SNAME                   in varchar2, -- Наименование
    SDIRECTION              in varchar2  -- Направление (см. константы SORDER_DIRECTION_*)
  ) return                  TORDER       -- Результат работы
  is
    RRES                    TORDER;      -- Буфер для результата
  begin
    /* Формируем объект */
    RRES.SNAME      := SNAME;
    RRES.SDIRECTION := SDIRECTION;
    /* Возвращаем результат */
    return RRES;
  end TORDER_MAKE;
  
  /* Десериализация сортировок */
  function TORDERS_FROM_XML
  (
    CORDERS                 in clob              -- Сериализованное представление сотрировок (BASE64(<orders><name>ИМЯ</name><direction>ASC/DESC</direction></orders>...))
  ) return                  TORDERS              -- Результат работы
  is
    RORDERS                 TORDERS;             -- Буфер для результата работы
    XDOC                    PKG_XPATH.TDOCUMENT; -- Документ XML
    XROOT                   PKG_XPATH.TNODE;     -- Корень документа XML
    XNODE                   PKG_XPATH.TNODE;     -- Буфер узла документа
    XNODES                  PKG_XPATH.TNODES;    -- Буфер коллекции узлов документа
  begin
    /* Инициализируем выходную коллекцию */
    RORDERS := TORDERS();
    /* Разбираем XML */
    XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => '<' || SRQ_TAG_XROOT || '>' ||
                                               BLOB2CLOB(LBDATA   => BASE64_DECODE(LCSRCE => CORDERS),
                                                         SCHARSET => PKG_CHARSET.CHARSET_UTF_()) || '</' ||
                                               SRQ_TAG_XROOT || '>');
    /* Считываем корневой узел */
    XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC);
    /* Считывание списка записей */
    XNODES := PKG_XPATH.LIST_NODES(RPARENT_NODE => XROOT, SPATTERN => '/' || SRQ_TAG_XROOT || '/' || SRQ_TAG_XORDERS);
    /* Цикл по списку записией */
    for I in 1 .. PKG_XPATH.COUNT_NODES(RNODES => XNODES)
    loop
      /* Считаем элемент по его номеру */
      XNODE := PKG_XPATH.ITEM_NODE(RNODES => XNODES, INUMBER => I);
      /* Добавим его в коллекцию */
      RORDERS.EXTEND();
      RORDERS(RORDERS.LAST) := TORDER_MAKE(SNAME      => PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => SRQ_TAG_SNAME),
                                           SDIRECTION => PKG_XPATH.VALUE(RNODE => XNODE, SPATTERN => SRQ_TAG_SDIRECTION));
    end loop;
    /* Освободим документ */
    PKG_XPATH.FREE(RDOCUMENT => XDOC);
    /* Вернём результат */
    return RORDERS;
  exception
    when others then
      /* Освободим документ */
      PKG_XPATH.FREE(RDOCUMENT => XDOC);
      /* Вернем ошибку */
      PKG_STATE.DIAGNOSTICS_STACKED();
      P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
  end TORDERS_FROM_XML;
  
  /* Применение параметров сортировки в запросе */
  procedure TORDERS_SET_QUERY
  (
    RDATA_GRID              in TDATA_GRID,      -- Описание таблицы
    RORDERS                 in TORDERS,         -- Коллекция сортировок
    SPATTERN                in varchar2,        -- Шаблон для подстановки условий отбора в запрос    
    CSQL                    in out nocopy clob  -- Буфер запроса
  )
  is
    CSQL_ORDERS             clob;               -- Буфер для условий сортировки в запросе
  begin
    /* Если сортировка задана */
    if ((RORDERS is not null) and (RORDERS.COUNT > 0)) then
      CSQL_ORDERS := ' order by ';
      for I in RORDERS.FIRST .. RORDERS.LAST
      loop
        /* Перед добавлением в запрос - обязательная проверка, чтобы избежать SQL-инъекций */
        if ((TCOL_DEFS_FIND(RCOL_DEFS => RDATA_GRID.RCOL_DEFS, SNAME => RORDERS(I).SNAME).SNAME is not null) and
           (RORDERS(I).SDIRECTION in (SORDER_DIRECTION_ASC, SORDER_DIRECTION_DESC))) then
          CSQL_ORDERS := CSQL_ORDERS || RORDERS(I).SNAME || ' ' || RORDERS(I).SDIRECTION;
          if (I < RORDERS.LAST) then
            CSQL_ORDERS := CSQL_ORDERS || ', ';
          end if;
        end if;
      end loop;
    end if;
    CSQL := replace(CSQL, SPATTERN, CSQL_ORDERS);
  end TORDERS_SET_QUERY;
  
  /* Проверка корректности наименования дополнительного атрибута задачи диаграммы Ганта */
  procedure TGANTT_TASK_ATTR_NAME_CHECK
  (
    SNAME                   in varchar2 -- Наименование
  )
  is
  begin
    if (SNAME in (SRESP_ATTR_ID,
                  SRESP_ATTR_RN,
                  SRESP_ATTR_NUMB,
                  SRESP_ATTR_CAPTION,
                  SRESP_ATTR_FULL_NAME,
                  SRESP_ATTR_START,
                  SRESP_ATTR_END,
                  SRESP_ATTR_TASK_PROGRESS,
                  SRESP_ATTR_TASK_BG_COLOR,
                  SRESP_ATTR_TASK_TEXT_COLOR,                  
                  SRESP_ATTR_TASK_RO,
                  SRESP_ATTR_TASK_RO_PRGRS,
                  SRESP_ATTR_TASK_RO_DATES,
                  SRESP_ATTR_TASK_DEPS)) then
      P_EXCEPTION(0,
                  'Наименование атрибута "%s" является зарезервированным.',
                  SNAME);
    end if;
  end TGANTT_TASK_ATTR_NAME_CHECK;
  
  /* Поиск атрибута задачи диаграммы Ганта по наименованию */
  function TGANTT_TASK_ATTR_FIND
  (
    RTASK_ATTRS             in TGANTT_TASK_ATTRS, -- Описание атрибутов задачи диаграммы Ганта
    SNAME                   in varchar2           -- Наименование
  ) return                  TGANTT_TASK_ATTR      -- Найденное описание (null - если не нашли)
  is
  begin
    /* Обходим атрибуты из описания */
    if ((RTASK_ATTRS is not null) and (RTASK_ATTRS.COUNT > 0)) then
      for I in RTASK_ATTRS.FIRST .. RTASK_ATTRS.LAST
      loop
        if (RTASK_ATTRS(I).SNAME = SNAME) then
          return RTASK_ATTRS(I);
        end if;
      end loop;
    end if;
    /* Ничего не нашли */
    return null;
  end TGANTT_TASK_ATTR_FIND;
  
  /* Поиск цвета задачи диаграммы Ганта по параметрам */
  function TGANTT_TASK_COLOR_FIND
  (
    RTASK_COLORS            in TGANTT_TASK_COLORS, -- Описание цветов задачи диаграммы Ганта
    SBG_COLOR               in varchar2 := null,   -- Цвет заливки задачи (формат - HTML-цвет, #RRGGBBAA)
    STEXT_COLOR             in varchar2 := null    -- Цвет текста заголовка задачи (формат - HTML-цвет, #RRGGBBAA)
  ) return                  TGANTT_TASK_COLOR      -- Найденное описание цвета (null - если не нашли)
  is
  begin
    /* Обходим цвета из описания */
    if ((RTASK_COLORS is not null) and (RTASK_COLORS.COUNT > 0)) then
      for I in RTASK_COLORS.FIRST .. RTASK_COLORS.LAST
      loop
        if ((CMP_VC2(V1 => RTASK_COLORS(I).SBG_COLOR, V2 => SBG_COLOR) = 1) and
           (CMP_VC2(V1 => RTASK_COLORS(I).STEXT_COLOR, V2 => STEXT_COLOR) = 1)) then
          return RTASK_COLORS(I);
        end if;
      end loop;
    end if;
    /* Ничего не нашли */
    return null;
  end TGANTT_TASK_COLOR_FIND;
  
  /* Формирование задачи для диаграммы Ганта */
  function TGANTT_TASK_MAKE
  (
    NRN                     in number,           -- Рег. номер
    SNUMB                   in varchar2,         -- Номер
    SCAPTION                in varchar2,         -- Заголовок
    SNAME                   in varchar2,         -- Наименование
    DSTART                  in date,             -- Дата начала
    DEND                    in date,             -- Дата окончания
    NPROGRESS               in number := null,   -- Прогресс (% готовности) задачи (null - не определен)
    SBG_COLOR               in varchar2 := null, -- Цвет заливки задачи (null - использовать цвет по умолчанию из стилей, формат - HTML-цвет, #RRGGBBAA)
    STEXT_COLOR             in varchar2 := null, -- Цвет текста заголовка задачи (null - использовать цвет по умолчанию из стилей, формат - HTML-цвет, #RRGGBBAA)
    BREAD_ONLY              in boolean := null,  -- Сроки и прогресс задачи только для чтения (null - как указано в описании диаграммы)
    BREAD_ONLY_DATES        in boolean := null,  -- Сроки задачи только для чтения (null - как указано в описании диаграммы)
    BREAD_ONLY_PROGRESS     in boolean := null   -- Прогресс задачи только для чтения (null - как указано в описании диаграммы)
  ) return                  TGANTT_TASK          -- Результат работы
  is
    RRES                    TGANTT_TASK;         -- Буфер для результата
  begin
    /* Проверим параметры */
    if ((NRN is null) or (SNUMB is null) or (SCAPTION is null) or (SNAME is null) or (DSTART is null) or (DEND is null)) then
      P_EXCEPTION(0,
                  'Регистрационный номер, номер, заголовок, наименование, даты начала и окончания являются обязательными при создании задачи для диаграммы Ганта.');
    end if;
    if ((NPROGRESS is not null) and (not (NPROGRESS between 0 and 100))) then
      P_EXCEPTION(0, 'Прогресс задачи должен быть значением от 0 до 100');
    end if;
    /* Формируем объект */
    RRES.NRN                 := NRN;
    RRES.SNUMB               := SNUMB;
    RRES.SCAPTION            := SCAPTION;
    RRES.SNAME               := SNAME;
    RRES.DSTART              := DSTART;
    RRES.DEND                := DEND;
    RRES.NPROGRESS           := NPROGRESS;
    RRES.SBG_COLOR           := SBG_COLOR;
    RRES.STEXT_COLOR         := STEXT_COLOR;
    RRES.BREAD_ONLY          := BREAD_ONLY;
    RRES.BREAD_ONLY_DATES    := BREAD_ONLY_DATES;
    RRES.BREAD_ONLY_PROGRESS := BREAD_ONLY_PROGRESS;
    RRES.RATTR_VALS          := TGANTT_TASK_ATTR_VALS();
    RRES.RDEPENDENCIES       := TGANTT_TASK_DEPENDENCIES();
    /* Возвращаем результат */
    return RRES;
  end TGANTT_TASK_MAKE;
  
  /* Добавление значения атрибута к задаче диаграммы Ганта */
  procedure TGANTT_TASK_ADD_ATTR_VAL
  (
    RGANTT                  in TGANTT,                 -- Описание диаграммы
    RTASK                   in out nocopy TGANTT_TASK, -- Описание задачи
    SNAME                   in varchar2,               -- Наименование
    SVALUE                  in varchar2,               -- Значение
    BCLEAR                  in boolean := false        -- Флаг очистки коллекции значений атрибутов (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Проверим наименование */
    TGANTT_TASK_ATTR_NAME_CHECK(SNAME => SNAME);
    /* Проверим, что такой атрибут зарегистрирован */
    if (TGANTT_TASK_ATTR_FIND(RTASK_ATTRS => RGANTT.RTASK_ATTRS, SNAME => SNAME).SNAME is null) then
      P_EXCEPTION(0,
                  'Атрибут "%s" задачи диаграммы Ганта не зарегистрирован.',
                  SNAME);
    end if;
    /* Инициализируем коллекцию если необходимо */
    if ((RTASK.RATTR_VALS is null) or (BCLEAR)) then
      RTASK.RATTR_VALS := TGANTT_TASK_ATTR_VALS();
    end if;
    /* Добавляем элемент */
    RTASK.RATTR_VALS.EXTEND();
    RTASK.RATTR_VALS(RTASK.RATTR_VALS.LAST).SNAME := SNAME;
    RTASK.RATTR_VALS(RTASK.RATTR_VALS.LAST).SVALUE := SVALUE;
  end TGANTT_TASK_ADD_ATTR_VAL;
  
  /* Добавление предшествующей задачи к задаче диаграммы Ганта */
  procedure TGANTT_TASK_ADD_DEPENDENCY
  (
    RTASK                   in out nocopy TGANTT_TASK, -- Описание задачи
    NDEPENDENCY             in number,                 -- Рег. номер предшествующей задачи
    BCLEAR                  in boolean := false        -- Флаг очистки коллекции предшествущих задач (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Инициализируем коллекцию если необходимо */
    if ((RTASK.RDEPENDENCIES is null) or (BCLEAR)) then
      RTASK.RDEPENDENCIES := TGANTT_TASK_DEPENDENCIES();
    end if;
    /* Добавляем элемент */
    RTASK.RDEPENDENCIES.EXTEND();
    RTASK.RDEPENDENCIES(RTASK.RDEPENDENCIES.LAST) := NDEPENDENCY;
  end TGANTT_TASK_ADD_DEPENDENCY;
  
  /* Сериализация описания задач диаграммы Ганта */
  procedure TGANTT_TASKS_TO_XML
  (
    RTASKS                  in TGANTT_TASKS   -- Коллекция задач диаграммы Ганта
  )
  is
    SDEPS                   PKG_STD.TLSTRING; -- Буфер для списка зависимых
  begin
    /* Обходим задачи из коллекции */
    if ((RTASKS is not null) and (RTASKS.COUNT > 0)) then
      for I in RTASKS.FIRST .. RTASKS.LAST
      loop
        /* Открываем строку */
        PKG_XFAST.DOWN_NODE(SNAME => SRESP_TAG_XGANTT_TASKS);
        /* Статические тарибуты */
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_ID, SVALUE => 'taskId' || RTASKS(I).NRN);
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_RN, NVALUE => RTASKS(I).NRN);
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_NUMB, SVALUE => RTASKS(I).SNUMB);
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_NAME, SVALUE => RTASKS(I).SCAPTION);
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_FULL_NAME, SVALUE => RTASKS(I).SNAME);
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_START, DVALUE => RTASKS(I).DSTART);
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_END, DVALUE => RTASKS(I).DEND);
        if (RTASKS(I).NPROGRESS is not null) then
          PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TASK_PROGRESS, NVALUE => RTASKS(I).NPROGRESS);
        end if;
        if (RTASKS(I).SBG_COLOR is not null) then
          PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TASK_BG_COLOR, SVALUE => RTASKS(I).SBG_COLOR);
        end if;
        if (RTASKS(I).STEXT_COLOR is not null) then
          PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TASK_TEXT_COLOR, SVALUE => RTASKS(I).STEXT_COLOR);
        end if;
        if (RTASKS(I).BREAD_ONLY is not null) then
          PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TASK_RO, BVALUE => RTASKS(I).BREAD_ONLY);
        end if;
        if (RTASKS(I).BREAD_ONLY_DATES is not null) then
          PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TASK_RO_DATES, BVALUE => RTASKS(I).BREAD_ONLY_DATES);
        end if;
        if (RTASKS(I).BREAD_ONLY_PROGRESS is not null) then
          PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TASK_RO_PRGRS, BVALUE => RTASKS(I).BREAD_ONLY_PROGRESS);
        end if;
        if ((RTASKS(I).RDEPENDENCIES is not null) and (RTASKS(I).RDEPENDENCIES.COUNT > 0)) then
          SDEPS := null;
          for J in RTASKS(I).RDEPENDENCIES.FIRST .. RTASKS(I).RDEPENDENCIES.LAST
          loop
            SDEPS := COALESCE(SDEPS, '') || 'taskId' || TO_CHAR(RTASKS(I).RDEPENDENCIES(J)) || ',';
          end loop;
          PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TASK_DEPS, SVALUE => RTRIM(SDEPS, ','));
        end if;
        /* Динамические атрибуты */
        if ((RTASKS(I).RATTR_VALS is not null) and (RTASKS(I).RATTR_VALS.COUNT > 0)) then
          for J in RTASKS(I).RATTR_VALS.FIRST .. RTASKS(I).RATTR_VALS.LAST
          loop
            PKG_XFAST.ATTR(SNAME => RTASKS(I).RATTR_VALS(J).SNAME, SVALUE => RTASKS(I).RATTR_VALS(J).SVALUE);
          end loop;
        end if;
        /* Закрываем задачу */
        PKG_XFAST.UP();
      end loop;
    end if;
  end TGANTT_TASKS_TO_XML;

  /* Формирование диаграммы Ганта */
  function TGANTT_MAKE
  (
    STITLE                  in varchar2 := null,           -- Заголовок (null - не отображать)
    NZOOM                   in number := NGANTT_ZOOM_WEEK, -- Текущий масштаб (см. константы NGANTT_ZOOM_*)
    BZOOM_BAR               in boolean := true,            -- Обображать панель масштабирования
    BREAD_ONLY              in boolean := false,           -- Сроки и прогресс задач только для чтения
    BREAD_ONLY_DATES        in boolean := false,           -- Сроки задач только для чтения
    BREAD_ONLY_PROGRESS     in boolean := false            -- Прогресс задач только для чтения
  ) return                  TGANTT                         -- Результат работы
  is
    RRES                    TGANTT;                        -- Буфер для результата
  begin
    /* Формируем объект */
    RRES.STITLE              := STITLE;
    RRES.NZOOM               := COALESCE(NZOOM, NGANTT_ZOOM_WEEK);
    RRES.BZOOM_BAR           := COALESCE(BZOOM_BAR, true);
    RRES.BREAD_ONLY          := COALESCE(BREAD_ONLY, false);
    RRES.BREAD_ONLY_DATES    := COALESCE(BREAD_ONLY_DATES, false);
    RRES.BREAD_ONLY_PROGRESS := COALESCE(BREAD_ONLY_PROGRESS, false);
    RRES.RTASK_ATTRS         := TGANTT_TASK_ATTRS();
    RRES.RTASK_COLORS        := TGANTT_TASK_COLORS();
    RRES.RTASKS              := TGANTT_TASKS();
    /* Возвращаем результат */
    return RRES;
  end TGANTT_MAKE;
  
  /* Добавление описания атрибута карточки задачи диаграммы Ганта */
  procedure TGANTT_ADD_TASK_ATTR
  (
    RGANTT                  in out nocopy TGANTT, -- Описание диаграммы Ганта
    SNAME                   in varchar2,          -- Наименование
    SCAPTION                in varchar2,          -- Заголовок
    BCLEAR                  in boolean := false   -- Флаг очистки коллекции атрибутов (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Проверим наименование */
    TGANTT_TASK_ATTR_NAME_CHECK(SNAME => SNAME);
    /* Проверим, что такого ещё нет */
    if (TGANTT_TASK_ATTR_FIND(RTASK_ATTRS => RGANTT.RTASK_ATTRS, SNAME => SNAME).SNAME is not null) then
      P_EXCEPTION(0,
                  'Атрибут "%s" задачи диаграммы Ганта уже зарегистрирован.',
                  SNAME);
    end if;
    /* Инициализируем коллекцию если необходимо */
    if ((RGANTT.RTASK_ATTRS is null) or (BCLEAR)) then
      RGANTT.RTASK_ATTRS := TGANTT_TASK_ATTRS();
    end if;
    /* Добавляем элемент */
    RGANTT.RTASK_ATTRS.EXTEND();
    RGANTT.RTASK_ATTRS(RGANTT.RTASK_ATTRS.LAST).SNAME := SNAME;
    RGANTT.RTASK_ATTRS(RGANTT.RTASK_ATTRS.LAST).SCAPTION := SCAPTION;
  end TGANTT_ADD_TASK_ATTR;

  /* Добавление описания цвета задачи диаграммы Ганта */
  procedure TGANTT_ADD_TASK_COLOR
  (
    RGANTT                  in out nocopy TGANTT, -- Описание диаграммы Ганта
    SBG_COLOR               in varchar2 := null,  -- Цвет заливки задачи (формат - HTML-цвет, #RRGGBBAA)
    STEXT_COLOR             in varchar2 := null,  -- Цвет текста заголовка задачи (формат - HTML-цвет, #RRGGBBAA)
    SDESC                   in varchar2,          -- Описание
    BCLEAR                  in boolean := false   -- Флаг очистки коллекции цветов (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Проверим параметры */
    if ((SBG_COLOR is null) and (STEXT_COLOR is null)) then
      P_EXCEPTION(0,
                  'Должен быть указан цвет заливки или цвет текста задачи.');
    end if;
    if (SDESC is null) then
      P_EXCEPTION(0, 'Описание цвета должно быть задано.');
    end if;
    /* Проверим, что такого ещё нет */
    if (TGANTT_TASK_COLOR_FIND(RTASK_COLORS => RGANTT.RTASK_COLORS, SBG_COLOR => SBG_COLOR, STEXT_COLOR => STEXT_COLOR)
       .SDESC is not null) then
      P_EXCEPTION(0,
                  'Такое описание цвета для задачи диаграммы Ганта уже зарегистрировано.');
    end if;
    /* Инициализируем коллекцию если необходимо */
    if ((RGANTT.RTASK_COLORS is null) or (BCLEAR)) then
      RGANTT.RTASK_COLORS := TGANTT_TASK_COLORS();
    end if;
    /* Добавляем элемент */
    RGANTT.RTASK_COLORS.EXTEND();
    RGANTT.RTASK_COLORS(RGANTT.RTASK_COLORS.LAST).SBG_COLOR := SBG_COLOR;
    RGANTT.RTASK_COLORS(RGANTT.RTASK_COLORS.LAST).STEXT_COLOR := STEXT_COLOR;
    RGANTT.RTASK_COLORS(RGANTT.RTASK_COLORS.LAST).SDESC := SDESC;
  end TGANTT_ADD_TASK_COLOR;

  /* Добавление задачи к диаграмме Ганта */
  procedure TGANTT_ADD_TASK
  (
    RGANTT                  in out nocopy TGANTT, -- Описание диаграммы Ганта
    RTASK                   in TGANTT_TASK,       -- Задача
    BCLEAR                  in boolean := false   -- Флаг очистки коллекции задач диаграммы (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Инициализируем коллекцию если необходимо */
    if ((RGANTT.RTASKS is null) or (BCLEAR)) then
      RGANTT.RTASKS := TGANTT_TASKS();
    end if;
    /* Добавляем элемент */
    RGANTT.RTASKS.EXTEND();
    RGANTT.RTASKS(RGANTT.RTASKS.LAST) := RTASK;
  end TGANTT_ADD_TASK;
  
  /* Сериализация описания заголовка диаграммы Ганта */
  procedure TGANTT_DEF_TO_XML
  (
    RGANTT                  in TGANTT   -- Описание диаграммы Ганта
  )
  is    
  begin
    /* Открываем описание заголовка */
    PKG_XFAST.DOWN_NODE(SNAME => SRESP_TAG_XGANTT_DEF);
    /* Cтатические атрибуты заголовка */
    PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TITLE, SVALUE => RGANTT.STITLE);
    PKG_XFAST.ATTR(SNAME => SRESP_ATTR_ZOOM, NVALUE => RGANTT.NZOOM);
    PKG_XFAST.ATTR(SNAME => SRESP_ATTR_GANTT_ZOOM_BAR, BVALUE => RGANTT.BZOOM_BAR);
    PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TASK_RO, BVALUE => RGANTT.BREAD_ONLY);
    PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TASK_RO_DATES, BVALUE => RGANTT.BREAD_ONLY_DATES);
    PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TASK_RO_PRGRS, BVALUE => RGANTT.BREAD_ONLY_PROGRESS);
    /* Если есть динамические атрибуты */
    if ((RGANTT.RTASK_ATTRS is not null) and (RGANTT.RTASK_ATTRS.COUNT > 0)) then
      /* Обходим динамические атрибуты задачи */
      for I in RGANTT.RTASK_ATTRS.FIRST .. RGANTT.RTASK_ATTRS.LAST
      loop
        /* Открываем динамический атрибут задачи */
        PKG_XFAST.DOWN_NODE(SNAME => SRESP_ATTR_TASK_ATTRIBUTES);
        /* Наполняем его атрибутами */
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_NAME, SVALUE => RGANTT.RTASK_ATTRS(I).SNAME);
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_CAPTION, SVALUE => RGANTT.RTASK_ATTRS(I).SCAPTION);
        /* Закрываем динамический атрибут задачи */
        PKG_XFAST.UP();
      end loop;
    end if;
    /* Если есть описание цветов */
    if ((RGANTT.RTASK_COLORS is not null) and (RGANTT.RTASK_COLORS.COUNT > 0)) then
      /* Обходим описание цветов задачи */
      for I in RGANTT.RTASK_COLORS.FIRST .. RGANTT.RTASK_COLORS.LAST
      loop
        /* Открываем описание цвета задачи */
        PKG_XFAST.DOWN_NODE(SNAME => SRESP_ATTR_TASK_COLORS);
        /* Наполняем его атрибутами */
        if (RGANTT.RTASK_COLORS(I).SBG_COLOR is not null) then
          PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TASK_BG_COLOR, SVALUE => RGANTT.RTASK_COLORS(I).SBG_COLOR);
        end if;
        if (RGANTT.RTASK_COLORS(I).STEXT_COLOR is not null) then
          PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TASK_TEXT_COLOR, SVALUE => RGANTT.RTASK_COLORS(I).STEXT_COLOR);
        end if;
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_DESC, SVALUE => RGANTT.RTASK_COLORS(I).SDESC);
        /* Закрываем описание цвета задачи */
        PKG_XFAST.UP();
      end loop;
    end if;
    /* Закрываем описание заголовка */
    PKG_XFAST.UP();
  end TGANTT_DEF_TO_XML;
  
  /* Сериализация диаграммы Ганта */
  function TGANTT_TO_XML
  (
    RGANTT                  in TGANTT,     -- Описание диаграммы Ганта
    NINCLUDE_DEF            in number := 1 -- Включить описание заголовка (0 - нет, 1 - да)
  ) return                  clob           -- XML-описание
  is
    CRES                    clob;          -- Буфер для результата
  begin
    /* Начинаем формирование XML */
    PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
    /* Открываем корень */
    PKG_XFAST.DOWN_NODE(SNAME => SRESP_TAG_XDATA);
    /* Если необходимо включить описание колонок */
    if (NINCLUDE_DEF = 1) then
      TGANTT_DEF_TO_XML(RGANTT => RGANTT);
    end if;
    /* Формируем описание задач */
    TGANTT_TASKS_TO_XML(RTASKS => RGANTT.RTASKS);
    /* Закрываем корень */
    PKG_XFAST.UP();
    /* Сериализуем */
    CRES := PKG_XFAST.SERIALIZE_TO_CLOB();
    /* Завершаем формирование XML */
    PKG_XFAST.EPILOGUE();
    /* Возвращаем полученное */
    return CRES;
  exception
    when others then
      /* Завершаем формирование XML */
      PKG_XFAST.EPILOGUE();
      /* Вернем ошибку */
      PKG_STATE.DIAGNOSTICS_STACKED();
      P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
  end TGANTT_TO_XML;
  
  /* Проверка корректности наименования дополнительного атрибута элемента данных графика */
  procedure TCHART_DATASET_ITEM_ATTR_NM_CH
  (
    SNAME                   in varchar2 -- Наименование
  )
  is
  begin
    if (SNAME in (SRESP_ATTR_CHART_DS_I_VAL)) then
      P_EXCEPTION(0,
                  'Наименование атрибута "%s" является зарезервированным.',
                  SNAME);
    end if;
  end TCHART_DATASET_ITEM_ATTR_NM_CH;
  
  /* Сериализация меток графика */
  procedure TCHART_LABELS_TO_XML
  (
    RLABELS                 in TCHART_LABELS -- Описание диаграммы Ганта
  )
  is    
  begin
    /* Если есть метки */
    if ((RLABELS is not null) and (RLABELS.COUNT > 0)) then
      /* Обходим метки */
      for I in RLABELS.FIRST .. RLABELS.LAST
      loop
        /* Открываем описание метки */
        PKG_XFAST.DOWN_NODE(SNAME => SRESP_ATTR_CHART_LABELS);
        /* Добавляем значение */
        PKG_XFAST.VALUE(SVALUE => RLABELS(I));
        /* Закрываем описание метки */
        PKG_XFAST.UP();
      end loop;
    end if;
  end TCHART_LABELS_TO_XML;
  
  /* Добавление дополнительного атрибута элемента данных графика */
  procedure TCHART_DATASET_ITM_ATTR_VL_ADD
  (
    RATTR_VALS              in out nocopy TCHART_DATASET_ITEM_ATTR_VALS, -- Коллекция дополнительных атрибутов элемента данных графика
    SNAME                   PKG_STD.TSTRING,                             -- Наименование
    SVALUE                  PKG_STD.TSTRING,                             -- Значение
    BCLEAR                  in boolean := false                          -- Флаг очистки коллекции дополнительных атрибутов элемента данных (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Проверим корректность наименования */
    TCHART_DATASET_ITEM_ATTR_NM_CH(SNAME => SNAME);
    /* Инициализируем коллекцию если необходимо */
    if ((RATTR_VALS is null) or (BCLEAR)) then
      RATTR_VALS := TCHART_DATASET_ITEM_ATTR_VALS();
    end if;
    /* Добавляем элемент */
    RATTR_VALS.EXTEND();
    RATTR_VALS(RATTR_VALS.LAST).SNAME := SNAME;
    RATTR_VALS(RATTR_VALS.LAST).SVALUE := SVALUE;
  end TCHART_DATASET_ITM_ATTR_VL_ADD;
  
  /* Формирование набора данных графика */
  function TCHART_DATASET_MAKE
  (
    SCAPTION                in varchar2,         -- Заголовок
    SBORDER_COLOR           in varchar2 := null, -- Цвет границы элемента данных на графике (null - использовать цвет по умолчанию из стилей, формат - HTML-цвет, #RRGGBBAA)
    SBG_COLOR               in varchar2 := null  -- Цвет заливки элемента данных на графике (null - использовать цвет по умолчанию из стилей, формат - HTML-цвет, #RRGGBBAA)
  ) return                  TCHART_DATASET       -- Результат работы
  is
    RRES                    TCHART_DATASET;      -- Буфер для результата
  begin
    /* Формируем объект */
    RRES.SCAPTION      := SCAPTION;
    RRES.SBORDER_COLOR := SBORDER_COLOR;
    RRES.SBG_COLOR     := SBG_COLOR;
    RRES.RITEMS        := TCHART_DATASET_ITEMS();
    /* Возвращаем результат */
    return RRES;
  end TCHART_DATASET_MAKE;
  
  /* Добавление элемента в набор данных графика */
  procedure TCHART_DATASET_ADD_ITEM
  (
    RDATASET                in out nocopy TCHART_DATASET,             -- Описание набора данных графика
    NVALUE                  in number,                                -- Значение элемента данных, отображаемое на графике
    RATTR_VALS              in TCHART_DATASET_ITEM_ATTR_VALS := null, -- Значения дополнительных атрбутов (null - дополнительные атрибуты не определены)
    BCLEAR                  in boolean := false                       -- Флаг очистки коллекции элементов набора данных (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Инициализируем коллекцию если необходимо */
    if ((RDATASET.RITEMS is null) or (BCLEAR)) then
      RDATASET.RITEMS := TCHART_DATASET_ITEMS();
    end if;
    /* Добавляем элемент */
    RDATASET.RITEMS.EXTEND();
    RDATASET.RITEMS(RDATASET.RITEMS.LAST).NVALUE := NVALUE;
    RDATASET.RITEMS(RDATASET.RITEMS.LAST).RATTR_VALS := RATTR_VALS;
  end TCHART_DATASET_ADD_ITEM;
  
  /* Сериализация коллекции наборов данных графика */
  procedure TCHART_DATASETS_TO_XML
  (
    RDATASETS               in TCHART_DATASETS -- Наборы данных графика
  )
  is
  begin
    /* Если есть наборы данных */
    if ((RDATASETS is not null) and (RDATASETS.COUNT > 0)) then
      /* Обходим наборы данных */
      for I in RDATASETS.FIRST .. RDATASETS.LAST
      loop
        /* Открываем набор данных */
        PKG_XFAST.DOWN_NODE(SNAME => SRESP_ATTR_CHART_DATASETS);
        /* Добавляем статические атрибуты */
        PKG_XFAST.ATTR(SNAME => SRESP_ATTR_CHART_DS_LABEL, SVALUE => RDATASETS(I).SCAPTION);
        if (RDATASETS(I).SBORDER_COLOR is not null) then
          PKG_XFAST.ATTR(SNAME => SRESP_ATTR_CHART_DS_BR_CLR, SVALUE => RDATASETS(I).SBORDER_COLOR);
        end if;
        if (RDATASETS(I).SBG_COLOR is not null) then
          PKG_XFAST.ATTR(SNAME => SRESP_ATTR_CHART_DS_BG_CLR, SVALUE => RDATASETS(I).SBG_COLOR);
        end if;
        /* Если в наборе данных есть элементы */
        if ((RDATASETS(I).RITEMS is not null) and (RDATASETS(I).RITEMS.COUNT > 0)) then
          /* Обходим элементы набора данных для формирования отображаемых значений */
          for J in RDATASETS(I).RITEMS.FIRST .. RDATASETS(I).RITEMS.LAST
          loop
            /* Открываем значение элемента набора данных */
            PKG_XFAST.DOWN_NODE(SNAME => SRESP_ATTR_CHART_DS_DATA);
            /* Формируем значение элемента */
            PKG_XFAST.VALUE(NVALUE => RDATASETS(I).RITEMS(J).NVALUE);
            /* Закрываем значение элемента набора данных */
            PKG_XFAST.UP();
          end loop;
          /* Обходим элементы набора данных для формирования из самих */
          for J in RDATASETS(I).RITEMS.FIRST .. RDATASETS(I).RITEMS.LAST
          loop
            /* Открываем элемент набора данных */
            PKG_XFAST.DOWN_NODE(SNAME => SRESP_ATTR_CHART_DS_ITEMS);
            /* Добавляем статические атрибуты */
            PKG_XFAST.ATTR(SNAME => SRESP_ATTR_CHART_DS_I_VAL, NVALUE => RDATASETS(I).RITEMS(J).NVALUE);
            /* Добавляем дополнительные атрибуты */
            if ((RDATASETS(I).RITEMS(J).RATTR_VALS is not null) and (RDATASETS(I).RITEMS(J).RATTR_VALS.COUNT > 0)) then
              for K in RDATASETS(I).RITEMS(J).RATTR_VALS.FIRST .. RDATASETS(I).RITEMS(J).RATTR_VALS.LAST
              loop
                PKG_XFAST.ATTR(SNAME  => RDATASETS(I).RITEMS(J).RATTR_VALS(K).SNAME,
                               SVALUE => RDATASETS(I).RITEMS(J).RATTR_VALS(K).SVALUE);
              end loop;
            end if;
            /* Закрываем элемент набора данных */
            PKG_XFAST.UP();
          end loop;
        end if;
        /* Закрываем набор данных */
        PKG_XFAST.UP();
      end loop;
    end if;
  end TCHART_DATASETS_TO_XML;
  
  /* Формирование графика */
  function TCHART_MAKE
  (
    STYPE                   in varchar2,         -- Тип (см. константы SCHART_TYPE_*)
    STITLE                  in varchar2 := null, -- Заголовок (null - не отображать)
    SLGND_POS               in varchar2 := null  -- Расположение легенды (null - не отображать, см. константы SCHART_LGND_POS_*)
  ) return                  TCHART               -- Результат работы
  is
    RRES                    TCHART;              -- Буфер для результата
  begin
    /* Формируем объект */
    RRES.STYPE     := STYPE;
    RRES.STITLE    := STITLE;
    RRES.SLGND_POS := SLGND_POS;
    RRES.RLABELS   := TCHART_LABELS();
    RRES.RDATASETS := TCHART_DATASETS();
    /* Возвращаем результат */
    return RRES;
  end TCHART_MAKE;
  
  /* Добавление метки значения графика */
  procedure TCHART_ADD_LABEL
  (
    RCHART                  in out nocopy TCHART, -- Описание графика
    SLABEL                  in varchar2,          -- Метка значения
    BCLEAR                  in boolean := false   -- Флаг очистки коллекции меток (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Инициализируем коллекцию если необходимо */
    if ((RCHART.RLABELS is null) or (BCLEAR)) then
      RCHART.RLABELS := TCHART_LABELS();
    end if;
    /* Добавляем элемент */
    RCHART.RLABELS.EXTEND();
    RCHART.RLABELS(RCHART.RLABELS.LAST) := SLABEL;
  end TCHART_ADD_LABEL;
  
  /* Добавление набора данных графика */
  procedure TCHART_ADD_DATASET
  (
    RCHART                  in out nocopy TCHART, -- Описание графика
    RDATASET                in TCHART_DATASET,    -- Набор данных
    BCLEAR                  in boolean := false   -- Флаг очистки коллекции наборов данных (false - не очищать, true - очистить коллекцию перед добавлением)
  )
  is
  begin
    /* Инициализируем коллекцию если необходимо */
    if ((RCHART.RDATASETS is null) or (BCLEAR)) then
      RCHART.RDATASETS := TCHART_DATASETS();
    end if;
    /* Добавляем элемент */
    RCHART.RDATASETS.EXTEND();
    RCHART.RDATASETS(RCHART.RDATASETS.LAST) := RDATASET;
  end TCHART_ADD_DATASET;
  
  /* Сериализация описания заголовка графика */
  procedure TCHART_DEF_TO_XML
  (
    RCHART                  in TCHART   -- Описание графика
  )
  is    
  begin
    /* Cтатические атрибуты заголовка */
    PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TYPE, SVALUE => RCHART.STYPE);
    PKG_XFAST.ATTR(SNAME => SRESP_ATTR_TITLE, SVALUE => RCHART.STITLE);
    PKG_XFAST.ATTR(SNAME => SRESP_ATTR_CHART_LGND_POS, SVALUE => RCHART.SLGND_POS);
  end TCHART_DEF_TO_XML;
  
  /* Сериализация графика */
  function TCHART_TO_XML
  (
    RCHART                  in TCHART,     -- Описание графика
    NINCLUDE_DEF            in number := 1 -- Включить описание заголовка (0 - нет, 1 - да)
  ) return                  clob           -- XML-описание
  is
    CRES                    clob;          -- Буфер для результата
  begin
    /* Начинаем формирование XML */
    PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
    /* Открываем корень */
    PKG_XFAST.DOWN_NODE(SNAME => SRESP_TAG_XDATA);
    /* Открываем график */
    PKG_XFAST.DOWN_NODE(SNAME => SRESP_TAG_XCHART);
    /* Если необходимо включить описание колонок */
    if (NINCLUDE_DEF = 1) then
      TCHART_DEF_TO_XML(RCHART => RCHART);
    end if;
    /* Формируем описание меток */
    TCHART_LABELS_TO_XML(RLABELS => RCHART.RLABELS);
    /* Формируем описание наборов данных */
    TCHART_DATASETS_TO_XML(RDATASETS => RCHART.RDATASETS);
    /* Закрываем график */
    PKG_XFAST.UP();
    /* Закрываем корень */
    PKG_XFAST.UP();
    /* Сериализуем */
    CRES := PKG_XFAST.SERIALIZE_TO_CLOB();
    /* Завершаем формирование XML */
    PKG_XFAST.EPILOGUE();
    /* Возвращаем полученное */
    return CRES;
  exception
    when others then
      /* Завершаем формирование XML */
      PKG_XFAST.EPILOGUE();
      /* Вернем ошибку */
      PKG_STATE.DIAGNOSTICS_STACKED();
      P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
  end TCHART_TO_XML;
  
end PKG_P8PANELS_VISUAL;
/
