When creating a custom Odoo Sales / Quote report, how do I refer always to the customer company in Qweb code?
In Odoo the data model has Companies and Persons. Persons can be linked to the companies. Let's have an example: my Odoo company is called My Company. We have registered a client The Client Ltd. And a person John Smith has been registered as a representative for The Client Ltd.
Now, to to create a quote for The Client the salespersons of My Company can send the documents to either "The Client Ltd" or "The Client Ltd, John Smith".
In my custom report I'd like to print out the name of the client company. So, I call <span t-field="o.partner_id.name"/>. This works if the quote is issued to the company. But if the quote is issued to "The Client Ltd, John Smith", the output is just "John Smith".
I've tried refering to the client company with <span t-field="o.partner_id.parent_id.name"/> and it works as long as all quotes are issued to persons registered with a company, like "The Company Ltd, John Smith". But... as soon as there is one quote issued to a company without a dedicated person, the <span t-field="o.partner_id.parent_id.name"/> evaluates to "My Company" which is obviously wrong.
Tried also with couple of cases. Trying to sell to:
Individual Person (i.e. without a company)
The Client Ltd (i.e. without a person)
The Client Ltd, John Smith.
Results with
N <span t-field="o.partner_id.name"/>
id <span t-field="o.partner_id"/>
are following:
Individual Person:
N: Individual Person
id: Individual Person
The client Ltd (without person):
N: The Client Ltd
id: The Client Ltd
The client ltd, John Smith:
N: John Smith
id: The Client Ltd, John Smith
How can I ensure that the offers are always issued to clients, not to my own company?
I found an answer. The trick is to see if a parent exists and only then use partner_id.parent_id. If parent_id does not exist, an attempt to refer to it overflows to Odoo company's name.
So the QWeb report requires just one simple if-then-else. Here is the code block:
<t t-if="o.partner_id.parent_id">
<!-- Logic: if a parent connection exists, print parent's name-->
<span t-field="o.partner_id.parent_id"/>
</t>
<t t-else="">
<!-- If parent does not exist, just print the partner_id-->
<span t-field="o.partner_id"/>
</t>
Related
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'"/>
everyone! First of all, thanks in advance.
I've searched everywhere for a solution for my problem. Even when copying from the source code, it doesn't solve it.
I'm doing the Advanced B: ACL and Record Rules tutorial
When completing the sub-chapter on multi-company security, I can't do it.
My user can't get access to my new company
The problems I've encountered so far are:
The new company isn't in company_ids
When changing the Default Company and allowed companies, if:
The company is the new company
The only allowed company is the new company
Then, I get the error: Access Error: Access to unauthorized or invalid companies.
I don't know why my new company is invalid
I'm trying to access another company's records
This is my rule:
<record id="estate_private_companies_properties" model="ir.rule">
<field name="name">Privacy Plan Multi-Company</field>
<field name="model_id" ref="model_estate_property"/>
<field name="global" eval="True"/>
<field name="domain_force">[
('company_id', 'in', company_ids)
]</field>
</record>
EDIT:
Sorry for the confusion. I know the xml is not the problem
The problem is that my company is unauthorized or invalid and it doesn't show up in my company_ids
What the xml does is filter so you can only access the estate properties that are assigned to the currently active company (or companies). So it hasn't any effect on creating a company or making a company accessible.
If this is not the problem, please rephrase your question.
Make sure the user you test with has access to the company.
Login as the admin user
go to settings
Users & companies -> Users
select your user
in the user form you see a new field "Allowed Companies" which is only visible once you added a new company.
Make sure the new company is in this many2many list
login as your user
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>
Whats the correct use of schema for local business address, there doesn't seem to be a facility for town, city and county?? In the example below I would use addressRegion twice but I'm thinking its wrong to use twice?
<div itemscope itemtype="http://schema.org/LocalBusiness">
<span itemprop="name">Company Name</span>
<span itemprop="streetAddress">Some Road Name</span>
<span itemprop="addressLocality">Town</span>
<span itemprop="addressRegion">City</span>
<span itemprop="addressRegion">County</span>
<span itemprop="postalCode">Postcode</span>
</div>
I'm looking into this right now.
The "county" is an obselete bit of UK postal addresses. As one tedious example, an address in Bristol has no county at all, since it's a unitary authority.
addressRegion is given in the example documents as the state, i.e "CA". My suspicion therefore is that it ought to look like
streetAddress = 12 Acacia Avenue, Town
addressLocality = Bristol
addressRegion = England
postalCode = BS1 1AA
...but I wonder where would be a sensible place to ask?
I need to display the following information on PDF account statements in Netsuite;
Customer Name
Accounts Contact (or Primary Contact)
Default Email Address
Phone / Fax Number
From the look of it, I can't edit the actual entry/transaction forms, so I need a way to show these values using the scriptIDs; ${record.customer}* or ${statement.contactprimary}* etc. and including those on the template..
*I know these are incorrect IDs, just using them for examples.
The statement template has access to the customer record, so you can print most of these fields:
<p>
Customer Name: ${customer.companyname} <br/>
Email: ${customer.email} <br/>
Phone: ${customer.phone} <br/>
Fax: ${customer.fax} <br/>
Primary Contact: ${customer.contact} <br/>
</p>