Search code examples
pythonodoo

How to add constrain to One2many field in Odoo?


If have two entities "payment" and "bill". With each payment the user must be able to pay one or more "biils" That is done by adding a One2Many field (of type bill) in the payment model. How can I add a constrain to ensure that a payment should have at least one bill ? (ensure that the One2Many list is not empty). I have tried this code but it is not working cause the user can create one payment without having to add "bill" to the One2Many Bills attibute.

class PaymentCenter(models.Model):
    _name = 'center.payment'
    _description = 'Payment'
    _inherit = ['mail.thread', 'mail.activity.mixin']

    bill_ids = fields.One2many('center.bill',
                                    "payment_id",
                                    string=" Bills", required=True)

    @api.constrains('bill_ids')
    def _constrains_bill_ids(self):
        if not self.bill_ids or len(self.bill_ids)==0:
            raise ValidationError("You must add at least one bill to the payment")

class BillCenter(models.Model):
    _name = 'center.bill'
    _inherits = {'ir.attachment': 'attachment_id'}

    payment_id = fields.Many2one('center.payment', string="Payment")

Solution

  • @constrains will be triggered only if the declared fields in the decorated method are included in the create or write call. It implies that fields not present in a view will not trigger a call during record creation.
    A override of create is necessary to make sure a constraint will always be triggered (e.g. to test the absence of value).

    Try to override create method and check if bill_ids is present:

    @api.model
    def create(self, values):
        if 'bill_ids' not in values:
            values['bill_ids'] = False
        return super(PaymentCenter, self).create(values)  
    

    When You add the code above the constrains should work and If you try to delete bills and save It raises a validation error (it is because of bill_ids is present in values argument of write method. Example of one bill deleted {'bill_ids': [[2, id, False]]}).

    You can also raise the error in the create method.

    You can let users create payments without filling bill_ids and raise a user error when they try to validate a payment without a bill.

    def validate_payment(self):
        for payment in self:
            if not payment.bill_ids:
                raise UserError(_("You must add at least one bill to the payment."))