RecursionError: maximum recursion depth exceeded odoo 12 - odoo

While editing notes and saving, I got error as:
self[a[0]] = a[1]
RecursionError: maximum recursion depth exceeded
Code:
notes = fields.Text('Terms', compute="_default_terms",inverse="_inverse_terms")
#api.multi
def _default_terms(self):
terms = "\n\nPayment Terms:\nThis LPO is on the goods / services named and is governed by the below mentioned Terms and Conditions\n\
LPO is a must in order to start with the Job \n\
If the supplier fails to deliver on time and thereby resulting on the rejection or cancellation of job, MAI BLUE has the right to cancel the job and has no financial obligation towards the supplier, and if MN BLUE has already paid any down payment; the supplier must return it to the company immediately.\n\
MAI BLUE can demand an amendment / modification due to sub-standard / low quality products / services provided, at supplier's cost. if the supplier is unable to rectify the product before final delivery MAI BLUE has the right not to pay the balance amount and demand back any advance payments made for the job.\n\
Delays in delivery and/or delivering the goods not as per agreed quality / specifications will lead to job cancellation and/or 50% reduction in payment from MAI BLUE towards the supplier.\n\
Once the supplier submits their final Quotation, no changes in materials or specification will be accepted after the supplier confirms or submit his quotation. Any changes will be under the supplier's responsibility/cost.\n\
"
self.notes = terms
#api.multi
def _inverse_terms(self):
terms = "\n\nPayment Terms:\nThis LPO is on the goods / services named and is governed by the below mentioned Terms and Conditions\n\
LPO is a must in order to start with the Job \n\
If the supplier fails to deliver on time and thereby resulting on the rejection or cancellation of job, MAI BLUE has the right to cancel the job and has no financial obligation towards the supplier, and if MN BLUE has already paid any down payment; the supplier must return it to the company immediately.\n\
MAI BLUE can demand an amendment / modification due to sub-standard / low quality products / services provided, at supplier's cost. if the supplier is unable to rectify the product before final delivery MAI BLUE has the right not to pay the balance amount and demand back any advance payments made for the job.\n\
Delays in delivery and/or delivering the goods not as per agreed quality / specifications will lead to job cancellation and/or 50% reduction in payment from MAI BLUE towards the supplier.\n\
Once the supplier submits their final Quotation, no changes in materials or specification will be accepted after the supplier confirms or submit his quotation. Any changes will be under the supplier's responsibility/cost.\n\
"
self.notes= terms

If you want to use an inverse method to change values of computed fields, the computed field should be computed on persistent and changeable values.
Otherwise, you get the effect/error you're seeing right now: Your field is computed on a string in code. So the inverse method should change the value on that string, which isn't possible (or very, very difficult).
Just define a default value or default method, like in Kenly's answer:
#api.model
def _default_terms(self):
return "terms..."
notes = fields.Text('Terms', default=_default_terms)
Another good way would be defining a system parameter or configurable field on the company, which could be configured on a settings page, like a lot of other things in Odoo.
You can then get that value as default from that parameter or company field as default. That way, the default is changeable in Odoo itself.

