OAF-компоненты. Attachments

1. Динамическое создание компонента attachmentTable

import oracle.apps.fnd.framework.webui.beans.layout.OAAttachmentTableBean;
import oracle.apps.fnd.framework.webui.OAWebBeanConstants;
import java.util.Hashtable;
import java.util.Dictionary;
...
OAAttachmentTableBean attTable = (OAAttachmentTableBean)pageContext.getWebBeanFactory().createWebBean(pageContext, OAWebBeanConstants.ATTACHMENT_TABLE_BEAN, null, null);

attTable.setID("xxTestAttachmentId");
attTable.setViewUsageName("ApplicantAssignmentVO");
attTable.setSearchRegionDisplayed(false);
attTable.setDocumentCatalogEnabled(true);

attTable.setText("Сводка дополнений");

Hashtable<String, Object> entity = new Hashtable<String, Object>();
entity.put("entityId", "PER_ASSIGNMENTS_F"); // entity
entity.put("showAll", Boolean.FALSE);
entity.put("insertAllowed", Boolean.TRUE);
entity.put("deleteAllowed", Boolean.TRUE);
entity.put("updateAllowed", Boolean.TRUE);

String[] pkAttrNames = new String[1];
pkAttrNames[0] = "AssignmentId";
entity.put("pkColumns", pkAttrNames);

Dictionary[] entityMaps = new Dictionary[1];
entityMaps[0] = entity;
attTable.setEntityMappings(entityMaps);

webBean.addIndexedChild(attTable);

https://community.oracle.com/thread/2249676
https://community.oracle.com/thread/2404025

2. Не появляются всплывающие popup-ы (Inline Attachments)

В некоторых невыясненных ситуациях, при добавлении компонента attachmentTable на стандартную форму динамически или через персонализацию, не работают popup-ы для просмотра/добавления/редактирования/удаления вложений (Inline Attachments). Было обнаружено, как минимум, 2 условия, которые влияют на эту ситуацию (полный список условий выяснить не удалось):

  • Страница открывается 1-й раз
  • У компонента attachmentTable отключен регион поиска (searchRegionRendered="false")

Для устранения этого неудобства удалось найти workaround. Нужно добавить в контроллер (processRequest) следующий код:

// Тут важен процесс создания компонента, оказывающий положительное влияние на popup-ы, если они не хотят всплывать.
// На форме компонент показывать не нужно.
OAMessageChoiceBean akAttachmentCategory = (OAMessageChoiceBean)createWebBean(pageContext, "MESSAGE_POPLIST", null, "XxTmp");
akAttachmentCategory.setPickListViewObjectDefinitionName("oracle.apps.fnd.server.FndCategoryNameVO");
akAttachmentCategory.setValue(pageContext, "1");

3. Изменения в модели данных
Если форма работает с вложениями (attachmentLink, attachmentTable), то в модели данных происходят следующие динамические изменения:

  • Добавляется дочерний Application Module с именем oracle_apps_fnd_server_OAAttachmentsAM
  • В этом AM, для каждого компонента, работающего с вложениями, создается набор ViewObject-ов. Наиболее интересные — VO, связанные с таблицами fnd_documents и fnd_attached_documents. Именуются они по шаблону Attach_<num>_FndDocumentsDomExtensionVO и Attach_<num>_FndAttachedDocumentsDomExtensionVO. Например, Attach_0_FndAttachedDocumentsDomExtensionVO.

Перед проходом по строкам VO, нужно обязательно проверить, что заданы bind-переменные. Это наиболее вероятная ситуация, но попадались случаи, когда для объекта нет привязанных вложений, и bind-переменные в Attach…VO были не установлены. Попытка сделать проход по строкам VO, в такой ситуации, приведет к тому, что SQL-запрос вернет данные из таблиц без фильтра (например, все записи из fnd_attached_documents). Cтраница зависнет, а потом упадет по timeout-у.
Пример кода.

