create or replace package PKG_P8PANELS_BASE as

  /*Константы - Типовой постфикс тега для массива (при переводе XML -> JSON) */
  SXML_ALWAYS_ARRAY_POSTFIX constant PKG_STD.TSTRING := '__SYSTEM__ARRAY__';

  /* Константы - признаки наличия доступа */
  NACCESS_YES               constant number(1) := 1; -- Доступ есть
  NACCESS_NO                constant number(1) := 0; -- Доступа нет

  /* Конвертация строки в число */
  function UTL_S2N
  (
    SVALUE                  in varchar2 -- Конвертируемое строковое значение
  ) return                  number;     -- Конвертированное число

  /* Конвертация даты в число */
  function UTL_S2D
  (
    SVALUE                  in varchar2 -- Конвертируемое строковое значение
  ) return                  date;       -- Конвертированная дата
  
  /* Проверка доступности документа */
  function UTL_DOC_ACCESS_CHECK
  (
    NCOMPANY                in number,   -- Рег. номер организации
    SUNIT_CODE              in varchar2, -- Код раздела
    NDOCUMENT               in number    -- Рег. номер документа
  ) return                  number;      -- Флаг доступности (см. константы NACCESS_*)
  
  /* Подготовка пользовательской строки поиска для вставки в запрос */
  procedure UTL_SEARCH_PREPARE
  (
    SSEARCH                 in varchar2, -- Пользовательская строка поиска
    SSEARCH_PREPARED        out varchar2 -- Подготовленная строка поиска
  );
  
  /* Базовое исполнение действий */
  procedure PROCESS
  (
    CIN                     in clob,    -- Входные параметры
    COUT                    out clob    -- Результат
  );
  
  /* Базовая инициализация буфера отмеченных записей для панели */
  procedure SELECTLIST_INIT
  (
    NIDENT                  in number   -- Идентификатор буфера отмеченных записей  
  );
  
  /* Базовая очистка буфера отмеченных записей для панели */
  procedure SELECTLIST_CLEAR
  (
    NIDENT                  in number   -- Идентификатор буфера отмеченных записей
  );

