Odoo 8: Nested form view of Many2one field - odoo-8

I am in the process of refactoring a model (let's call it Parent), which stores a number of Many2one fields that reference records of the same model (Child). The Parent form view contains a notebook and each page displays details for a specific Child, currently described within the page in a repetitive fashion and with lots of related fields. I'm trying to avoid the repetition in the view, and get rid of the need for the related fields.
class Child(models.Model):
_name = "child"
# ...
class Parent(models.Model):
_name = "parent"
child_1 = fields.Many2one('child', 'Child 1')
child_1_age = fields.Float(related='child_1.age', string='Child Age')
# ...
child_2 = fields.Many2one('child', 'Child 2')
child_2_age = fields.Float(related='child_2.age', string='Child Age')
# ...
<!-- ... -->
<field name="model">parent</field>
<field name="type">form</field>
<field name="arch" type="xml">
<notebook colspan="8" col="8">
<page>
<!-- buttons, a bunch of related Child fields, etc -->
</page>
<page>
<!-- buttons, a bunch of related Child fields, etc -->
</page>
</notebook>
</field>
I have a special form view defined for the Child, but I don't know how to insert it in the notebook pages of the Parent view. Since it is possible to insert tree views in forms (like for One2many fields for example), I guess there has to be a way to do it with forms as well. An example how to achieve this effect would be greatly appreciated.
Please ignore any syntax errors, the above is just a simple visual representation to help better describe my case.

You can use the widget attribute in order to assign a widget to your relational field, for example
<field name="my_field" widget="my_widget"/>
To see an example as to how a widget works and is created take a look at the following example:
Go to addons/account/project/wizard/account_analytic_journal_report_view.xml and see the line that defines a many2many_tags widget.
This form widget is assigned on addons/web/static/src/js/view_form.js at around line 6396 that comments Registry of form fields ...
The many2many_tags string that we used is assigned a actual widget, that widget has a template, and that template is rendered in place of your field.
TL;DR Give a widget element to your field, define that widget, assign that widget to work on a template and create the template that contains your view.

Related

Odoo DIN5008 append or change information_block via XPATH

I'm in a similar situation like:
Odoo DIN5008 append or change information_block
I'd like to add some fields (e.g. the customer number) to the DIN5008 information_block in quotation and invoice PDF reports, but I'd like to do it via QWeb inheritance only, i.e. without patching the python code. However, I cannot seem to get to the information_block via XPATH at all, neither via <xpath expr="//t[#t-foreach='template_data']" position="after"> (as taken from the source template) nor via <xpath expr="//div[#class='information_block'] position=after> (as taken from the code that's been rendered to the client).
I assume this is due to the template being a different file than what I can reach via inheriting from report_saleorder_document.
I can't figure out how to inherit from external_layout_din5008, and even if I figured that out, how would I differentiate the document type that's currently rendered?
You can use the inherit_id template attribute, used to alter existing templates in-place.
Example:
<template id="external_layout_din5008" inherit_id="l10n_de.external_layout_din5008">
<xpath expr="//t[#t-foreach='template_data']" position="after">
<tr><td>Mobile:</td><td><t t-esc="o.partner_id.mobile"/></td></tr>
</xpath>
</template>
You can use o._name to get the model name (purchase.order in the following example) but it it better to customize the information block for each document by defining the following three fields in the model: l10n_de_template_data, l10n_de_document_title and l10n_de_addresses
Example:
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
l10n_de_template_data = fields.Binary(compute='_compute_l10n_de_template_data')
l10n_de_document_title = fields.Char(compute='_compute_l10n_de_document_title')
l10n_de_addresses = fields.Binary(compute='_compute_l10n_de_addresses')
def _compute_l10n_de_template_data(self):
for record in self:
record.l10n_de_template_data = data = []
if record.state == 'draft':
data.append((_("Request for Quotation No."), record.name))
elif record.state in ['sent', 'to approve', 'purchase', 'done']:
data.append((_("Purchase Order No."), record.name))
elif record.state == 'cancel':
data.append((_("Cancelled Purchase Order No."), record.name))
if record.user_id:
data.append((_("Purchase Representative"), record.user_id.name))
if record.partner_ref:
data.append((_("Order Reference"), record.partner_ref))
if record.date_order:
data.append((_("Order Date"), format_date(self.env, record.date_order)))
if record.incoterm_id:
data.append((_("Incoterm"), record.incoterm_id.code))
def _compute_l10n_de_document_title(self):
for record in self:
if record.state in ['draft', 'sent', 'to approve']:
record.l10n_de_document_title = _("Request for Quotation")
elif record.state in ['purchase', 'done']:
record.l10n_de_document_title = _("Purchase Order")
elif record.state == 'cancel':
record.l10n_de_document_title = _("Cancelled Purchase Order")
def _compute_l10n_de_addresses(self):
for record in self:
record.l10n_de_addresses = data = []
if record.dest_address_id:
data.append((_("Shipping Address:"), record.dest_address_id))
elif 'picking_type_id' in record._fields and record.picking_type_id.warehouse_id:
data.append((_("Shipping Address:"), record.picking_type_id.warehouse_id.partner_id))
Example taken from l10n_de_purchase
Update:
The information bloc values come from the l10n_de_template_data field and in the compute method you can see that it just appends a tuple holding the field label and value to a list so you can use the t-esc directive to append new values to l10n_de_template_data field before calling the external layout.
Example:
<?xml version="1.0"?>
<data inherit_id="sale.report_saleorder_document">
<xpath expr="//th[#name='th_taxes']" position="replace" />
<xpath expr="//td[#name='td_taxes']" position="replace" />
<xpath expr="//t[#t-call='web.external_layout']" position="before">
<t t-esc="doc.l10n_de_template_data.append(('Mobile', doc.partner_id.mobile))"/>
</xpath>
</data>

How to limit the characters (to show in tree view) of text field in odoo

I have a text field.
description = fields.Text('Description')
I want to show this field in tree view, but not the whole value of the field.
I mean if my field value is "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", I just want to show
"AAA..." in tree view.
How to achieve this? Thanks.
Please use the below xml code for the field
xml : <tree string="Tree String" class="my_class"> <field name="description" />
in css write below:
css : .my_class [data-id="description"]{width: 200px;}
You can define a computed field to render just a few characters from the "description" field, and expose it in the tree view (as read-only)

xpath remove an attribute from an dynamic attribute list

I want remove an xml attribute via xpath, but
the xml element could have more atrributes in the future.
html code:
<p class="red, blue, green">test/<p>
xpath:
<xpath expr="//p[contains(#class, 'green')]" position="attributes">
<attribute name="class">red, blue</attribute>
</xpath>
Is where a better way for fixtext "red, blue"?
In order to suppport possible new version of the html file like
"<p class="red, blue, green, brown">test</p>" in the future without need to change the xpath code again.
for instance actual attribute list as var + an xpath function
What about setting the #class to
concat(substring-before(#class, "green"), substring-after(#class, "green"))
You'll need to solve the abandoned commas, too, but as Björn Tantau commented, in real HTML the classes would be separated by spaces, so you can just wrap the result into normalize-space.

Flow3-Fluid iterate object grouped by property

I've a list of objects (question-items), each with a property named category and title.
(The list of objects is ordered by its category property)
With this Fluid Template I want to iterate over the question items:
<f:for each="{questions}" as="question">
<!-- every time category changes, display it as a new headline ??? -->
<!-- if (question.category != previousQuestion.category) ?.....? -->
<span>{question.title}</span>
</f:for>
How can I check, if the category property has changed in the for?
The output should be something like this:
Category-A
Question
Question
Category-B
Question
Category-C
Question
Question
Question
...
You are looking for the f:groupedFor viewhelper: http://docs.typo3.org/typo3cms/ExtbaseGuide/Fluid/ViewHelper/GroupedFor.html
<f:groupedFor each="{questions}" as="categoryQuestion" groupBy="category" groupKey="category">
<h2>{category}</h2>
<f:for each="{categoryQuestion}" as="question">
<span>{question.title}</span>
</f:for>
</f:groupedFor>
Your approach with the <f:if> viewhelper won't work, because there are no real template-variables in fluid, just xml-scoped "constants", so even if you define previousQuestion via the <f:alias> viewhelper, you wouldn't be able to compare it to the current value.

Retrieving a parent tag with a given attribute that contains a subelement by using XPath

How I can retrieve multiple DIVs (with a given class attribute "a") that contain a span tag with a class attribute "b" by using Xpath?
<div class='a'>
<span class='b'/>
</div>
The structure of my XML is not defined so basically the span could be at any level of the div and the div itself could be at any level of the XML tree.
This should work:
//div[#class='a'][span/#class='b']
// means search anywhere if it starts the expression.
If the span is deeper in the div, use descendant:: which can be shortened to // again:
//div[#class='a'][.//span/#class='b']

Resources