OAApplicationModule am = pageContext.getApplicationModule(webBean);
// Опорные имена, по которым ищем AM и VO с вложениями
String attachmentsAMName = "oracle_apps_fnd_server_OAAttachmentsAM";
String attachDocsVOFlag = "FndAttachedDocumentsDomExtensionVO";
String docsVOFlag = "FndDocumentsDomExtensionVO";
// Application Module, в котором содержатся ViewObject-ы, имеющие отношения к вложениям (attachemnts)
OAApplicationModule amA = (OAApplicationModule)am.findApplicationModule(attachmentsAMName);
if (amA != null) {
    writeDiag(pageContext, "AM = " + amA.getDefFullName());
    String[] voNames = amA.getViewObjectNames();
    writeDiag(pageContext, "viewobjectNames Length = " + voNames.length);
    for (int j = 0; j < voNames.length; j++) {
        // Обрабатываем ViewObject-ы (их может быть несколько - свой для каждого претендентства), в которых находятся данные по fnd_attached_documents if (voNames[j].lastIndexOf(attachDocsVOFlag) > 0)
        if (voNames[j].lastIndexOf(attachDocsVOFlag) > 0) {
            String voDName = voNames[j].substring(0, voNames[j].indexOf("_", 8) + 1) + docsVOFlag; // "Attach__"
            writeDiag(pageContext, "     AttachedDocuments ViewObject Name = " + voNames[j]);
            writeDiag(pageContext, "     Documents ViewObject Name = " + voDName);
            
            OAViewObject voA = (OAViewObject)amA.findViewObject(voNames[j]);
            // ОБЯЗАТЕЛЬНО!!! Проверяем, чтобы были заданы bind-переменные.
            if (voA.getWhereClauseParams().length > 1) {
                Object[] params = voA.getWhereClauseParams();
                for(int p = 0; p < params.length; p++)
                    writeDiag(pageContext, "params[" + p + "] = " + params[p].toString());
                for (Row rowA = voA.first(); rowA != null; rowA = voA.next()) {
                    writeDiag(pageContext, "DocumentId = " + rowA.getAttribute("DocumentId"));
                    writeDiag(pageContext, "LastUpdateDate = " + rowA.getAttribute("LastUpdateDate"));
                    writeDiag(pageContext, "newRowState = " + ((ViewRowImpl)rowA).getNewRowState());
                }
            }
        }
    }
}

OAF-компоненты. AdvancedTable

1. Использование элементов ввода в области tableActions

Для элементов области tableActions, в которые предполагается ввод данных пользователем (например, MessageChoice или MessageTextInput), была замечена особенность: если ViewObject, связанный c AdvancedTable, не инициализирован, тогда нет доступа к новым значениям элементов, введенных на форме. Признаком того, что ViewObject не инициализирован, является надпись в табличной части «Поиск не выполнен». Если ViewObject инициализирован, но в нем нет строк, то надпись будет «Нет результатов поиска». В контроллере этот факт можно проверить с помощью функции VO isPreparedForExecution. Для кнопок таких проблем не возникает — с точки зрения обработки, важен только сам факт нажатия кнопки, который всегда можно отследить и обработать в контроллере.

Если есть необходимость работать с элементами, допускающими ввод данных пользователем в tableActions, то нужно чтобы VO был в инициализированном состоянии. Например, в контроллере processRequest, можно написать следующий код (чтобы состояние «VO НЕ инициализирован, строк нет» перевести в состояние «VO инициализирован, строк нет»)

if (!vo.isPreparedForExecution()) {
    vo.setWhereClause("1=0");
    vo.setWhereClauseParams(null);
    vo.executeQuery();
    vo.setWhereClause(null);
}

2. Доступ к элементам, находящимся в tableActions или tableSelection

Для доступа к элементам из областей tableActions или tableSelection, нужно сначала получить саму область, а потом искать элемент внутри нее

OAAdvancedTableBean tableBean = (OAAdvancedTableBean)webBean.findIndexedChildRecursive("invoicesTable");
OARowLayoutBean actionsBean = (OARowLayoutBean)tableBean.getTableActions(); // или OAFlowLayoutBean
OAMessageChoiceBean pt = (OAMessageChoiceBean)actionsBean.findIndexedChildRecursive("prepaymentTypesItem");
pt.setValue(pageContext, pageContext.getProfile("XXAP001_INVOICE_PREPAYMENT_DEFAULT"));

