You are browsing the archive for 2008 十一月.

解决EBS中混淆经营单位和库存组织问题

十一月 13, 2008 in Oracle EBS二次开发

 

 

Oracle EBS 11i , Oracle EBS 12

 

 

问题背景

 

前两天刚好到一个项目上面去了解项目的开发需求,遇到项目中需要将增值税发 票传到金税系统中进行增值税发 票的打印。

Oracle EBS12版本中专门开发了一个金税接口(JMF)模块来解决Oracle EBS和金税系统之间的接口,正好能够满足项目的需求。

财务顾问们按照官方文档和一些官方的演示材料,进行详细的设置和测试,两个星期下来就是无法将数据从应收模块传入金税接口模块。

正好求助我能够从技术的角度进行一下“研究”。

 

 

发现问题

 

我查看了传送AR发 票信息到金税接口请求的程序代码,代码中使用了fnd_log来输出日志信息,使用了Oracle EBS的日志架构,日志思想不错。

  1. 设置预知文件
  2. 开启日志输写功能
  3. 重新运行请求
  4. fnd_log_messages表中查看日志信息

 

从日志信息很短时间定位到下面这段代码:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
BEGIN
  SELECT
    ......
    INTO
    ......
    FROM mtl_system_items_b
   WHERE organization_id= p_org_id
     AND inventory_item_id= p_inventory_item_id ;
EXCEPTION
  WHEN no_data_found THEN
    IF(FND_LOG.LEVEL_EXCEPTION >= FND_LOG.G_CURRENT_RUNTIME_LEVEL)
    THEN
      fnd_log.STRING(fnd_log.LEVEL_EXCEPTION
                   , G_MODULE_PREFIX || l_procedure_name
                   , 'no date found ');
    END IF;
  RAISE;
END;

 

看了上面这段代码,如果要抱出异常,那很有可能就是p_org_id有问题了,我想“是不是编写这段程序的同学将经营单位当作库存组织来使用啦,

但是Oracle EBS模块中总不至于犯这样的错误吧?不管怎么样要看看代码才知道怎么回事了

于是查找调用这个过程的地方,果然看到了如下传给p_org_id这个参数的值:

 

1
l_org_id  NUMBER := mo_global.get_current_org_id;

 

果然是将经营单位当作库存组织来使用了,查看了代码中其它涉及到库存组织的地方,都是错误的。

心想“这个错误也太不应该了吧,好歹也是Oracle EBS系统的一个标准模块啊”,但是后面也想想当年自己刚入门的时候,在自己的程序代码中也曾出现过两者混淆的情况。

 

 

经营单位和库存组织

 

在Oracle EBS系统中,存在很多组织的概念和分类,经营单位和库存组织在系统中都是属于组织,但它们属于不同的分类,因此所具备的特征和能力也不一样。

 

经营单位(Operating Unit)是指具备独立会计核算能力的单位实体,Oracle EBS系统中地应收、应付、销售、采购、现金管理和项目会计等模块都在经营单位下进行对立会计核算。

简单的按照通常企业的表现来描述,经营单位可以简单的理解为企业中的一个部门(实际上会有较大的差异)

 

库存组织(Inventory Organization)是用来跟踪库存业务和余额以及分销商品的一种组织形式。Oracle EBS系统中的库存、物料单、工艺设计、在产品、主生产计划/MRP、能力、质量、成本管理、供应链计划等模块都按库存组织进行安全性控制。

库存组织决定了销售和采购模块可获得的物料信息。库存组织可以简单的理解为是企业中的一个仓库(库存组织可以虚拟)

 

按照上面的说法应该不会讲两者混淆才对啊!

 

有几个原因导致这个问题的发生:

  1. 在Oracle EBS系统中,经营单位和库存组织都是组织,两者只是分类不一样而已,因此可以将一个名称同样叫做“OracleSeeker”的组织名称同时设置为经营单位和库存组织,所以内部ID也是一样的
  2. 在组织定义的基表中,组织内部ID的列名被命名为:ORGANIZATION_ID,而在外键表中有两种常用的写方法来表示:ORGANIZATION_ID 和 ORG_ID,很多时候ORG_ID都表示经营单位,ORGANIZATION_ID表示库存组织,但是有的地方不是,有些混乱
  3. Oracle EBS中组织架构比较复杂,对于初学者不容易搞清楚经营单位和库存组织的关系

 

 

问题的启示

 

