| 
32 | 32 |     _get_unique_indexes_with,  | 
33 | 33 |     _validate_table,  | 
34 | 34 |     column_exists,  | 
 | 35 | +    column_nullable,  | 
35 | 36 |     column_type,  | 
36 | 37 |     column_updatable,  | 
37 | 38 |     explode_execute,  | 
@@ -310,6 +311,70 @@ def remove_asset(cr, name):  | 
310 | 311 | # fmt:on  | 
311 | 312 | 
 
  | 
312 | 313 | 
 
  | 
 | 314 | +def remove_action(cr, xml_id=None, action_id=None):  | 
 | 315 | +    assert bool(xml_id) ^ bool(action_id)  | 
 | 316 | + | 
 | 317 | +    action_model = None  | 
 | 318 | +    if action_id:  | 
 | 319 | +        cr.execute("SELECT type FROM ir_actions WHERE id=%s", [action_id])  | 
 | 320 | +        [action_model] = cr.fetchone()  | 
 | 321 | +    else:  | 
 | 322 | +        action_id = ref(cr, xml_id)  | 
 | 323 | +        module, _, name = xml_id.partition(".")  | 
 | 324 | +        cr.execute("SELECT model FROM ir_model_data WHERE module=%s AND name=%s", [module, name])  | 
 | 325 | +        [action_model] = cr.fetchone()  | 
 | 326 | +        if action_model not in [ihn.model for ihn in for_each_inherit(cr, "ir.actions.actions")]:  | 
 | 327 | +            raise ValueError(  | 
 | 328 | +                "%r should point to a model inheriting from 'ir.actions.actions', not a %r" % (xml_id, action_model)  | 
 | 329 | +            )  | 
 | 330 | + | 
 | 331 | +    if version_gte("12.0"):  | 
 | 332 | +        cr.execute(  | 
 | 333 | +            """  | 
 | 334 | +            SELECT name,  | 
 | 335 | +                   model,  | 
 | 336 | +                   on_delete  | 
 | 337 | +              FROM ir_model_fields  | 
 | 338 | +             WHERE relation = 'ir.actions.actions'  | 
 | 339 | +               AND on_delete IN ( 'cascade', 'set null' )  | 
 | 340 | +               AND ttype = 'many2one'  | 
 | 341 | +            """  | 
 | 342 | +        )  | 
 | 343 | + | 
 | 344 | +        for column_name, model, on_delete in cr.fetchall():  | 
 | 345 | +            model_table = table_of_model(cr, model)  | 
 | 346 | +            if on_delete == "cascade":  | 
 | 347 | +                query = format_query(cr, "DELETE FROM {} WHERE {} = %s", model_table, column_name)  | 
 | 348 | +            else:  | 
 | 349 | +                query = format_query(cr, "UPDATE {} SET {} = NULL WHERE {} = %s", model_table, column_name, column_name)  | 
 | 350 | +            cr.execute(query, (action_id,))  | 
 | 351 | +    else:  | 
 | 352 | +        # Before V11, we did not have `on_delete` on `ir_model_fields`.  | 
 | 353 | +        # However, we can infer the intended behaviour by checking if the column containing the deleted action's id is  | 
 | 354 | +        # nullable or not:  | 
 | 355 | +        # if not nullable, the record containing the deleted action should also be deleted (cascade);  | 
 | 356 | +        # else, we just set the column to NULL (set null)  | 
 | 357 | +        cr.execute(  | 
 | 358 | +            """  | 
 | 359 | +            SELECT name,  | 
 | 360 | +                   model  | 
 | 361 | +              FROM ir_model_fields  | 
 | 362 | +             WHERE relation = 'ir.actions.actions'  | 
 | 363 | +               AND ttype = 'many2one'  | 
 | 364 | +            """  | 
 | 365 | +        )  | 
 | 366 | + | 
 | 367 | +        for column_name, model in cr.fetchall():  | 
 | 368 | +            model_table = table_of_model(cr, model)  | 
 | 369 | +            if not column_nullable(cr, model_of_table, column_name):  | 
 | 370 | +                query = format_query(cr, "DELETE FROM {} WHERE {} = %s", model_table, column_name)  | 
 | 371 | +            else:  | 
 | 372 | +                query = format_query(cr, "UPDATE {} SET {} = NULL WHERE {} = %s", model_table, column_name, column_name)  | 
 | 373 | +            cr.execute(query, (action_id,))  | 
 | 374 | + | 
 | 375 | +    remove_records(cr, action_model, [action_id])  | 
 | 376 | + | 
 | 377 | + | 
313 | 378 | def remove_record(cr, name):  | 
314 | 379 |     """  | 
315 | 380 |     Remove a record and its references corresponding to the given :term:`xml_id <external identifier>`.  | 
@@ -352,6 +417,10 @@ def remove_record(cr, name):  | 
352 | 417 |         _logger.log(NEARLYWARN, "Removing group %r", name)  | 
353 | 418 |         return remove_group(cr, group_id=res_id)  | 
354 | 419 | 
 
  | 
 | 420 | +    if model in [ihn.model for ihn in for_each_inherit(cr, "ir.actions.actions")]:  | 
 | 421 | +        _logger.log(NEARLYWARN, "Removing action %r", name)  | 
 | 422 | +        return remove_action(cr, action_id=res_id)  | 
 | 423 | + | 
355 | 424 |     return remove_records(cr, model, [res_id])  | 
356 | 425 | 
 
  | 
357 | 426 | 
 
  | 
 | 
0 commit comments