mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-20 13:12:02 +02:00
17.0 vanilla
This commit is contained in:
parent
2e65bf056a
commit
df627a6bba
328 changed files with 578149 additions and 759311 deletions
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import ast
|
||||
import base64
|
||||
import contextlib
|
||||
import io
|
||||
|
|
@ -23,8 +24,13 @@ class BaseLanguageExport(models.TransientModel):
|
|||
lang = fields.Selection(_get_languages, string='Language', required=True, default=NEW_LANG_KEY)
|
||||
format = fields.Selection([('csv','CSV File'), ('po','PO File'), ('tgz', 'TGZ Archive')],
|
||||
string='File Format', required=True, default='po')
|
||||
export_type = fields.Selection([('module', 'Module'), ('model', 'Model')],
|
||||
string='Export Type', required=True, default='module')
|
||||
modules = fields.Many2many('ir.module.module', 'rel_modules_langexport', 'wiz_id', 'module_id',
|
||||
string='Apps To Export', domain=[('state','=','installed')])
|
||||
model_id = fields.Many2one('ir.model', string='Model to Export', domain=[('transient', '=', False)])
|
||||
model_name = fields.Char(string="Model Name", related="model_id.model")
|
||||
domain = fields.Char(string="Model Domain", default='[]')
|
||||
data = fields.Binary('File', readonly=True, attachment=False)
|
||||
state = fields.Selection([('choose', 'choose'), ('get', 'get')], # choose language or get the file
|
||||
default='choose')
|
||||
|
|
@ -32,15 +38,21 @@ class BaseLanguageExport(models.TransientModel):
|
|||
def act_getfile(self):
|
||||
this = self[0]
|
||||
lang = this.lang if this.lang != NEW_LANG_KEY else False
|
||||
mods = sorted(this.mapped('modules.name')) or ['all']
|
||||
|
||||
with contextlib.closing(io.BytesIO()) as buf:
|
||||
tools.trans_export(lang, mods, buf, this.format, self._cr)
|
||||
if this.export_type == 'model':
|
||||
ids = self.env[this.model_name].search(ast.literal_eval(this.domain)).ids
|
||||
tools.trans_export_records(lang, this.model_name, ids, buf, this.format, self._cr)
|
||||
else:
|
||||
mods = sorted(this.mapped('modules.name')) or ['all']
|
||||
tools.trans_export(lang, mods, buf, this.format, self._cr)
|
||||
out = base64.encodebytes(buf.getvalue())
|
||||
|
||||
filename = 'new'
|
||||
if lang:
|
||||
filename = tools.get_iso_codes(lang)
|
||||
elif this.export_type == 'model':
|
||||
filename = this.model_name.replace('.', '_')
|
||||
elif len(mods) == 1:
|
||||
filename = mods[0]
|
||||
extension = this.format
|
||||
|
|
|
|||
|
|
@ -8,16 +8,20 @@
|
|||
<form string="Export Translations">
|
||||
<field invisible="1" name="state"/>
|
||||
<field name="name" invisible="1"/>
|
||||
<group states="choose" string="Export Settings">
|
||||
<group invisible="state != 'choose'" string="Export Settings">
|
||||
<field name="lang"/>
|
||||
<field name="format"/>
|
||||
<field name="modules" widget="many2many_tags" options="{'no_create': True}"/>
|
||||
<field name="export_type"/>
|
||||
<field name="modules" widget="many2many_tags" options="{'no_create': True}" invisible="export_type == 'model'"/>
|
||||
<field name="model_id" options="{'no_create': True}" invisible="export_type == 'module'" required="export_type == 'model'"/>
|
||||
<field name="model_name" invisible="1"/>
|
||||
<field name="domain" widget="domain" options="{'model': 'model_name'}" invisible="export_type == 'module'"/>
|
||||
</group>
|
||||
<div states="get">
|
||||
<div invisible="state != 'get'">
|
||||
<h2>Export Complete</h2>
|
||||
<p>Here is the exported translation file: <field name="data" readonly="1" filename="name"/></p>
|
||||
<p>This file was generated using the universal <strong>Unicode/UTF-8</strong> file encoding, please be sure to view and edit
|
||||
using the same encoding.</p>
|
||||
using the same encoding.</p>
|
||||
<p>The next step depends on the file format:
|
||||
<ul>
|
||||
<li>CSV format: you may edit it directly with your favorite spreadsheet software,
|
||||
|
|
@ -30,12 +34,12 @@
|
|||
<p>For more details about translating Odoo in your language, please refer to the
|
||||
<a href="https://github.com/odoo/odoo/wiki/Translations" target="_blank">documentation</a>.</p>
|
||||
</div>
|
||||
<footer states="choose">
|
||||
<footer invisible="state != 'choose'">
|
||||
<button name="act_getfile" string="Export" type="object" class="btn-primary" data-hotkey="q"/>
|
||||
<button special="cancel" data-hotkey="z" string="Cancel" type="object" class="btn-secondary"/>
|
||||
<button special="cancel" data-hotkey="x" string="Cancel" type="object" class="btn-secondary"/>
|
||||
</footer>
|
||||
<footer states="get">
|
||||
<button special="cancel" data-hotkey="z" string="Close" type="object" class="btn-primary"/>
|
||||
<footer invisible="state != 'get'">
|
||||
<button special="cancel" data-hotkey="x" string="Close" type="object" class="btn-primary"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
|
@ -43,7 +47,6 @@
|
|||
|
||||
<record id="action_wizard_lang_export" model="ir.actions.act_window">
|
||||
<field name="name">Export Translation</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">base.language.export</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ class BaseLanguageImport(models.TransientModel):
|
|||
_logger.warning('Could not import the file due to a format mismatch or it being malformed.')
|
||||
raise UserError(
|
||||
_('File %r not imported due to format mismatch or a malformed file.'
|
||||
' (Valid formats are .csv, .po, .pot)\n\nTechnical Details:\n%s') % \
|
||||
(base_lang_import.filename, tools.ustr(e))
|
||||
' (Valid formats are .csv, .po)\n\nTechnical Details:\n%s',
|
||||
base_lang_import.filename, tools.ustr(e))
|
||||
)
|
||||
translation_importer.save(overwrite=overwrite)
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@
|
|||
<group>
|
||||
<field name="name" placeholder="e.g. English"/>
|
||||
<field name="code" string="Code" placeholder="e.g. en_US"/>
|
||||
<field name="data" filename="filename"/>
|
||||
<field name="data" filename="filename" options="{'accepted_file_extensions': '.csv,.po'}"/>
|
||||
<field name="filename" invisible="1"/>
|
||||
<field name="overwrite" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="import_lang" string="Import" type="object" class="btn-primary" data-hotkey="q"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="z" />
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="x" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
<record id="action_view_base_import_language" model="ir.actions.act_window">
|
||||
<field name="name">Import Translation</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">base.language.import</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@
|
|||
<field name="overwrite" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="lang_install" string="Add" type="object" class="btn-primary"/>
|
||||
<button special="cancel" data-hotkey="z" string="Cancel" class="btn-secondary"/>
|
||||
<button name="lang_install" string="Add" data-hotkey="q" type="object" class="btn-primary"/>
|
||||
<button special="cancel" data-hotkey="x" string="Cancel" class="btn-secondary"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
|
@ -45,7 +45,6 @@
|
|||
|
||||
<record id="action_view_base_language_install" model="ir.actions.act_window">
|
||||
<field name="name">Add Languages</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">base.language.install</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,11 @@ class BaseModuleUninstall(models.TransientModel):
|
|||
def _compute_module_ids(self):
|
||||
for wizard in self:
|
||||
modules = wizard._get_modules().sorted(lambda m: (not m.application, m.sequence))
|
||||
wizard.module_ids = modules if wizard.show_all else modules.filtered('application')
|
||||
wizard.module_ids = modules if wizard.show_all else wizard._modules_to_display(modules)
|
||||
|
||||
@api.model
|
||||
def _modules_to_display(self, modules):
|
||||
return modules.filtered('application')
|
||||
|
||||
def _get_models(self):
|
||||
""" Return the models (ir.model) to consider for the impact. """
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@
|
|||
</field>
|
||||
<footer>
|
||||
<button string="Uninstall" class="btn-secondary" type="object" name="action_uninstall" data-hotkey="q"/>
|
||||
<button string="Discard" class="btn-primary" special="cancel" data-hotkey="z"/>
|
||||
<button string="Discard" class="btn-primary" special="cancel" data-hotkey="x"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
|||
|
|
@ -8,22 +8,22 @@
|
|||
<field name="arch" type="xml">
|
||||
<form string="Update Module List">
|
||||
<field name="state" invisible="1"/>
|
||||
<separator string="Module Update Result" states="done"/>
|
||||
<group states="init">
|
||||
<separator string="Module Update Result" invisible="state != 'done'"/>
|
||||
<group invisible="state != 'init'">
|
||||
<span class="o_form_label" colspan="2">Click on Update below to start the process...</span>
|
||||
</group>
|
||||
<group states="done" >
|
||||
<group invisible="state != 'done'" >
|
||||
<field name="updated"/>
|
||||
<field name="added" />
|
||||
</group>
|
||||
<footer>
|
||||
<div states="init">
|
||||
<button name="update_module" string="Update" type="object" class="btn-primary"/>
|
||||
<button special="cancel" data-hotkey="z" string="Cancel" class="btn-secondary"/>
|
||||
<div invisible="state != 'init'" class="d-flex gap-1">
|
||||
<button name="update_module" string="Update" type="object" class="btn-primary" data-hotkey="q"/>
|
||||
<button special="cancel" data-hotkey="x" string="Cancel" class="btn-secondary"/>
|
||||
</div>
|
||||
<div states="done">
|
||||
<div invisible="state != 'done'" class="d-flex gap-1">
|
||||
<button name="action_module_open" string="Open Apps" type="object" class="btn-primary" data-hotkey="q"/>
|
||||
<button special="cancel" data-hotkey="z" string="Close" class="btn-secondary"/>
|
||||
<button special="cancel" data-hotkey="x" string="Close" class="btn-secondary"/>
|
||||
</div>
|
||||
</footer>
|
||||
</form>
|
||||
|
|
@ -32,7 +32,6 @@
|
|||
|
||||
<record id="action_view_base_module_update" model="ir.actions.act_window">
|
||||
<field name="name">Module Update</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">base.module.update</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
|
|
|
|||
|
|
@ -28,15 +28,12 @@ class BaseModuleUpgrade(models.TransientModel):
|
|||
if view_type != 'form':
|
||||
return res
|
||||
|
||||
if not(self._context.get('active_model') and self._context.get('active_id')):
|
||||
return res
|
||||
|
||||
if not self.get_module_list():
|
||||
res['arch'] = '''<form string="Upgrade Completed">
|
||||
<separator string="Upgrade Completed" colspan="4"/>
|
||||
<footer>
|
||||
<button name="config" string="Start Configuration" type="object" class="btn-primary" data-hotkey="q"/>
|
||||
<button special="cancel" data-hotkey="z" string="Close" class="btn-secondary"/>
|
||||
<button special="cancel" data-hotkey="x" string="Close" class="btn-secondary"/>
|
||||
</footer>
|
||||
</form>'''
|
||||
|
||||
|
|
@ -60,13 +57,11 @@ class BaseModuleUpgrade(models.TransientModel):
|
|||
FROM ir_module_module m
|
||||
JOIN ir_module_module_dependency d ON (m.id = d.module_id)
|
||||
LEFT JOIN ir_module_module m2 ON (d.name = m2.name)
|
||||
WHERE m.id in %s and (m2.state IS NULL or m2.state IN %s) """
|
||||
self._cr.execute(query, (tuple(mods.ids), ('uninstalled',)))
|
||||
WHERE m.id = any(%s) and (m2.state IS NULL or m2.state = %s) """
|
||||
self._cr.execute(query, (mods.ids, 'uninstalled'))
|
||||
unmet_packages = [row[0] for row in self._cr.fetchall()]
|
||||
if unmet_packages:
|
||||
raise UserError(_('The following modules are not installed or unknown: %s') % ('\n\n' + '\n'.join(unmet_packages)))
|
||||
|
||||
mods.download()
|
||||
raise UserError(_('The following modules are not installed or unknown: %s', '\n\n' + '\n'.join(unmet_packages)))
|
||||
|
||||
# terminate transaction before re-creating cursor below
|
||||
self._cr.commit()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<field name="module_info"/>
|
||||
<footer>
|
||||
<button name="upgrade_module" string="Confirm" type="object" class="btn-primary" data-hotkey="q"/>
|
||||
<button string="Cancel" class="btn-secondary" name="upgrade_module_cancel" type="object" data-hotkey="z"/>
|
||||
<button string="Cancel" class="btn-secondary" name="upgrade_module_cancel" type="object" data-hotkey="x"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
<record id="action_view_base_module_upgrade" model="ir.actions.act_window">
|
||||
<field name="name">Apply Schedule Upgrade</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">base.module.upgrade</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
|
|
@ -42,11 +41,11 @@
|
|||
<field name="priority" eval="20"/>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Apply Schedule Upgrade">
|
||||
<div><span class="o_form_label">The selected modules have been updated / installed !</span></div>
|
||||
<div><span class="o_form_label">The selected modules have been updated/installed!</span></div>
|
||||
<div><span class="o_form_label">We suggest to reload the menu tab to see the new menus (Ctrl+T then Ctrl+R)."</span></div>
|
||||
<footer>
|
||||
<button name="config" string="Start configuration" type="object" class="btn-primary" data-hotkey="q"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="z"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="x"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
|
@ -54,7 +53,6 @@
|
|||
|
||||
<record id="action_view_base_module_upgrade_install" model="ir.actions.act_window">
|
||||
<field name="name">Module Upgrade Install</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">base.module.upgrade</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_base_module_upgrade_install"/>
|
||||
|
|
|
|||
|
|
@ -213,6 +213,27 @@ class MergePartnerAutomatic(models.TransientModel):
|
|||
|
||||
self.env.flush_all()
|
||||
|
||||
# Company-dependent fields
|
||||
try:
|
||||
with self._cr.savepoint():
|
||||
params = {
|
||||
'destination_id': f'res.partner,{dst_partner.id}',
|
||||
'source_ids': tuple(f'res.partner,{src}' for src in src_partners.ids),
|
||||
}
|
||||
self._cr.execute("""
|
||||
UPDATE ir_property AS _ip1
|
||||
SET res_id = %(destination_id)s
|
||||
WHERE res_id IN %(source_ids)s
|
||||
AND NOT EXISTS (
|
||||
SELECT
|
||||
FROM ir_property AS _ip2
|
||||
WHERE _ip2.res_id = %(destination_id)s
|
||||
AND _ip2.fields_id = _ip1.fields_id
|
||||
AND _ip2.company_id IS NOT DISTINCT FROM _ip1.company_id
|
||||
)""", params)
|
||||
except psycopg2.Error:
|
||||
_logger.info(f'Could not move ir.property from partners: {src_partners.ids} to partner: {dst_partner.id}')
|
||||
|
||||
def _get_summable_fields(self):
|
||||
""" Returns the list of fields that should be summed when merging partners
|
||||
"""
|
||||
|
|
@ -243,7 +264,9 @@ class MergePartnerAutomatic(models.TransientModel):
|
|||
if field.type not in ('many2many', 'one2many') and field.compute is None:
|
||||
for item in itertools.chain(src_partners, [dst_partner]):
|
||||
if item[column]:
|
||||
if column in summable_fields and values.get(column):
|
||||
if field.type == 'reference':
|
||||
values[column] = item[column]
|
||||
elif column in summable_fields and values.get(column):
|
||||
values[column] += write_serializer(item[column])
|
||||
else:
|
||||
values[column] = write_serializer(item[column])
|
||||
|
|
@ -294,6 +317,10 @@ class MergePartnerAutomatic(models.TransientModel):
|
|||
if partner_ids & child_ids:
|
||||
raise UserError(_("You cannot merge a contact with one of his parent."))
|
||||
|
||||
# check if the list of partners to merge are linked to more than one user
|
||||
if len(partner_ids.with_context(active_test=False).user_ids) > 1:
|
||||
raise UserError(_("You cannot merge contacts linked to more than one user even if only one is active."))
|
||||
|
||||
if extra_checks and len(set(partner.email for partner in partner_ids)) > 1:
|
||||
raise UserError(_("All contacts must have the same email. Only the Administrator can merge contacts with different emails."))
|
||||
|
||||
|
|
|
|||
|
|
@ -14,22 +14,22 @@
|
|||
<field name='arch' type='xml'>
|
||||
<form string='Automatic Merge Wizard' class="o_partner_merge_wizard">
|
||||
<sheet>
|
||||
<group attrs="{'invisible': [('state', '!=', 'finished')]}">
|
||||
<group invisible="state != 'finished'">
|
||||
<h2 colspan="2">There are no more contacts to merge for this request</h2>
|
||||
<button name="%(action_partner_deduplicate)d" string="Deduplicate the other Contacts" class="oe_highlight" type="action" colspan="2"/>
|
||||
</group>
|
||||
<p class="oe_grey" attrs="{'invisible': [('state', '!=', ('option'))]}">
|
||||
<p class="oe_grey" invisible="state != 'option'">
|
||||
Select the list of fields used to search for
|
||||
duplicated records. If you select several fields,
|
||||
Odoo will propose you to merge only those having
|
||||
all these fields in common. (not one of the fields).
|
||||
</p>
|
||||
<group attrs="{'invisible': ['|', ('state', 'not in', ('selection', 'finished')), ('number_group', '=', 0)]}">
|
||||
<group invisible="state not in ('selection', 'finished') or number_group == 0">
|
||||
<field name="state" invisible="1" />
|
||||
<field name="number_group"/>
|
||||
</group>
|
||||
<group string="Search duplicates based on duplicated data in"
|
||||
attrs="{'invisible': [('state', 'not in', ('option',))]}">
|
||||
invisible="state not in ('option',)">
|
||||
<field name='group_by_email' />
|
||||
<field name='group_by_name' />
|
||||
<field name='group_by_is_company' />
|
||||
|
|
@ -37,17 +37,17 @@
|
|||
<field name='group_by_parent_id' />
|
||||
</group>
|
||||
<group string="Exclude contacts having"
|
||||
attrs="{'invisible': [('state', 'not in', ('option',))]}">
|
||||
invisible="state not in ('option',)">
|
||||
<field name='exclude_contact' />
|
||||
<field name='exclude_journal_item' />
|
||||
</group>
|
||||
<separator string="Options" attrs="{'invisible': [('state', 'not in', ('option',))]}"/>
|
||||
<group attrs="{'invisible': [('state', 'not in', ('option','finished'))]}">
|
||||
<field name='maximum_group' attrs="{'readonly': [('state', 'in', ('finished'))]}"/>
|
||||
<separator string="Options" invisible="state not in ('option',)"/>
|
||||
<group invisible="state not in ('option', 'finished')">
|
||||
<field name='maximum_group' readonly="state == 'finished'"/>
|
||||
</group>
|
||||
<separator string="Merge the following contacts"
|
||||
attrs="{'invisible': [('state', 'in', ('option', 'finished'))]}"/>
|
||||
<group attrs="{'invisible': [('state', 'in', ('option', 'finished'))]}" col="1">
|
||||
invisible="state in ('option', 'finished')"/>
|
||||
<group invisible="state in ('option', 'finished')" col="1">
|
||||
<p class="oe_grey">
|
||||
Selected contacts will be merged together.
|
||||
All documents linked to one of these contacts
|
||||
|
|
@ -57,9 +57,8 @@
|
|||
<group col="2">
|
||||
<field name="dst_partner_id"
|
||||
domain="[('id', 'in', partner_ids or False)]"
|
||||
attrs="{'required': [('state', '=', 'selection')]}"
|
||||
context="{'partner_show_db_id': True}"
|
||||
options="{'always_reload': True}"/>
|
||||
required="state == 'selection'"
|
||||
context="{'partner_show_db_id': True}"/>
|
||||
</group>
|
||||
<field name="partner_ids" nolabel="1">
|
||||
<tree string="Partners">
|
||||
|
|
@ -77,26 +76,26 @@
|
|||
<button name='action_merge' string='Merge Contacts'
|
||||
class='oe_highlight'
|
||||
type='object' data-hotkey="q"
|
||||
attrs="{'invisible': [('state', 'in', ('option', 'finished' ))]}" />
|
||||
invisible="state in ('option', 'finished')" />
|
||||
<button name='action_skip' string='Skip these contacts'
|
||||
type='object'
|
||||
attrs="{'invisible': [('state', '!=', 'selection')]}" />
|
||||
invisible="state != 'selection'" />
|
||||
<button name='action_start_manual_process'
|
||||
string='Merge with Manual Check' data-hotkey="x"
|
||||
string='Merge with Manual Check' data-hotkey="w"
|
||||
type='object' class='oe_highlight'
|
||||
attrs="{'invisible': [('state', '!=', 'option')]}" />
|
||||
invisible="state != 'option'" />
|
||||
<button name='action_start_automatic_process'
|
||||
string='Merge Automatically' data-hotkey="l"
|
||||
type='object' class='oe_highlight'
|
||||
confirm="Are you sure to execute the automatic merge of your contacts ?"
|
||||
attrs="{'invisible': [('state', '!=', 'option')]}" />
|
||||
confirm="Are you sure to execute the automatic merge of your contacts?"
|
||||
invisible="state != 'option'" />
|
||||
<button name='action_update_all_process'
|
||||
string='Merge Automatically all process'
|
||||
type='object' data-hotkey="y"
|
||||
confirm="Are you sure to execute the list of automatic merges of your contacts ?"
|
||||
attrs="{'invisible': [('state', '!=', 'option')]}" />
|
||||
<button special="cancel" data-hotkey="z" string="Cancel" type="object" class="btn btn-secondary oe_inline" attrs="{'invisible': [('state', '=', 'finished')]}"/>
|
||||
<button special="cancel" data-hotkey="z" string="Close" type="object" class="btn btn-secondary oe_inline" attrs="{'invisible': [('state', '!=', 'finished')]}"/>
|
||||
confirm="Are you sure to execute the list of automatic merges of your contacts?"
|
||||
invisible="state != 'option'" />
|
||||
<button special="cancel" data-hotkey="x" string="Cancel" type="object" class="btn btn-secondary oe_inline" invisible="state == 'finished'"/>
|
||||
<button special="cancel" data-hotkey="x" string="Close" type="object" class="btn btn-secondary oe_inline" invisible="state != 'finished'"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue