http://blog.itpub.net/25164132/viewspace-746657/

接收入库是项目中会经常碰到的开发,这类开发一般来说比较简单,但是接收入库在Oracle中其实涉及到很多模块,其中层次较多,也可以做得比较复杂。本文将尽可能深入细致地讨论接收入库的流程,种类,数据来源,数据的流动路径,以及各个数据点之间的对应关系。最后将给出一个较为全面的接收入库的样例代码。

接收入库的种类接收入库可以按照数据来源分为2种:
1.         对于po订单以及发放的接收入库;
2.         对于内部请购单生成的sales order(后面的部分简称so)的接收入库;

接收入库还可以按照入库方式分为3种:
1.  直接入库:接收后自动入库;
2.  标准入库:先接收,后入库,分两个步骤;
3.  需要检验的接收入库:先接收,然后检验,最后入库;

对于这些不同类型的入库,程序的差别可能会很大。所以一个完整灵活可以应付所有接收入库的程序也是较为复杂的。

数据流动         我们将分析接收入库的数据来源,针对不同的接收入库类别,给出最常见实用的取数逻辑。最后我们将给出一个总体的数据流图,其中包含了各个数据点间的层次关系,帮助大家理解。
其中涉及到的较为重要的表(我们忽略了一些显而易见的表,比如po_headers_all,po_line_locations_all等等):

mtl_supply 最重要的一张表,其中包括了所有可以接收入库的记录
Rcv_transaction 这张表的内容是已经接收入库的事物处理记录,在标准入库或检验入库的时候,我们可能需要其中的接收信息。
Rcv_shipment_headers 发运头表
Rcv_shipment_lines 发运行表
   

标准订单以及一揽子采购协议发放1.  对于标准订单,当订单被approve之后,系统会在mtl_supply这张表中插入该订单相关的记录,系统默认为每一个订单分配行生成一条mtl_supply数据,其supply_type_code值为“PO”,表示物料供应的来源,目前是来自po订单。所以mtl_supply表中,对于标准订单以及一揽子发放,其数据层次是明细到分配行的,即根据distribution_id可以唯一确定一个mtl_supply行:

SELECT pha.segment1,
       pla.line_num,
       plla.shipment_num,
       pda.distribution_num,
       ms.supply_type_code,
       ms.quantity
FROM   mtl_supply            ms,
       po_headers_all        pha,
       po_lines_all          pla,
       po_line_locations_all plla,
       po_distributions_all  pda
WHERE  ms.po_distribution_id = pda.po_distribution_id
AND    ms.supply_type_code = 'PO'
AND    pha.po_header_id = pla.po_header_id
AND    plla.po_header_id = pha.po_header_id
AND    plla.po_line_id = pla.po_line_id
AND    pda.line_location_id = plla.line_location_id
AND    pha.segment1 = '1000100'

以上sql语句将筛选出订单编号为‘1000100’的订单可以接收的distribution行以及可以接收的数量。

注意:在这个时候,rcv_shipmet_headers以及rcv_shipment_lines这两张表中还没有数据,这是po订单与内部请购单接收数据流中的一个较大的差异。
造成这种差异的原因是:
对于po订单,其发运对象是外部供应商,系统无法控制以及检测外部供应商的发运动作,所以只能在做接收的时候,才能生成发运信息,在任何接收之前,系统是不知道任何发运数据的;
而对于内部请购单生成的Sales Order来说,发运对象是公司内部其它组织或ou,发运信息是在其他组织或OU做SO的发运时就产生了,而不必等到接收的时候才产生相应的发运信息。

由于以上差异,在po订单做接收时,接口中不必提供shipment信息,而在po入库以及内部请购单生成的so接收的时候,则需要提供shipment信息。

2.  接下来,当我们为po订单做接收的时候,系统会生成相应的shipment信息,经过测试,每次接收会生成一个新的shipment头,每个接收行会生成一个新的shipment行。所以,对于po接收,其接收的transaction id与发运行的shipment line id是一一对应的,在做po入库的时候,可以利用这一点,通过shipment line 找到可以入库的parent transaction id。另外,对于po接收,其在发运行中的quantity received与quantity shipped字段的值总是一样的,因为发运记录是完全参照接收记录生成的。
3.  对于直接入库的订单,接收后将同时在RCV_TRANSACTION中生成接收记录以及入库记录;