本人记录这个问题,并不是想澄清经营单位和库存组织的概念和关系,而是想通过这个问题也能够对使自己在进行Oracle EBS二次开发的时候能够意识到其它问题:

 

  1. 对于一个Oracle EBS二次开发技术顾问来说,掌握技术实现是基础,这样才能更快的完成开发任务
  2. 仅仅掌握技术并不能做好开发,还需要了解Oracle EBS系统的应用架构和系统功能,这样才能正确地编写程序,不至于出现混淆经营单位和库存组织的情况
  3. 需要能够灵活运用Oracle EBS提供的各种基础函数和基础架构,如日志记录等等,当程序出现问题的时候才能够更快更好的定位问题。
  4. 对于开发人员,一定要有“程序一定不会骗人,要骗人也是自己骗自己”的思想来对待任何一个问题,出现问题时不能仅凭表面现象或经验判断,一定要看程序的测试说话

 

 

 

 

 

面试故事一则:“朱”“牛”不分

十一月 12, 2008 in 生活点滴/Enjoy Life

 

 

下面的故事发生在2007年的招聘期间,实属发生在本人身上的真实故事,如有雷同,纯属巧合!!!

 

 

那天我们几个同事还是一如往常地白天在各高校进行宣讲并收取简历,晚饭过后进行简历的筛选,

由于简历比较多,往往筛选好简历并进行必要的数据录入统计之后,都会进行到午夜12点之后,

而为了进行第二天的面试工作,都会紧接着电话通知各位同学第二天的面试时间和地点,因此当同学们

接到面试通知电话的时候都比较晚,很多人都是从睡梦中被电话惊醒。

 

而那天晚上本人打了很多电话,可能是也被“惊醒”了,一时间脑子不够清醒,发生了如下至今被同事当作

招聘笑料的故事。

 

本人:我是xx公司的,请问你是“朱xx”吗,我通知你明天面试?

对方:对不起,我不是(明显是被电话吵醒后的声音)

本人:(肯定还没有清醒过来,接到面试通知都还没有反应过来自己叫什么呢,本着负责的态度,再问一遍请问是“朱”小姐吗?

 

这时候已经引起了旁边几位同事的注意,

甲:好像没有猪小姐阿

已:我记得我们刚才挑选简历的时候是没有什么猪小姐

丙:是不是搞错了说话同时将我手上的简历抢过去了),人家姓牛,你怎么叫人家”猪“小姐啊?

 

此话一出几位同事立马笑得人仰马翻,真是一点都不夸张阿,还有同事居然抱着肚子大笑并在宾馆的地毯上面滚,一时间搞得我也被弄笑场了。

看着同事们大笑,而且在地毯上面滚,不停的大喊:“猪牛不分的家伙!!!”,搞得我自己也笑得不行了。

 

只听对面的电话:喂,你说话啊?你说话啊?(我想这位同学也是听到说通知面试才耐心等我电话,否则早掐了)

 

后面的故事我想就不用描述了,本人意识到问题后,赶快解释自己搞错了,通知同学来面试。

 

 

在此本人也表示对那位同学的歉意。

 

创建新的实体行

十一月 11, 2008 in Oracle 融合中间件

 

 

为了创建一行新的实体行,需要进行如下的步骤:

 

  1. 查找实体定义:使用getDefinitionObject()方法取得实体定义对象
  2. 创建新的实体实例:使用实体定义的createInstance2()方法创建实体的一个实例
  3. 设置属性:使用实体对象的属性设置方法设置各属性的值
  4. 提交事务处理:调用当前事务处理对象的commit()方法
  5. 返回ID

 

下面的代码假设DEPARTMENTS实体定义中将DEPARTMENT_ID的类型设置为DBSequence,它是通过数据库触发器来赋值。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    /* Create a new Department and Return its new id */
    public long createDepartment(String name, Number managerId){
        EntityDefImpl deptDef = DepartmentsImpl.getDefinitionObject();
        DepartmentsImpl newDepartment = (DepartmentsImpl)deptDef.createInstance2(getDBTransaction(),null);
        newDepartment.setDepartmentName(name);
        newDepartment.setManagerId(managerId);
        try{
            getDBTransaction().commit();
        }catch(JboException ex){
            getDBTransaction().rollback();
            throw ex;
        }
        DBSequence newIdAssigned = newDepartment.getDepartmentId();
        return newIdAssigned.getSequenceNumber().longValue();         
    }

 

