There is a workaround to the mutating table problem that I normally use.
You can use method Fnd_Event_Action_API.Action_Executeonlinesql and post that as a background job. You can send in any PL/SQL block into that method as a parameter so you can do almost anything in the backgroun job, if the code is not too long.
Below is an example (quickly written and might contain syntax errors etc.) of how to use it for updating CO lines as you wanted. (You write the PL/SQL block you want to run in the stmt_ variable.)
DECLARE
attr_ VARCHAR2(32000);
sql_msg_ VARCHAR2(32000);
stmt_ VARCHAR2(32000);
BEGIN
stmt_ := '
DECLARE
info_ VARCHAR2(32000);
attr_ VARCHAR2(32000);
objid_ VARCHAR2(32000);
objversion_ VARCHAR2(32000);
new_value_ VARCHAR2(5) := ''FALSE'';
BEGIN
SELECT objid, objversion
INTO objid_ , objversion_
FROM customer_order_line
WHERE order_no = ''&NEW:ORDER_NO''
AND line_no = ''&NEW:LINE_NO''
AND rel_no = ''&NEW:REL_NO''
AND line_item_no = &NEW:LINE_ITEM_NO;
Client_SYS.Clear_Attr(attr_);
Client_SYS.Add_To_Attr(''SOME_FIELD_TO_UPDATE'', new_value_, attr_);
Customer_Order_Line_API.Modify__(info_ , objid_ , objversion_ , attr_ , ''DO'');
END;';
sql_msg_ := Message_SYS.Construct('UPDATECOLINE');
Message_SYS.Add_Attribute(sql_msg_, 'SQL', stmt_);
Client_SYS.Clear_Attr(attr_);
Client_SYS.Add_To_Attr('SQL_DATA_', sql_msg_, attr_);
Client_SYS.Add_To_Attr('MSG_', '', attr_);
Transaction_SYS.Deferred_Call(
'Fnd_Event_Action_API.Action_Executeonlinesql',
'PARAMETER',
attr_,
Language_SYS.Translate_Constant('Event', 'NOTRANS: Event - Name of Event - Modify CO line &NEW:ORDER_NO - &NEW:LINE_NO - &NEW:REL_NO - &NEW:LINE_ITEM_NO', NULL, ));
END;