create or replace package PKG_P8PANELS_SAMPLES as

  /* Получение списка контрагентов */
  procedure AGNLIST_GET
  (
    COUT                    out clob    -- Список контрагентов
  );

  /* Добавление контрагента */
  procedure AGNLIST_INSERT
  (
    SAGNABBR                in varchar2, -- Мнемокод
    SAGNNAME                in varchar2, -- Наименование
    NRN                     out number   -- Рег. номер добавленного
  );

  /* Удаление контрагента */
  procedure AGNLIST_DELETE
  (
    NRN                     in number   -- Рег. номер удаляемого
  );
  
  /* Таблица данных */
  procedure DATA_GRID
  (
    NPAGE_NUMBER            in number, -- Номер страницы (игнорируется при NPAGE_SIZE=0)
    NPAGE_SIZE              in number, -- Количество записей на странице (0 - все)
    CFILTERS                in clob,   -- Фильтры
    CORDERS                 in clob,   -- Сортировки
    NINCLUDE_DEF            in number, -- Признак включения описания колонок таблицы в ответ
    COUT                    out clob   -- Сериализованная таблица данных
  );

  /* График */
  procedure CHART
  (
    COUT                    out clob    -- Сериализованный график
  );
  
  /* Инициализация буфера данных для диаграмма Ганта */
  procedure GANTT_INIT
  (
    NIDENT                  in out number -- Идентификатор буфера сформированных данных (null - сгенерировать новый, !null - удалить старые данные и пересоздать с указанным идентификатором)
  );
  
  /* Сбор данных для отображения диаграммы Ганта */
  procedure GANTT
  (
    NIDENT                  in number,  -- Идентификатор процесса
    COUT                    out clob    -- Сериализованные данные для диаграммы Ганта
  );
  
  /* Исправление задачи в диаграмме Ганта */
  procedure GANTT_MODIFY
  (
    NIDENT                  in number,  -- Идентификатор буфера
    NRN                     in number,  -- Рег. номер записи
    DDATE_FROM              in date,    -- Дата начала задачи
    DDATE_TO                in date     -- Дата окончания задачи 
  );
  
  /* Инициализация буфера данных для диаграммы Ганта */
  procedure CYCLOGRAM_INIT
  (
    NIDENT                  in out number -- Идентификатор буфера сформированных данных (null - сгенерировать новый, !null - удалить старые данные и пересоздать с указанным идентификатором)
  );
  
  /* Сбор данных для отображения циклограммы */
  procedure CYCLOGRAM
  (
    NIDENT                  in number,  -- Идентификатор процесса
    COUT                    out clob    -- Сериализованные данные для циклограммы
  );
  
  /* Изменение задачи циклограммы */
  procedure CYCLOGRAM_TASK_MODIFY
  (
    NIDENT                  in number,   -- Идентификатор буфера
    NRN                     in number,   -- Рег. номер записи
    SDATE_FROM              in varchar2, -- Дата начала (в строковом представлении)
    SDATE_TO                in varchar2  -- Дата окончания (в строковом представлении)
  );
  
