API实现批次序列号的销售出库

需求简介:

         随着制造企业对生产销售的控制越来越高,越来越多的制造型企业增加了对物料的批次、序列号的控制。我们可以在库存职责中的:物料 –> 主组织物料/组织产品 -> 库存TAB页:来这只某个库存组织中是否启用序列或者是批次。

 

 

         启用物料的批次、序列控制后,该无聊在采购入库、销售出库都需要填写相应有效批次序列。

         Ebs中我们经常会碰到要实现自动化的公司间事务处理,比如自动销售出库,自动的采购入库。本文就将讲解如何实现带序列号、批次控制的销售出库。

         Ebs的界面上手工的进行销售出库,我们是去修改物料搬运单的属性,物料搬运单 -> 处理物料搬运单 -> 处理物料搬运单分配。如果启用序列、批次号控制,“序列/批次”按钮将会亮起来。  


         既然实在处理物料搬运单时修改的批次、序列,我们首先就会想到物料搬运单的API来实现这个功能:(挑库确认之前)可使用的API有:

Ø  INV_MOVE_ORDER_PUB.Process_Move_Order

Ø  INV_MOVE_ORDER_PUB.Process_Move_Order_Line

 

API中的p_trolin_tbl(i).lot_numberp_trolin_tbl(i).serial_number_startp_trolin_tbl(i).serial_number_end是允许设置和修改。但是让人失望的是,虽然修改了但是挑库发运却看不出任何效果,发出去的物料根本就不是我们指定的批次和序列。

这是因为,库存事务处理是根据下面三个表中的数据进行处理的,而不是根据物料搬运单行。

Ø  MTL_MATERIAL_TRANSACTIONS_TEMP (库存事务处理临时表)

Ø  MTL_TRANSACTION_LOTS_TEMP       (批次临时表)

Ø  MTL_SERIAL_NUMBERS_TEMP         (序列号临时表)

然而这三个表ORACLE并没有提供API来修改它们。

那么我们想:既然是临时表,为何不直接UPDATE它们?那么我们就试一试。

处理步骤:

1.         创建销售订单

2.         挑库发放(非自动确认)

3.         使用INV_MOVE_ORDER_PUB.Process_Move_Order_Line修改物料搬运单行

4.         更新MMTMLTMST表中的相关数据

5.         挑库确认

6.         发运确认

 

如果你的版本是12.1以后,那么发运过程中的请求将会报错,ERROR_CODESerial Mssing(序列号控制的情况下出现,批次中不会出现,MetaLink补丁解决这个BUG)。

 

但是不管怎么样,去直接UPDATE一个表总让人觉得不好(而且还有BUG),那么如果办呢?

我们可以使用一个Oracle未公开的API来觉得这个问题:inv_replenish_detail_pub.line_details_pub.


 

解决方案:

         跳出开始的固定思维,既然Oracle没有API(包括未公开的)去修改物料搬运单的序列和批次,为什么我们不直接做出来一个符合我们要求的物料搬运单呢?

         下面给出一个销售订单自动事务处理的API方案:

 

1.  oe_order_pub.process_order            创建销售订单.

2.  wsh_picking_batches_pub.Create_Batch 创建批次号(自动确认:/自动分配:).

3.  wsh_picking_batches_pub.Release_Batch发放销售订单(并发,ONLINE都行).

4.  INV_Trolin_Util.Query_Rows             获取物料搬运单行

5.  INV_MOVE_ORDER_PUB.Process_Move_Order_Line

                                         修改物料搬运单行(头上的发出子库无需修改).