The inverse function is used for reversing the computation and setting the relevant fields. In your example above, it does not reverse the computation but set the value of the field itself, which is not possible using the inverse function.
There are plenty of examples in Odoo source code, for example in project model, is_favorite field is set to True when the current user is in project favorite_user_ids and to inverse the calculation we check if the current user is a favorite user and remove it from the set and if not add it to the project favorite users.
What you are trying to achieve is the same as setting a default value for the text field.
Default values are defined as parameters on fields, either a value:
notes = fields.Text('Terms', default="Payment Terms:...")
Or a function called to compute the default value, which should return that value:
def compute_default_value(self):
return self.get_value()
notes = fields.Text('Terms', default=compute_default_value)
The best way to do this is by defining a system parameter and a field in the company that can be edited later by users.
The following code adds a parameter to control the use of the default payment terms in the sale order, and a related field to define the current user company default payment terms.
Python Code:
class ResCompany(models.Model):
_inherit = 'res.company'
payment_terms = fields.Text(string='Payment Terms', translate=True)
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
payment_terms = fields.Text(related='company_id.payment_terms', string="Terms", readonly=False)
use_payment_terms = fields.Boolean(
string='Payment Terms',
config_parameter='sale.use_payment_terms')
class SaleOrder(models.Model):
_inherit = 'sale.order'
#api.model
def compute_default_value(self):
return self.env['ir.config_parameter'].sudo().get_param('sale.use_payment_terms') and self.env.user.company_id.payment_terms or ''
notes = fields.Text('Terms', default=compute_default_value)
XML: (Add the payment terms inside the Quotations & Orders configuration)
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.sale</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[#id='sale_config_online_confirmation_pay']/parent::div" position="inside">
<div class="col-6 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="use_payment_terms"/>
</div>
<div class="o_setting_right_pane">
<label for="use_payment_terms"/>
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific." aria-label="Values set here are company-specific."
groups="base.group_multi_company" role="img"/>
<div class="text-muted">
Show payment terms on orders
</div>
<div class="content-group" attrs="{'invisible': [('use_payment_terms','=',False)]}">
<div class="mt16">
<field name="payment_terms" placeholder="Insert your terms here..."/>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>

Related

Show customer address from S.O. on delivery slip

In Odoo v15 I am trying to display customer address from Sales Order on delivery slip (report - printed document).
Customer and delivery address are not the same on sales order and on report when I print delivery slip by default it is showing me the same information for contact and delivery address. I tested this on runbot in v16 also - it is strange.
To solve this I inherited 'stock.report_delivery_document' and tried to replace div with name 'partner_header' inside of which should customer address display with this code:
<xpath expr="//div[#name='partner_header']" position="replace">
<span class="pt-5" t-esc="o.partner_id.name"/><br />
</xpath>
The code is giving me info from delivery address, not from Customer that is on Sales Order.
Please help - sorry if creating module is not the way to do it.
I found solution - in inherited "stock.report_delivery_document" I accessed customer address from related Sales Order using o.sale_id.partner_id
Here is the code - used contact widget to format it:
<div t-field="o.sale_id.partner_id" t-options-widget="'contact'"/>

How to domain/filter PO based on user allowed branch in odoo

I want to display PO based on allowed branch in odoo. So if you log in as user Ana with allowed branch = Singapore, then the PO that appear are only PO with the Singapore branch. Maybe someone knows and can give advice?
Thanks
Best Regards
you can use the domain field on a model to filter records based on certain criteria. This can be useful for restricting which records are visible to a user based on their allowed.
Another alterative to make a custom domain (maybe dificult because you need to access the current user)
I think you can define a new security rule, to discard PO with no match in the branch. but not sure if you can do user.branch_id or some other field distinct od id.
<record id="branch_po_rule" model="ir.rule">
<field name="name">PO own branch</field>
<field name="model_id" ref="model_purchase_order"/>
<field eval="True" name="global"/>
<field name="domain_force">('branch_id','=',user.branch_id))]</field>
</record>
But security rules are meant to do this kind of discriminations

Selection Filtering By Another Selection