对于标准接收则分为接收和入库两个步骤,接收生成接收事务处理记录,并且同时在MTL_SUPPLY中可以找到mtl_supply_type为“RECEIVING”的记录,表示存在可入库记录了,入库来源是“RECEIVING”。入库后再同一张表(RCV_TRANSACTION)中生成入库事务处理记录;虽然接收后在MTL_SUPPLY表中可以查到可入库的信息,但是因为接收后已经在RCV_TRANSACTION中产生了接收事务处理记录,理论上来说,以上两者的数据应当是等价的,但是这时候我们习惯于从RCV_TRANSACTION表中查找可以入库的记录。

对于需要检验的接收入库,在接收后,需要完成检验,对接收记录打上检验通过标记后,才能进行入库动作,最终生成入库事务处理记录。

标准订单接收入库数据流向图

对内部请购单生成的Sales Order的接收入库内部请购单因为涉及到内部销售订单以及发运信息,所以比PO单的接收多了若干环节,显得较为复杂。基本的数据流如下:
1.  创建内部请购单,审批成功后,通过请求生成对应的Sales Order,完成从PO表到OE表的第一个步骤的数据流动。可以通过OE表的Reference字段来追溯对应的请购单(IR)。这时候虽然在MTL_SUPPLY中也产生了供应数据,但是这个时候是无法对这个IR进行接收的,因为对应的SO还没有发运记录,没有发运怎么能接收呢。
2.  对SO进行挑库发放,确认发运后,在 RCV_SHIPMENT中生成了发运记录。完成了第二个步骤的数据流动。注意,由于存在SO拆行,以及分批发运的状况,所以发运行与IR的行是不存在严格意义上的数量对应关系的,一个IR行生成的SO行,可能被拆分在若干个发运记录中,这也是在做IR接收入库时,接口表要求插入发运信息的原因,对于IR的接收,是以发运事务处理为单位的。发放确认后,在MTL_SUPPLY中,将可以找到supply_type_code为“SHIPMENT”的供应记录,表示从这个时刻起,这个IR相关的订单可以开始接收了,接收来源是“SHIPMENT”;
3.  接下来的步骤基本和PO接收入库一致。值得注意的是在对IR生成的SO做接收时,可以发现一个明显的特点,接收事务处理界面中的可接收记录根据发运方式被拆成了多行,而不像PO接收时那样一个PO行对应一行可接收记录。这也直接说明了对于IR来说接收是以发运为单位进行的。

接口表注意事项不同的入库方式Oracle提供了三种可选的入库方式(在PO订单分配行界面维护):
1.  直接入库:接收后自动入库;
2.  标准入库:接收后,进行入库动作,才完成最终入库;
3.  要求检验的入库:接收后,进行检验,打印检验通过标记后,才能进行入库动作。

对于这三种接收入库,接口表通过不同的控制字段来完成期望的接收入库动作:

a)       对于直接入库的接收,插接收事务处理的接口行表(rcv_transactions_interface)时的几个关键的状态字段:

                  rcv_transactions_interface.transaction_type         := 'RECEIVE';
            rcv_transactions_interface.auto_transact_code      := 'DELIVER';
            rcv_transactions_interface.destination_type_code    := 'INVENTORY';

b)       对于标准以及需检验的入库的接收,插接收事务处理的接口行表(rcv_transactions_interface)时的几个关键的状态字段:

                  rcv_transactions_interface.transaction_type         := 'RECEIVE';
            rcv_transactions_interface.auto_transact_code      := NULL;
            rcv_transactions_interface.destination_type_code    := ' RECEIVING ';

c)       对于接收甚至检验后的数据做入库时,插接收事务处理的接口行表(rcv_transactions_interface)时的几个关键的状态字段:

rcv_transactions_interface.transaction_type         := 'DELIVER';
rcv_transactions_interface.auto_transact_code      := NULL;
rcv_transactions_interface.destination_type_code    := 'INVENTORY';

可入库数量我们需要判断可入库数量,这是对入库接口数据的一项基本校验,是否有足够的数量可以入库,我们分别给出PO以及IR的可入库数量的算法:

对于PO单(标准采购订单,一揽子发放):

    FUNCTION check_po_valid_quantity(p_routing_id       IN NUMBER
                                  ,p_line_location_id IN NUMBER
                                  ,p_deliver_qty      IN NUMBER
                                  ,p_uom_code         IN VARCHAR2
                                  ,p_item_id          IN NUMBER) RETURN VARCHAR2 IS
    l_qty            NUMBER := 0;
    l_valid_quantity NUMBER := 0;
    l_ship_uom_code  VARCHAR2(3);
  
    CURSOR cur_valid_lines(i_routing_id IN NUMBER) IS
      SELECT ms.quantity, muom.uom_code
      FROM   mtl_supply ms, rcv_transactions rt, mtl_units_of_measure muom
      WHERE  rt.transaction_id = ms.rcv_transaction_id
      AND    rt.inspection_status_code =
             decode(i_routing_id, 1, rt.inspection_status_code, 2, 'ACCEPTED')
      AND    ms.supply_type_code = 'RECEIVING'
      AND    ms.po_line_location_id = p_line_location_id
      AND    muom.unit_of_measure = ms.unit_of_measure;
  
  BEGIN
    IF (p_routing_id = '1' OR p_routing_id = '2') THEN
      --standard or inspect required deliver
      FOR rec_valid_line IN cur_valid_lines(p_routing_id) LOOP
        l_qty := rec_valid_line.quantity;
        --transact quantity according to uom
        IF (rec_valid_line.uom_code <> p_uom_code) THEN
          l_qty := inv_convert.inv_um_convert(p_item_id,
                                              6,
                                              l_qty,
                                              rec_valid_line.uom_code,
                                              p_uom_code,
                                              NULL,
                                              NULL);
        END IF;
        l_valid_quantity := l_valid_quantity + l_qty;
      END LOOP;
    ELSIF (p_routing_id = '3') THEN
      -- direct deliver   
      SELECT plla.quantity - plla.quantity_received - plla.quantity_cancelled, muom.uom_code
      INTO   l_valid_quantity, l_ship_uom_code
      FROM   po_line_locations_all plla, mtl_units_of_measure muom
      WHERE  plla.quantity - plla.quantity_received - plla.quantity_cancelled > 0
      AND    muom.unit_of_measure = plla.unit_meas_lookup_code
      AND    plla.line_location_id = p_line_location_id;
      --transact quantity according to uom
      IF (l_ship_uom_code <> p_uom_code) THEN
        l_valid_quantity := inv_convert.inv_um_convert(p_item_id,
                                                       6,
                                                       l_valid_quantity,
                                                       l_ship_uom_code,
                                                       p_uom_code,
                                                       NULL,
                                                       NULL);
      END IF;
    END IF;
  
    xxdomi_cn_conc_utl.debug('l_valid_quantity : ' || l_valid_quantity || ' ;p_deliver_qty : ' ||
                             p_deliver_qty);
  
    IF (l_valid_quantity >= p_deliver_qty) THEN
      RETURN 'VALID';
    ELSE
      RETURN 'INVALID';
    END IF;
  EXCEPTION
    WHEN OTHERS THEN
      xxdomi_cn_conc_utl.log_msg('check_po_valid_quantity: ' || SQLERRM);
      RETURN 'INVALID';

END check_po_valid_quantity;

