0
votes

I'm working on a table that's inside a form. That table is filled automatically with the input of the first field that the user has to do. In the table there is only one column that the user has to enter data in and I made it possible for them to directly enter datas in the table instead of opening a new form. Those datas need to be checked using values hidden in the table, I just have to check if the value entered by the user respect some tolerances.

Now my problem is, for checking the values I need to know the ids of the values to be able to check with the rights tolerances but I can't get them if the user doesn't save the form.

So I have two questions : Is it possible to call a function just after saving ? I know I can override the "create" function but that's not what I want, I want a function to be called right after saving, thanks to that I would be able to know the ids and it would be possible to check the values. If that's not possible, my second question is : Is it possible to get the row number when the user enters its value in ? It would be useful to get the row number with api.onchange so I could simply loop into the rows until I find the right one and then use the datas that are there.

Thanks for your time

EDIT1 : Here the code I made to loop through the datas :

class ControlMeasure(models.Model):
    """
        Modèle pour la mesure d'une pièce
    """

# Nom de la table
_name = 'spc.control.measure' 

# Champs de la table
name                = fields.Many2one(string='Name', related='control_point_id.measure_type_id', readonly=True)
value               = fields.Float(string='Measured value')
validity            = fields.Boolean(string='Validity', readonly=True)
nominal_value       = fields.Float(string='Nominal value', related='control_point_id.nominal_value', readonly=True)

unit_id             = fields.Many2one('product.uom', string='Unit', related='control_point_id.product_uom_id', readonly=True)            
control_part_id     = fields.Many2one('spc.control.part', string='Controled piece')
control_point_id    = fields.Many2one('spc.control.point', string='Control point')

# Champs visuels
control_device_id   = fields.Many2one('spc.control.device', string='Used device', related='control_point_id.control_device_id')

# Vérifie si la valeur mesurée est dans la tolérance
@api.multi
@api.onchange('value')
def is_valid(self):   

   # raise Exception('Appel réussi')

    sql= """
            SELECT p.nominal_value, p.inferior_tolerance, p.superior_tolerance FROM spc_control_point p
            WHERE p.control_plan_id = %(ctrlid)s
         """

    if self.control_part_id.control_plan_id == False:
        raise Exception('False ' + str(self.control_part_id.control_plan_id))
    else:
        self.env.cr.execute(sql, {'ctrlid' : self.control_part_id.control_plan_id.id})
        row = self.env.cr.fetchall()

        for i in range(0, len(row)):
            #raise Exception(str(self.value) + ' < ' +  str(row[i][0]) + ' - ' +  str(abs(row[i][1])) +  ' or ' +  str(self.value) + ' > ' +  str(row[i][0]) + ' + ' +  str(abs(row[i][2])))
            if self.value < row[i][0] - abs(row[i][1]) or self.value > row[i][0] + abs(row[i][2]):
                self.validity = False 
            else:
                self.validity = True 

Here the whole code works how I want except the loop that I don't know how to make it properly, I checked if the datas which are tested are the right ones and they are, the test itself works as I want, but as you see everytime I check all the rows and the end, only the last one count. That's why I came with the idea to get the row number where the user clicked but I don't know if it's possible to do it or even call a function after saving which would be easy because I would know the ids and I could check the datas after saving.

1

1 Answers

0
votes

Two possible solutions come up on my mind:

  1. As you mentioned, you can create a method with @api.onchange decorator, to check the entered data each time user passed it in, without actually saving it or any other manipulations.

    @api.multi
    @api.onchange('your_field', 'your_field2')
    def _onchange_your_fields(self):
        for record in self:
            if record.your_field != record.validation_field:
                raise exceptions.ValidationError(_('Sorry, pal, try again'))
    

The good thing about this, is that it will validate the field's value rapidly: after the user will put a cursor on the next field. Although, it can be irritating from a user's perspective.

  1. You can try @api.constrains decorator. It behaves slightly different from the @api.onchange decorator, but it's a common practice to use it for a validation.

    @api.multi
    @api.constrains('your_field', 'your_field2')
    def _check_your_fields(self):
        for record in self:
            if record.your_field != record.validation_field:
                raise exceptions.ValidationError(_('Sorry, pal, try again'))
    

The difference is that it will be called after you'll hit "Save" button, but before actually executing create or write functions.

Hope, that will help.

UPDATE 1:

# Vérifie si la valeur mesurée est dans la tolérance
@api.multi
@api.onchange('value')
def validate(self):
    # Because we are using @api.multi decorator, our 'self' is actually a recordset.
    # That means, that we cannot treat it as a regular record, so "self.control_part_id...." would be an odd code
    for record in self:
        # So now we took a one record from recordset, and now we can continue on validating it.
        if not record.control_part_id.control_plan_id:
            raise exceptions.UserError(_('False %s' % str(record.control_part_id.control_plan_id)))

        # You executed here a SQL query, which is actually takes our records from database, not from cache.
        # But we haven't write(or create) our changes yet, and that makes no sense.

        # What we wanna do, is to take values from cache and validate them. You can simply do it just via model's field
        validation_record = record.control_point_id

        if record.value < validation_row.nominal_value - validation_row.inferior_tolerance \
            or record.value > validation_row.nominal_value + validation_row.superior_tolerance:
                record.validity = True

        else:
            # You can get rid of this line by simply defining the default value of record.validity as "False"
            record.validity = False


# I recommend you to check the final validation this way
@api.multi
@api.constrains('validity')
def check_valid(self):
    for record in self:
        if not record.validity:
            raise exceptions.ValidationError(_("Cannot pass a validation rest"))