I'm looking for a way to filter subsequent selections depending on which ones of them are filled.
I want to have a tree-like structure for setting locations as such:
=>Country
==>State
===> District
So say you enter the district first, you'd only have one choice in the other two.
Say you enter the country first, you'd have a more limited selection of the latter two.
I know i should be going with many2one fields but all i can get working at this point is multiple selections that turn visible/invisible which is highly inefficient.
I'm fairly new to Odoo and am finding the docs somewhat lacking. Any help would be appreciated!
I would play around with many2one fields and domains using the operator =?
For example:
class MyModel(models.Model):
_name = "my.model"
country_id = fields.Many2one(comodel_name="res.country")
state_id = fields.Many2one(comodel_name="res.country.state")
disctrict_id = fields.Many2one(comodel_name="res.country.state.district")
I don't know if the model names are correct, but you should already know them.
And in the view use the domain with the mentioned operator:
<field name="country_id"
domain="[('state_ids', '=?', state_id),('state_ids.district_ids', '=?', district_id)]" />
<field name="state_id"
domain="[('country_id', '=?', country_id),('district_ids', '=?', district_id)]" />
<field name="disctrict_id"
domain="[('state_id', '=?', state_id),('state_ids.country_id', '=?', country_id)]" />
I didn't make any tests on those AND conditions/domains. And i don't know the structure of those 3 models. My assumption is, that there is a one2many or many2many relation on res.country -> res.country.state and res.country.state -> res.country.state.district.

How to allow only create option in many2one field

How can I hide all the items from the many2one field. I want to allow the user only to create a new payment term.
We have this code in account module in account_invoice.py:
class AccountInvoice(models.Model):
_name = "account.invoice"
payment_term_id = fields.Many2one('account.payment.term',
string='Payment Terms',
oldname='payment_term',
readonly=True,
states={'draft': [('readonly', False)]})
In account_invoice_view.xml we have:
<field name="payment_term_id"
options="{'no_create': True}"
attrs="{'invisible': [('payment_term_id','=',False)]}"/>
I tried this code {'no_open':True} but it didn't work.
If you want to hide all the elements on the list try adding a domain that is always False:
<field name="id" />
<field name="payment_term_id"
domain="[('id', '=', -1)]"
attrs="{'invisible': [('payment_term_id','=',False)]}"/>
With options="{'no_create': True}" you get the opposite of what you want if I understood well
I think you going to find this solution good for you:
what is suggest is add a field to your model this field is many2many field
contains the list of payment_term_id that are created in the current record.
# this field is for technical purpose
create_payment_terms_ids = fields.Many2many(co_model='account.payment.term',
relation='created_payment_rel',
column1= 'invoice_id',
column2= 'paymen_id',
string='Created payment terms')
After this use onchange method to keep track of created payments and add the new
Payment terms to this field
#api.onchange('payment_term_id')
def onchange_payment_terms(self):
""" keep track of created payment from the current
invoice and show only them in drop down list."""
if self.payment_term_id:
# keep list of old ids here
payment_ids = self.create_payment_terms_ids and self.create_payment_terms_ids.ids or []
# check if list payment is empty this means that this is the first created payment
# second check if the selected payment is a new payment that is created
# if one of the two is true add the selected value to the list of payment
if not self.create_payment_terms_ids or self.payment_term_id.id not in payment_ids:
payment_ids.append(self.payment_term_id.id)
self.create_payment_terms_ids = [(4, self.payment_term_id.id)]
# if list is not empty we change the domain to show only payment that exist in the list
# else we use a special domain to show empty set.
domain = payment_ids and [('payment_term_id', 'in', payment_ids)] or [('id', '=', -1)]
return {'domain': {'payment_term_id': domain}}
In your view add the new field with visible = "1" you don't the user to see it's value.
you need to put it in the view because you need it's value in onchange event if you don't put it your many2many field will always be empty.
<field name="create_payment_terms_ids" invisible="1"/>
and remove options="{'no_create':False}" this will remove the create and edit option in the drop down and you don't want that.
Note: when you are in development remove invisible="1" to see if the many2many field contains the list of payment that you have
created.

Odoo 10 - Print address labels for partners and contacts

I would like to know if there is something "out of the box" in Odoo 10 community to print labels (shipping labels).
I see a "Label" field in res.partner view, but not sure if it is related or not.
Thanks,
There is no relation of Label tag with partner address and contact labels. Label tag is used to give string for particular field.
<label for="type" string="Type"/>
<div name="div_type">
<field name="type" class="oe_inline"/>
</div>