对于IR   FUNCTION check_ir_valid_quantity(p_routing_id  IN NUMBER
                                  ,p_req_line_id IN NUMBER
                                  ,p_deliver_qty IN NUMBER
                                  ,p_uom_code    IN VARCHAR2
                                  ,p_item_id     IN NUMBER) RETURN VARCHAR2 IS
    l_qty            NUMBER := 0;
    l_valid_quantity NUMBER := 0;
  
    --valid lines for routing_id 1&2
    CURSOR cur_valid_lines(i_routing_id IN NUMBER) IS
      SELECT ms.quantity, muom.uom_code
      FROM   mtl_supply ms, rcv_transactions rt, mtl_units_of_measure muom
      WHERE  ms.req_line_id = p_req_line_id
      AND    ms.supply_type_code = 'RECEIVING'
      AND    ms.rcv_transaction_id = rt.transaction_id
      AND    rt.inspection_status_code =
             decode(i_routing_id, 1, rt.inspection_status_code, 2, 'ACCEPTED')
      AND    muom.unit_of_measure = ms.unit_of_measure;
  
    --valid lines for routing_id 3
    CURSOR cur_dir_valid_lines IS
      SELECT ms.quantity, muom.uom_code
      FROM   mtl_supply ms, mtl_units_of_measure muom
      WHERE  ms.req_line_id = p_req_line_id
      AND    ms.supply_type_code = 'SHIPMENT'
      AND    muom.unit_of_measure = ms.unit_of_measure;
  
  BEGIN
    IF (p_routing_id = '1' OR p_routing_id = '2') THEN    
      --standard or inspect required deliver
      FOR rec_valid_line IN cur_valid_lines(p_routing_id) LOOP
        l_qty := rec_valid_line.quantity;
        --transact quantity according to uom
        IF (rec_valid_line.uom_code <> p_uom_code) THEN
          l_qty := inv_convert.inv_um_convert(p_item_id,
                                              6,
                                              l_qty,
                                              rec_valid_line.uom_code,
                                              p_uom_code,
                                              NULL,
                                              NULL);
        END IF;
        l_valid_quantity := l_valid_quantity + l_qty;
      END LOOP;
    ELSIF (p_routing_id = '3') THEN
      --direct deliver 
      FOR rec_dir_valid_line IN cur_dir_valid_lines LOOP
        l_qty := rec_dir_valid_line.quantity;
        --transact quantity according to uom
        IF (rec_dir_valid_line.uom_code <> p_uom_code) THEN
          l_qty := inv_convert.inv_um_convert(p_item_id,
                                              6,
                                              l_qty,
                                              rec_dir_valid_line.uom_code,
                                              p_uom_code,
                                              NULL,
                                              NULL);
        END IF;
        l_valid_quantity := l_valid_quantity + l_qty;
      END LOOP;
    END IF;
  
    IF (l_valid_quantity >= p_deliver_qty) THEN
      RETURN 'VALID';
    ELSE
      RETURN 'INVALID';
    END IF;
  EXCEPTION
    WHEN OTHERS THEN
      xxdomi_cn_conc_utl.log_msg('check_ir_valid_quantity: ' || SQLERRM);
      RETURN 'INVALID';
  END check_ir_valid_quantity;

以上检查程序综合考虑了不同的三种入库方式,其中p_routing_id有三种值:
1:表示标准入库;
2:表示需要检验的入库;
3:表示直接入库;
                   
以上程序也考虑到了单位的转换,支持入库单位与接收单位不想同的情况下的入库数量校验。
对于带批次或序列号的接收入库         对于带批次或序列号的接收入库,对于1行rcv_transactions_interface记录需要额外插两张MTL的接口表。这里有一个比较诡异的地方,就是接收入库时,插rcv_serials_interface,以及rcv_lots_interface这两张表是没用的,而一定要插 mtl_transaction_lots_interface以及mtl_serial_numbers_interface这两张MTL表,而且对应的要设置
rcv_transactions_interface.use_mtl_lot := 2;
rcv_transactions_interface.use_mtl_serial := 2;
以下程序在11i下验证通过。

  --lot info------------
        IF (rec_line.lot_number IS NOT NULL) THEN
         
          mtl_transaction_lots_interfac.last_update_date       := SYSDATE;
          mtl_transaction_lots_interfac.last_updated_by        := fnd_global.user_id;
          mtl_transaction_lots_interfac.creation_date          := SYSDATE;
          mtl_transaction_lots_interfac.created_by             := fnd_global.user_id;
          mtl_transaction_lots_interfac.last_update_login      := -1;
          mtl_transaction_lots_interfac.product_code           := 'RCV';
          mtl_transaction_lots_interfac.product_transaction_id := rcv_transactions_interface.interface_transaction_id;
          mtl_transaction_lots_interfac.lot_number             := rec_line.lot_number;
          mtl_transaction_lots_interfac.transaction_quantity   := l_iface_rcv_rec.quantity;
          mtl_transaction_lots_interfac.primary_quantity       := l_primary_qty;
        
          SELECT mtl_material_transactions_s.NEXTVAL
          INTO   mtl_transaction_lots_interfac.transaction_interface_id
          FROM   dual;
        
          l_primary_qty :=1;
        
          INSERT INTO mtl_transaction_lots_interface VALUES mtl_transaction_lots_interfac;
        
        END IF;
      
        --serial info
        IF (serial_number IS NOT NULL) THEN
          mtl_serial_numbers_interface.last_update_date       := SYSDATE;
          mtl_serial_numbers_interface.last_updated_by        := fnd_global.user_id;
          mtl_serial_numbers_interface.creation_date          := SYSDATE;
          mtl_serial_numbers_interface.created_by             := fnd_global.user_id;
          mtl_serial_numbers_interface.last_update_login      := -1;
          mtl_serial_numbers_interface.product_code           := 'RCV';
          mtl_serial_numbers_interface.fm_serial_number       :=serial_number;
          mtl_serial_numbers_interface.to_serial_number       :=serial_number;
          mtl_serial_numbers_interface.process_flag           := 1;
          mtl_serial_numbers_interface.product_transaction_id := l_iface_rcv_rec.interface_transaction_id;
        
          SELECT mtl_material_transactions_s.NEXTVAL
          INTO   mtl_serial_numbers_interface.transaction_interface_id
          FROM   dual;
        
          INSERT INTO mtl_serial_numbers_interface VALUES mtl_serial_numbers_interface;