上面的代码的DEPARTMENT_ID是通过数据库触发器来实现的,另外可以修改实体实现类中的create()方法,

在实体行创建的时候通过Sequence取值给实体中的属性赋默认值:

 

1
2
3
4
5
6
7
8
9
10
    protected void create(AttributeList attributeList) {
        super.create(attributeList);
        for(AttributeDef def : getEntityDef().getAttributeDefs()){
            String sequenceName = (String)def.getProperty("SequenceName");
            if(sequenceName != null){
                SequenceImpl seq = new SequenceImpl(sequenceName,getDBTransaction());
                setAttribute(def.getIndex(),seq.getSequenceNumber());
            }
        }
    }

 

JDeveloper11g中开发GWT应用

十一月 7, 2008 in Oracle 融合中间件

 

 

最近在做一些离线Web应用的研究,刚好GWT+ Google Gears 是比较好的搭配,而本人做Oracle EBS的二次开发,经常使用JDeveloper作为Java开发工具,

因此也自然希望使用JDeveloper来进行GWT的应用开发,下面记录一下配置的过程和一些容易出错的地方。

 

前提:

1,下载GWT开发工具包,并设置相关环境变量,如添加GWT路径到环境PATH中

2,下载GWT for JDeveloper并安装开发插件:GWT Developer

 

 

一、JDeveloper中设置GWT Developer安装路径

0.gwt_path

 

 

 

二、运行applicationCreator 脚本来创建GWT应用框架

 

打开命令行窗口,切换到希望创建应用的目录下,运行下面的命令

1
applicationCreator -out offline com.oracleseeker.offline.client.Offline

 

脚本会创建项目的结构和相关文件,同时会产生一个“Hello World”的简单示例,同时有两个脚本:Offline-shell.cmd 用来运行应用;Offline-compile.cmd 用来编译应用

 

三、JDeveloper中创建一个Web Project

 

创建Web Project向导中输入项目名称,选择通过applicationCreator 创建的GWT应用的路径

 1.project_name

 

设置Web Project的配置信息

Document Root:GWT应用下的www目录

Java EE Web Application Name:offline

Java EE Context Root:offline

2.web_project_profile

 

给项目添加Google Web Toolkit库,安装并设置GWT Developer安装路径之后,会创建Google Web Toolkit库,直接引用就可以了

添加GWT应用中的源代码文件夹Src到项目库中,这个步骤很重要,如果漏了没有办法运行

 

4.gwt_libary 

 

上面没有提到的配置都使用默认配置。

 

 

四、修改项目运行配置

Default Run TargetGWT安装目录\gwt-dev-windows.jar!\com\google\gwt\dev\GWTShell.class。这里不能够直接输入,一定需要通过浏览选择,否则运行的时候报错找不到main class

VM Java Options:-Xmx256M。这里不设置的话,默认为128M,运行的时候会报out of memory

Program Arguments:-out www com.oracleseeker.offline.Offline/Offline.html。这里需要注意中间的空格

Run Directory:GWT应用项目的顶层路径

 

5.run_config

 

 

五、运行应用

 

6.run_app

 

 

 

如何更新和删除实体行

十一月 7, 2008 in Oracle 融合中间件

 

 

一旦获得了一条实体行,需要更新和删除它非常的容易。

下面添加一个updateEmployeeSalary()方法来更新员工的工资。

 

1,通过辅助方法取得员工实体行

使用以前所写的retrieveEmployeeById()方法,根据员工ID取得Employees实体对象

 

2,设置新的属性值

使用EntityImpl类的setAttribute()方法,设置新的属性值

 

3,提交事务处理

使用应用模块的getDBTransaction()方法,调用commit()方法将新的修改提交给数据库

 

 

更新员工工资

1
2
3
4
5
6
7
8
9
10
11
12
13
    /* Update the salary of an existing employee */
    public void updateEmployeeSalary(long employeeId, Number newSalary){
        EmployeesImpl emp = retrieveEmployeeById(employeeId);
        if(emp != null){
            emp.setSalary(newSalary);
            try{
                getDBTransaction().commit();
            }catch(JboException ex){
                getDBTransaction().rollback();
                throw ex ;
            }
        }
    }

 

删除员工

1
2
3
4
5
6
7
    /* delete the salary of an existing employee */
    public void deleteEmployee(long employeeId){
        EmployeesImpl emp = retrieveEmployeeById(employeeId);
        if (emp != null){
            emp.remove();
        }
    }