14

I have a transient model that serves as a dialog. In my form view I have a button like this:

<footer states="partnerId">
   <button name="check_tax_id" string="Tovább" type="object"/>
</footer>

The button invokes this function (I can confirm it actually invokes):

@api.one
    def check_tax_id(self, context=None):
        self.state = "partnerDetails"

        return None;

My problem is that the dialog window is closed immediately once I click this button! What am I doing wrong?

ChesuCR
  • 9,352
  • 5
  • 51
  • 114
Aron Lorincz
  • 1,677
  • 3
  • 19
  • 29

7 Answers7

35

Solution 0

@api.multi
def check_tax_id(self):
    self.ensure_one()
    self.name = "New name"
    return {
        "type": "ir.actions.do_nothing",
    }

This solution was provided here by Tadeusz Karpinski.

Solution 1

You can return a new form with the same record id.

@api.multi
def check_tax_id(self):
    self.ensure_one()
    self.name = "New name"
    return {
        'context': self.env.context,
        'view_type': 'form',
        'view_mode': 'form',
        'res_model': 'model_name',
        'res_id': self.id,
        'view_id': False,
        'type': 'ir.actions.act_window',
        'target': 'new',
    }

Solution 2

You can create a widget in jQuery. This will open the wizard and you can assign the behaviour you want to the buttons manually. You can use the call function to call python functions as well:

[...]

new instance.web.Dialog(this, { 
    title: _t("Title"), 
    width: '95%', 
    buttons: [
          { text: _t("First button"), click: function() { self.first_button(); }}, 
          { text: _t("Second button"), click: function() { self.second_button(); }},
          { text: _t("Close"), click: function() {  dialog.close(); }},                       
      ],
});

[...]

Solution 3

Of course you can override the create method as well to avoid the creation of the record in some cases

Solution 4

One last option. Create a workflow with a state field. Create workflow buttons in order to send signals to change the state. You can show or hide the rest of the fields using the attrs attribute and the state field. But I do not know if that would adapt to your needs.

ChesuCR
  • 9,352
  • 5
  • 51
  • 114
  • Currently your solution seems the only one that works. – Aron Lorincz Aug 17 '15 at 12:58
  • Record is already created on wizard launch, it won't create again if you pass id of current wizard but will be created if you pass False in res_id. – StackUP Feb 29 '16 at 00:55
  • It looks like this solution requires you to accept the fact window is hiding and showing again. Looks like this should be the accepted answer: http://stackoverflow.com/a/42421758/548696 – Tadeck Feb 23 '17 at 17:03
  • @Tadeck Yes, you are right, that should be the right answer. I didn't know about it. Should I remove my answer? I am going to add it to my solutions for now – ChesuCR May 13 '17 at 10:16
  • `"type": "set_scrollTop",` (answer by @user3778322) is better than `"type": "ir.actions.do_nothing"`, as this doesn't throw an exception – Ardee Aram Jan 03 '20 at 01:55
  • that works perfectly with solution 0, thank you so much ChesuCR – Thai Laoquoc Sep 24 '20 at 03:14
9

In my case this code works.

@api.multi
def test(self):
    l = logging.getLogger()
    l.warn("xD")
    return {
        "type": "ir.actions.do_nothing",
    }
Tadeck
  • 132,510
  • 28
  • 152
  • 198
6

The simplest this to do is :

@api.multi
def null_action(self):
    return {
        "type": "set_scrollTop",
    }

As the type is used to call any method on the class ActionManager (javascript)

It's better than "type": "ir.actions.do_nothing" which generate an exception (this attribute doesn't exist)

user3778322
  • 61
  • 1
  • 3
3

yesterday I bumped on this same issue. I needed to show a button to do something without submitting the whole wizaard. I worked around it by not using a button at all. It's pretty simple and effective. What you need:

  1. a boolean flag in your wizard model
  2. an onchange attached to the flag (that replaces you sumbmit function)
  3. replace the button in the view w/ the flag w/ invisible="1" and a label to be styled as a button

Here's the code:

source_it = fields.Boolean(string='Source')
[...]
def action_source(self):
    # do stuff

@api.onchange('source_it')
def onchange_source_it(self):
    if self.env.context.get('sourcing_now') or not self.source_it:
        return
    self.action_source()
[...]
<label for="source_it" class="pull-left btn btn-success" />
<field name="source_it" invisible="1" />

The trick works because when a label has for attribute is going to act like the checkbox itself, so if you click on the label you are actually switching the checkbox.

simahawk
  • 2,421
  • 1
  • 16
  • 22
  • One benefit of this solution is the update in the ui of changed values which does not happen with the other solutions provided. – OlafW Mar 09 '22 at 13:37
1

What you can do is have the button open another wizard passing context with all the values entered into the first wizard. This allows you to execute some function ie. your button. And maintain the state of your wizard. So the default value for fields in your wizard must check context first and fallback to something else.

Here is an example:

class MyWizard(models.TransientModel):
    _name = 'myaddon.mywizard'

    def _get_default_char(self):
        return self._context.get('mychar',"")

    mychar = fields.Char(string="My Char", default=_get_default_char)

    @api.multi
    def my_button(self):
        # Execute Function Here
        # reload wizard with context

        return {
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'myaddon.mywizard',
            'type': 'ir.actions.act_window',
            'target': 'new',
            'context': '{"mychar":'HELLO WORLD'}',
        }
Phillip Stack
  • 3,308
  • 1
  • 15
  • 24
0

on odoo 7

def traszero(self ,cr ,uid ,ids ,context=None):
        data_obj = self.pool.get('stock.return.picking.line')
        ret_wizard = self.browse(cr, uid, ids, context=context)
        if ret_wizard.product_return_moves:
            line_ids = ret_wizard.product_return_moves.mapped('id')
            data_obj.write(cr, uid, line_ids, {'quantity': 0}, context=context)
        return {'name':"Return Shipment",
            'res_model':"stock.return.picking",
            'src_model':"stock.picking",
            'view_mode':"form",
            'target':"new",
            'key2':"client_action_multi",
            'multi':"True",
            'res_id':ids[0],
            'type': 'ir.actions.act_window',
            }
Hamza Salem
  • 11
  • 1
  • 3
0

In Odoo16

def _action_open_modal(self):
    """Allow to keep the wizard modal open after executing the action."""
    return {
        'name': _('Portal Access Management'),
        'type': 'ir.actions.act_window',
        'res_model': 'portal.wizard',
        'view_type': 'form',
        'view_mode': 'form',
        'res_id': self.id,
        'target': 'new',
    }

addons/portal/wizard/portal_wizard.py

HERAwais
  • 357
  • 5
  • 21