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. 疯狂java——第一章 java语言概述与开发环境

    J2ME: 主要用于控制移动设备和信息家电等有限存储的设备. J2SE: 整个java技术的核心和基础,它是J2ME和J2EE编程的基础. J2EE: Java技术中应用最广泛的部分,J2EE提供了企 ...

  2. nodejs 与 json

    nodeJs读取文件(readfile) j json 处理: var fileData = fs.readFileSync(file);if (fileData) { var j = {}; cal ...

  3. HTTP Response Code 中文详解

      引自:https://blog.csdn.net/lplj717/article/details/70053560   1xx - 信息提示这些状态代码表示临时的响应.客户端在收到常规响应之前,应 ...

  4. SpringCloud 简单理解

    0.SpringCloud,微服务架构.包括 服务发现(Eureka),断路器(Hystrix),服务网关(Zuul),客户端负载均衡(Ribbon).服务跟踪(Sleuth).消息总线(Bus).消 ...

  5. ubuntu下安装.deb包的安装方法

    ubuntu16.04的软件中心应该是有bug,安装不了第三方.deb文件,我们只有使用dpkg -i 或者gdebi的方式安装,我使用的是后者,因为后者功能更加强大.要使用gdebi命令先要安装它: ...

  6. 安卓机在按HOME键时,UNITY触发的APPLICATION_PAUSE事件

    安卓机在按HOME键时,UNITY触发的APPLICATION_PAUSE事件 此时安卓程序会返回,在这一瞬间,程序可以通过SOCKET发送数据包给服务器告知, 经测试在这短暂的时间内,这个数据包能发 ...

  7. GridView,datalist添加序号列

    GridView添加序号列:这个是经常需要的一个功能 <asp:TemplateField HeaderText="序号"> <ItemTemplate> ...

  8. 机械硬盘怎么看是否4k对齐

    在XP.VISTA.win7系统下,点击“开始”,“运行”,输入“MSINFO32”,点击“确定”,出现如下显示的界面,依次点击“组件/存储/磁盘”,查看“分区起始偏移”的数值,如果不能被4096整除 ...

  9. apache配置防盗链

    1.确保apache已开启rewrite.   2.在.htaccess文件中添加如下: RewriteEngine On RewriteCond %{HTTP_REFERER} !^http://X ...

  10. 22条常用JavaScript开发小技巧

    1.使用var声明变量 如果给一个没有声明的变量赋值,默认会作为一个全局变量(即使在函数内赋值).要尽量避免不必要的全局变量. 2.行尾使用分号 虽然JavaScript允许省略行尾的分号,但是有时不 ...