6.  inv_replenish_detail_pub.line_details_pub

                                         创建物料搬运单分配行(注1

7.  inv_pick_wave_pick_confirm_pub.pick_confirm

                                         挑库确认

8.  wsh_deliveries_pub.delivery_action     交货号发运

9.  wsh_ship_confirm_actions.interface_all 发运确认

 

1:如果挑库发放因为业务需求的原因不能这是自动分配:否,或者由于其他原因在此处已经有了物料搬运单行的分配行,那么请使用API

inv_mo_line_detail_util.reduce_allocation_quantity

来删除配分行(当减小数量等于发运的数量时,事务处理行将被删除)。

 

下面给出修改物料搬运单行,重新生成分配的单行代码的调用示例:

 

 

DECLARE -- Common Declarations l_api_version NUMBER := 1.0; l_init_msg_list VARCHAR2(2) := FND_API.G_TRUE; l_return_values VARCHAR2(2) := FND_API.G_FALSE; l_commit VARCHAR2(2) := FND_API.G_FALSE; x_return_status VARCHAR2(2); x_msg_count NUMBER := 0; x_msg_data VARCHAR2(255); -- API specific declarations l_header_id NUMBER := 0; l_trohdr_rec INV_MOVE_ORDER_PUB.TROHDR_REC_TYPE; l_trohdr_val_rec INV_MOVE_ORDER_PUB.TROHDR_VAL_REC_TYPE; l_trolin_tbl INV_MOVE_ORDER_PUB.TROLIN_TBL_TYPE; o_trolin_tbl INV_MOVE_ORDER_PUB.TROLIN_TBL_TYPE; l_trolin_val_tbl INV_MOVE_ORDER_PUB.TROLIN_VAL_TBL_TYPE; x_trolin_tbl INV_MOVE_ORDER_PUB.TROLIN_TBL_TYPE; x_trolin_val_tbl INV_MOVE_ORDER_PUB.TROLIN_VAL_TBL_TYPE; x_trohdr_rec INV_MOVE_ORDER_PUB.TROHDR_REC_TYPE; x_trohdr_val_rec INV_MOVE_ORDER_PUB.TROHDR_VAL_REC_TYPE; -- Cursor to load Move Order Headers l_mold_tbl INV_MO_LINE_DETAIL_UTIL.g_mmtt_tbl_type; x_mold_tbl INV_MO_LINE_DETAIL_UTIL.g_mmtt_tbl_type; l_move_order_type NUMBER := 3; l_msg_return NUMBER; x_number_of_rows NUMBER ; x_detailed_qty NUMBER ; x_revision VARCHAR2(20) ; x_locator_id NUMBER ; x_transfer_to_location NUMBER ; x_lot_number VARCHAR2(80) ; x_expiration_date DATE ; x_transaction_temp_id NUMBER ; p_transaction_header_id NUMBER ; p_transaction_mode NUMBER ; p_move_order_type NUMBER := 3; p_serial_flag VARCHAR2(1) ; p_plan_tasks BOOLEAN ; p_auto_pick_confirm BOOLEAN ; p_commit BOOLEAN ; l_t_header_id NUMBER; l_lot_number VARCHAR2(80) := 'SLT0021'; --mtl_lot_numbers.lot_number; l_serial_number VARCHAR2(20) := '3.06.S0019'; --mtl_serial_numbers.serial_number l_subinvetory_code VARCHAR2(10) := 'BJJF_CLK'; --mtl_secondary_inventories.secondary_inventory_name l_locator_id NUMBER := 42; --mtl_item_locations.inventory_location_id l_trx_header_id NUMBER := 93023; --mtl_txn_request_headers.header_id BEGIN FND_GLOBAL.APPS_INITIALIZE(1371, 50627, 660); -- Suhasini / Mfg Mgr / INV INV_MOVE_ORDER_PUB.Get_Move_Order( P_API_VERSION_NUMBER => l_api_version , P_INIT_MSG_LIST => l_init_msg_list , P_RETURN_VALUES => l_return_values , X_RETURN_STATUS => x_return_status , X_MSG_COUNT => x_msg_count , X_MSG_DATA => x_msg_data , P_HEADER_ID => l_trx_header_id--93023 , P_HEADER => NULL , X_TROHDR_REC => l_trohdr_rec , X_TROHDR_VAL_REC => l_trohdr_val_rec , X_TROLIN_TBL => l_trolin_tbl , X_TROLIN_VAL_TBL => l_trolin_val_tbl ); -- Print the Move Order Header/Lines to be processed IF (x_return_status = FND_API.G_RET_STS_SUCCESS) THEN l_trohdr_rec.operation := INV_GLOBALS.G_OPR_UPDATE; -- FOR i IN 1..l_trolin_tbl.COUNT LOOP l_trolin_tbl(1).from_subinventory_code := l_subinvetory_code; l_trolin_tbl(1).from_locator_id := l_locator_id; l_trolin_tb1(i).lot_number := l_lot_number; l_trolin_tbl(1).serial_number_start := l_serial_number; l_trolin_tbl(1).serial_number_end := l_serial_number; l_trolin_tbl(1).attribute1 := 'update move order test!'; l_trolin_tbl(1).operation := INV_GLOBALS.G_OPR_UPDATE; -- END LOOP; ELSE DBMS_OUTPUT.PUT_LINE('Get_Move_Order error!'); RETURN; END IF; INV_MOVE_ORDER_PUB.Process_Move_Order_Line( p_api_version_number => 1.0 ,p_init_msg_list => l_init_msg_list ,p_return_values => l_return_values ,p_commit => l_commit ,x_return_status => x_return_status ,x_msg_count => x_msg_count ,x_msg_data => x_msg_data ,p_trolin_tbl => l_trolin_tbl ,p_trolin_old_tbl => l_trolin_tbl ,x_trolin_tbl => x_trolin_tbl); IF (x_return_status <> FND_API.G_RET_STS_SUCCESS) THEN FOR i IN 1..x_msg_count LOOP fnd_msg_pub.get(p_msg_index => i ,p_encoded => 'F' ,p_data => x_msg_data ,p_msg_index_out => l_msg_return); DBMS_OUTPUT.PUT_LINE(x_msg_data); END LOOP; RETURN; ELSE DBMS_OUTPUT.PUT_LINE('MODIFY SUCESS!'); END IF; /* inv_mo_line_detail_util.reduce_allocation_quantity( x_return_status => x_return_status ,p_transaction_temp_id => 2242994 ,p_quantity => 1 ,p_secondary_quantity => 1) ; IF x_return_status <> 'S' THEN DBMS_OUTPUT.PUT_LINE('EE'); ELSE DBMS_OUTPUT.PUT_LINE('SS'); END IF;*/ inv_replenish_detail_pub.line_details_pub( p_line_id => l_trolin_tbl(1).line_id , x_number_of_rows => x_number_of_rows , x_detailed_qty => x_detailed_qty , x_return_status => x_return_status , x_msg_count => x_msg_count , x_msg_data => x_msg_data , x_revision => x_revision , x_locator_id => x_locator_id , x_transfer_to_location => x_transfer_to_location , x_lot_number => x_lot_number , x_expiration_date => x_expiration_date , x_transaction_temp_id => x_transaction_temp_id , p_transaction_header_id => NULL , p_transaction_mode => NULL , p_move_order_type => p_move_order_type , p_serial_flag => FND_API.G_FALSE , p_plan_tasks => FALSE , p_auto_pick_confirm => FALSE , p_commit => FALSE); IF (x_return_status <> FND_API.G_RET_STS_SUCCESS) THEN DBMS_OUTPUT.PUT_LINE('E'); FOR i IN 1..x_msg_count LOOP fnd_msg_pub.get(p_msg_index => i ,p_encoded => 'F' ,p_data => x_msg_data ,p_msg_index_out => l_msg_return); DBMS_OUTPUT.PUT_LINE(x_msg_data); END LOOP; RETURN; ELSE DBMS_OUTPUT.PUT_LINE('ssssss'); DBMS_OUTPUT.PUT_LINE('x_number_of_rows: ' || to_char(x_number_of_rows)); DBMS_OUTPUT.PUT_LINE('x_locator_id: ' || to_char(x_locator_id)); DBMS_OUTPUT.PUT_LINE('x_lot_number: ' || to_char(x_lot_number)); DBMS_OUTPUT.PUT_LINE('x_transfer_to_location: ' || to_char(x_transfer_to_location)); DBMS_OUTPUT.PUT_LINE('x_transaction_temp_id: ' || to_char(x_transaction_temp_id)); END IF; l_trolin_tbl := INV_Trolin_Util.Query_Rows(p_line_id => l_trolin_tbl(1).line_id); l_mold_tbl := INV_MO_LINE_DETAIL_UTIL.query_rows(p_line_id => l_trolin_tbl(1).line_id); INV_PICK_WAVE_PICK_CONFIRM_PUB.Pick_Confirm(p_api_version_number => l_api_version, p_init_msg_list => l_init_msg_list, p_commit => l_commit, x_return_status => x_return_status, x_msg_count => x_msg_count, x_msg_data => x_msg_data, p_move_order_type => l_move_order_type, p_transaction_mode => 1, p_trolin_tbl => l_trolin_tbl, p_mold_tbl => l_mold_tbl, x_mmtt_tbl => x_mold_tbl, x_trolin_tbl => x_trolin_tbl); IF (x_return_status <> FND_API.G_RET_STS_SUCCESS) THEN DBMS_OUTPUT.PUT_LINE('E'); FOR i IN 1..x_msg_count LOOP fnd_msg_pub.get(p_msg_index => i ,p_encoded => 'F' ,p_data => x_msg_data ,p_msg_index_out => l_msg_return); DBMS_OUTPUT.PUT_LINE(x_msg_data); END LOOP; RETURN; ELSE DBMS_OUTPUT.PUT_LINE('S'); END IF; -- END LOOP; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE ||':'||SQLERRM); END;

 

