随着制造企业对生产销售的控制越来越高,越来越多的制造型企业增加了对物料的批次、序列号的控制。我们可以在库存职责中的:物料 –> 主组织物料/组织产品 -> 库存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_number
、
p_trolin_tbl
(i).serial_number_start
、
p_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. 更新MMT、MLT、MST表中的相关数据
5. 挑库确认
6. 发运确认
如果你的版本是12.1以后,那么发运过程中的请求将会报错,ERROR_CODE:Serial 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 :=
;
-
-
x_msg_data VARCHAR2(
255
);
-
-
-
-
-- API specific declarations
-
-
l_header_id NUMBER :=
;
-
-
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 :=
;
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
;