You are browsing the archive for 2008 12月.

OAF中如何更改query的默认查询

2:26 am in Oracle EBS二次开发 by aronezhang

 

 

OA Framework使用Query Region来创建查询,可以创建简单的查询、高级查询和保存用户个性化的查询。query Region有三种构建方式:

 

1,resultsBasedSearch

OA Framework根据查询结果表格(或HGrid)中定义的可查询的Item自动生成查询面板。OA Framework在用户选择查询按钮后自动根据用户输入的值,生成Where子句,并执行查询。

 

2,autoCustomizationCriteria

OA Framework根据开发者定义的简单查询和高级查询Region生成查询面板。OA Framework在用户选择查询按钮后,根据用户输入的值,生成Where子句,并执行查询。

利用这种模式可以更改查询项的类型,比如使用LOV、Poplist等,还可以加入结果表(或HGrid)中不存在的查询项目。

 

3,None

OA Framework根据开发者定义的简单查询和高级查询Region生成查询面板。开发人员需要自己实现视图对象的查询逻辑。这种方式一般用于无法使用query Region所提供的功能的情况。

 

本文主要介绍在autoCustomizationCriteria模式下如何控制query的查询。比如对于日期的查询一般用区间范围进行查询,而默认情况下,OA Framework生成的Where子句做相等比较。

  1. 为query Region创建简单查询面板,创建各个查询Item,包括日期从和日期至。
  2. 为各个查询Item创建SearchMapping,除了日期从和日期至两个查询项。此时除了日期范围,其余项目的查询都已经可用。
  3. 在query Region所在的Controller类processFormRequest()方法中加入以下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    OAQueryBean querybean = (OAQueryBean)webBean.findIndexedChildRecursive("QueryRN");
    // 判断用户是否选择了查询按钮
    if (pageContext.getParameter(querybean.getGoButtonName()) != null)
    { 
      String dateFrom = pageContext.getParameter("SearchDateFrom");
      String dateTo = pageContext.getParameter("SearchDateTo");
     
      OAApplicationModule am = pageContext.getApplicationModule(webBean);
      Serializable[] params = {dateFrom, dateTo};
     
      am.invokeMethod("bindDateRangeWhereClause", params);
    }
  4. 应用程序模块中方法如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    public void bindDateRangeWhereClause(String dateFrom, String dateTo)
    {
       OAViewObject vo = getXxxVO1();
     
       String clause = null;
       String joinStr = "";
       if (vo.getWhereClause() != null)
       {
         joinStr = "AND";
       }
     
       if ((dateFrom != null) && (!("".equals(dateFrom.trim()))))
       {
         clause = " RECEIPT_DATE >= :";
         whereClause.append(++bindCount);
         parameters.addElement(Date.valueOf(dateFrom));
         clauseCount++;
    }
    // 条件:接收日期至
    if ((dateTo != null) && (!("".equals(dateTo.trim()))))
    {
      if (clauseCount > 0)
       {
         whereClause.append(" AND "); 
       }
      whereClause.append(" RECEIPT_DATE <= :");
      whereClause.append(++bindCount);
      parameters.addElement(Date.valueOf(dateTo));
      clauseCount++;
    }

 

 

OAF中如何新开一个窗口

2:00 am in Oracle EBS二次开发 by aronezhang

 

 

OAF页面要新开窗口,可以在带有导航功能的Item,如link和button(非提交按钮),指定Target Frame属性为”_blank“,就可以新开窗口打开Destination URI属性指定的路径。

如果用户希望新开的窗口能隐藏地址栏、工具栏、菜单栏或者想控制窗口大小,那么在上面所说的Destination URI属性中使用

javascript:window.open(‘OA.jsp?page=XXX’,'<windowName>’, ‘<params>’)

 

注意:在对按钮的Destination URI属性使用Javascript的新开窗口时,需要在首选项中设置可访问功能为标准可访问性,否则Javascript将无法执行。

虽然以上的方法能够实现新开窗口,但还有很多的限制,比如如果在新开窗口前需要执行某些特定操作、或者新开窗口的URL是需要通过执行某些逻辑才能得到的,那么上面的方法就无法实现了。

在OAF中,Oracle不建议在OAF中使用Javascript,但是OAF还是提供了API用于执行Javascript:

1
2
3
4
OAPageContext.putJavascriptFunction(String name, String function)
OAPageContext.removeJavascriptFunction(String name)
OAPageContext.putJavascriptLibrary(String name, String library)
OAPageContext.removeJavascriptLibrary(String name)

 

 

假设用户提交了请求(点击链接、提交按钮等等触发事件的情况)需要新开窗口,在processFormRequest方法中,按一下步骤进行:

1、首先执行pageContext.removeJavascripFunction()

这是非常重要的一点,因为一旦Javascript注册到OAPageContext之后,就一直存在于OAPageContext中,这样会导致每次页面的刷新时都会执行Javascript。

 

2、在特定的事件中,执行程序逻辑,注册Javascript到OAPageContext对象中

1
2
3
4
5
6
7
8
9
if (pageContext.getParameter("Open") != null)
{
  // 执行其它处理
  ...
 
  // 定义Javascript
  String jScript = "window.open('OA.jsp?page=XXX')";
  pageContext.putJavascriptFunction("FunctionName", jScript)
}

 

注意:在调用putJavascriptFunction()时,必须指定Name参数,只有这样才可以调用removeJavascriptFunction()。

 

 

– 

OAF中判断当前事务存在未提交的更改

1:52 am in Oracle EBS二次开发 by aronezhang

 

 

开发需求

在OAF的开发中,可能有这样的需求,在选择保存按钮时,如果存在改动的数据,则提交事务,保存到数据库中;

如果不存在改动的数据,就提示用户当前没有数据可更改。

 

解决思路

判断页面中所使用的视图对象是否发生过改动

 

方法一、调用OADBTransaction.isDirty()方法

此方法用于判断当前事务中,视图对象是否发生过变更。但是此方法只对基于实体对象的视图对象有效,如果存在基于查询的视图对象,需要调用PL/SQL来改变数据库的就不行了。

 

方法二、调用OAViewObject.isDirty()方法

此方法可以判断单个视图对象是否发生过变更,无论是基于实体对象的视图对象还是基于查询的视图对象。

 

方法三、调用实体对象的getPostState()方法来判断

有时使用OAViewObject.isDirty()也有一定的限制。如果重新执行了查询,则视图对象的Dirty标志被重置,此时不可以使用该方法来判断是否存在变更,而需要使用下面所说的方法或第四种方法。

调用ViewRowImpl.getEntity(0).getPostState()可以判断视图行对应的实体对象是否发生过变更,这样就可以不顾是否重新查询导致Dirty标志重置。

 

方法四、使用数据缓存

进入页面前把数据缓存到Transaction中,在保存按钮事件时,比较Transaction保存的实体对象行的每个属性是否和当前视图对象行属性一致。

使用方法四的理由是,如果存在insertRow(),然后又删除了新建的行row.remove(),此时系统就会认为视图对象存在未保存的变更。而使用第四种方法可以避免这种情况(一般情况下,应该也能说服客户接受这种情况)。

另外如果是基于查询建立的视图对象(使用PL/SQL保存到数据库中),并且存在临时属性的变更的情况(调用过row.setAttribute(),即使属性是不需要保存到数据库),就只能使用第四种方法。

 

 

OAF中实现动态LOV

12:09 pm in Oracle EBS二次开发 by aronezhang

 

EBS 10.5.10

 

介绍 

Oracle标准页面上也有使用动态LOV的情况,主要是子在应用Key Flex。代码可参考oracle.apps.ap.oie.framework.kff.webui.KffRegionCO

但是Oracle标准页面上的动态LOV存在Bug,不能在弹出的LOV查询页面中多次输入条件查询,原因在下面会提及。

不知在R12中,这个Bug是否已经修正。本文所介绍的方法在11.5.10版本下运行正常,而且没有标准动态LOV的Bug,并实际运用在项目上。

 

要在OAF中动态创建LOV的功能是很复杂的一件事,本文所讲述的动态LOV创建场合用于事先不能知道页面上会有多少个LOV,而且LOV所使用的SQL查询,也是由用户交互而获得的。

思路如下:首先创建一个通用的LOV Region,建立一个查询的视图对象(Select … From Dual),这个LOV并不能查出任何内容,在运行时动态更改视图对象的定义。

 

1,创建LOV

 

首先在使用LOV的页面的控制器的processRequest方法中创建一个LOV

1
2
3
4
5
6
7
8
9
10
11
OAMessageLovInputBean lovItem = (OAMessageLovInputBean)createWebBean(pageContext,LOV_TEXT,null );
region.addIndexedChild(lovItem);
 
lovItem.setAttributeValue(REGION_CODE, "/xxx/oracle/apps/cux/lov/webui"); 
lovItem.setAttributeValue(REGION_APPLICATION_ID,
new Integer(pageContext.getResponsibilityApplicationId()));
 
addLovItem.setLovRegion(pageContext,"/xxx/oracle/apps/cux/lov/webui/CommonLovRN");
lovItem.setUnvalidated(false);
lovItem.addLovRelations(pageContext,lovItem.getID(),,LOV_RESULT,LOV_REQUIRED_NO);
lovItem.setPrompt();

 

需要注意的是动态建立LOV时,不要使用addLovRelations去增加LOV_CRITERIA(criteria item),如果设置了criteria item,

那么LOV就会用原始定义的SQL去拼页面上的值组成SQL查询,而不会使用我们设置的动态SQL去查询。

(顺便提一下,对于不是动态创建的LOV,而需要动态修改LOV对应的视图对象定义的情况,因为LOV基本的对应项必须设置成criteria item,

为了防止LOV的自动查询,可以设置lovMAP的Programmatic QueryTrue,这样就不会把页面上的值自动加到WHERE子句后查询。) 

 

2,修改视图对象的查询

 

在LOV的控制器类的processRequest中动态修改视图对象的查询,因为我们屏蔽了LOV的自动查询,所以在processRequest中还必须处理自动查询的情况

(自动查询的情况是因为用户输入了值或之前已经选择了,重新打开LOV),这里不给出具体的代码。在LOV控制器的processFormRequest方法中必须做以下处理,

因为尽管我们更改了视图对象的定义,但是当点查询按钮时,LOV还是按最原始定义的SQL绑定条件来查询,所以这里的操作是不让LOV执行查询,而进入processRequest重新更改视图的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if ("lovFilter".equals(pageContext.getParameter("event"))) {
    try {
        String strRedirectUrl = pageContext.getRequestUrl();
        int intIndex = strRedirectUrl.indexOf("&amp;");
        strRedirectUrl =
                strRedirectUrl.substring(0, intIndex + 1) + "searchText=" +
                URLEncoder.encode(pageContext.getParameter("searchText")) +
                "&amp;" + "FormRequest=Y&amp;" +
                strRedirectUrl.substring(intIndex + 1);
 
        pageContext.sendRedirect(strRedirectUrl);
    } catch (Exception expt) {
    }
}

 

 

 

– 

OAF个性化开发实例

4:43 am in Oracle EBS二次开发 by aronezhang

 

EBS 11.5.10.2

 

 

需求

下图是Oracle标准页面,有一个简单的开发需求,当Expense Template等于“管理费用”时,Approver的LOV输入框默认为“XXX”,并且LOV只读,不能进行选择。

oaf_per_example1

 

分析

 

这个看似极其简单的开发,其背后所隐藏的技术知识却是不那么简单的。

当Expense Template选择为“管理费用”时,Approver LOV输入框只读,这里牵涉到的开发基本为给Expense Template加上FireAction的功能,

然后在Expense Template的Value Change事件中去修改Approver LOV的只读属性。

因为开发规范是不允许直接修改Oracle的代码,所以此开发将通过集成标准的控制器类,在客户化的控制器类中实现添加Expense Template的FireAction功能,和修改Approver LOV只读属性,

然后通过个性化,将控制器类替换成客户化的控制器类。  

 

技术要点

 

1,修改Approver LOV的只读属性

刚刚进行OAF开发的开发人员可能觉得这是很简单的事,只要在客户化的控制器类的processFormRequest()方法中调用以下代码:

1
2
OAMessageLovInputBean lovBean = (OAMessageLovInputBean)webBean.findChildRecursive("Approver");
lovBean.setReadOnly(true);

就可以实现了,如果这样做开发,那么就错了,在运行时会抛出异常:

1
2
3
The OA passivation framework coding standard has been violated. 
Web bean properties cannot be modified in the controller processFormData or processFormRequest method. 
Web bean properties should be modified in the processRequest method only.

 

(这里有两个方法是例外,setValue()和setText(),因为Value并不影响控件树)为什么把LOV设为只读会出错呢?这是因为OAF在运行时,表现为一棵控件树。

当进入页面时,控件树被初始化创建,而当用户事件触发POST请求时,控件树并不会被重新生成(提高性能),要改变控件的属性只有两个办法,一个是重新生成控件树,二是使用PPR。

 

方法一:重构控件树

重构控件树就意味着在processFormRequest()方法处理事件时,需要Forward到本页,并通过参数控制来设置Approver LOV只读,这样会刷新整个页面,而且对编码也带来困难。

方法二:使用PPR

实现方法是利用OAF数据绑定机制,将Approver LOV的ReadOnly属性绑定到视图对象的属性,通过改变视图对象属性的值来控制Approver LOV的只读。

这里又引出第2个技术要点:

 

2,动态创建视图对象

 

创建视图对象需要首先创建OAViewDef对象,然后利用OAViewDef对象来创建视图对象。

这里谈一下Oracle的一个开发规范,OAViewDef接口在oracle.apps.fnd.framework.server包下,按照Oracle的规范,任何webui下的类不允许import任何server下的类。我是一个规范的严格遵守者,这样就会使开发变得更复杂。

要在server包下引用OAViewDef接口,就必须创建应用程序模块,将动态创建视图对象的代码移到应用程序模块中实现。在客户化控制器类中,动态创建应用程序模块,然后调用应用程序模块的方法。

 

实现步骤

 

假设开发人员对Oracle标准的页面结构都已经非常了解了,包括需要集成哪个标准的控制器,页面上所使用的视图对象。

1、创建客户化应用程序模块cux.oracle.apps.ap.oie.entry.server.CuxPVOAM

2、创建视图对象cux.oracle.apps.ap.oie.entry.server.CuxPVO,视图对象中只有一个临时属性ReadOnlyFlag,类型为Boolean(此步可选)

3、创建客户化控制器类,继承标准的控制器类

4、在CuxPVOAMImpl.java中创建两个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import oracle.apps.fnd.framework.OAViewObject;
import oracle.apps.fnd.framework.server.OAViewDefImpl;
import oracle.jbo.AttributeDef;
import oracle.jbo.Row;
……
 
public void initPVO()
{
    OAApplicationModuleImpl rootAM = (OAApplicationModuleImpl)getRootApplicationModule();
 
    // 创建视图对象
    OAViewDefImpl viewDef = (OAViewDefImpl viewDef)getOADBTransaction().createViewDef();
    viewDef.setFullName("CuxPVODef");
    viewDef.setViewObjectClass("oracle.apps.fnd.framework.server.OAViewObjectImpl ");
    viewDef.addTransientAttrDef("ReadOnlyFlag",
                             "java.lang.Boolean",
                             null,
                             false,
                             AttributeDef.UPDATEABLE);
 
    OAViewObject pvo = (OAViewObject)rootAM.createViewObject("CuxViewPVO", viewDef);
    // 初始化PVO
    if (!pvo.isPreparedForExecution())
    {
      pvo.executeQuery();
    }
    pvo.setMaxFetchSize(1);
    Row row = pvo.createRow();
    pvo.insertRow(row);
    row.setAttribute("ReadOnlyFlag", Boolean.FALSE);
  }
 
  public void handleTemplateChange()
  {
    OAApplicationModuleImpl rootAM = (OAApplicationModuleImpl)getRootApplicationModule();
    OAViewObject vo = (OAViewObject)rootAM.findViewObject("XxxVO1"); //标准的VO
    OAViewObject pvo = (OAViewObject)rootAM.findViewObject("CuxViewPVO");
 
    if () 
    {
      pvo.first().setAttribute("ReadOnlyFlag", Boolean.TRUE);
      vo.first().setAttribute("Xxx", ); // 设置Approver LOV绑定的视图对象属性值
    }
    else
    {
      pvo.first().setAttribute("ReadOnlyFlag", Boolean.FALSE);
    }    
  }
}

 

5、在客户化控制器中添加相应代码

processRequest代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public void processRequest(OAPageContext pageContext, OAWebBean webBean)
{
  super.processRequest(pageContext, webBean);
 
  // 启用Expense Template的FireAction功能
	OAMessageChoiceBean choiceBean = (OAMessageChoiceBean)webBean.findChildRecursive("ExpenseTemplate");
  choiceBean.setFireActionForSubmit("change",
                                null,
                                null,
                                true);
 
  // 创建客户化应用程序模块
  OAApplicationModule rootAM = pageContext.getRootApplicationModule();
  OAApplicationModule pvoAM = (OAApplicationModule)rootAM.findApplicationModule("CuxPVOAM");
  if(pvoAM == null)
  {
      rootAM.createApplicationModule("CuxPVOAM","cux.oracle.apps.ap.oie.entry.server.CuxPVOAM");
      pvoAM = HssCustomizeHelper.getNestedAMInstance(rootAM, "CuxPVOAM");
  }
  pvoAM.invokeMethod("initPVO");
 
  // 将ReadOnly属性绑定到视图对象属性
	OAMessageLovBean approverLov = (OAMessageChoiceBean)webBean.findChildRecursive("Approver");
  approverLov.setAttributeValue(READ_ONLY_ATTR, new OADataBoundValueViewObject(approverLov,"ReadOnlyFlag","CuxPVO"));
}

将开发测试后的代码发布到服务器。(测试可以在本地,将页面上的控制器类替换成客户化的控制器来进行)

 

processFormRequest代码: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void processFormRequest(OAPageContext pageContext, OAWebBean webBean)
{
  super.processFormRequest(pageContext, webBean);
 
  OAApplicationModule rootAM = pageContext.getRootApplicationModule();
  OAApplicationModule pvoAM =
      (OAApplicationModule)rootAM.findApplicationModule("CuxPVOAM");
  if(pvoAM == null)
  {
      rootAM.createApplicationModule(
          "CuxPVOAM","cux.oracle.apps.ap.oie.entry.server.CuxPVOAM");
      pvoAM = HssCustomizeHelper.getNestedAMInstance(rootAM, "CuxPVOAM");
  }
 
  if ("change".equals(pageContext.getParameter(EVENT_PARAM))) 
  {
    pvoAM.invokeMethod("handleTemplateChange");
  }
}

 

HssCustomizeHelper.getNestedAMInstance()的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static OAApplicationModule getNestedAMInstance(OAApplicationModule parentAM, String nestedAMName)
{
  OAApplicationModule am = null;
 
  String[] amNames = parentAM.getApplicationModuleNames();
  for (int i=0; i&lt; amNames.length; i++)
  {
    if (amNames[i].endsWith(nestedAMName))
    {
      return (OAApplicationModule)parentAM.findApplicationModule(amNames[i]);
    }
    else
    {
      am = getNestedAMInstance((OAApplicationModule)parentAM.findApplicationModule(amNames[i]),nestedAMName);
      if (am != null)
      {
        return am;
      }
    }
  }
  return am;
}

7、重启Apache

8、进入页面,设置个性化,替换标准的控制器类为客户化控制器类。