其他需求情况

         有可能业务上只需要在发运出库的时候在交货号显示序列号和批次号,但是实际库存控制并没有这个需求,也可以在挑库确认后,通过API来修改交货号的属性来达到显示序列号和批次号的目的:wsh_delivery_details_pub.Update_Shipping_Attributes

但是这个并不能达到实际出库控制的效果

 

下面也给出一个示例:

 

DECLARE

  --这只是一个取数的例子,修改成别的取数逻辑,

  --cux开头的是客户化的表

  CURSOR dev_header_cur(p_item_key  VARCHAR2) IS

  SELECT

      wdd.source_header_id            AS source_header_id

     ,wdd.source_header_number        AS source_header_number

     ,wdd.source_line_id              AS source_line_id

     ,wda.delivery_id                 AS delivery_id

     ,wdd.delivery_detail_id          AS delivery_detail_id

     ,wdd.organization_id             AS organization_id

     ,wdd.source_code                 AS source_code

     ,wdd.requested_quantity          AS requested_quantity

     ,tll.batch_number                AS lot_number

     ,tll.sequence_number             AS serial_number

     ,tll.line_number                 AS line_number

     ,tll.header_id                   AS load_header_id

     ,tll.line_id                     AS load_line_id

     ,tll.send_sec_inv_code           AS send_subinventory

     ,tll.send_inv_location_id        AS send_locator_id

    FROM

      cux_tr_load_doc_wf_headers      cwh

     ,cux_tr_load_doc_wf_lines        cwl

     ,cux_tr_load_doc_lines_all       tll

     ,oe_order_headers_all            ooh

     ,oe_order_lines_all              ool

     ,wsh_delivery_details            wdd

     ,wsh_delivery_assignments        wda

   WHERE

         cwh.l_inner_oe_header_id = ooh.header_id

     AND cwh.header_id = cwl.header_id

     AND cwl.load_doc_line_id = tll.line_id

     AND cwl.l_inner_oe_line_id = ool.line_id

     AND ool.line_id = wdd.source_line_id --bug fix 2010-08-19

     AND wdd.delivery_detail_id = wda.delivery_detail_id

     --

     -- the value of parameter

     --

     AND cwh.item_key = 'STH001';

 

 

  l_index                      NUMBER;

  l_msg_return                 NUMBER;

  x_return_status              VARCHAR2(1);

  x_msg_count                  NUMBER;

  x_msg_data                   VARCHAR2(2000);

  l_source_code                VARCHAR2(40);

  l_serialrangetabtype         wsh_glbl_var_strct_grp.ddserialrangetabtype;

  l_changedattributetabtype    wsh_delivery_details_pub.changedattributetabtype;

