[IMP] *: converting custom list views adding a doAction button
In this commit, we will convert all the customisations of the list view consist in adding an always present button in the control panel that performs an action/calls a model's method. To do this, we will use the new display="always" parameter applicable to buttons in the header of list views. This allows us to define a button that is always visible in a list view. closes odoo/odoo#106519 Taskid: 3082303 Related: odoo/enterprise#34387 Related: odoo/documentation#3044 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import fields, models, _
|
||||
|
||||
|
||||
class Lead(models.Model):
|
||||
@@ -11,3 +11,13 @@ class Lead(models.Model):
|
||||
|
||||
def _merge_get_fields(self):
|
||||
return super(Lead, self)._merge_get_fields() + ['lead_mining_request_id']
|
||||
|
||||
def action_generate_leads(self):
|
||||
return {
|
||||
"name": _("Generate Leads"),
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "crm.iap.lead.mining.request",
|
||||
"target": "new",
|
||||
"views": [[False, "form"]],
|
||||
"context": {"is_modal": True},
|
||||
}
|
||||
|
||||
@@ -1,24 +1,8 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { useGenerateLeadsButton } from "@crm_iap_mine/views/generate_leads_hook";
|
||||
import { crmKanbanView } from "@crm/views/crm_kanban/crm_kanban_view";
|
||||
import { ListController } from "@web/views/list/list_controller";
|
||||
import { listView } from "@web/views/list/list_view";
|
||||
|
||||
export class LeadMiningRequestListController extends ListController {
|
||||
setup() {
|
||||
super.setup();
|
||||
useGenerateLeadsButton();
|
||||
}
|
||||
}
|
||||
|
||||
registry.category("views").add("crm_iap_lead_mining_request_tree", {
|
||||
...listView,
|
||||
Controller: LeadMiningRequestListController,
|
||||
buttonTemplate: "LeadMiningRequestListView.buttons",
|
||||
});
|
||||
|
||||
patch(crmKanbanView.Controller.prototype, "crm_iap_lead_mining_request_kanban", {
|
||||
setup() {
|
||||
|
||||
@@ -7,13 +7,6 @@
|
||||
</button>
|
||||
</t>
|
||||
|
||||
<t t-name="LeadMiningRequestListView.buttons" t-inherit="web.ListView.Buttons" t-inherit-mode="primary" owl="1">
|
||||
<!-- Before the export button -->
|
||||
<xpath expr="//t[contains(@t-if, 'isExportEnable')]" position="before">
|
||||
<t t-call="LeadMiningRequest.generate_leads_button"/>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<t t-name="LeadMiningRequestKanbanView.buttons" t-inherit="web.KanbanView.Buttons" t-inherit-mode="primary" owl="1">
|
||||
<xpath expr="//div[@t-if='props.showButtons']" position="inside">
|
||||
<t t-call="LeadMiningRequest.generate_leads_button"/>
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="inherit_id" ref="crm.crm_case_tree_view_oppor" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//tree" position="attributes">
|
||||
<attribute name="js_class">crm_iap_lead_mining_request_tree</attribute>
|
||||
<xpath expr="//header" position="inside">
|
||||
<button name="action_generate_leads" type="object" class="o_button_generate_leads"
|
||||
string="Generate Leads" groups="sales_team.group_sale_manager" display="always"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -17,8 +18,11 @@
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="inherit_id" ref="crm.crm_case_tree_view_leads" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//tree" position="attributes">
|
||||
<attribute name="js_class">crm_iap_lead_mining_request_tree</attribute>
|
||||
<xpath expr="//tree" position="inside">
|
||||
<header>
|
||||
<button name="action_generate_leads" type="object" class="o_button_generate_leads"
|
||||
string="Generate Leads" groups="sales_team.group_sale_manager" display="always"/>
|
||||
</header>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -22,7 +22,6 @@ export class ExpenseListController extends ListController {
|
||||
this.rpc = useService("rpc");
|
||||
this.user = useService("user");
|
||||
this.isExpenseSheet = this.model.rootParams.resModel === "hr.expense.sheet";
|
||||
this.isExpense = this.model.rootParams.resModel === "hr.expense";
|
||||
|
||||
onWillStart(async () => {
|
||||
this.is_expense_team_approver = await this.user.hasGroup("hr_expense.group_hr_expense_team_approver");
|
||||
@@ -30,10 +29,6 @@ export class ExpenseListController extends ListController {
|
||||
});
|
||||
}
|
||||
|
||||
displayCreateReport() {
|
||||
return this.isExpense;
|
||||
}
|
||||
|
||||
displaySubmit() {
|
||||
const records = this.model.root.selection;
|
||||
return records.length && records.every(record => record.data.state === 'draft') && this.isExpenseSheet;
|
||||
@@ -59,13 +54,6 @@ export class ExpenseListController extends ListController {
|
||||
this.render(true);
|
||||
}
|
||||
|
||||
async onCreateReportClick() {
|
||||
const records = this.model.root.selection;
|
||||
const recordIds = records.map((a) => a.resId);
|
||||
const action = await this.orm.call('hr.expense', 'get_expenses_to_submit', [recordIds]);
|
||||
this.actionService.doAction(action);
|
||||
}
|
||||
|
||||
}
|
||||
patch(ExpenseListController.prototype, 'expense_list_controller_upload', ExpenseDocumentUpload);
|
||||
|
||||
|
||||
@@ -13,13 +13,6 @@
|
||||
<input type="file" name="ufile" class="d-none" t-ref="fileInput" multiple="1" accept="*" t-on-change="onChangeFileInput"/>
|
||||
</xpath>
|
||||
|
||||
<!-- hr.expense -->
|
||||
<xpath expr="//button[hasclass('o_button_upload_expense')]" position="after">
|
||||
<button t-if="displayCreateReport()" class="d-none d-md-block btn btn-secondary" t-on-click="onCreateReportClick">
|
||||
Create Report
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
<!-- hr.expense.sheet -->
|
||||
<xpath expr="//button[hasclass('o_button_upload_expense')]" position="after">
|
||||
<button t-if="displaySubmit()" class="d-none d-md-block btn btn-secondary" t-on-click="() => this.onClick('action_submit_sheet')">
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
<field name="model">hr.expense</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Expenses" multi_edit="1" sample="1" js_class="hr_expense_tree" decoration-info="state == 'draft'">
|
||||
<header>
|
||||
<button name="get_expenses_to_submit" string="Create Report" type="object" display="always"/>
|
||||
</header>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="company_currency_id" invisible="1"/>
|
||||
<field name="attachment_number" invisible="True"/>
|
||||
|
||||
@@ -185,6 +185,12 @@ class MassMailingContact(models.Model):
|
||||
for r in self
|
||||
}
|
||||
|
||||
def action_import(self):
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing.mailing_contact_import_action")
|
||||
action['context'] = self.env.context
|
||||
|
||||
return action
|
||||
|
||||
def action_add_to_mailing_list(self):
|
||||
ctx = dict(self.env.context, default_contact_ids=self.ids)
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing.mailing_contact_to_list_action")
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { ListController } from "@web/views/list/list_controller";
|
||||
import { listView } from '@web/views/list/list_view';
|
||||
import { registry } from '@web/core/registry';
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
/**
|
||||
* List view for the <mailing.contact> model.
|
||||
*
|
||||
* Add an import button to open the wizard <mailing.contact.import>. This wizard
|
||||
* allows the user to import contacts line by line.
|
||||
*/
|
||||
export class MailingContactListController extends ListController {
|
||||
async setup() {
|
||||
super.setup();
|
||||
this.actionService = useService("action");
|
||||
}
|
||||
|
||||
onImport() {
|
||||
const context = this.props.context;
|
||||
const actionParams = { additionalContext: context };
|
||||
if (!context.default_mailing_list_ids && context.active_model === 'mailing.list' && context.active_ids) {
|
||||
actionParams.additionalContext.default_mailing_list_ids = context.active_ids;
|
||||
}
|
||||
this.actionService.doAction('mass_mailing.mailing_contact_import_action', actionParams);
|
||||
}
|
||||
};
|
||||
|
||||
registry.category('views').add('mailing_contact_list', {
|
||||
...listView,
|
||||
Controller: MailingContactListController,
|
||||
buttonTemplate: 'MailingContactListView.buttons',
|
||||
});
|
||||
@@ -1,13 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="mailing_contact_views" xml:space="preserve">
|
||||
<t t-name="MailingContactListView.buttons" t-inherit="web.ListView.Buttons" t-inherit-mode="primary" owl="1">
|
||||
<xpath expr="//t[@t-if='props.showButtons']" position="inside">
|
||||
<button type="button" class="btn btn-secondary o_mass_mailing_import_contact" t-on-click="onImport">
|
||||
Import
|
||||
</button>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<t t-name="MailingContactKanbanView.buttons" t-inherit="web.KanbanView.Buttons" t-inherit-mode="primary" owl="1">
|
||||
<xpath expr="//div[@t-if='props.showButtons']" position="inside">
|
||||
<button type="button" class="btn btn-secondary o_mass_mailing_import_contact" t-on-click="onImport">
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Mailing List Contacts" sample="1" multi_edit="1" js_class="mailing_contact_list">
|
||||
<header>
|
||||
<button name="action_import" string="Import" type="object" display="always"/>
|
||||
<button name="action_add_to_mailing_list" string="Add to List" type="object"/>
|
||||
</header>
|
||||
<field name="create_date" optional="show"/>
|
||||
|
||||
@@ -430,6 +430,24 @@ class StockQuant(models.Model):
|
||||
self._apply_inventory()
|
||||
self.inventory_quantity_set = False
|
||||
|
||||
def action_inventory_at_date(self):
|
||||
# Handler called when the user clicked on the 'Inventory at Date' button.
|
||||
# Opens wizard to display, at choice, the products inventory or a computed
|
||||
# inventory at a given date.
|
||||
context = {}
|
||||
if ("default_product_id" in self.env.context):
|
||||
context.product_id = self.env.context.default_product_id
|
||||
elif ("product_tmpl_id" in self.env.context):
|
||||
context.product_tmpl_id = self.env.context.product_tmpl_id
|
||||
|
||||
return {
|
||||
"res_model": "stock.quantity.history",
|
||||
"views": [[False, "form"]],
|
||||
"target": "new",
|
||||
"type": "ir.actions.act_window",
|
||||
"context": context,
|
||||
}
|
||||
|
||||
def action_inventory_history(self):
|
||||
self.ensure_one()
|
||||
action = {
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
|
||||
<t t-name="InventoryReport.Buttons" t-inherit="web.ListView.Buttons" t-inherit-mode="primary" owl="1">
|
||||
<xpath expr="(//div/*)[last()]" position="after">
|
||||
<button t-if="props.resModel == 'stock.quant' and (!props.context.inventory_mode or props.context.inventory_report_mode) and !props.context.no_at_date"
|
||||
type="button" class="btn btn-primary o_button_at_date me-2" t-on-click="onClickInventoryAtDate">
|
||||
Inventory at Date
|
||||
</button>
|
||||
<button t-if="props.context.inventory_mode and !props.context.inventory_report_mode"
|
||||
class="btn btn-secondary" type="button" t-on-click="onClickApplyAll">
|
||||
Apply All
|
||||
|
||||
@@ -12,29 +12,6 @@ export class InventoryReportListController extends ListController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler called when the user clicked on the 'Valuation at Date' button.
|
||||
* Opens wizard to display, at choice, the products inventory or a computed
|
||||
* inventory at a given date.
|
||||
*/
|
||||
async onClickInventoryAtDate() {
|
||||
const context = {
|
||||
active_model: this.props.resModel,
|
||||
};
|
||||
if (this.props.context.default_product_id) {
|
||||
context.product_id = this.props.context.default_product_id;
|
||||
} else if (this.props.context.product_tmpl_id) {
|
||||
context.product_tmpl_id = this.props.context.product_tmpl_id;
|
||||
}
|
||||
this.actionService.doAction({
|
||||
res_model: "stock.quantity.history",
|
||||
views: [[false, "form"]],
|
||||
target: "new",
|
||||
type: "ir.actions.act_window",
|
||||
context,
|
||||
});
|
||||
}
|
||||
|
||||
get actionMenuItems() {
|
||||
const actionMenus = super.actionMenuItems;
|
||||
if (
|
||||
|
||||
@@ -110,6 +110,12 @@
|
||||
<tree editable="bottom"
|
||||
create="1" edit="1" js_class="inventory_report_list"
|
||||
sample="1">
|
||||
<header>
|
||||
<button name="action_inventory_at_date" string="Inventory at Date" type="object"
|
||||
invisible="((context.get('inventory_mode') and not context.get('inventory_report_mode')) or context.get('no_at_date'))"
|
||||
class="btn-primary ms-1"
|
||||
display="always"/>
|
||||
</header>
|
||||
<field name="create_date" invisible="1"/>
|
||||
<field name="write_date" invisible="1"/>
|
||||
<field name="id" invisible="1"/>
|
||||
@@ -153,6 +159,12 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Inventory Valuation" js_class="inventory_report_list"
|
||||
create="0" delete="0">
|
||||
<header>
|
||||
<button name="action_inventory_at_date" string="Inventory at Date" type="object"
|
||||
invisible="((context.get('inventory_mode') and not context.get('inventory_report_mode')) or context.get('no_at_date'))"
|
||||
class="btn-primary ms-1"
|
||||
display="always"/>
|
||||
</header>
|
||||
<field name="product_id" invisible="context.get('single_product', False)"/>
|
||||
<field name="location_id" invisible="context.get('hide_location', False)"/>
|
||||
<field name="lot_id" groups="stock.group_production_lot"
|
||||
@@ -354,6 +366,10 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree default_order="location_id, inventory_date, product_id, package_id, lot_id, owner_id" decoration-warning='is_outdated' editable="bottom" create="1" edit="1" import="1" js_class="inventory_report_list" class="o_inventory_adjustments" sample="1">
|
||||
<header>
|
||||
<button name="action_inventory_at_date" string="Inventory at Date" type="object"
|
||||
invisible="((context.get('inventory_mode') and not context.get('inventory_report_mode')) or context.get('no_at_date'))"
|
||||
class="btn-primary ms-1"
|
||||
display="always"/>
|
||||
<button name="stock.action_stock_inventory_adjustement_name" groups="stock.group_stock_manager" type="action" string="Apply"/>
|
||||
<button name="action_reset" type="object" string="Clear"/>
|
||||
<button name="stock.action_stock_request_count" groups="stock.group_stock_manager" type="action" string="Request a Count"/>
|
||||
|
||||
@@ -44,7 +44,6 @@ Dashboard / Reports for Warehouse Management includes:
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'stock_account/static/src/js/report_stock_forecasted.js',
|
||||
'stock_account/static/src/xml/inventory_report.xml',
|
||||
],
|
||||
},
|
||||
'license': 'LGPL-3',
|
||||
|
||||
@@ -71,6 +71,24 @@ class StockValuationLayer(models.Model):
|
||||
'res_id': self.id,
|
||||
}
|
||||
|
||||
def action_valuation_at_date(self):
|
||||
# Handler called when the user clicked on the 'Valuation at Date' button.
|
||||
# Opens wizard to display, at choice, the products inventory or a computed
|
||||
# inventory at a given date.
|
||||
context = {}
|
||||
if ("default_product_id" in self.env.context):
|
||||
context.product_id = self.env.context.default_product_id
|
||||
elif ("product_tmpl_id" in self.env.context):
|
||||
context.product_tmpl_id = self.env.context.product_tmpl_id
|
||||
|
||||
return {
|
||||
"res_model": "stock.quantity.history",
|
||||
"views": [[False, "form"]],
|
||||
"target": "new",
|
||||
"type": "ir.actions.act_window",
|
||||
"context": context,
|
||||
}
|
||||
|
||||
def action_open_reference(self):
|
||||
self.ensure_one()
|
||||
if self.stock_move_id:
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-inherit="stock.InventoryReport.Buttons" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//button[hasclass('o_button_at_date')]" position="after">
|
||||
<button t-if="props.resModel == 'stock.valuation.layer'" type="button"
|
||||
class="btn btn-primary o_button_at_date me-2" t-on-click="onClickInventoryAtDate">
|
||||
Valuation at Date
|
||||
</button>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -44,6 +44,12 @@
|
||||
<tree default_order="id desc" create="0"
|
||||
import="0" js_class="inventory_report_list"
|
||||
action="action_open_reference" type="object">
|
||||
<header>
|
||||
<button name="action_valuation_at_date" string="Valuation at Date" type="object"
|
||||
invisible="((context.get('inventory_mode') and not context.get('inventory_report_mode')) or context.get('no_at_date'))"
|
||||
class="btn-primary ms-1"
|
||||
display="always"/>
|
||||
</header>
|
||||
<field name="create_date" string="Date" />
|
||||
<field name="reference"/>
|
||||
<button name="action_open_layer" icon="fa-arrow-right" title="Open Valuation Layer" type="object"/>
|
||||
|
||||
Reference in New Issue
Block a user