I just did it for v13 replacing Create button with my custom button, you can edit to replace import, export, delete or any button you wish.
You need to extend the ListView XML, create the JS action handler and the python method:
static/src/xml
<?xml version="1.0" encoding="UTF-8"?>
<template xml:space="preserve">
<t t-extend="ListView.buttons">
<t t-jquery="button.o_list_button_add" t-operation="replace">
<button t-if="widget.modelName == 'my.model'" class="btn btn-primary o_list_tender_button_create" type="button">Custom Button</button>
<button t-if="widget.modelName != 'my.model'" class="btn btn-primary o_list_button_add" type="button">Create</button>
</t>
</t>
</template>
static/src/js
odoo.define('invoice.action_button', function (require) {
"use strict";
/**
* Button 'Create' is replaced by Custom Button
**/
var core = require('web.core');
var ListController = require('web.ListController');
ListController.include({
renderButtons: function($node) {
this._super.apply(this, arguments);
if (this.$buttons) {
this.$buttons.find('.o_list_tender_button_create').click(this.proxy('action_def'));
}
},
//--------------------------------------------------------------------------
// Define Handler for new Custom Button
//--------------------------------------------------------------------------
/**
* @private
* @param {MouseEvent} event
*/
action_def: function (e) {
var self = this;
var active_id = this.model.get(this.handle).getContext()['active_ids'];
var model_name = this.model.get(this.handle).getContext()['active_model'];
this._rpc({
model: 'my.model',
method: 'js_python_method',
args: ["", model_name, active_id],
}).then(function (result) {
self.do_action(result);
});
},
});
});
models
def js_python_method(self, model_name, active_id):
""" Method called from JS custom button ""
.... your python logic here! .....