end PKG_P8PANELS_BASE;
/
create or replace package body PKG_P8PANELS_BASE as

  /* Константы - коды дествий запросов */
  SRQ_ACTION_EXEC_STORED    constant PKG_STD.TSTRING := 'EXEC_STORED'; -- Запрос на исполнение хранимой процедуры

  /* Константы - тэги запросов */
  SRQ_TAG_XREQUEST          constant PKG_STD.TSTRING := 'XREQUEST';   -- Корневой тэг запроса
  SRQ_TAG_XPAYLOAD          constant PKG_STD.TSTRING := 'XPAYLOAD';   -- Тэг для данных запроса
  SRQ_TAG_SACTION           constant PKG_STD.TSTRING := 'SACTION';    -- Тэг для действия запроса
  SRQ_TAG_SSTORED           constant PKG_STD.TSTRING := 'SSTORED';    -- Тэг для имени хранимого объекта в запросе
  SRQ_TAG_SRESP_ARG         constant PKG_STD.TSTRING := 'SRESP_ARG';  -- Тэг для имени аргумента, формирующего данные ответа
  SRQ_TAG_XARGUMENTS        constant PKG_STD.TSTRING := 'XARGUMENTS'; -- Тэг для списка аргументов хранимого объекта/выборки в запросе
  SRQ_TAG_XARGUMENT         constant PKG_STD.TSTRING := 'XARGUMENT';  -- Тэг для аргумента хранимого объекта/выборки в запросе
  SRQ_TAG_SNAME             constant PKG_STD.TSTRING := 'SNAME';      -- Тэг для наименования в запросе
  SRQ_TAG_SDATA_TYPE        constant PKG_STD.TSTRING := 'SDATA_TYPE'; -- Тэг для типа данных в запросе
  SRQ_TAG_VALUE             constant PKG_STD.TSTRING := 'VALUE';      -- Тэг для значения в запросе

  /* Константы - тэги ответов */
  SRESP_TAG_XPAYLOAD        constant PKG_STD.TSTRING := 'XPAYLOAD';       -- Тэг для данных ответа
  SRESP_TAG_XOUT_ARGUMENTS  constant PKG_STD.TSTRING := 'XOUT_ARGUMENTS'; -- Тэг для выходных аргументов хранимого объекта в ответе
  SRESP_TAG_SDATA_TYPE      constant PKG_STD.TSTRING := 'SDATA_TYPE';     -- Тэг для типа данных в ответе
  SRESP_TAG_VALUE           constant PKG_STD.TSTRING := 'VALUE';          -- Тэг для значения в ответе
  SRESP_TAG_SNAME           constant PKG_STD.TSTRING := 'SNAME';          -- Тэг для наименования в ответе

  /* Константы - типы данных */
  SDATA_TYPE_STR            constant PKG_STD.TSTRING := 'STR';  -- Тип данных "строка"
  SDATA_TYPE_NUMB           constant PKG_STD.TSTRING := 'NUMB'; -- Тип данных "число"
  SDATA_TYPE_DATE           constant PKG_STD.TSTRING := 'DATE'; -- Тип данных "дата"
  SDATA_TYPE_CLOB           constant PKG_STD.TSTRING := 'CLOB'; -- Тип данных "текст"
  
  /* Константы - состояния объектов БД */
  SDB_OBJECT_STATE_VALID    constant PKG_STD.TSTRING := 'VALID'; -- Объект валиден

  /* Типы данных - аргументы */
  type TARGUMENT is record
  (
    SNAME                   PKG_STD.TSTRING,  -- Наименование
    SDATA_TYPE              PKG_STD.TSTRING,  -- Тип данных (см. константы SPWS_DATA_TYPE_*)
    SVALUE                  PKG_STD.TSTRING,  -- Значение (строка)
    NVALUE                  PKG_STD.TLNUMBER, -- Значение (число)
    DVALUE                  PKG_STD.TLDATE,   -- Значение (дата)
    CVALUE                  clob              -- Значение (текст)
  );

  /* Типы данных - коллекция аргументов запроса */
  type TARGUMENTS is table of TARGUMENT;

  /* Конвертация строки в число */
  function UTL_S2N
  (
    SVALUE                  in varchar2      -- Конвертируемое строковое значение
  ) return                  number           -- Конвертированное число
  is
    NVALUE                  PKG_STD.TNUMBER; -- Результат работы
  begin
    /* Пробуем конвертировать */
    NVALUE := TO_NUMBER(replace(SVALUE, ',', '.'));
    /* Отдаём результат */
    return NVALUE;
  exception
    when others then
      P_EXCEPTION(0, 'Неверный формат числа (%s).', SVALUE);
  end UTL_S2N;
  
  /* Конвертация даты в число */
  function UTL_S2D
  (
    SVALUE                  in varchar2      -- Конвертируемое строковое значение
  ) return                  date             -- Конвертированная дата
  is
    DVALUE                  PKG_STD.TLDATE; -- Результат работы
  begin
    /* Пробуем конвертировать */
    begin
      DVALUE := TO_DATE(SVALUE, 'YYYY-MM-DD');
    exception
      when others then
        begin
          DVALUE := TO_DATE(SVALUE, 'YYYY/MM/DD');
        exception
          when others then
            begin
              DVALUE := TO_DATE(SVALUE, 'DD.MM.YYYY');
            exception
              when others then
                DVALUE := TO_DATE(SVALUE, 'DD/MM/YYYY');
            end;
        end;
    end;
    /* Отдаём результат */
    return DVALUE;
  exception
    when others then
      P_EXCEPTION(0, 'Неверный формат даты (%s).', SVALUE);
  end UTL_S2D;

  /* Проверка доступности документа */
  function UTL_DOC_ACCESS_CHECK
  (
    NCOMPANY                in number,       -- Рег. номер организации
    SUNIT_CODE              in varchar2,     -- Код раздела
    NDOCUMENT               in number        -- Рег. номер документа
  ) return                  number           -- Флаг доступности (см. константы NACCESS_*)
  is
    NRES                    PKG_STD.TNUMBER; -- Буфер для результата
    NVERSION                PKG_STD.TREF;    -- Рег. номер версии
    NCATALOG                PKG_STD.TREF;    -- Рег. номер каталога
    NJUR_PERS               PKG_STD.TREF;    -- Рег. номер юридической принадлежности
    NHIERARCHY              PKG_STD.TREF;    -- Рег. номер ирерархии
    BTMP                    boolean;         -- Буфер для расчетов
    NTMP                    PKG_STD.TNUMBER; -- Буфер для расчетов
  begin
    /* Считаем стандартную атрибутику */
    PKG_DOCUMENT.GET_ATTRS(NFLAG_SMART => 0,
                           SUNITCODE   => SUNIT_CODE,
                           NDOCUMENT   => NDOCUMENT,
                           BFOUND      => BTMP,
                           NCOMPANY    => NTMP,
                           NVERSION    => NVERSION,
                           NCATALOG    => NCATALOG,
                           NJUR_PERS   => NJUR_PERS,
                           NHIERARCHY  => NHIERARCHY);
    /* Проверким доступ */
    PKG_ENV.SMART_ACCESS(NCOMPANY   => NCOMPANY,
                         NVERSION   => NVERSION,
                         NCATALOG   => NCATALOG,
                         NJUR_PERS  => NJUR_PERS,
                         NHIERARCHY => NHIERARCHY,
                         SUNIT      => SUNIT_CODE,
                         SACTION    => null,
                         NRESULT    => NRES);
    /* Вернём результат */
    if (NRES = 1) then
      return NACCESS_YES;
    else
      return NACCESS_NO;
    end if;
  exception
    /* В случае ошибки - доступ закрыт */
    when others then
      return NACCESS_NO;
  end UTL_DOC_ACCESS_CHECK;

  /* Подготовка пользовательской строки поиска для вставки в запрос */
  procedure UTL_SEARCH_PREPARE
  (
    SSEARCH                 in varchar2,                                     -- Пользовательская строка поиска
    SSEARCH_PREPARED        out varchar2                                     -- Подготовленная строка поиска
  )
  is
    /* Локальные константы */
    SANY_SYS                constant char(1) := '%';                         -- Маска "любое количество любых символов" Oracle
    SONE_SYS                constant char(1) := '_';                         -- Маска "любой один символ" Oracle
    SENT_WRD_DELIM          constant varchar2(1) := ' ';                     -- Разделитель слов в предложении
    SANY_PRS                constant varchar2(240) := PKG_OPTIONS.STARSYMB;  -- Маска "любое количество любых символов" Парус
    SONE_PRS                constant varchar2(240) := PKG_OPTIONS.QUESTSYMB; -- Маска "любой один символ" Парус
  begin
    /* Если пользовательская строка пустая - то это всё что угодно */
    if (SSEARCH is null)
    then
      SSEARCH_PREPARED := SANY_SYS;
    else
      /* Подменим пользовательские маски на системные и соберем подготовленную строку поиска */
      SSEARCH_PREPARED := '%' || replace(replace(replace(SSEARCH
                                                        ,SANY_PRS
                                                        ,SANY_SYS)
                                                ,SONE_PRS
                                                ,SONE_SYS)
                                        ,SENT_WRD_DELIM
                                        ,SANY_SYS) || '%';
    end if;
  end UTL_SEARCH_PREPARE;
  
  /* Формирование сообщения об отсутствии значения */
  function MSG_NO_DATA_MAKE
  (
    SPATH                   in varchar2 := null, -- Путь по которому ожидалось значение
    SMESSAGE_OBJECT         in varchar2 := null  -- Наимемнование объекта для формулирования сообщения об ошибке
  ) return                  varchar2             -- Сформированное сообщение об ошибке
  is
    SPATH_                  PKG_STD.TSTRING;     -- Буфер для пути
    SMESSAGE_OBJECT_        PKG_STD.TSTRING;     -- Буфер для наименования объекта
  begin
    /* Подготовим путь к выдаче */
    if (SPATH is not null) then
      SPATH_ := ' (' || SPATH || ')';
    end if;
    /* Подготовим наименование объекта к выдаче */
    if (SMESSAGE_OBJECT is not null) then
      SMESSAGE_OBJECT_ := ' элемента "' || SMESSAGE_OBJECT || '"';
    else
      SMESSAGE_OBJECT_ := ' элемента';
    end if;
    /* Вернём сформированное сообщение */
    return 'Не указано значение' || SMESSAGE_OBJECT_ || SPATH_ || '.';
  end MSG_NO_DATA_MAKE;

  /* Конвертация стандартного типа данных (PKG_STD) в тип данных сервиса (PWS) */
  function STD_DATA_TYPE_TO_STR
  (
    NSTD_DATA_TYPE          in number        -- Станартный тип данных
  ) return                  varchar2         -- Соответствующий тип данных сервиса
  is
    SRES                    PKG_STD.TSTRING; -- Буфер для результата
  begin
    /* Работаем от типа данных */
    case NSTD_DATA_TYPE
      /* Строка */
      when PKG_STD.DATA_TYPE_STR then
        SRES := SDATA_TYPE_STR;
      /* Число */
      when PKG_STD.DATA_TYPE_NUM then
        SRES := SDATA_TYPE_NUMB;
      /* Дата */
      when PKG_STD.DATA_TYPE_DATE then
        SRES := SDATA_TYPE_DATE;
      /* Текст */
      when PKG_STD.DATA_TYPE_CLOB then
        SRES := SDATA_TYPE_CLOB;
      /* Неизвестный тип данных */
      else
        P_EXCEPTION(0,
                    'Тип данных "%s" не поддерживается.',
                    COALESCE(TO_CHAR(NSTD_DATA_TYPE), '<НЕ ОПРЕДЕЛЁН>'));
    end case;
    /* Возвращаем результат */
    return SRES;
  end STD_DATA_TYPE_TO_STR;

  /* Считывание значения ветки XML (строка) */
  function NODE_SVAL_GET
  (
    XROOT                   in PKG_XPATH.TNODE, -- Корневая ветка для считывания значения
    SPATH                   in varchar2,        -- Путь для считывания данных
    NREQUIRED               in number := 0,     -- Флаг выдачи сообщения об ошибке в случае отсутствия значения (0 - не выдавать, 1 - выдавать)
    SMESSAGE_OBJECT         in varchar2 := null -- Наимемнование объекта для формулирования сообщения об ошибке
  ) return                  varchar2            -- Считанное значение
  is
    XNODE                   PKG_XPATH.TNODE;    -- Искомая ветка со значением (подходящая под шаблон)
    SVAL                    PKG_STD.TSTRING;    -- Результат работы
  begin
    /* Найдем нужную ветку по шаблону */
    XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => SPATH);
    /* Если там нет ничего */
    if (PKG_XPATH.IS_NULL(RNODE => XNODE)) then
      /* Его и вернём */
      SVAL := null;
    else
      /* Что-то есть - читаем данные */
      begin
        SVAL := PKG_XPATH.VALUE(RNODE => XNODE);
      exception
        when others then
          P_EXCEPTION(0, 'Неверный формат строки (%s).', SPATH);
      end;
    end if;
    /* Если значения нет, а оно должно быть - скажем об этом */
    if ((SVAL is null) and (NREQUIRED = 1)) then
      P_EXCEPTION(0, MSG_NO_DATA_MAKE(SPATH => SPATH, SMESSAGE_OBJECT => SMESSAGE_OBJECT));
    end if;
    /* Отдаём результат */
    return SVAL;
  end NODE_SVAL_GET;

  /* Считывание значения ветки XML (число) */
  function NODE_NVAL_GET
  (
    XROOT                   in PKG_XPATH.TNODE, -- Корневая ветка для считывания значения
    SPATH                   in varchar2,        -- Путь для считывания данных
    NREQUIRED               in number := 0,     -- Флаг выдачи сообщения об ошибке в случае отсутствия значения (0 - не выдавать, 1 - выдавать)
    SMESSAGE_OBJECT         in varchar2 := null -- Наимемнование объекта для формулирования сообщения об ошибке
  ) return                  number              -- Считанное значение
  is
    XNODE                   PKG_XPATH.TNODE;    -- Искомая ветка со значением (подходящая под шаблон)
    NVAL                    PKG_STD.TLNUMBER;   -- Результат работы
  begin
    /* Найдем нужную ветку по шаблону */
    XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => SPATH);
    /* Если там нет ничего */
    if (PKG_XPATH.IS_NULL(RNODE => XNODE)) then
      /* Его и вернём */
      NVAL := null;
    else
      /* Что-то есть - читаем данные */
      begin
        NVAL := PKG_XPATH.VALUE_NUM(RNODE => XNODE);
      exception
        when others then
          P_EXCEPTION(0, 'Неверный формат числа (%s).', SPATH);
      end;
    end if;
    /* Если значения нет, а оно должно быть - скажем об этом */
    if ((NVAL is null) and (NREQUIRED = 1)) then
      P_EXCEPTION(0, MSG_NO_DATA_MAKE(SPATH => SPATH, SMESSAGE_OBJECT => SMESSAGE_OBJECT));
    end if;
    /* Отдаём результат */
    return NVAL;
  end NODE_NVAL_GET;

  /* Считывание значения ветки XML (дата) */
  function NODE_DVAL_GET
  (
    XROOT                   in PKG_XPATH.TNODE, -- Корневая ветка для считывания значения
    SPATH                   in varchar2,        -- Путь для считывания данных
    NREQUIRED               in number := 0,     -- Флаг выдачи сообщения об ошибке в случае отсутствия значения (0 - не выдавать, 1 - выдавать)
    SMESSAGE_OBJECT         in varchar2 := null -- Наимемнование объекта для формулирования сообщения об ошибке
  ) return                  date                -- Считанное значение
  is
    XNODE                   PKG_XPATH.TNODE;    -- Искомая ветка со значением (подходящая под шаблон)
    DVAL                    PKG_STD.TLDATE;     -- Результат работы
  begin
    /* Найдем нужную ветку по шаблону */
    XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => SPATH);
    /* Если там нет ничего */
    if (PKG_XPATH.IS_NULL(RNODE => XNODE)) then
      /* Его и вернём */
      DVAL := null;
    else
      /* Что-то есть - читаем данные */
      begin
        DVAL := PKG_XPATH.VALUE_DATE(RNODE => XNODE);
      exception
        when others then
          begin
            DVAL := PKG_XPATH.VALUE_TS(RNODE => XNODE);
          exception
            when others then
              begin
                DVAL := PKG_XPATH.VALUE_TZ(RNODE => XNODE);
              exception
                when others then
                  P_EXCEPTION(0,
                              'Неверный формат даты (%s). Ожидалось: YYYY-MM-DD"T"HH24:MI:SS.FF3tzh:tzm, YYYY-MM-DD"T"HH24:MI:SS.FF3, YYYY-MM-DD"T"HH24:MI:SS, YYYY-MM-DD.',
                              SPATH);
              end;
          end;
      end;
    end if;
    /* Если значения нет, а оно должно быть - скажем об этом */
    if ((DVAL is null) and (NREQUIRED = 1)) then
      P_EXCEPTION(0, MSG_NO_DATA_MAKE(SPATH => SPATH, SMESSAGE_OBJECT => SMESSAGE_OBJECT));
    end if;
    /* Отдаём результат */
    return DVAL;
  end NODE_DVAL_GET;
  
  /* Считывание значения ветки XML (текст) */
  function NODE_CVAL_GET
  (
    XROOT                   in PKG_XPATH.TNODE, -- Корневая ветка для считывания значения
    SPATH                   in varchar2,        -- Путь для считывания данных
    NREQUIRED               in number := 0,     -- Флаг выдачи сообщения об ошибке в случае отсутствия значения (0 - не выдавать, 1 - выдавать)
    SMESSAGE_OBJECT         in varchar2 := null -- Наимемнование объекта для формулирования сообщения об ошибке
  ) return                  clob                -- Считанное значение
  is
    XNODE                   PKG_XPATH.TNODE;    -- Искомая ветка со значением (подходящая под шаблон)
    CVAL                    clob;               -- Результат работы
  begin
    /* Найдем нужную ветку по шаблону */
    XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => SPATH);
    /* Если там нет ничего */
    if (PKG_XPATH.IS_NULL(RNODE => XNODE)) then
      /* Его и вернём */
      CVAL := null;
    else
      /* Что-то есть - читаем данные */
      begin
        CVAL := PKG_XPATH.VALUE_CLOB(RNODE => XNODE);
      exception
        when others then
          P_EXCEPTION(0, 'Неверный формат текстовых данных (%s).', SPATH);
      end;
    end if;
    /* Если значения нет, а оно должно быть - скажем об этом */
    if ((CVAL is null) and (NREQUIRED = 1)) then
      P_EXCEPTION(0, MSG_NO_DATA_MAKE(SPATH => SPATH, SMESSAGE_OBJECT => SMESSAGE_OBJECT));
    end if;
    /* Отдаём результат */
    return CVAL;
  end NODE_CVAL_GET;
  
  /* Считывание аргумента из коллекции */
  function TARGUMENTS_GET
  (
    ARGUMENTS               in TARGUMENTS, -- Коллекция аргументов
    SARGUMENT               in varchar2,   -- Код аргумента
    NREQUIRED               in number := 0 -- Флаг выдачи сообщения об ошибке в случае отсутствия значения (0 - не выдавать, 1 - выдавать)
  ) return                  TARGUMENT      -- Найденный аргумент
  is
  begin
    /* Если данные в коллекции есть */
    if ((ARGUMENTS is not null) and (ARGUMENTS.COUNT > 0)) then
      /* Обходим её */
      for I in ARGUMENTS.FIRST .. ARGUMENTS.LAST
      loop
        /* Если встретился нужный аргумент */
        if (ARGUMENTS(I).SNAME = SARGUMENT) then
          /* Вернём его */
          return ARGUMENTS(I);
        end if;
      end loop;
    end if;
    /* Если мы здесь - аргумент не нашелся, будем выдавать сообщение об ошибке если он был обязательным */
    if (NREQUIRED = 1) then
      P_EXCEPTION(0, 'Не задан обязательный аргумент "%s".', SARGUMENT);
    else
      /* Он не обязательный - вернём отсутствие данных */
      return null;
    end if;
  end TARGUMENTS_GET;

  /* Считывание значения аргумента из коллекции (строка) */
  function TARGUMENTS_SVAL_GET
  (
    ARGUMENTS               in TARGUMENTS, -- Коллекция аргументов
    SARGUMENT               in varchar2,   -- Код аргумента
    NREQUIRED               in number := 0 -- Флаг выдачи сообщения об ошибке в случае отсутствия значения (0 - не выдавать, 1 - выдавать)
  ) return                  varchar2       -- Значение аргумента
  is
  begin
    /* Считаем и вернём значение */
    return TARGUMENTS_GET(ARGUMENTS => ARGUMENTS, SARGUMENT => SARGUMENT, NREQUIRED => NREQUIRED).SVALUE;
  end TARGUMENTS_SVAL_GET;

  /* Считывание значения параметра из запроса (число) */
  function TARGUMENTS_NVAL_GET
  (
    ARGUMENTS               in TARGUMENTS, -- Коллекция аргументов
    SARGUMENT               in varchar2,   -- Код аргумента
    NREQUIRED               in number := 0 -- Флаг выдачи сообщения об ошибке в случае отсутствия значения (0 - не выдавать, 1 - выдавать)
  ) return                  number         -- Значение аргумента
  is
  begin
    /* Считаем и вернём значение */
    return TARGUMENTS_GET(ARGUMENTS => ARGUMENTS, SARGUMENT => SARGUMENT, NREQUIRED => NREQUIRED).NVALUE;
  end TARGUMENTS_NVAL_GET;

  /* Считывание значения параметра из запроса (дата) */
  function TARGUMENTS_DVAL_GET
  (
    ARGUMENTS               in TARGUMENTS, -- Коллекция аргументов
    SARGUMENT               in varchar2,   -- Код аргумента
    NREQUIRED               in number := 0 -- Флаг выдачи сообщения об ошибке в случае отсутствия значения (0 - не выдавать, 1 - выдавать)
  ) return                  date           -- Значение аргумента
  is
  begin
    /* Считаем и вернём значение */
    return TARGUMENTS_GET(ARGUMENTS => ARGUMENTS, SARGUMENT => SARGUMENT, NREQUIRED => NREQUIRED).DVALUE;
  end TARGUMENTS_DVAL_GET;

  /* Считывание значения параметра из запроса (текст) */
  function TARGUMENTS_CVAL_GET
  (
    ARGUMENTS               in TARGUMENTS, -- Коллекция аргументов
    SARGUMENT               in varchar2,   -- Код аргумента
    NREQUIRED               in number := 0 -- Флаг выдачи сообщения об ошибке в случае отсутствия значения (0 - не выдавать, 1 - выдавать)
  ) return                  clob           -- Значение аргумента
  is
  begin
    /* Считаем и вернём значение */
    return TARGUMENTS_GET(ARGUMENTS => ARGUMENTS, SARGUMENT => SARGUMENT, NREQUIRED => NREQUIRED).CVALUE;
  end TARGUMENTS_CVAL_GET;

  /* Получение корневого элемента тела запроса */
  function RQ_ROOT_GET
  (
    CRQ                     in clob         -- Запрос
  ) return                  PKG_XPATH.TNODE -- Корневой элемент первой ветки тела документа
  is
  begin
    /* Возвращаем корневой элемент документа */
    return PKG_XPATH.ROOT_NODE(RDOCUMENT => PKG_XPATH.PARSE_FROM_CLOB(LCXML => CRQ));
  end RQ_ROOT_GET;

  /* Получение пути к запросу */
  function RQ_PATH_GET
  return                    varchar2    -- Путь к запросу
  is
  begin
    return '/' || SRQ_TAG_XREQUEST;
  end RQ_PATH_GET;

  /* Получение пути к элементу действия запроса */
  function RQ_ACTION_PATH_GET
  return                    varchar2    -- Путь к элементу действия запроса
  is
  begin
    return RQ_PATH_GET() || '/' || SRQ_TAG_SACTION;
  end RQ_ACTION_PATH_GET;
  
  /* Получение кода действия запроса */
  function RQ_ACTION_GET
  (
    XRQ_ROOT                in PKG_XPATH.TNODE := null, -- Корневая ветка запроса
    NREQUIRED               in number := 0              -- Флаг выдачи сообщения об ошибке в случае отсутствия значения (0 - не выдавать, 1 - выдавать)
  ) return                  varchar2                    -- Код действия запроса
  is
  begin
    /* Вернем значение элемента тела с кодом действия */
    return NODE_SVAL_GET(XROOT           => XRQ_ROOT,
                         SPATH           => RQ_ACTION_PATH_GET(),
                         NREQUIRED       => NREQUIRED,
                         SMESSAGE_OBJECT => 'Код действия');
  end RQ_ACTION_GET;

  /* Получение пути к параметрам запроса */
  function RQ_PAYLOAD_PATH_GET
  return                    varchar2    -- Путь к параметрам запроса
  is
  begin
    /* Вернем значение */
    return RQ_PATH_GET() || '/' || SRQ_TAG_XPAYLOAD;
  end RQ_PAYLOAD_PATH_GET;

  /* Получение пути к элкменту параметров запроса */
  function RQ_PAYLOAD_ITEM_PATH_GET
  (
    SITEM_TAG               in varchar2 -- Тэг элемента
  )
  return                    varchar2    -- Путь к элементу параметров запроса
  is
  begin
    /* Вернем значение */
    return RQ_PAYLOAD_PATH_GET() || '/' || SITEM_TAG;
  end RQ_PAYLOAD_ITEM_PATH_GET;

  /* Считывание наименования исполняемого хранимого объекта из запроса */
  function RQ_PAYLOAD_STORED_GET
  (
    XRQ_ROOT                in PKG_XPATH.TNODE := null, -- Корневая ветка запроса
    NREQUIRED               in number := 0              -- Флаг выдачи сообщения об ошибке в случае отсутствия значения (0 - не выдавать, 1 - выдавать)
  ) return                  varchar2                    -- Наименование исполняемого хранимого объекта из запроса
  is
  begin
    /* Вернем значение элемента тела с наименованием хранимого объекта */
    return NODE_SVAL_GET(XROOT           => XRQ_ROOT,
                         SPATH           => RQ_PAYLOAD_ITEM_PATH_GET(SITEM_TAG => SRQ_TAG_SSTORED),
                         NREQUIRED       => NREQUIRED,
                         SMESSAGE_OBJECT => 'Наименование процедуры/функции');
  end RQ_PAYLOAD_STORED_GET;
  
  /* Проверка исполняемого хранимого объекта из запроса */
  procedure RQ_PAYLOAD_STORED_CHECK
  (
    XRQ_ROOT                in PKG_XPATH.TNODE,       -- Корневая ветка запроса
    SSTORED                 in varchar2 := null       -- Наименование проверяемого хранимого объекта (null - автоматическое считывание из запроса)
  )
  is
    SSTORED_                PKG_STD.TSTRING;          -- Буфер для наименования проверяемого хранимого объекта
    RSTORED                 PKG_OBJECT_DESC.TSTORED;  -- Описание хранимого объекта из БД
    SPROCEDURE              PKG_STD.TSTRING;          -- Буфер для наименования хранимой процедуры
    SPACKAGE                PKG_STD.TSTRING;          -- Буфер для наименования пакета, содержащего хранимый объект
    RPACKAGE                PKG_OBJECT_DESC.TPACKAGE; -- Описание пакета, содержащего хранимый объект
  begin
    /* Считаем наименование объекта из запроса или используем переданное в параметрах */
    if (SSTORED is not null) then
      SSTORED_ := SSTORED;
    else
      SSTORED_ := RQ_PAYLOAD_STORED_GET(XRQ_ROOT => XRQ_ROOT, NREQUIRED => 1);
    end if;
    /* Проверим, что это процедура или функция и она вообще существует */
    if (PKG_OBJECT_DESC.EXISTS_STORED(SSTORED_NAME => SSTORED_) = 0) then
      P_EXCEPTION(0,
                  'Хранимая процедура/функция "' || SSTORED_ || '" не определена.');
    else
      /* Проверим, что в имени нет ссылки на пакет */
      PKG_EXS.UTL_STORED_PARSE_LINK(SSTORED => SSTORED_, SPROCEDURE => SPROCEDURE, SPACKAGE => SPACKAGE);
      /* Если в имени есть ссылка на пакет - сначала проверим его состояние */
      if (SPACKAGE is not null) then
        RPACKAGE := PKG_OBJECT_DESC.DESC_PACKAGE(SPACKAGE_NAME => SPACKAGE, BRAISE_ERROR => false);
      end if;
      /* Если есть ссылка на пакет, или он не валиден - это ошибка */
      if ((SPACKAGE is not null) and (RPACKAGE.STATUS <> SDB_OBJECT_STATE_VALID)) then
        P_EXCEPTION(0,
                    'Пакет "' || SPACKAGE ||
                    '", содержащий хранимую процедуру/функцию, невалиден. Обращение к объекту невозможно.');
      else
        /* Нет ссылки на пакет или он валиден - проверяем глубже, получим описание объекта из БД */
        RSTORED := PKG_OBJECT_DESC.DESC_STORED(SSTORED_NAME => SSTORED_, BRAISE_ERROR => false);
        /* Проверим, что валидна */
        if (RSTORED.STATUS <> SDB_OBJECT_STATE_VALID) then
          P_EXCEPTION(0,
                      'Хранимая процедура/функция "' || SSTORED_ || '" невалидна. Обращение к объекту невозможно.');
        else
          /* Проверим, что это клиентский объект */
          if (PKG_OBJECT_DESC.EXISTS_PRIV_EXECUTE(SSTORED_NAME => COALESCE(RSTORED.PACKAGE_NAME, SSTORED_)) = 0) then
            P_EXCEPTION(0,
                        'Хранимая процедура/функция "' || SSTORED_ ||
                        '" не является клиентской. Обращение к объекту невозможно.');
          end if;
        end if;
      end if;
    end if;
  end RQ_PAYLOAD_STORED_CHECK;

  /* Считывание списка аргументов из запроса */
  function RQ_PAYLOAD_ARGUMENTS_GET
  (
    XRQ_ROOT                in PKG_XPATH.TNODE, -- Корневая ветка запроса
    NREQUIRED               in number := 0      -- Флаг выдачи сообщения об ошибке в случае отсутствия значения (0 - не выдавать, 1 - выдавать)
  ) return                  TARGUMENTS          -- Коллекция аргументов из запроса
  is
    RES                     TARGUMENTS;         -- Результат работы
    SRQ_ARGUMENTS_PATH      PKG_STD.TSTRING;    -- Полный путь до аргументов выборки в запросе
    XRQ_ARGUMENTS           PKG_XPATH.TNODES;   -- Коллекция элементов документа запроса с аргументами
    XRQ_ARGUMENT            PKG_XPATH.TNODE;    -- Элемент документа запроса с аргументов
  begin
    /* Инициализируем результат */
    RES := TARGUMENTS();
    /* Сформируем полный путь до аргументов в выборке */
    SRQ_ARGUMENTS_PATH := RQ_PAYLOAD_ITEM_PATH_GET(SITEM_TAG => SRQ_TAG_XARGUMENTS) || '/' || SRQ_TAG_XARGUMENT;
    /* Считаем коллекцию аргументов из документа */
    XRQ_ARGUMENTS := PKG_XPATH.LIST_NODES(RPARENT_NODE => XRQ_ROOT, SPATTERN => SRQ_ARGUMENTS_PATH);
    /* Обходим коллекцию аргументов из документа */
    for I in 1 .. PKG_XPATH.COUNT_NODES(RNODES => XRQ_ARGUMENTS)
    loop
      /* Берем очередной аргумент */
      XRQ_ARGUMENT := PKG_XPATH.ITEM_NODE(RNODES => XRQ_ARGUMENTS, INUMBER => I);
      /* Добавляем его в выходную коллекцию */
      RES.EXTEND();
      RES(RES.LAST).SNAME := NODE_SVAL_GET(XROOT => XRQ_ARGUMENT, SPATH => SRQ_TAG_SNAME);
      RES(RES.LAST).SDATA_TYPE := NODE_SVAL_GET(XROOT => XRQ_ARGUMENT, SPATH => SRQ_TAG_SDATA_TYPE);
      /* Проверим корректность данных - наименование */
      if (RES(RES.LAST).SNAME is null) then
        P_EXCEPTION(0,
                    'Для аргумента не задано наименование (%s).',
                    SRQ_ARGUMENTS_PATH || '/' || SRQ_TAG_SNAME);
      end if;
      /* Проверим корректность данных - тип данных */
      if (RES(RES.LAST).SDATA_TYPE is null) then
        P_EXCEPTION(0,
                    'Для аргумента "%s" не задан тип данных (%s).',
                    RES(RES.LAST).SNAME,
                    SRQ_ARGUMENTS_PATH || '/' || SRQ_TAG_SDATA_TYPE);
      end if;
      /* Считаем значение в зависимости от типа данных */
      case
        /* Строка */
        when (RES(RES.LAST).SDATA_TYPE = SDATA_TYPE_STR) then
          RES(RES.LAST).SVALUE := NODE_SVAL_GET(XROOT => XRQ_ARGUMENT, SPATH => SRQ_TAG_VALUE);
        /* Число */
        when (RES(RES.LAST).SDATA_TYPE = SDATA_TYPE_NUMB) then
          RES(RES.LAST).NVALUE := NODE_NVAL_GET(XROOT => XRQ_ARGUMENT, SPATH => SRQ_TAG_VALUE);
        /* Дата */
        when (RES(RES.LAST).SDATA_TYPE = SDATA_TYPE_DATE) then
          RES(RES.LAST).DVALUE := NODE_DVAL_GET(XROOT => XRQ_ARGUMENT, SPATH => SRQ_TAG_VALUE);
        /* Текст */
        when (RES(RES.LAST).SDATA_TYPE = SDATA_TYPE_CLOB) then
          RES(RES.LAST).CVALUE := NODE_CVAL_GET(XROOT => XRQ_ARGUMENT, SPATH => SRQ_TAG_VALUE);
        /* Неподдерживаемый тип данных */
        else
          P_EXCEPTION(0,
                      'Указанный для аргумента "%s" тип данных "%s" не поддерживается (%s).',
                      RES(RES.LAST).SNAME,
                      RES(RES.LAST).SDATA_TYPE,
                      SRQ_ARGUMENTS_PATH || '/' || SRQ_TAG_SDATA_TYPE);
      end case;
    end loop;
    /* Проверка обязательности */
    if ((RES.COUNT = 0) and (NREQUIRED = 1)) then
      P_EXCEPTION(0, 'Не указаны аргументы (' || SRQ_ARGUMENTS_PATH || ').');
    end if;
    /* Возвращаем результат */
    return RES;
  end RQ_PAYLOAD_ARGUMENTS_GET;

  /* Исполнение хранимой процедуры */
  procedure EXEC_STORED
  (
    XRQ_ROOT                in PKG_XPATH.TNODE,         -- Корневой элемент тела документа запроса
    COUT                    out clob                    -- Ответ на запрос
  )
  is
    SRQ_STORED              PKG_STD.TSTRING;            -- Наименование исполняемого хранимого объекта из запроса
    SRQ_RESP_ARG            PKG_STD.TSTRING;            -- Наименование выходного аргумента хранимого объекта из запроса для формирования тела ответа
    RQ_ARGUMENTS            TARGUMENTS;                 -- Коллекция аргументов хранимого объекта из запроса
    ARGS                    PKG_OBJECT_DESC.TARGUMENTS; -- Коллекция формальных параметров хранимого объекта
    RARG                    PKG_OBJECT_DESC.TARGUMENT;  -- Формальный параметр хранимого объекта
    ARGS_VALS               PKG_CONTPRMLOC.TCONTAINER;  -- Контейнер для фактических параметров хранимого объекта
    RARG_VAL                PKG_CONTAINER.TPARAM;       -- Фактический параметр хранимого объекта
    SARG_NAME               PKG_STD.TSTRING;            -- Наименование текущего обрабатываемого фактического параметра хранимого объекта
    XRESP                   integer;                    -- Документ для ответа
    XRESP_OUT_ARGUMENTS     PKG_XMAKE.TNODE;            -- Элемент для коллекции выходных параметров хранимого объекта
    RRESP_ARGUMENT_VALUE    PKG_XMAKE.TVALUE;           -- Значение выходного параметра хранимого объекта
    BRESP_ARG_FOUND         boolean := false;           -- Флаг присутствия в составе выходных аргументов аргумента с типом CLOB и именем, указанным в параметре запроса SRESP_ARG
  begin
    /* Создаём документ для ответа */
    XRESP := PKG_XMAKE.OPEN_CURSOR();
    /* Проверим хранимый объект в запросе */
    RQ_PAYLOAD_STORED_CHECK(XRQ_ROOT => XRQ_ROOT);
    /* Считываем наименование хранимого объекта из запроса */
    SRQ_STORED := RQ_PAYLOAD_STORED_GET(XRQ_ROOT => XRQ_ROOT, NREQUIRED => 1);
    /* Считываем наименование выходного аргумента хранимого объекта из запроса для формирования тела ответа */
    SRQ_RESP_ARG := NODE_SVAL_GET(XROOT           => XRQ_ROOT,
                                  SPATH           => RQ_PAYLOAD_ITEM_PATH_GET(SITEM_TAG => SRQ_TAG_SRESP_ARG),
                                  NREQUIRED       => 0,
                                  SMESSAGE_OBJECT => 'Наименование выходного аргумента для формирования тела ответа');
    /* Считаем список аргументов из запроса */
    RQ_ARGUMENTS := RQ_PAYLOAD_ARGUMENTS_GET(XRQ_ROOT => XRQ_ROOT);
    /* Считываем описание параметров хранимого объекта */
    ARGS := PKG_OBJECT_DESC.DESC_ARGUMENTS(SSTORED_NAME => SRQ_STORED, BRAISE_ERROR => true);
    /* Обходим входные параметры и формируем коллекцию значений */
    for I in 1 .. PKG_OBJECT_DESC.COUNT_ARGUMENTS(RARGUMENTS => ARGS)
    loop
      /* Считываем очередной параметр */
      RARG := PKG_OBJECT_DESC.FETCH_ARGUMENT(RARGUMENTS => ARGS, IINDEX => I);
      /* Если это входной параметр */
      if (RARG.IN_OUT in (PKG_STD.PARAM_TYPE_IN, PKG_STD.PARAM_TYPE_IN_OUT)) then
        /* Добавим его значение в коллекцию фактических параметров */
        case RARG.DATA_TYPE
          /* Строка */
          when PKG_STD.DATA_TYPE_STR then
            PKG_CONTPRMLOC.APPENDS(RCONTAINER => ARGS_VALS,
                                   SNAME      => RARG.ARGUMENT_NAME,
                                   SVALUE     => TARGUMENTS_SVAL_GET(ARGUMENTS => RQ_ARGUMENTS,
                                                                     SARGUMENT => RARG.ARGUMENT_NAME),
                                   NIN_OUT    => RARG.IN_OUT);
          /* Число */
          when PKG_STD.DATA_TYPE_NUM then
            PKG_CONTPRMLOC.APPENDN(RCONTAINER => ARGS_VALS,
                                   SNAME      => RARG.ARGUMENT_NAME,
                                   NVALUE     => TARGUMENTS_NVAL_GET(ARGUMENTS => RQ_ARGUMENTS,
                                                                     SARGUMENT => RARG.ARGUMENT_NAME),
                                   NIN_OUT    => RARG.IN_OUT);
          /* Дата */
          when PKG_STD.DATA_TYPE_DATE then
            PKG_CONTPRMLOC.APPENDD(RCONTAINER => ARGS_VALS,
                                   SNAME      => RARG.ARGUMENT_NAME,
                                   DVALUE     => TARGUMENTS_DVAL_GET(ARGUMENTS => RQ_ARGUMENTS,
                                                                     SARGUMENT => RARG.ARGUMENT_NAME),
                                   NIN_OUT    => RARG.IN_OUT);
          /* Текст */
          when PKG_STD.DATA_TYPE_CLOB then
            PKG_CONTPRMLOC.APPENDLC(RCONTAINER => ARGS_VALS,
                                    SNAME      => RARG.ARGUMENT_NAME,
                                    LCVALUE    => TARGUMENTS_CVAL_GET(ARGUMENTS => RQ_ARGUMENTS,
                                                                      SARGUMENT => RARG.ARGUMENT_NAME),
                                    NIN_OUT    => RARG.IN_OUT);
          /* Неизвестный тип данных */
          else
            P_EXCEPTION(0,
                        'Тип данных (%s) входного параметра "%s" не поддерживается.',
                        RARG.DB_DATA_TYPE,
                        RARG.ARGUMENT_NAME);
        end case;
      end if;
    end loop;
    /* Исполняем процедуру */
    PKG_SQL_CALL.EXECUTE_STORED(SSTORED_NAME => SRQ_STORED, RPARAM_CONTAINER => ARGS_VALS);
    /* Обходим выходные параметры и собираем их в ответ */
    SARG_NAME := PKG_CONTPRMLOC.FIRST_(RCONTAINER => ARGS_VALS);
    while (SARG_NAME is not null)
    loop
      /* Считываем значение параметра */
      RARG_VAL := PKG_CONTPRMLOC.GET(RCONTAINER => ARGS_VALS, SNAME => SARG_NAME);
      /* Считываем описание параметра */
      RARG := PKG_OBJECT_DESC.FETCH_ARGUMENT(RARGUMENTS => ARGS, SARGUMENT_NAME => SARG_NAME);
      /* Если это выходной параметр */
      if (RARG.IN_OUT in (PKG_STD.PARAM_TYPE_IN_OUT, PKG_STD.PARAM_TYPE_OUT)) then
        /* Сформируем для него значение в зависимости от его типа */
        case RARG.DATA_TYPE
          /* Строка */
          when PKG_STD.DATA_TYPE_STR then
            RRESP_ARGUMENT_VALUE := PKG_XMAKE.VALUE(ICURSOR => XRESP,
                                                    SVALUE  => PKG_CONTPRMLOC.GETS(RCONTAINER => ARGS_VALS,
                                                                                   SNAME      => RARG_VAL.NAME));
          /* Число */
          when PKG_STD.DATA_TYPE_NUM then
            RRESP_ARGUMENT_VALUE := PKG_XMAKE.VALUE(ICURSOR => XRESP,
                                                    NVALUE  => PKG_CONTPRMLOC.GETN(RCONTAINER => ARGS_VALS,
                                                                                   SNAME      => RARG_VAL.NAME));
          /* Дата */
          when PKG_STD.DATA_TYPE_DATE then
            RRESP_ARGUMENT_VALUE := PKG_XMAKE.VALUE(ICURSOR => XRESP,
                                                    DVALUE  => PKG_CONTPRMLOC.GETD(RCONTAINER => ARGS_VALS,
                                                                                   SNAME      => RARG_VAL.NAME));
          /* Текст */
          when PKG_STD.DATA_TYPE_CLOB then
            RRESP_ARGUMENT_VALUE := PKG_XMAKE.VALUE(ICURSOR => XRESP,
                                                    LCVALUE => PKG_CONTPRMLOC.GETLC(RCONTAINER => ARGS_VALS,
                                                                                    SNAME      => RARG_VAL.NAME));
            if ((SRQ_RESP_ARG is not null) and (RARG_VAL.NAME = SRQ_RESP_ARG)) then
              COUT            := COALESCE(PKG_CONTPRMLOC.GETLC(RCONTAINER => ARGS_VALS, SNAME => RARG_VAL.NAME), '<XDATA/>');
              BRESP_ARG_FOUND := true;
              exit;
            end if;
          /* Неизвестный тип данных */
          else
            P_EXCEPTION(0,
                        'Тип данных (%s) выходного параметра "%s" не поддерживается.',
                        RARG.DB_DATA_TYPE,
                        RARG.ARGUMENT_NAME);
        end case;
        /* Добавим ветку выходного параметра в выходную коллекцию */
        XRESP_OUT_ARGUMENTS := PKG_XMAKE.CONCAT(ICURSOR => XRESP,
                                                RNODE00 => XRESP_OUT_ARGUMENTS,
                                                RNODE01 => PKG_XMAKE.ELEMENT(ICURSOR => XRESP,
                                                                             SNAME   => SRESP_TAG_XOUT_ARGUMENTS,
                                                                             RNODE00 => PKG_XMAKE.ELEMENT(ICURSOR  => XRESP,
                                                                                                          SNAME    => SRESP_TAG_SNAME,
                                                                                                          RVALUE00 => PKG_XMAKE.VALUE(ICURSOR => XRESP,
                                                                                                                                      SVALUE  => RARG_VAL.NAME)),
                                                                             RNODE01 => PKG_XMAKE.ELEMENT(ICURSOR  => XRESP,
                                                                                                          SNAME    => SRESP_TAG_VALUE,
                                                                                                          RVALUE00 => RRESP_ARGUMENT_VALUE),
                                                                             RNODE02 => PKG_XMAKE.ELEMENT(ICURSOR  => XRESP,
                                                                                                          SNAME    => SRESP_TAG_SDATA_TYPE,
                                                                                                          RVALUE00 => PKG_XMAKE.VALUE(ICURSOR => XRESP,
                                                                                                                                      SVALUE  => STD_DATA_TYPE_TO_STR(NSTD_DATA_TYPE => RARG.DATA_TYPE)))));
      end if;
      /* Считываем наименование следующего параметра */
      SARG_NAME := PKG_CONTPRMLOC.NEXT_(RCONTAINER => ARGS_VALS, SNAME => SARG_NAME);
    end loop;
    /* Проверим, что был найден опциональный аргумент для формирования полного ответа */
    if ((SRQ_RESP_ARG is not null) and (not BRESP_ARG_FOUND)) then
      P_EXCEPTION(0,
                  'В составе выходных параметров "%s" отсуствует аргумент "%s" типа "CLOB".',
                  SRQ_STORED,
                  SRQ_RESP_ARG);
    end if;
    /* Собираем ответ (только если не формировали полный ответ через аргумент для формирования полного ответа) */
    if (not BRESP_ARG_FOUND) then
      COUT := PKG_XMAKE.SERIALIZE_TO_CLOB(ICURSOR => XRESP,
                                          ITYPE   => PKG_XMAKE.CONTENT_,
                                          RNODE   => PKG_XMAKE.ELEMENT(ICURSOR => XRESP,
                                                                       SNAME   => SRESP_TAG_XPAYLOAD,
                                                                       RNODE00 => XRESP_OUT_ARGUMENTS));
    end if;
    /* Очистим контейнер параметров */
    PKG_CONTPRMLOC.PURGE(RCONTAINER => ARGS_VALS);
    /* Освобождаем документ результата */
    PKG_XMAKE.CLOSE_CURSOR(ICURSOR => XRESP);
  exception
    when others then
      /* Закроем курсор и вернем ошибку */
      PKG_XMAKE.CLOSE_CURSOR(ICURSOR => XRESP);
      /* Покажем ошибку */
      PKG_STATE.DIAGNOSTICS_STACKED();
      P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
  end EXEC_STORED;
  
  /* Базовое исполнение действий */
  procedure PROCESS
  (
    CIN                     in clob,         -- Входные параметры
    COUT                    out clob         -- Результат
  )
  is
    XRQ_ROOT                PKG_XPATH.TNODE; -- Корневой элемент тела документа запроса
    SRQ_ACTION              PKG_STD.TSTRING; -- Код действия из запроса
  begin
    /* Разбираем запрос */
    XRQ_ROOT := RQ_ROOT_GET(CRQ => CIN);
    /* Считываем код действия из запроса */
    SRQ_ACTION := RQ_ACTION_GET(XRQ_ROOT => XRQ_ROOT, NREQUIRED => 1);
    /* Вызываем обработчик в зависимости от кода действия */
    case SRQ_ACTION
    /* Исполнение хранимой процедуры */
      when SRQ_ACTION_EXEC_STORED then
        EXEC_STORED(XRQ_ROOT => XRQ_ROOT, COUT => COUT);
        /* Неизвестное действие */
      else
        P_EXCEPTION(0, 'Действие "%s" не поддерживается.', SRQ_ACTION);
    end case;
  end PROCESS;
  
  /* Базовое добавление в буфер отмеченных записей для панели */
  procedure SELECTLIST_INSERT
  (
    NIDENT                  in number,           -- Идентификатор процесса
    SAUTHID                 in varchar2,         -- Пользователь
    SSESSION_ID             in varchar2,         -- Идентификатор сеанса
    SCONNECT_EXT            in varchar2,         -- Внешний идентификатор сеанса
    NCOMPANY                in number := null,   -- Организация
    NDOCUMENT               in number,           -- Документ
    SUNITCODE               in varchar2 := null, -- Код раздела документа
    SACTIONCODE             in varchar2 := null, -- Код действия документа
    NCRN                    in number := null,   -- Каталог документа
    NDOCUMENT1              in number := null,   -- Документ 1
    SUNITCODE1              in varchar2 := null, -- Код раздела документа 1
    SACTIONCODE1            in varchar2 := null, -- Код действия документа 1  
    NRN                     out number           -- Рег. номер добавленной записи
  )
  is    
  begin
    /* Формируем рег. номер */
    NRN := GEN_ID();
    /* Добвляем запись */
    insert into P8PNL_SELECTLIST
      (RN,
       IDENT,
       authid,
       SESSION_ID,
       CONNECT_EXT,
       COMPANY,
       DOCUMENT,
       UNITCODE,
       ACTIONCODE,
       CRN,
       DOCUMENT1,
       UNITCODE1,
       ACTIONCODE1)
    values
      (NRN,
       NIDENT,
       SAUTHID,
       SSESSION_ID,
       SCONNECT_EXT,
       NCOMPANY,
       NDOCUMENT,
       SUNITCODE,
       SACTIONCODE,
       NCRN,
       NDOCUMENT1,
       SUNITCODE1,
       SACTIONCODE1);
  end SELECTLIST_INSERT;
  
  /* Удаление записи буфера отмеченных записей для панели */
  procedure SELECTLIST_DELETE
  (
    NRN                     in number   -- Рег. номер удаляемой записи
  )
  is
  begin
    delete from P8PNL_SELECTLIST T where T.RN = NRN;
  end SELECTLIST_DELETE;
  
  /* Базовая инициализация буфера отмеченных записей для панели */
  procedure SELECTLIST_INIT
  (
    NIDENT                  in number     -- Идентификатор буфера отмеченных записей  
  )
  is
    NRN                     PKG_STD.TREF; -- Рег. номер добавленной записи буфера панелей
  begin
    /* Очищаем буфер панелей */
    SELECTLIST_CLEAR(NIDENT => NIDENT);
    /* Обходим системный буфер отмеченных записей */
    for C in (select T.* from SELECTLIST T where T.IDENT = NIDENT)
    loop
      /* Копируем запись */
      SELECTLIST_INSERT(NIDENT       => C.IDENT,
                        SAUTHID      => C.AUTHID,
                        SSESSION_ID  => C.SESSION_ID,
                        SCONNECT_EXT => C.CONNECT_EXT,
                        NCOMPANY     => C.COMPANY,
                        NDOCUMENT    => C.DOCUMENT,
                        SUNITCODE    => C.UNITCODE,
                        SACTIONCODE  => C.ACTIONCODE,
                        NCRN         => C.CRN,
                        NDOCUMENT1   => C.DOCUMENT1,
                        SUNITCODE1   => C.UNITCODE1,
                        SACTIONCODE1 => C.ACTIONCODE1,
                        NRN          => NRN);
    end loop;
  end SELECTLIST_INIT;
  
  /* Базовая очистка буфера отмеченных записей для панели */
  procedure SELECTLIST_CLEAR
  (
    NIDENT                  in number   -- Идентификатор буфера отмеченных записей
  )
  is
  begin
    /* Обходим буфер панелей */
    for C in (select T.RN from P8PNL_SELECTLIST T where T.IDENT = NIDENT)
    loop
      SELECTLIST_DELETE(NRN => C.RN);
    end loop;
  end SELECTLIST_CLEAR;

end PKG_P8PANELS_BASE;
/