END IF;

Oracle EBS 采购 接收入库 接口开发的更多相关文章

  1. 详解EBS接口开发之库事务处理带提前发运通知(ASN)采购接收入库-补充

     A)   Via ROI Create a ASN [ship,ship]  for a quantity =3 on STANDARD PURCHASE ORDER Create  via R ...

  2. Oracle EBS PO 接收事务处理查不到对应的数据

    1. 有一种情况是采购订单的借记账户不对 不匹配OU 2. 有可能是因为接口表卡住了 PENDING状态的把对应的数据删除掉即可  3. 接收时发生异常那个,丢失了接收头,rcv_shipment_h ...

  3. oracle ebs 采购订单关闭之PL/SQL实现方法

    应客户需求,需要写个脚本,批量关闭Bonus Item类型的采购订单,在metalink上搜索到一些方法,但是都测试不通.原来需要将代码生成一个并发程序.下面是测试成功的代码. 1.首先创建一个存储过 ...

  4. Oracle EBS PO 接受入库

  5. 详解EBS接口开发之库存事务处理采购接收和退货

    (一)接收&退货常用标准表简介 1.1   常用标准表 如下表中列出了与采购接收&退货导入相关的表和说明: 表名 说明 其他信息 RCV_TRANSACTIONS 采购接收事务表 事务 ...

  6. 转:oracle ebs po模块一揽子采购协议小结

    转自:http://yedward.net/?id=193 oracle ebs po模块一揽子采购协议小结 本文总结oracle ebs采购订单(po)模块一揽子采购协议的相关知识,总结如下: 1. ...

  7. Oracle EBS Report 输出字符字段前部"0"被Excel自动去掉问题

    Oracle  EBS 提供多种报表的开发和输出形式,由于MS Excel在处理数据方面的优势明显,报表输出用Excel打开是很常见的开发项. 但是正是由于Excel的"过于智能而不智能&q ...

  8. Oracle EBS json

    JSON:  JavaScript 对象表示法 JavaScript Object Notation JSON 是存储和交换文本信息的语法.类似XML. JSON 比 XML更小.更快,更易解析. 使 ...

  9. ORACLE EBS xml publisher 报表输出字符字段前部"0"被EXCEL自动去掉问题

    http://www.cnblogs.com/lzsu1989/archive/2012/10/17/2728528.html   Oracle  EBS 提供多种报表的开发和输出形式,由于MS Ex ...

随机推荐

  1. Subquery typo with using in(转)

    Subquery typo with using in Do you use the following syntax?   SELECT * FROM TABLE WHERE COLUMN IN ( ...

  2. 单点登录(SSO)解决方案之 CAS客户端与Spring Security集成

    接上篇:单点登录(SSO)解决方案之 CAS服务端数据源设置及页面改造 Spring Security Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制 ...

  3. android显示和隐藏软键盘

    显示键盘: EditText editText.setFocusable(true); editText.setFocusableInTouchMode(true); editText.request ...

  4. River Hopscotch

    River Hopscotch http://poj.org/problem?id=3258 Time Limit: 2000MS   Memory Limit: 65536K Total Submi ...

  5. python之类之多继承

    class A(object): def test_func(self): print("from A") class B(A): pass # def test_func(sel ...

  6. 215. Kth Largest Element in an Array(QuickSort)

    Find the kth largest element in an unsorted array. Note that it is the kth largest element in the so ...

  7. 3.滑雪-深搜&dp

    //Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你.Michael想知道载一个区域中最长底滑 ...

  8. actionView

    类似于actionBar也是在导航栏里用但是他使用的是menu菜单设置菜单项的AsAction=“always|withText” 本例使用LoaderCallbacks<Cursor>接 ...

  9. 批量去重URL地址并剔除打不开网址

    #coding=utf-8 import os import httplib import socket dictlist ={}; def ReadHost(): hosts = []; obn = ...

  10. discuz回贴通知插件实现-插件后台管理配置

    1.登出discuz后台,再次设计插件 2.使用变量