3. Связывание свойств с атрибутами VO для элементов внутри Столбца

Для некоторых элементов, являющихся дочерними по отношению к столбцу AdvancedTable, не удалось связать свойства с атрибутами VO. Такая особенность наблюдалась для любого региона (flowLayout, tableLayout и т.п.) и для компонента spacer. Например, свойство Rendered не получилось связать ни с использованием EL, ни через OADataBoundValueViewObject. Для spacer-а можно применить workaround:

  • вместо компонента spacer использовать компонент rawText
  • в свойстве Text указать HTML-код, который генерится компонентом spacer (задать нужные значения в атрибутах width и height): <img src="/OA_HTML/cabo/images/skyros/t.gif" width="10" height="10">
  • свойство Rendered связать с атрибутом VO
Categories: OA Framework Tags: ,

Регулярные выражения. Удаление последней буквы или цифры в строке.

4 Апрель 2018 2 comments

Используем регулярное выражение с REGEXP_LIKE для удаления последней буквы в строке:

with t as
(           select 'H18.DHSHJ7' as str from dual
 union all  select 'H18.AAA1A' from dual
 union all  select 'H18.AAA10' from dual 
 union all  select 'H18.AAAB4B' from dual  
)
select t.str, regexp_replace(str, '[[:alpha:]]$','')
from t