end PKG_P8PANELS_SAMPLES;
/
create or replace package body PKG_P8PANELS_SAMPLES as

  /* Константы - циклограмма */
  NCG_MULTIPLIER            constant PKG_STD.TNUMBER := 5; -- Множитель для ширины отображения

  /* Получение списка контрагентов */
  procedure AGNLIST_GET
  (
    COUT                    out clob      -- Список контрагентов
  )
  is
    NVERSION                PKG_STD.TREF; -- Рег. номер версии словаря контрагентов
  begin
    NVERSION := GET_SESSION_VERSION(SUNITCODE => 'AGNLIST');
    PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
    PKG_XFAST.DOWN_NODE(SNAME => 'DATA');
    for C in (select TMP.*
                from (select D.*,
                             ROWNUM NROWNUM
                        from (select T.RN      NRN,
                                     T.AGNABBR SAGNABBR,
                                     T.AGNNAME SAGNNAME
                                from AGNLIST T
                               where T.VERSION = NVERSION
                                 and exists (select /*+ INDEX(UP I_USERPRIV_CATALOG_ROLEID) */
                                       null
                                        from USERPRIV UP
                                       where UP.CATALOG = T.CRN
                                         and UP.ROLEID in (select /*+ INDEX(UR I_USERROLES_AUTHID_FK) */
                                                            UR.ROLEID
                                                             from USERROLES UR
                                                            where UR.AUTHID = UTILIZER)
                                      union all
                                      select /*+ INDEX(UP I_USERPRIV_CATALOG_AUTHID) */
                                       null
                                        from USERPRIV UP
                                       where UP.CATALOG = T.CRN
                                         and UP.AUTHID = UTILIZER)
                               order by T.RN desc) D) TMP
               where TMP.NROWNUM <= 10)
    loop
      PKG_XFAST.DOWN_NODE(SNAME => 'AGENTS');
      PKG_XFAST.ATTR(SNAME => 'NRN', NVALUE => C.NRN);
      PKG_XFAST.ATTR(SNAME => 'SAGNABBR', SVALUE => C.SAGNABBR);
      PKG_XFAST.ATTR(SNAME => 'SAGNNAME', SVALUE => C.SAGNNAME);
      PKG_XFAST.UP();
    end loop;
    PKG_XFAST.UP();
    COUT := PKG_XFAST.SERIALIZE_TO_CLOB();
    PKG_XFAST.EPILOGUE();
  end AGNLIST_GET;

  /* Добавление контрагента */
  procedure AGNLIST_INSERT
  (
    SAGNABBR                in varchar2,                           -- Мнемокод
    SAGNNAME                in varchar2,                           -- Наименование
    NRN                     out number                             -- Рег. номер добавленного
  )
  is
    NCOMPANY                PKG_STD.TREF := GET_SESSION_COMPANY(); -- Текущая организация
    NCRN                    PKG_STD.TREF;                          -- Каталог размещения контрагента
  begin
    if (SAGNABBR is null) then
      P_EXCEPTION(0, 'Не указан мнемокод.');
    end if;
    if (SAGNABBR is null) then
      P_EXCEPTION(0, 'Не указано наименование.');
    end if;
    FIND_ROOT_CATALOG(NCOMPANY => NCOMPANY, SCODE => 'AGNLIST', NCRN => NCRN);
    P_AGNLIST_INSERT(NCOMPANY          => NCOMPANY,
                     CRN               => NCRN,
                     AGNABBR           => SAGNABBR,
                     AGNTYPE           => 0,
                     AGNNAME           => SAGNNAME,
                     AGNIDNUMB         => null,
                     ECONCODE          => null,
                     ORGCODE           => null,
                     AGNFAMILYNAME     => null,
                     AGNFIRSTNAME      => null,
                     AGNLASTNAME       => null,
                     AGNFAMILYNAME_TO  => null,
                     AGNFIRSTNAME_TO   => null,
                     AGNLASTNAME_TO    => null,
                     AGNFAMILYNAME_FR  => null,
                     AGNFIRSTNAME_FR   => null,
                     AGNLASTNAME_FR    => null,
                     AGNFAMILYNAME_AC  => null,
                     AGNFIRSTNAME_AC   => null,
                     AGNLASTNAME_AC    => null,
                     AGNFAMILYNAME_ABL => null,
                     AGNFIRSTNAME_ABL  => null,
                     AGNLASTNAME_ABL   => null,
                     EMP               => 0,
                     EMPPOST           => null,
                     EMPPOST_FROM      => null,
                     EMPPOST_TO        => null,
                     EMPPOST_AC        => null,
                     EMPPOST_ABL       => null,
                     AGNBURN           => null,
                     PHONE             => null,
                     PHONE2            => null,
                     FAX               => null,
                     TELEX             => null,
                     MAIL              => null,
                     IMAGE             => null,
                     DDISCDATE         => null,
                     AGN_COMMENT       => null,
                     NSEX              => 0,
                     SPENSION_NBR      => null,
                     SMEDPOLICY_SER    => null,
                     SMEDPOLICY_NUMB   => null,
                     SPROPFORM         => null,
                     SREASON_CODE      => null,
                     NRESIDENT_SIGN    => 0,
                     STAXPSTATUS       => null,
                     SOGRN             => null,
                     SPRFMLSTS         => null,
                     SPRNATION         => null,
                     SCITIZENSHIP      => null,
                     ADDR_BURN         => null,
                     SPRMLREL          => null,
                     SOKATO            => null,
                     SPFR_NAME         => null,
                     DPFR_FILL_DATE    => null,
                     DPFR_REG_DATE     => null,
                     SPFR_REG_NUMB     => null,
                     SSFR_REG_NUMB     => null,
                     SFULLNAME         => null,
                     SOKFS             => null,
                     SOKOPF            => null,
                     STFOMS            => null,
                     SFSS_REG_NUMB     => null,
                     SFSS_SUBCODE      => null,
                     NCOEFFIC          => 0,
                     DAGNDEATH         => null,
                     NOLD_RN           => null,
                     SOKTMO            => null,
                     SINN_CITIZENSHIP  => null,
                     DTAX_REG_DATE     => null,
                     SORIGINAL_NAME    => null,
                     NIND_BUSINESSMAN  => 0,
                     SFNS_CODE         => null,
                     SCTZNSHP_TYPE     => null,
                     NCONTACT_METHOD   => null,
                     SMF_ID            => null,
                     SOKOGU            => null,
                     NJURPERS_SUBDIV   => 0,
                     NRN               => NRN);
  end AGNLIST_INSERT;

  /* Удаление контрагента */
  procedure AGNLIST_DELETE
  (
    NRN                     in number   -- Рег. номер удаляемого
  )
  is
  begin
    P_AGNLIST_DELETE(NCOMPANY => GET_SESSION_COMPANY(), RN => NRN);
  end AGNLIST_DELETE;
  
  /* Таблица данных */
  procedure DATA_GRID
  (
    NPAGE_NUMBER            in number,                             -- Номер страницы (игнорируется при NPAGE_SIZE=0)
    NPAGE_SIZE              in number,                             -- Количество записей на странице (0 - все)
    CFILTERS                in clob,                               -- Фильтры
    CORDERS                 in clob,                               -- Сортировки
    NINCLUDE_DEF            in number,                             -- Признак включения описания колонок таблицы в ответ
    COUT                    out clob                               -- Сериализованная таблица данных
  )
  is
    NCOMPANY                PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
    NIDENT                  PKG_STD.TREF := GEN_IDENT();           -- Идентификатор отбора
    RF                      PKG_P8PANELS_VISUAL.TDG_FILTERS;       -- Фильтры
    RO                      PKG_P8PANELS_VISUAL.TDG_ORDERS;        -- Сортировки
    RDG                     PKG_P8PANELS_VISUAL.TDG;               -- Описание таблицы
    RAGN_TYPES              PKG_P8PANELS_VISUAL.TDG_COL_VALS;      -- Предопределенные значения "Типа контрагентов"
    RDG_ROW                 PKG_P8PANELS_VISUAL.TDG_ROW;           -- Строка таблицы
    NROW_FROM               PKG_STD.TREF;                          -- Номер строки с
    NROW_TO                 PKG_STD.TREF;                          -- Номер строки по
    CSQL                    clob;                                  -- Буфер для запроса
    ICURSOR                 integer;                               -- Курсор для исполнения запроса
    SGROUP                  PKG_STD.TSTRING;                       -- Буфер для группы
    SAGNINFO                PKG_STD.TSTRING;                       -- Буфер для "Сведений"
    SAGNNAME                PKG_STD.TSTRING;                       -- Буфер для "Наименования"
    NAGNTYPE                PKG_STD.TREF;                          -- Буфер для "Типа"
  begin
    /* Читаем фильтры */
    RF := PKG_P8PANELS_VISUAL.TDG_FILTERS_FROM_XML(CFILTERS => CFILTERS);
    /* Читем сортировки */
    RO := PKG_P8PANELS_VISUAL.TDG_ORDERS_FROM_XML(CORDERS => CORDERS);
    /* Преобразуем номер и размер страницы в номер строк с и по */
    PKG_P8PANELS_VISUAL.UTL_ROWS_LIMITS_CALC(NPAGE_NUMBER => NPAGE_NUMBER,
                                             NPAGE_SIZE   => NPAGE_SIZE,
                                             NROW_FROM    => NROW_FROM,
                                             NROW_TO      => NROW_TO);
    /* Инициализируем таблицу данных */
    RDG := PKG_P8PANELS_VISUAL.TDG_MAKE(BFIXED_HEADER => true, NFIXED_COLUMNS => 2);
    /* Описываем колонки таблицы данных */
    PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
                                        SNAME      => 'SAGNABBR',
                                        SCAPTION   => 'Мнемокод',
                                        SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
                                        SCOND_FROM => 'AgentAbbr',
                                        BVISIBLE   => true,
                                        BORDER     => true,
                                        BFILTER    => true,
                                        NWIDTH     => 150);
    PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID  => RDG,
                                        SNAME       => 'SAGNINFO',
                                        SCAPTION    => 'Сведения',
                                        SDATA_TYPE  => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
                                        BVISIBLE    => true,
                                        BORDER      => false,
                                        BFILTER     => false,
                                        BEXPANDABLE => true,
                                        NWIDTH      => 300);
    PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
                                        SNAME      => 'SAGNNAME',
                                        SCAPTION   => 'Наименование',
                                        SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
                                        SCOND_FROM => 'AgentName',
                                        BVISIBLE   => true,
                                        BORDER     => true,
                                        BFILTER    => true,
                                        SPARENT    => 'SAGNINFO',
                                        NWIDTH     => 200);
    PKG_P8PANELS_VISUAL.TDG_COL_VALS_ADD(RCOL_VALS => RAGN_TYPES, NVALUE => 0);
    PKG_P8PANELS_VISUAL.TDG_COL_VALS_ADD(RCOL_VALS => RAGN_TYPES, NVALUE => 1);
    PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
                                        SNAME      => 'NAGNTYPE',
                                        SCAPTION   => 'Тип',
                                        SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
                                        SCOND_FROM => 'AgentType',
                                        BVISIBLE   => true,
                                        BORDER     => true,
                                        BFILTER    => true,
                                        SPARENT    => 'SAGNINFO',
                                        NWIDTH     => 100,
                                        RCOL_VALS  => RAGN_TYPES,
                                        SHINT      => 'В Системе бывают контрагенты двух типов:<br>' ||
                                                      '<b style="color:blue">Юридическое лицо</b> - организация, которая имеет в собственности, хозяйственном ведении ' ||
                                                      'или оперативном управлении обособленное имущество, отвечает по своим обязательствам этим имуществом, может от своего ' ||
                                                      'имени приобретать и осуществлять имущественные и личные неимущественные права, отвечать по своим обязанностям.<br>' ||
                                                      '<b style="color:green">Физическое лицо</b> - субъект правовых отношений, представляющий собой одного человека.');
    PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
                                        SNAME      => 'SFULLNAME',
                                        SCAPTION   => 'Полное наименование',
                                        SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
    PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
                                        SNAME      => 'SAGNIDNUMB',
                                        SCAPTION   => 'ИНН',
                                        SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
    /* Обходим данные */
    begin
      /* Добавляем подсказку совместимости */
      CSQL := PKG_SQL_BUILD.COMPATIBLE(SSQL => CSQL);
      /* Формируем запрос */
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => 'select *');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '  from (select D.*,');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => PKG_SQL_BUILD.SQLROWNUM() || ' NROW');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '          from (select AG.AGNABBR SAGNABBR,');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                       AG.AGNNAME SAGNNAME,');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                       AG.AGNTYPE NAGNTYPE,');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                       AG.FULLNAME SFULLNAME,');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                       AG.AGNIDNUMB SAGNIDNUMB');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                  from AGNLIST AG');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                where exists (select ' || PKG_SQL_BUILD.SET_HINT(SHINT => 'INDEX(UP I_USERPRIV_CATALOG_ROLEID)') || ' null');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                                from USERPRIV UP');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                               where UP."CATALOG" = AG.CRN');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                                 and UP.ROLEID in (select ' || PKG_SQL_BUILD.SET_HINT(SHINT => 'INDEX(UR I_USERROLES_AUTHID_FK)') || ' UR.ROLEID');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                                                     from USERROLES UR where UR.AUTHID = UTILIZER())');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                                                    union all');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                                                   select ' || PKG_SQL_BUILD.SET_HINT(SHINT => 'INDEX(UP I_USERPRIV_CATALOG_AUTHID)') || ' null');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                                                     from USERPRIV UP where UP."CATALOG" = AG.CRN and UP.AUTHID = UTILIZER())');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => '                  and AG.RN in (select ID from COND_BROKER_IDSMART where IDENT = :NIDENT) %ORDER_BY%) D) F');
      PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where F.NROW between :NROW_FROM and :NROW_TO');
      /* Учтём сортировки */
      PKG_P8PANELS_VISUAL.TDG_ORDERS_SET_QUERY(RDATA_GRID => RDG,
                                               RORDERS    => RO,
                                               SPATTERN   => '%ORDER_BY%',
                                               CSQL       => CSQL);
      /* Учтём фильтры */
      PKG_P8PANELS_VISUAL.TDG_FILTERS_SET_QUERY(NIDENT     => NIDENT,
                                                NCOMPANY   => NCOMPANY,
                                                SUNIT      => 'AGNLIST',
                                                SPROCEDURE => 'P_AGNLIST_BASE_COND',
                                                RDATA_GRID => RDG,
                                                RFILTERS   => RF);
      /* Разбираем его */
      ICURSOR := PKG_SQL_DML.OPEN_CURSOR(SWHAT => 'SELECT');
      PKG_SQL_DML.PARSE(ICURSOR => ICURSOR, SQUERY => CSQL);
      /* Делаем подстановку параметров */
      PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NIDENT', NVALUE => NIDENT);
      PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NROW_FROM', NVALUE => NROW_FROM);
      PKG_SQL_DML.BIND_VARIABLE_NUM(ICURSOR => ICURSOR, SNAME => 'NROW_TO', NVALUE => NROW_TO);
      /* Описываем структуру записи курсора */
      PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 1);
      PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 2);
      PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 3);
      PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 4);
      PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 5);
      PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 6);
      /* Делаем выборку */
      if (PKG_SQL_DML.EXECUTE(ICURSOR => ICURSOR) = 0) then
        null;
      end if;
      /* Обходим выбранные записи */
      while (PKG_SQL_DML.FETCH_ROWS(ICURSOR => ICURSOR) > 0)
      loop
        /* Добавляем колонки с данными */
        PKG_SQL_DML.COLUMN_VALUE_STR(ICURSOR => ICURSOR, IPOSITION => 2, SVALUE => SAGNNAME);
        PKG_SQL_DML.COLUMN_VALUE_NUM(ICURSOR => ICURSOR, IPOSITION => 3, NVALUE => NAGNTYPE);
        if (NAGNTYPE = 0) then
          SGROUP   := 'JUR';
          SAGNINFO := SAGNNAME || ', ЮЛ';
          PKG_P8PANELS_VISUAL.TDG_ADD_GROUP(RDATA_GRID  => RDG,
                                            SNAME       => SGROUP,
                                            SCAPTION    => 'Юридические лица',
                                            BEXPANDABLE => true,
                                            BEXPANDED   => false);
        else
          SGROUP   := 'PERS';
          SAGNINFO := SAGNNAME || ', ФЛ';
          PKG_P8PANELS_VISUAL.TDG_ADD_GROUP(RDATA_GRID  => RDG,
                                            SNAME       => SGROUP,
                                            SCAPTION    => 'Физические лица',
                                            BEXPANDABLE => true,
                                            BEXPANDED   => false);
        end if;
        RDG_ROW := PKG_P8PANELS_VISUAL.TDG_ROW_MAKE(SGROUP => SGROUP);
        PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW      => RDG_ROW,
                                                 SNAME     => 'SAGNABBR',
                                                 ICURSOR   => ICURSOR,
                                                 NPOSITION => 1);
        PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SAGNINFO', SVALUE => SAGNINFO);
        PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SAGNNAME', SVALUE => SAGNNAME);
        PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NAGNTYPE', NVALUE => NAGNTYPE);
        PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW      => RDG_ROW,
                                                 SNAME     => 'SFULLNAME',
                                                 ICURSOR   => ICURSOR,
                                                 NPOSITION => 4);
        PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW      => RDG_ROW,
                                                 SNAME     => 'SAGNIDNUMB',
                                                 ICURSOR   => ICURSOR,
                                                 NPOSITION => 5);
        /* Добавляем строку в таблицу */
        PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW);
      end loop;
      /* Освобождаем курсор */
      PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR);
    exception
      when others then
        PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR);
        raise;
    end;
    /* Сериализуем описание */
    COUT := PKG_P8PANELS_VISUAL.TDG_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => NINCLUDE_DEF);
  end DATA_GRID;
  
  /* График */
  procedure CHART
  (
    COUT                    out clob                                           -- Сериализованный график
  )
  is
    NCOMPANY                PKG_STD.TREF := GET_SESSION_COMPANY();             -- Организация сеанса
    RCH                     PKG_P8PANELS_VISUAL.TCHART;                        -- График
    RCH_DS                  PKG_P8PANELS_VISUAL.TCHART_DATASET;                -- Набор данных
    RATTR_VALS              PKG_P8PANELS_VISUAL.TCHART_DATASET_ITEM_ATTR_VALS; -- Атрибуты элемента набора данных
  begin
    /* Сформируем заголовок графика */
    RCH := PKG_P8PANELS_VISUAL.TCHART_MAKE(STYPE     => PKG_P8PANELS_VISUAL.SCHART_TYPE_BAR,
                                           STITLE    => 'Топ 5 контрагентов по сумме договоров',
                                           SLGND_POS => PKG_P8PANELS_VISUAL.SCHART_LGND_POS_RIGHT);
    /* Сформируем набор данных */
    RCH_DS := PKG_P8PANELS_VISUAL.TCHART_DATASET_MAKE(SCAPTION => 'Сумма договоров');
    /* Обходим договоры, сгруппированные по контрагентам */
    for C in (select D.SAGENT,
                     D.NSUM
                from (select AG.AGNABBR SAGENT,
                             sum(CN.DOC_SUMTAX * (CN.CURBASE / CN.CURCOURS)) NSUM
                        from CONTRACTS CN,
                             AGNLIST   AG
                       where CN.COMPANY = NCOMPANY
                         and CN.AGENT = AG.RN
                       group by AG.AGNABBR
                       order by 2 desc) D
               where ROWNUM <= 5)
    loop
      /* Добавим метку для контрагента */
      PKG_P8PANELS_VISUAL.TCHART_ADD_LABEL(RCHART => RCH, SLABEL => C.SAGENT);
      /* Сформируем дополнительные атрибуты для клиентского приложения - будем использовать их при открытии раздела "Договоры" для отбора */
      PKG_P8PANELS_VISUAL.TCHART_DATASET_ITM_ATTR_VL_ADD(RATTR_VALS => RATTR_VALS,
                                                         SNAME      => 'SCOND',
                                                         SVALUE     => 'in_SAGENT',
                                                         BCLEAR     => true);
      PKG_P8PANELS_VISUAL.TCHART_DATASET_ITM_ATTR_VL_ADD(RATTR_VALS => RATTR_VALS,
                                                         SNAME      => 'SCOND_VALUE',
                                                         SVALUE     => C.SAGENT);
      /* Добавим контрагента в набор данных */
      PKG_P8PANELS_VISUAL.TCHART_DATASET_ADD_ITEM(RDATASET => RCH_DS, NVALUE => C.NSUM, RATTR_VALS => RATTR_VALS);
    end loop;
    /* Добавим набор данных в график */
    PKG_P8PANELS_VISUAL.TCHART_ADD_DATASET(RCHART => RCH, RDATASET => RCH_DS);
    /* Сериализуем описание */
    COUT := PKG_P8PANELS_VISUAL.TCHART_TO_XML(RCHART => RCH, NINCLUDE_DEF => 1);
  end CHART;
  
  /* Очистка буфера данных для диаграммы Ганта */
  procedure GANTT_BASE_CLEAN
  (
    NIDENT                  in number   -- Идентификатор буфера
  )
  is
  begin
    /* Удалим из буфера всё с указанным идентификатором */
    delete from P8PNL_SMPL_GANTT T where T.IDENT = NIDENT;
  end GANTT_BASE_CLEAN;

  /* Добавление данных в буфер диаграммы Ганта */
  procedure GANTT_BASE_INSERT
  (
    NIDENT                  in number,   -- Идентификатор буфера
    NTYPE                   in number,   -- Тип задачи (0 - этап/веха, 1 - работа)
    SNUMB                   in varchar2, -- Номер задачи
    SNAME                   in varchar2, -- Наименование задачи
    DDATE_FROM              in date,     -- Дата начала задачи
    DDATE_TO                in date,     -- Дата окончания задачи
    NSTATE                  in number    -- Состояние (0 - не выполняется, 1 - выполняется)
  )
  is
  begin
    /* Добавим запись */
    insert into P8PNL_SMPL_GANTT
      (RN, IDENT, type, NUMB, name, DATE_FROM, DATE_TO, STATE)
    values
      (GEN_ID(), NIDENT, NTYPE, SNUMB, SNAME, DDATE_FROM, DDATE_TO, NSTATE);
  end GANTT_BASE_INSERT;
  
  /* Исправление данных в буфере диаграммы Ганта */
  procedure GANTT_BASE_UPDATE
  (
    NIDENT                  in number,   -- Идентификатор буфера
    NRN                     in number,   -- Рег. номер записи
    NTYPE                   in number,   -- Тип задачи (0 - этап/веха, 1 - работа)
    SNUMB                   in varchar2, -- Номер задачи
    SNAME                   in varchar2, -- Наименование задачи
    DDATE_FROM              in date,     -- Дата начала задачи
    DDATE_TO                in date      -- Дата окончания задачи
  )
  is
  begin
    /* Изменим запись */
    update P8PNL_SMPL_GANTT T
       set T.TYPE      = NTYPE,
           T.NUMB      = SNUMB,
           T.NAME      = SNAME,
           T.DATE_FROM = DDATE_FROM,
           T.DATE_TO   = DDATE_TO
     where T.RN = NRN
       and T.IDENT = NIDENT;
  end GANTT_BASE_UPDATE;
 
  /* Инициализация буфера данных для диаграммы Ганта */
  procedure GANTT_INIT
  (
    NIDENT                  in out number                  -- Идентификатор буфера сформированных данных (null - сгенерировать новый, !null - удалить старые данные и пересоздать с указанным идентификатором)
  )
  is
    /* Константы */
    NSTAGE_LEN              constant PKG_STD.TNUMBER := 3; -- Длительность этапа (в месяцах)
    /* Переменные */
    NYEAR                   PKG_STD.TNUMBER;               -- Текущий год
    NMONTH                  PKG_STD.TNUMBER;               -- Месяц начала этапа
    DDATE_FROM              PKG_STD.TLDATE;                -- Дата начала этапа
    DDATE_TO                PKG_STD.TLDATE;                -- Дата окончания этапа
    DDATE_FROM_J            PKG_STD.TLDATE;                -- Дата начала работы
    DDATE_TO_J              PKG_STD.TLDATE;                -- Дата окончания работы
    NSTATE                  PKG_STD.TNUMBER;               -- Состояние задачи
  begin
    /* Удаляем старые данные из буфера */
    if (NIDENT is not null) then
      GANTT_BASE_CLEAN(NIDENT => NIDENT);
    else
      /* Илиформируем новый идентификатор, если не задан */
      NIDENT := GEN_IDENT();
    end if;
    /* Фиксируем текущий год */
    NYEAR := EXTRACT(year from sysdate);
    /* Этапы */
    for ST in 1 .. 12 / NSTAGE_LEN
    loop
      /* Сформируем период этапа */
      NMONTH     := ST * NSTAGE_LEN - NSTAGE_LEN + 1;
      DDATE_FROM := TO_DATE('01.' || NMONTH || '.' || TO_CHAR(NYEAR), 'dd.mm.yyyy');
      DDATE_TO   := LAST_DAY(ADD_MONTHS(DDATE_FROM, NSTAGE_LEN) - 1);
      /* Сформируем значение для атриуба "состояние" этапа */
      if (TRUNC(sysdate) between TRUNC(DDATE_FROM) and TRUNC(DDATE_TO)) then
        NSTATE := 1;
      else
        NSTATE := 0;
      end if;
      /* Добавим его в буфер */
      GANTT_BASE_INSERT(NIDENT     => NIDENT,
                        NTYPE      => 0,
                        SNUMB      => TO_CHAR(ST),
                        SNAME      => 'Этап ' || TO_CHAR(ST),
                        DDATE_FROM => DDATE_FROM,
                        DDATE_TO   => DDATE_TO,
                        NSTATE     => NSTATE);
      /* Работы */
      for J in 0 .. NSTAGE_LEN - 1
      loop
        /* Сформируем период работы */
        DDATE_FROM_J := ADD_MONTHS(DDATE_FROM, J);
        DDATE_TO_J   := LAST_DAY(ADD_MONTHS(DDATE_FROM, J + 1) - 1);
        /* Сформируем значение для атриуба "состояние" работы */
        if (TRUNC(sysdate) between TRUNC(DDATE_FROM_J) and TRUNC(DDATE_TO_J)) then
          NSTATE := 1;
        else
          NSTATE := 0;
        end if;
        /* Добавим в буфер */
        GANTT_BASE_INSERT(NIDENT     => NIDENT,
                          NTYPE      => 1,
                          SNUMB      => TO_CHAR(ST) || '.' || TO_CHAR(J + 1),
                          SNAME      => 'Работа ' || TO_CHAR(J + 1) || ' этапа ' || TO_CHAR(ST),
                          DDATE_FROM => DDATE_FROM_J,
                          DDATE_TO   => DDATE_TO_J,
                          NSTATE     => NSTATE);
      end loop;
    end loop;
  end GANTT_INIT;
  
  /* Сбор данных для отображения диаграммы Ганта */
  procedure GANTT
  (
    NIDENT                  in number,                                -- Идентификатор процесса
    COUT                    out clob                                  -- Сериализованные данные для диаграммы Ганта
  )
  is
    /* Константы */
    SBG_COLOR_STAGE         constant PKG_STD.TSTRING := 'cadetblue';  -- Цвет заливки этапов
    SBG_COLOR_JOB           constant PKG_STD.TSTRING := 'lightgreen'; -- Цвет заливки работ
    /* Переменные */
    RG                      PKG_P8PANELS_VISUAL.TGANTT;               -- Описание диаграммы Ганта
    RGT                     PKG_P8PANELS_VISUAL.TGANTT_TASK;          -- Описание задачи для диаграммы    
    STASK_BG_COLOR          PKG_STD.TSTRING;                          -- Цвет фона задачи
  begin
    /* Инициализируем диаграмму Ганта */
    RG := PKG_P8PANELS_VISUAL.TGANTT_MAKE(STITLE => 'Задачи на ' || TO_CHAR(EXTRACT(year from sysdate)) || ' год',
                                          NZOOM  => PKG_P8PANELS_VISUAL.NGANTT_ZOOM_MONTH);
    /* Добавим динамические атрибуты к задачам */
    PKG_P8PANELS_VISUAL.TGANTT_ADD_TASK_ATTR(RGANTT => RG, SNAME => 'type', SCAPTION => 'Тип', BVISIBLE => true);
    PKG_P8PANELS_VISUAL.TGANTT_ADD_TASK_ATTR(RGANTT   => RG,
                                             SNAME    => 'state',
                                             SCAPTION => 'Состояние',
                                             BVISIBLE => false);
    /* Добавим описание цветов задач */
    PKG_P8PANELS_VISUAL.TGANTT_ADD_TASK_COLOR(RGANTT    => RG,
                                              SBG_COLOR => SBG_COLOR_JOB,
                                              SDESC     => 'Этот цвет для задач.');
    PKG_P8PANELS_VISUAL.TGANTT_ADD_TASK_COLOR(RGANTT    => RG,
                                              SBG_COLOR => SBG_COLOR_STAGE,
                                              SDESC     => 'Этим цветом выделены этапы.');
    /* Обходим буфер */
    for C in (select T.* from P8PNL_SMPL_GANTT T where T.IDENT = NIDENT order by T.RN)
    loop
      /* Определимся с форматированием */
      if (C.TYPE = 0) then
        STASK_BG_COLOR := SBG_COLOR_STAGE;
      else
        STASK_BG_COLOR := SBG_COLOR_JOB;
      end if;
      /* Сформируем задачу */
      RGT := PKG_P8PANELS_VISUAL.TGANTT_TASK_MAKE(NRN       => C.RN,
                                                  SNUMB     => C.NUMB,
                                                  SCAPTION  => C.NUMB || ' - ' || C.NAME,
                                                  SNAME     => C.NAME,
                                                  DSTART    => C.DATE_FROM,
                                                  DEND      => C.DATE_TO,
                                                  SBG_COLOR => STASK_BG_COLOR);
      PKG_P8PANELS_VISUAL.TGANTT_TASK_ADD_ATTR_VAL(RGANTT => RG,
                                                   RTASK  => RGT,
                                                   SNAME  => 'type',
                                                   SVALUE => C.TYPE,
                                                   BCLEAR => true);
      PKG_P8PANELS_VISUAL.TGANTT_TASK_ADD_ATTR_VAL(RGANTT => RG, RTASK => RGT, SNAME => 'state', SVALUE => C.STATE);
      /* Добавляем задачу в диаграмму */
      PKG_P8PANELS_VISUAL.TGANTT_ADD_TASK(RGANTT => RG, RTASK => RGT);
    end loop;
    /* Сериализуем собранные данные */
    COUT := PKG_P8PANELS_VISUAL.TGANTT_TO_XML(RGANTT => RG);
  end GANTT;
  
  /* Исправление задачи в диаграмме Ганта */
  procedure GANTT_MODIFY
  (
    NIDENT                  in number,  -- Идентификатор буфера
    NRN                     in number,  -- Рег. номер записи
    DDATE_FROM              in date,    -- Дата начала задачи
    DDATE_TO                in date     -- Дата окончания задачи 
  )
  is
  begin
    for C in (select T.*
                from P8PNL_SMPL_GANTT T
               where T.RN = NRN
                 and T.IDENT = NIDENT)
    loop
      GANTT_BASE_UPDATE(NIDENT     => C.IDENT,
                        NRN        => C.RN,
                        NTYPE      => C.TYPE,
                        SNUMB      => C.NUMB,
                        SNAME      => C.NAME,
                        DDATE_FROM => TRUNC(DDATE_FROM),
                        DDATE_TO   => TRUNC(DDATE_TO));
    end loop;
  end GANTT_MODIFY;
  
  /* Очистка буфера данных для циклограммы */
  procedure CYCLOGRAM_BASE_CLEAN
  (
    NIDENT                  in number   -- Идентификатор буфера
  )
  is
  begin
    /* Удалим из буфера всё с указанным идентификатором */
    delete from P8PNL_SMPL_CYCLOGRAM T where T.IDENT = NIDENT;
  end CYCLOGRAM_BASE_CLEAN;

  /* Добавление данных в буфер циклограммы */
  procedure CYCLOGRAM_BASE_INSERT
  (
    NIDENT                  in number,         -- Идентификатор буфера
    NTYPE                   in number,         -- Тип (0 - колонка, 1 - группа, 2 - задача)
    SNAME                   in varchar2,       -- Наименование
    NPOS_START              in number := null, -- Позиция начала элемента
    NPOS_END                in number := null, -- Позиция окончания элемента
    DDATE_FROM              in date := null,   -- Дата начала
    DDATE_TO                in date := null,   -- Дата окончания
    NTASK_GROUP             in number := null, -- Рег. номер группы
    NRN                     out number         -- Рег. номер записи
  )
  is
  begin
    /* Генерируем рег. номер записи */
    NRN := GEN_ID();
    /* Добавим запись */
    insert into P8PNL_SMPL_CYCLOGRAM
      (RN, IDENT, type, name, POS_START, POS_END, DATE_FROM, DATE_TO, TASK_GROUP)
    values
      (NRN, NIDENT, NTYPE, SNAME, NPOS_START, NPOS_END, DDATE_FROM, DDATE_TO, NTASK_GROUP);
  end CYCLOGRAM_BASE_INSERT;
  
  /* Исправление данных в буфере циклограммы */
  procedure CYCLOGRAM_BASE_UPDATE
  (
    NIDENT                  in number,   -- Идентификатор буфера
    NRN                     in number,   -- Рег. номер записи
    NTYPE                   in number,   -- Тип задачи (0 - этап/веха, 1 - работа)
    SNAME                   in varchar2, -- Наименование
    NPOS_START              in number,   -- Позиция начала
    NPOS_END                in number,   -- Позиция окончания
    DDATE_FROM              in date,     -- Дата начала
    DDATE_TO                in date,     -- Дата окончания
    NTASK_GROUP             in number    -- Рег. номер группы
  )
  is
  begin
    /* Изменим запись */
    update P8PNL_SMPL_CYCLOGRAM T
       set T.TYPE       = NTYPE,
           T.NAME       = SNAME,
           T.POS_START  = NPOS_START,
           T.POS_END    = NPOS_END,
           T.DATE_FROM  = DDATE_FROM,
           T.DATE_TO    = DDATE_TO,
           T.TASK_GROUP = NTASK_GROUP
     where T.RN = NRN
       and T.IDENT = NIDENT;
  end CYCLOGRAM_BASE_UPDATE;
  
  /* Инициализация буфера данных для циклограммы */
  procedure CYCLOGRAM_INIT
  (
    NIDENT                  in out number         -- Идентификатор буфера сформированных данных (null - сгенерировать новый, !null - удалить старые данные и пересоздать с указанным идентификатором)
  )
  is
    NYEAR                   PKG_STD.TNUMBER;      -- Текущий год
    DCYCLOGRAM_START        PKG_STD.TLDATE;       -- Дата начала циклограммы
    DMONTH_CUR              PKG_STD.TLDATE;       -- Текущий месяц (для расчетов)
    DMONTH_START            PKG_STD.TLDATE;       -- Начало месяца (для расчетов)
    DMONTH_END              PKG_STD.TLDATE;       -- Окончание месяца (для расчетов)
    NMONTH_DAYS             PKG_STD.TNUMBER;      -- Количество дней месяца
    NSTART                  PKG_STD.TNUMBER := 0; -- Позиция начала элемента
    NEND                    PKG_STD.TNUMBER := 0; -- Позиция окончания элемента
    NGROUP                  PKG_STD.TNUMBER;      -- Рег. номер группы
    NDUMMY                  PKG_STD.TNUMBER;      -- Буфер для рег. номера
    NMONTH                  PKG_STD.TNUMBER;      -- Месяц даты
    
    /* Инициализация группы */
    procedure INIT_GROUP
    (
      NIDENT                in number,       -- Идентификатор буфера
      DDATE                 in date,         -- Текущая обрабатываемая дата
      NRN                   in out number    -- Рег. номер группы
    )
    is
      NMONTH                PKG_STD.TNUMBER; -- Месяц даты
    begin
      /* Считываем текущий месяц */
      NMONTH := D_MONTH(DDATE => DDATE);
      /* Исходим от даты (формируем группу на начало каждого квартала) */
      case NMONTH
        /* Первый квартал */
        when 1 then
          /* Добавляем группу */
          CYCLOGRAM_BASE_INSERT(NIDENT => NIDENT, NTYPE => 1, SNAME => 'I группа', NRN => NRN);
        /* Второй квартал */
        when 4 then
          /* Добавляем группу */
          CYCLOGRAM_BASE_INSERT(NIDENT => NIDENT, NTYPE => 1, SNAME => 'II группа', NRN => NRN);
        /* Третий квартал */
        when 7 then
          /* Добавляем группу */
          CYCLOGRAM_BASE_INSERT(NIDENT => NIDENT, NTYPE => 1, SNAME => 'III группа', NRN => NRN);
        /* Четвертый квартал */
        when 10 then
          /* ДОбавляем группу */
          CYCLOGRAM_BASE_INSERT(NIDENT => NIDENT, NTYPE => 1, SNAME => 'IV группа', NRN => NRN);
        else
          null;
      end case;
    end INIT_GROUP;
  begin
    /* Удаляем старые данные из буфера */
    if (NIDENT is not null) then
      CYCLOGRAM_BASE_CLEAN(NIDENT => NIDENT);
    else
      /* Илиформируем новый идентификатор, если не задан */
      NIDENT := GEN_IDENT();
    end if;
    /* Фиксируем текущий год */
    NYEAR := EXTRACT(year from sysdate);
    /* Фиксируем дату начала циклограммы */
    DCYCLOGRAM_START := TO_DATE('01.01.' || NYEAR, 'dd.mm.yyyy');
    /* Добавляем колонки и групповые задачи (месяцы года) */
    for I in 0 .. 11
    loop
      /* Рассчитываем текущий месяц */
      DMONTH_CUR := ADD_MONTHS(DCYCLOGRAM_START, I);
      /* Считываем первый и последний день месяца */
      P_FIRST_LAST_DAY(DCALCDATE => DMONTH_CUR, DBGNDATE => DMONTH_START, DENDDATE => DMONTH_END);
      /* Рассчитываем количество дней месяца */
      NMONTH_DAYS := DMONTH_END - DMONTH_START + 1;
      /* Рассчитываем позицию окончания элемента */
      NEND := NSTART + (NMONTH_DAYS * NCG_MULTIPLIER);
      /* Определяем номер месяца */
      NMONTH := D_MONTH(DDATE => DMONTH_CUR);
      /* Добавляем колонку в таблицу */
      CYCLOGRAM_BASE_INSERT(NIDENT     => NIDENT,
                            NTYPE      => 0,
                            SNAME      => TO_CHAR(NMONTH),
                            NPOS_START => NSTART,
                            NPOS_END   => NEND,
                            NRN        => NDUMMY);
      /* Инициализируем группу */
      INIT_GROUP(NIDENT => NIDENT, DDATE => DMONTH_CUR, NRN => NGROUP);
      /* Добавляем задачу */
      CYCLOGRAM_BASE_INSERT(NIDENT      => NIDENT,
                            NTYPE       => 2,
                            SNAME       => 'Работа ' || TO_CHAR(I + 1),
                            NPOS_START  => NSTART,
                            NPOS_END    => NEND,
                            DDATE_FROM  => DMONTH_START,
                            DDATE_TO    => DMONTH_END,
                            NTASK_GROUP => NGROUP,
                            NRN         => NDUMMY);
      /* Если это февраль, май, август или ноябрь - добавляем особосбленную задачу */
      if (NMONTH in (2, 5, 8, 11)) then
        /* Добавляем обособленную задачу */
        CYCLOGRAM_BASE_INSERT(NIDENT      => NIDENT,
                              NTYPE       => 2,
                              SNAME       => 'Обособленная работа ' || NMONTH,
                              NPOS_START  => NSTART,
                              NPOS_END    => NEND,
                              DDATE_FROM  => DMONTH_START,
                              DDATE_TO    => DMONTH_END,
                              NRN         => NDUMMY);
      end if;
      /* Рассчитываем начало следующего месяца */
      NSTART := NEND;
    end loop;
  end CYCLOGRAM_INIT;
  
  /* Сбор данных для отображения циклограммы */
  procedure CYCLOGRAM
  (
    NIDENT                  in number,                           -- Идентификатор процесса
    COUT                    out clob                             -- Сериализованные данные для циклограммы
  )
  is
    CG                      PKG_P8PANELS_VISUAL.TCYCLOGRAM;      -- Описание циклограммы
    RTASK                   PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK; -- Описание задачи циклограммы
    NLINE_NUMB              PKG_STD.TNUMBER := 0;                -- Номер строки
    NLINE_NUMB_TMP          PKG_STD.TNUMBER := 0;                -- Номер строки (буфер)
    DTASK_DATE_START        PKG_STD.TLDATE;                      -- Дата начала этапа
    DTASK_DATE_END          PKG_STD.TLDATE;                      -- Дата окончания этапа
    NTASK_START             PKG_STD.TNUMBER := 0;                -- Позиция начала этапа
    NTASK_END               PKG_STD.TNUMBER := 0;                -- Позиция окончания этапа
    STASK_NAME              PKG_STD.TSTRING;                     -- Наименование задачи
    SCOLOR_WHITE            PKG_STD.TSTRING := 'white';          -- Цвет - белый
    SBG_TASK_COLOR_W_GRP    PKG_STD.TSTRING := '#6bc982';        -- Цвет задачи с группой
    SHL_TASK_COLOR_W_GRP    PKG_STD.TSTRING := '#7dd592';        -- Цвет наведения задачи с группой
    SBG_TASK_COLOR_WO_GRP   PKG_STD.TSTRING := '#e36d6d';        -- Цвет задачи без группы
    STEXT_COLOR_TASK_WO_GRP PKG_STD.TSTRING := '#e5e5e5';        -- Цвет текста задачи без группы
    SBG_TASK_COLOR_GRP      PKG_STD.TSTRING := 'cadetblue';      -- Цвет групповой задачи
    SHL_TASK_COLOR_GRP      PKG_STD.TSTRING := '#6fadaf';        -- Цвет наведения групповой задачи
    
    /* Считывание значений группирующей задачи */
    procedure GROUP_TASK_GET
    (
      NIDENT                in number,         -- Идентификатор процесса
      NGROUP                in number := null, -- Рег. номер группы
      NFLAG_WO_GROUP        in number := 0,    -- Признак отбора задач без групп (0 - нет, 1 - да)
      DTASK_DATE_START      out date,          -- Дата начала этапа
      DTASK_DATE_END        out date,          -- Дата окончания этапа
      NTASK_START           out number,        -- Позиция начала этапа
      NTASK_END             out number         -- Позиция окончания этапа
    )
    is
    begin
      /* Считываем начало и окончание этапа */
      begin
        select min(T.DATE_FROM),
               max(T.DATE_TO),
               min(T.POS_START),
               max(T.POS_END)
          into DTASK_DATE_START,
               DTASK_DATE_END,
               NTASK_START,
               NTASK_END
          from P8PNL_SMPL_CYCLOGRAM T
         where T.IDENT = NIDENT
           and (((NFLAG_WO_GROUP = 1) and (T.TASK_GROUP is null)) or ((NFLAG_WO_GROUP = 0) and (T.TASK_GROUP is not null)))
           and ((NGROUP is null) or ((NGROUP is not null) and (T.TASK_GROUP = NGROUP)))
           and T.TYPE = 2;
      end;
    end GROUP_TASK_GET;
  begin
    /* Инициализируем циклограмму */
    CG := PKG_P8PANELS_VISUAL.TCYCLOGRAM_MAKE(STITLE => 'Задачи на ' || TO_CHAR(EXTRACT(year from sysdate)) || ' год');
    /* Добавляем атрибуты */
    PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK_ATTR(RCYCLOGRAM => CG,
                                                 SNAME      => 'ddate_start',
                                                 SCAPTION   => 'Дата начала',
                                                 BVISIBLE   => true,
                                                 BCLEAR     => true);
    PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK_ATTR(RCYCLOGRAM => CG,
                                                 SNAME      => 'ddate_end',
                                                 SCAPTION   => 'Дата окончания',
                                                 BVISIBLE   => true);
    PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK_ATTR(RCYCLOGRAM => CG,
                                                 SNAME      => 'type',
                                                 SCAPTION   => 'Тип',
                                                 BVISIBLE   => false);
    /* Обходим колонки */
    for CLMN in (select T.NAME,
                        T.POS_START,
                        T.POS_END
                   from P8PNL_SMPL_CYCLOGRAM T
                  where T.IDENT = NIDENT
                    and T.TYPE = 0)
    loop
      /* Добавляем колонку */
      PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_COLUMN(RCYCLOGRAM => CG,
                                                SNAME      => CLMN.NAME,
                                                NSTART     => CLMN.POS_START,
                                                NEND       => CLMN.POS_END);
    end loop;
    /* Считываем значения для задач проекта */
    GROUP_TASK_GET(NIDENT           => NIDENT,
                   NFLAG_WO_GROUP   => 0,
                   DTASK_DATE_START => DTASK_DATE_START,
                   DTASK_DATE_END   => DTASK_DATE_END,
                   NTASK_START      => NTASK_START,
                   NTASK_END        => NTASK_END);
    /* Формируем задачу (этап) */
    RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN        => 1,
                                                      SCAPTION   => 'Задачи проекта',
                                                      SNAME      => 'Задачи проекта',
                                                      NLINE_NUMB => NLINE_NUMB,
                                                      NSTART     => NTASK_START,
                                                      NEND       => NTASK_END,
                                                      SBG_COLOR  => SCOLOR_WHITE);
    /* Добавляем атрибуты */
    PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
                                                     RTASK      => RTASK,
                                                     SNAME      => 'ddate_start',
                                                     SVALUE     => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_START),
                                                     BCLEAR     => true);
    PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
                                                     RTASK      => RTASK,
                                                     SNAME      => 'ddate_end',
                                                     SVALUE     => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_END));
    PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG, RTASK => RTASK, SNAME => 'type', SVALUE => 0);
    /* Добавляем задачу в циклограмму */
    PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
    /* Указываем следующую строку */
    NLINE_NUMB := NLINE_NUMB + 1;
    /* Обходим группы */
    for GRP in (select T.RN,
                       T.NAME,
                       ROWNUM RNUM
                  from P8PNL_SMPL_CYCLOGRAM T
                 where T.IDENT = NIDENT
                   and T.TYPE = 1
                 order by T.RN asc)
    loop
      /* Если это вторая группа - сохраним номер строки */
      if (GRP.RNUM = 2) then
        NLINE_NUMB_TMP := NLINE_NUMB;
      end if;
      /* Если это третья группа - вернемся на уровень второй группы */
      if (GRP.RNUM = 3) then
        NLINE_NUMB := NLINE_NUMB_TMP;
      end if;
      /* Добавляем группу */
      PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_GROUP(RCYCLOGRAM     => CG,
                                               SNAME          => GRP.NAME,
                                               NHEADER_HEIGHT => 30,
                                               NHEADER_WIDTH  => 200);
      /* Считываем значения этапа группы */
      GROUP_TASK_GET(NIDENT           => NIDENT,
                     NGROUP           => GRP.RN,
                     DTASK_DATE_START => DTASK_DATE_START,
                     DTASK_DATE_END   => DTASK_DATE_END,
                     NTASK_START      => NTASK_START,
                     NTASK_END        => NTASK_END);
      /* Формируем задачу (этап) */
      RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN              => GRP.RN,
                                                        SCAPTION         => 'Этап ' || TO_CHAR(GRP.RNUM),
                                                        SNAME            => 'Этап ' || TO_CHAR(GRP.RNUM),
                                                        NLINE_NUMB       => NLINE_NUMB,
                                                        NSTART           => NTASK_START,
                                                        NEND             => NTASK_END,
                                                        SBG_COLOR        => SBG_TASK_COLOR_GRP,
                                                        STEXT_COLOR      => SCOLOR_WHITE,
                                                        SHIGHLIGHT_COLOR => SHL_TASK_COLOR_GRP);
      /* Добавляем атрибуты */
      PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
                                                       RTASK      => RTASK,
                                                       SNAME      => 'ddate_start',
                                                       SVALUE     => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_START),
                                                       BCLEAR     => true);
      PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
                                                       RTASK      => RTASK,
                                                       SNAME      => 'ddate_end',
                                                       SVALUE     => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_END));
      PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG, RTASK => RTASK, SNAME => 'type', SVALUE => 1);
      /* Добавляем задачу в циклограмму */
      PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
      /* Обходим задачи группы */
      for TASK in (select T.RN,
                          T.NAME,
                          T.POS_START,
                          T.POS_END,
                          T.DATE_FROM,
                          T.DATE_TO,
                          ROWNUM RNUM
                     from P8PNL_SMPL_CYCLOGRAM T
                    where T.IDENT = NIDENT
                      and T.TYPE = 2
                      and T.TASK_GROUP = GRP.RN)
      loop
        /* Указываем следующую строку */
        NLINE_NUMB := NLINE_NUMB + 1;
        /* Формируем наименование задачи */
        STASK_NAME := 'Работа ' || TO_CHAR(TASK.RNUM) || ' этапа ' || TO_CHAR(GRP.RNUM);
        /* Формируем задачу */
        RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN              => TASK.RN,
                                                          SCAPTION         => STASK_NAME,
                                                          SNAME            => STASK_NAME,
                                                          NLINE_NUMB       => NLINE_NUMB,
                                                          NSTART           => TASK.POS_START,
                                                          NEND             => TASK.POS_END,
                                                          SGROUP           => GRP.NAME,
                                                          SBG_COLOR        => SBG_TASK_COLOR_W_GRP,
                                                          STEXT_COLOR      => SCOLOR_WHITE,
                                                          SHIGHLIGHT_COLOR => SHL_TASK_COLOR_W_GRP);
        /* Добавляем атрибуты */
        PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
                                                         RTASK      => RTASK,
                                                         SNAME      => 'ddate_start',
                                                         SVALUE     => PKG_XCONVERT.DATE_TO_XML(DVALUE => TASK.DATE_FROM),
                                                         BCLEAR     => true);
        PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
                                                         RTASK      => RTASK,
                                                         SNAME      => 'ddate_end',
                                                         SVALUE     => PKG_XCONVERT.DATE_TO_XML(DVALUE => TASK.DATE_TO));
        PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
                                                         RTASK      => RTASK,
                                                         SNAME      => 'type',
                                                         SVALUE     => 2);
        /* Добавляем задачу в циклограмму */
        PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
      end loop;
      /* Указываем следующую строку */
      NLINE_NUMB := NLINE_NUMB + 1;
    end loop;
    /* Указываем следующую строку */
    NLINE_NUMB := NLINE_NUMB + 1;
    /* Считываем значения для обособленных задач */
    GROUP_TASK_GET(NIDENT           => NIDENT,
                   NFLAG_WO_GROUP   => 1,
                   DTASK_DATE_START => DTASK_DATE_START,
                   DTASK_DATE_END   => DTASK_DATE_END,
                   NTASK_START      => NTASK_START,
                   NTASK_END        => NTASK_END);
    /* Формируем задачу (этап) */
    RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN        => 1,
                                                      SCAPTION   => 'Обособленные задачи',
                                                      SNAME      => 'Обособленные задачи',
                                                      NLINE_NUMB => NLINE_NUMB,
                                                      NSTART     => NTASK_START,
                                                      NEND       => NTASK_END,
                                                      SBG_COLOR  => SCOLOR_WHITE);
    /* Добавляем атрибуты */
    PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
                                                     RTASK      => RTASK,
                                                     SNAME      => 'ddate_start',
                                                     SVALUE     => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_START),
                                                     BCLEAR     => true);
    PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
                                                     RTASK      => RTASK,
                                                     SNAME      => 'ddate_end',
                                                     SVALUE     => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_END));
    PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG, RTASK => RTASK, SNAME => 'type', SVALUE => 0);
    /* Добавляем задачу в циклограмму */
    PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
    /* Указываем следующую строку */
    NLINE_NUMB := NLINE_NUMB + 1;
    /* Цикл по обособленным задачам */
    for REC in (select T.RN,
                       T.NAME,
                       T.POS_START,
                       T.POS_END,
                       T.DATE_FROM,
                       T.DATE_TO,
                       ROWNUM RNUM
                  from P8PNL_SMPL_CYCLOGRAM T
                 where T.IDENT = NIDENT
                   and T.TYPE = 2
                   and T.TASK_GROUP is null)
    loop
      /* Формируем наименование задачи */
      STASK_NAME := 'Работа ' || TO_CHAR(REC.RNUM) || ' без этапа ';
      /* Формируем задачу */
      RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN         => REC.RN,
                                                        SCAPTION    => STASK_NAME,
                                                        SNAME       => STASK_NAME,
                                                        NLINE_NUMB  => NLINE_NUMB,
                                                        NSTART      => REC.POS_START,
                                                        NEND        => REC.POS_END,
                                                        SBG_COLOR   => SBG_TASK_COLOR_WO_GRP,
                                                        STEXT_COLOR => STEXT_COLOR_TASK_WO_GRP);
      /* Добавляем атрибуты */
      PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
                                                       RTASK      => RTASK,
                                                       SNAME      => 'ddate_start',
                                                       SVALUE     => PKG_XCONVERT.DATE_TO_XML(DVALUE => REC.DATE_FROM),
                                                       BCLEAR     => true);
      PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
                                                       RTASK      => RTASK,
                                                       SNAME      => 'ddate_end',
                                                       SVALUE     => PKG_XCONVERT.DATE_TO_XML(DVALUE => REC.DATE_TO));
      PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG, RTASK => RTASK, SNAME => 'type', SVALUE => 2);
      /* Добавляем задачу в циклограмму */
      PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
    end loop;
    /* Формируем список */
    COUT := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TO_XML(RCYCLOGRAM => CG);
  end CYCLOGRAM;
  
  /* Изменение задачи циклограммы */
  procedure CYCLOGRAM_TASK_MODIFY
  (
    NIDENT                  in number,       -- Идентификатор буфера
    NRN                     in number,       -- Рег. номер записи
    SDATE_FROM              in varchar2,     -- Дата начала (в строковом представлении)
    SDATE_TO                in varchar2      -- Дата окончания (в строковом представлении)
  )
  is
    DDATE_FROM              PKG_STD.TLDATE;  -- Дата начала
    DDATE_TO                PKG_STD.TLDATE;  -- Дата окончания
    NYEAR                   PKG_STD.TNUMBER; -- Текущий год
    DCYCLOGRAM_START        PKG_STD.TLDATE;  -- Дата начала циклограммы
    NSTART                  PKG_STD.TNUMBER; -- Позиция начала элемента
    NEND                    PKG_STD.TNUMBER; -- Позиция окончания элемента
  begin
    /* Фиксируем текущий год */
    NYEAR := EXTRACT(year from sysdate);
    /* Переводим даты */
    DDATE_FROM := TO_DATE(SDATE_FROM, 'dd.mm.yyyy');
    DDATE_TO   := TO_DATE(SDATE_TO, 'dd.mm.yyyy');
    /* Если дата начала выходит за границы года */
    if (D_YEAR(DDATE => DDATE_FROM) <> NYEAR) then
      P_EXCEPTION(0,
                  'Дата начала задачи выходит за границы текущего года (%s).',
                  NYEAR);
    end if;
    /* Если дата окончания выходит за границы года */
    if (D_YEAR(DDATE => DDATE_TO) <> NYEAR) then
      P_EXCEPTION(0,
                  'Дата окончания задачи выходит за границы текущего года (%s).',
                  NYEAR);
    end if;
    /* Дата окончания не может быть меньше даты начала */
    if (DDATE_TO < DDATE_FROM) then
      P_EXCEPTION(0, 'Дата окончания не может быть меньше даты начала.');
    end if;
    /* Фиксируем дату начала циклограммы */
    DCYCLOGRAM_START := TO_DATE('01.01.' || NYEAR, 'dd.mm.yyyy');
    /* Рассчитываем новую позицию начала */
    NSTART := (DDATE_FROM - DCYCLOGRAM_START) * NCG_MULTIPLIER;
    /* Рассчитываем новую позицию окончания */
    NEND := NSTART + ((DDATE_TO - DDATE_FROM + 1) * NCG_MULTIPLIER);
    /* Считываем запись */
    for REC in (select T.*
                  from P8PNL_SMPL_CYCLOGRAM T
                 where T.RN = NRN
                   and T.IDENT = NIDENT)
    loop
      /* Обновляем запись циклограммы */
      CYCLOGRAM_BASE_UPDATE(NIDENT      => REC.IDENT,
                            NRN         => REC.RN,
                            NTYPE       => REC.TYPE,
                            SNAME       => REC.NAME,
                            NPOS_START  => NSTART,
                            NPOS_END    => NEND,
                            DDATE_FROM  => DDATE_FROM,
                            DDATE_TO    => DDATE_TO,
                            NTASK_GROUP => REC.TASK_GROUP);
    end loop;
  end CYCLOGRAM_TASK_MODIFY;

end PKG_P8PANELS_SAMPLES;
/