BEGIN

  fnd_global.APPS_INITIALIZE( user_id      => -1

                             ,resp_id      => -1

                             ,resp_appl_id => -1);

 

  l_index := 0;

  FOR dev_header_rec IN dev_header_cur('STH001') LOOP

    l_index        := l_index + 1;

    l_source_code  := dev_header_rec.source_code;

    

   

    l_changedattributetabtype(l_index).source_header_id    := dev_header_rec.source_header_id;

    l_changedattributetabtype(l_index).source_line_id      := dev_header_rec.source_line_id;

    l_changedattributetabtype(l_index).delivery_detail_id  := dev_header_rec.delivery_detail_id;

    l_changedattributetabtype(l_index).subinventory        := 'CLK_SD';

    l_changedattributetabtype(l_index).locator_id          := 42;--货位控制

    l_changedattributetabtype(l_index).lot_number          := 'LOT_SK001';--批次

   

    IF dev_header_rec.requested_quantity = 1 THEN

      l_changedattributetabtype(l_index).serial_number := 'LEOCHEN194';

    END IF;

   

    FOR i IN 1..dev_header_rec.requested_quantity LOOP

         

        l_serialrangetabtype(1).delivery_detail_id := dev_header_rec.delivery_detail_id;

        l_serialrangetabtype(1).from_serial_number := 'LEOCHEN194';--LEOCHEN165

        l_serialrangetabtype(1).to_serial_number   := 'LEOCHEN194';

        --v_serialRangeTabType(1).quantity           := 1;  --Dl.ordered_qty;

       

       

        l_serialrangetabtype(2).delivery_detail_id := dev_header_rec.delivery_detail_id;

        l_serialrangetabtype(2).from_serial_number := 'LEOCHEN195';--LEOCHEN165

        l_serialrangetabtype(2).to_serial_number   := 'LEOCHEN195';

       

        --......在此设置多个序列号在一行的情况

    END LOOP;

  END LOOP;

   

  wsh_delivery_details_pub.Update_Shipping_Attributes(p_api_version_number => 1.0,

                                                      p_init_msg_list      => FND_API.G_FALSE,

                                                      p_commit             => FND_API.G_FALSE,

                                                      x_return_status      => x_return_status,

                                                      x_msg_count          => x_msg_count,

                                                      x_msg_data           => x_msg_data,

                                                      p_changed_attributes => l_changedattributetabtype,

                                                      p_source_code        => l_source_code,

                                                      p_container_flag     => NULL ,

                                                      p_serial_range_tab   => l_serialrangetabtype );

     

  IF x_return_status <> fnd_api.G_RET_STS_SUCCESS THEN

    FOR i IN 1..x_msg_count LOOP

      fnd_msg_pub.get(p_msg_index     => i

                     ,p_encoded       => 'F'

                     ,p_data          => x_msg_data

                     ,p_msg_index_out => l_msg_return);

      dbms_output.put_line(x_msg_data);

    END LOOP;

  ELSE

    dbms_output.put_line('S');

  END IF;

END;

你可能感兴趣的:(ORACLE,EBS)