SQL> 
STR        REGEXP_REPLACE(STR,'[[:ALPHA:]
---------- ------------------------------------------------------------------
H18.DHSHJ7 H18.DHSHJ7
H18.AAA1A  H18.AAA1
H18.AAA10  H18.AAA10
H18.AAAB4B H18.AAAB4

Для удаления последней цифры заменяем [[:alpha:]] на [[:digit:]]

with t as
(           select 'H18.DHSHJ7' as str from dual
 union all  select 'H18.AAA1A' from dual
 union all  select 'H18.AAA10' from dual 
 union all  select 'H18.AAAB4B' from dual  
)
select t.str, regexp_replace(str, '[[:digit:]]$','')
from t

SQL> 
STR        REGEXP_REPLACE(STR,'[[:DIGIT:]
---------- -------------------------------------------------------------------
H18.DHSHJ7 H18.DHSHJ
H18.AAA1A  H18.AAA1A
H18.AAA10  H18.AAA1
H18.AAAB4B H18.AAAB4B

JSP страница для входа в OEBS с логином и паролем

26 Декабрь 2017 1 комментарий


Весьма полезная JSP страница для авто входа в OEBS с логином и паролем:

http://host:port/OA_HTML/fndvald.jsp?username=!login&password=!pwd

Пример ссылки:

Читать дальше про “JSP страница для входа в OEBS с логином и паролем” »

Ассоциативный массив: цикл по коллекции с ключом varchar2

27 Июль 2017 1 комментарий

Пример заполнения ассоциативного массива и цикл для вывода данных.
Ключом является строка varchar2(1):

declare
  -- объявление типа
  TYPE resultRec is record
  (
      ready  varchar2(1),
      cnt    number
  );
  type resultTab is table of resultRec INDEX BY VARCHAR2(1);
  --
  p_iter varchar2(1); -- итератор
  p_validation_res_tbl resultTab; -- экземпляр коллекции
  --
  -- запись данных в коллекцию
  procedure put_result(p_ready varchar2)
  is
  begin
    if (p_validation_res_tbl.exists(p_ready)) then
      p_validation_res_tbl(p_ready).cnt := p_validation_res_tbl(p_ready).cnt + 1;
    else
      p_validation_res_tbl(p_ready).ready := p_ready;
      p_validation_res_tbl(p_ready).cnt   := 1;
    end if;
  end;  
begin
  
  -- запись данных в коллекцию через вспомогательную процедуру
  put_result('E');
  put_result('E');
  put_result('S');
  put_result('W');
  
  -- первичное выставление итератора коллекции  
  p_iter := p_validation_res_tbl.FIRST;
  -- проверка, на то, что коллекция не пустая
  if (p_iter is null) then
    dbms_output.put_line('p_validation_res_tbl is null');
  else  
    -- цикл по коллекции
    WHILE p_iter IS NOT NULL
    LOOP
      -- Пример оращения к данным p_validation_res_tbl(p_iter).cnt, где
      --   p_validation_res_tbl: экземпляр коллекции
      --   p_iter : текущий итератор
      --   cnt : поле из record resultRec
      dbms_output.put_line(p_validation_res_tbl(p_iter).ready 
                           ||':'
                           ||p_validation_res_tbl(p_iter).cnt
                           ||' row(s)');
      -- получение следующей записи
      p_iter := p_validation_res_tbl.NEXT(p_iter);
    END LOOP; 
  end if; 
  
end;
>
E:2 row(s)
S:1 row(s)
W:1 row(s)

Список полномочий и групп запросов для отчета

15 Апрель 2016 1 комментарий

Запрос для вывода списка полномочий и групп запросов по коду или имени отчета/параллельной программы:

with params as (
select 'FNDATREP' as short_name
      ,''    as long_name
      ,'RU'  as lang /* RU;US */
from dual
)
select  p1.concurrent_program_name
       ,pt.user_concurrent_program_name
       ,fa.application_short_name as appl_resp
       ,r.responsibility_key
       ,rt.responsibility_name      
       ,fa1.application_short_name as appl_request_group
       ,g.request_group_name
from fnd_request_groups      g,
     fnd_request_group_units u,
     fnd_application    fa,
     fnd_application    fa1,         
     fnd_concurrent_programs_tl pt,
     fnd_concurrent_programs    p1,
     fnd_responsibility    r,
     fnd_responsibility_tl rt,
     params
where 1=1
      and u.application_id = g.application_id
      and u.request_group_id = g.request_group_id
      and (u.request_unit_id = pt.concurrent_program_id
          or u.request_unit_type = 'A')
      and u.unit_application_id = pt.application_id
      and pt.application_id = fa.application_id
      and pt.concurrent_program_id = p1.concurrent_program_id
      and g.application_id = fa1.application_id
      and r.request_group_id = g.request_group_id
      and r.responsibility_id = rt.responsibility_id
      and rt.language = params.lang
      and pt.language  = params.lang
      and (
            (p1.concurrent_program_name like '%'||params.short_name||'%' 
             and params.short_name is not null 
            ) 
            or 
            (pt.user_concurrent_program_name like '%'||params.long_name||'%' 
             and params.long_name is not null and params.short_name is null
            )
          )
order by 1,2;

Список полномочий где используется форма + путь для вызова

15 Апрель 2016 Нет комментариев

Скрипт для вывода списка полномочий, в которых используется форма.
Так же добавлен путь внутри полномочий для вызова формы.
Желаемый язык для вывода данных вынесен в параметры.

with params as (
select 'FNDSCRSP' as form_name
      ,''         as user_form_name
      ,''         as description_form
      ,'RU'       as lang /* RU;US */
from dual
)
select (select rt.responsibility_name from fnd_responsibility_tl rt , params
        where rt.responsibility_id  = r.responsibility_id
              and rt.application_id = r.application_id
              and rt.language = params.lang
       ) as responsibility_name
      ,(select ltrim(sys_connect_by_path(me.prompt, ' --> '),' --> ') 
        from (select b.menu_id,b.entry_sequence,b.sub_menu_id,b.function_id, t.prompt
             from fnd_menu_entries_tl t, fnd_menu_entries b, params
             where b.menu_id = t.menu_id
               and b.entry_sequence = t.entry_sequence
               and t.language = params.lang) me
        where me.prompt is not null 
              and me.function_id = ff1.function_id 
              and rownum=1
        start with me.menu_id = r.menu_id  
        connect by prior me.sub_menu_id = me.menu_id and prior me.prompt is not null
       ) path_to_form
      ,r.application_id
      ,r.responsibility_id
      ,r.responsibility_key 

      ,r.menu_id    
      ,(select mt.user_menu_name from fnd_menus_tl mt, params where mt.menu_id = r.menu_id and mt.language = params.lang) as user_menu_name
      ,ff1.function_id
      ,(select fft.user_function_name from fnd_form_functions_tl fft, params where fft.function_id = ff1.function_id and fft.language = params.lang) as user_function_name
      ,ff1.form_id 
      ,(select ff.form_name from fnd_form ff where ff.form_id = ff1.form_id ) as form_name      
      ,(select ftl.user_form_name from fnd_form_tl ftl, params where ftl.form_id = ff1.form_id  and ftl.language = params.lang) as user_form_name
from fnd_responsibility r
    ,(select ff.function_id, ff.form_id from fnd_form_functions ff 
      where ff.form_id in (select f.form_id 
                           from fnd_form f, fnd_form_tl ft, params
                           where f.form_id = ft.form_id
                                 and (
                                        f.form_name = params.form_name 
                                     or (ft.user_form_name like '%'||params.user_form_name||'%' and params.user_form_name is not null)
                                     or (ft.description like '%'||params.description_form||'%' and params.description_form is not null)
                                     )
                          )
     ) ff1 
where 1=1
      and sysdate between r.start_date and nvl(r.end_date, sysdate+1)
      and r.menu_id in (select me.menu_id from fnd_menu_entries me 
                        start with me.function_id = ff1.function_id
                        connect by prior me.menu_id = me.sub_menu_id 
                       )

SQL Склонение количества ошибок

9 Февраль 2016 2 comments


Пример SQL запроса для склонения слова «ошибка» для вывода количества ошибок: «Найдена 1 ошибка»; «Найдено 3 ошибки»; «Найдено 5 ошибок» :

select 
  val
 ,case when mod(val,10) between 2 and 4 then 'Найдено '||val||' ошибки'
       
       when val between 11 and 14 or mod(val,10) between 5 and 9 
            or mod(val,10) = 0 then 'Найдено '||val||' ошибок' 
                          
       when mod(val,10) = 1 then 'Найдена '||val||' ошибка'
 end str
from ( select level val from dual connect by level<=25 )

       VAL STR
---------- -------------------------------------------------------
         1 Найдена 1 ошибка
         2 Найдено 2 ошибки
         3 Найдено 3 ошибки
         4 Найдено 4 ошибки
         5 Найдено 5 ошибок
         6 Найдено 6 ошибок
         7 Найдено 7 ошибок
         8 Найдено 8 ошибок
         9 Найдено 9 ошибок
        10 Найдено 10 ошибок
        11 Найдено 11 ошибок
        12 Найдено 12 ошибки
        13 Найдено 13 ошибки
        14 Найдено 14 ошибки
        15 Найдено 15 ошибок
        16 Найдено 16 ошибок
        17 Найдено 17 ошибок
        18 Найдено 18 ошибок
        19 Найдено 19 ошибок
        20 Найдено 20 ошибок
        21 Найдена 21 ошибка
        22 Найдено 22 ошибки
        23 Найдено 23 ошибки
        24 Найдено 24 ошибки
        25 Найдено 25 ошибок
25 rows selected

Riga DevDay 2016

25 Январь 2016 Нет комментариев

Riga DevDay 2016
Уважаемые посетители моего блога,
обращаю ваше внимание на ежегодную конференцию для разработчиков Riga DevDay 2016, которая пройдёт со 2 по 4 марта в Риге.

Стоимость, расписание сессий, другие подробности по ссылке
www.rigadevday.lv и www.facebook.com/rigadevday

OEBS API INV: Создание заказа на перемещение

Пример использования API для создания заказа на перемещение

declare 
  lx_doc_hdr    inv_move_order_pub.Trohdr_Rec_Type;
  lx_doc_lines  inv_move_order_pub.Trolin_Tbl_Type;    
  --
  lx_doc_hdr_vals   inv_move_order_pub.Trohdr_Val_Rec_Type;
  lx_doc_lines_vals inv_move_order_pub.Trolin_Val_Tbl_Type;
  lx_return_status  varchar2(1);
  lx_msg_data       varchar2(4000);
  lx_msg_count      number;
begin
  -- тип заказа на перемещение = заявка
  lx_doc_hdr.move_order_type        := inv_globals.G_MOVE_ORDER_REQUISITION;
  lx_doc_hdr.organization_id        := 1775;
  lx_doc_hdr.date_required          := sysdate;
  
  lx_doc_hdr.operation              := inv_globals.G_OPR_CREATE;
  lx_doc_hdr.header_status          := inv_globals.G_TO_STATUS_PREAPPROVED;
    
  -- создание заголовка заказа на перемещение
  inv_move_order_pub.create_move_order_header(
    p_api_version_number => 1,
    p_init_msg_list      => fnd_api.G_TRUE,
    p_return_values      => fnd_api.G_TRUE,
    p_commit             => fnd_api.G_FALSE,
    x_return_status      => lx_return_status,
    x_msg_count          => lx_msg_count,
    x_msg_data           => lx_msg_data,
    p_trohdr_rec         => lx_doc_hdr,
    p_trohdr_val_rec     => lx_doc_hdr_vals,
    x_trohdr_rec         => lx_doc_hdr,
    x_trohdr_val_rec     => lx_doc_hdr_vals
  );
  
  dbms_output.put_line('create_move_order_header='||lx_return_status);
  dbms_output.put_line('header_id='||lx_doc_hdr.header_id);

  if (lx_return_status!='S') then
    FOR j IN 1 .. lx_msg_count LOOP
      dbms_output.put_line('error('||j||')= ' ||fnd_msg_pub.get(j,fnd_api.g_false));
    END LOOP;  
  else

    lx_doc_lines(1).header_id      := lx_doc_hdr.header_id;
    
    lx_doc_lines(1).operation      := inv_globals.G_OPR_CREATE;
    lx_doc_lines(1).line_status    := inv_globals.G_TO_STATUS_PREAPPROVED;
    lx_doc_lines(1).date_required  := sysdate;       
    lx_doc_lines(1).status_date    := sysdate;
    -- 64 Перенос заказа на перемещение    
    lx_doc_lines(1).transaction_type_id := inv_globals.g_type_transfer_order_subxfr;
    --
    lx_doc_lines(1).from_subinventory_code := 'ttttttt';
    lx_doc_lines(1).organization_id        := 1775;
    lx_doc_lines(1).to_subinventory_code   := 'fffffff';      
    lx_doc_lines(1).to_organization_id     := 1775;
          
    lx_doc_lines(1).inventory_item_id      := 57878;
    lx_doc_lines(1).uom_code               := 'шт'; 
    lx_doc_lines(1).quantity               := 5;    
    
    -- создание строки заказа на перемещение
    inv_move_order_pub.create_move_order_lines(
      p_api_version_number       => 1,
      p_init_msg_list            => fnd_api.G_TRUE,
      p_return_values            => fnd_api.G_TRUE,      
      p_commit                   => fnd_api.G_FALSE,
      x_return_status            => lx_return_status,
      x_msg_count                => lx_msg_count,
      x_msg_data                 => lx_msg_data,
      p_trolin_tbl               => lx_doc_lines,
      p_trolin_val_tbl           => lx_doc_lines_vals,
      x_trolin_tbl               => lx_doc_lines,
      x_trolin_val_tbl           => lx_doc_lines_vals
    );    
    
    dbms_output.put_line('create_move_order_lines='||lx_return_status);
    if (lx_return_status!='S') then
      FOR j IN 1 .. lx_msg_count LOOP
        dbms_output.put_line('error('||j||')='||fnd_msg_pub.get(j,fnd_api.g_false));
      END LOOP;  
    else
      dbms_output.put_line('line_id='||lx_doc_lines(1).line_id);      
    end if;    

  end if;
  
end;