Format relational data in Odoo web client - odoo

I've been working on a couple of modules for Odoo 15. What I'm having trouble with understanding though is how I can format certain types of information in more custom ways inside the backend/web client.
The example below is a simplified version of the kind of formatting I'm trying to to (I also think parent_id is a reserved name and can't actually be used, but it makes the example clearer).
Lets say I have three models: ModelA, ModelB, ModelC.
ModelA can have an x amount of child objects of the type ModelB, and ModelB can have an x amount of child objects of the type ModelC. These child objects also point back to their parent:
# ModelA
child_ids = fields.One2many("mymodule.ModelB", "parent_id") # link to multiple children
# ModelB
parent_id = fields.Many2one("mymodule.ModelA") # link to single parent
child_ids = fields.One2many("mymodule.ModelC", "parent_id") # link to multiple children
# ModelC
parent_id = fields.Many2one("mymodule.ModelB") # link to single parent
If I want to show this in a form in the web client, I can do something like
<field name="child_ids">
<tree create="0" delete="0" edit="0">
<field name="name" />
</tree>
</field>
to generate a simple list that only shows the name of each child, or use <field name="child_ids" widget="many2many_tags" /> to create inline tag-style objects.
But how can I access the child_ids data if I want to display it in a more graphical way. For example, lets say I want to generate some sort of nested boxes where for each object I draw a div which contains the divs for its children:
In other frameworks I'd write something like (pseudo code):
{{ for obj_a in a_objects }}
<div>
<h1>obj_a.name</h1>
{{ for obj_b in obj_a.children }}
<div>
<h2>obj_b.name</h2>
{{ for obj_c in obj_b.children }}
<div>
<h3>obj_c.name</h3>
</div>
{{ endfor }}}
</div>
{{ endfor }}
</div>
{{ endfor }}
I thought something similar might be done using the different t-directives:
<div t-foreach="child_ids" t-as="child">
<h2><t t-out="child.name" /><h2>
</div>
But the Many2one relational fields aren't lists of objects and can't be iterated over like this.
What am I missing? How do I access this data in ways that allow me to show information in my own layout but still within the web client (no website; I want this information in the same interface as my lists and forms).

Related

How to use `v-if` and `v-for` on the same element?

Hi I'm trying to figure out how to use v-if on a iterated element which also uses v-for. I need to check if the current element has any of a series of classes, which are numbers.
so the classes of each article would be:
<article class="post-item post-guide 12 22 19 30 55">...
this is the HTML that renders all:
<article v-if="showIfHasClass" :class="'post-item post-guide ' + guide.categories.toString().replace(/,/g, ' ')"
v-for="(guide, index) in guides" :key="index">
<header>
<h1 class="post-title">
{{ guide.title.rendered}}
</h1>
</header>
</article>
I have tried with methods that check the class of all elements, that works, but i'm trying to use a clean Vue built-in solution with v-if without success, i'm not able to retrieve the class of this in a successful way.
Should showIfHasClass be a computed property? I have tried with that too... but it seems, I'm missing something along the way.
my data I have to check against is an array:
data:{
guides: [...]
selectedCategories: [1, 22, 33, 100, 30];
}
or maybe it is better to directly loop over the guides and check if they have the selectedCategory or not, then remove the element from the guides data array?
What is more effective?
Besides the option to create an additional filtered computed (effectively eliminating the need to use v-for and v-if on the same element), you also have a template level way of dealing with such edge-cases: the <template> tag.
The <template> tag allows you to use arbitrary template logic without actually rendering an extra element. Just remember that, because it doesn't render any element, you have to place the keys from the v-for on the actual elements, like this:
<template v-for="(guide, index) in guides">
<article v-if="isGuideVisible(guide)"
:key="index"
class="post-item post-guide"
:class="[guide.categories.toString().replace(/,/g, ' ')]">
<header>
<h1 v-text="guide.title.rendered" />
</header>
</article>
</template>
isGuideVisible should be a method returning whether the item is rendered, so you don't have to write that logic inside your markup. One advantage of this method is that you can follow your v-if element with a fallback v-else element, should you want to replace the missing items with fallback content. Just remember to also :key="index" the fallback element as well.
Apart from the above use-case, <template> tags come in handy when rendering additional wrapper elements is not an option (would result in invalid HTML markup) (i.e: table > tr > td relations or ol/ul > li relations).
It's mentioned here as "invisible wrapper", but it doesn't have a dedicated section in the docs.
Side note: since you haven't actually shown what's inside guide.categories, I can't advise on it, but there's probably a cleaner way to deal with it than .toString().replace(). If guide.categories is an array of strings, you could simply go: :class="guide.categories".
I think the most Vue way is to create a computed property with filtered items from selected categories, then use that in v-for loop (the idea is to move the business logic away from template).
computed: {
filteredItems(){
return this.guides.filter(e => this.selectedCategories.includes(e.category))
}
}
Also, as a note, it is not recommended to use v-if and v-for on the same element, as it may interfere with the rendering and ordering of loop elements. If you don't want to add another level of nesting, you can loop on a 'template' element.
<template v-for="item in items">
// Note the key is defined on real element, and not on template
<item-element v-if='condition' :key="item.key"></item-element>
</template>

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>

Defining a relationship between a country and a recipe with Schema

I am building a web page that lists typical dishes for each individual country. Each dish is put in its own article and all of that goes fine. But I wonder if there is a way to link the Recipes to the country. Does it make sense to also specify the country with http://schema.org/Country, and if so how can I link that to the dishes?
I thought about defining the main as a country, and then using http://schema.org/additionalProperty but that doesn't seem to make sense as it expects a PropertyValue, which the recipes aren't.
<main>
<h1>France</h1>
<p>
<span class="capital" title="Capital">Paris</span>
<span class="member-since" title="Member of the EU since 1958">1958</span>
</p>
<article id="recipe-1" itemscope itemtype="http://schema.org/Recipe">
<h1>Éclairs</h1>
<!-- A lot of recipe-related stuff -->
</article>
<article id="recipe-2" itemscope itemtype="http://schema.org/Recipe">
<h1>Macaron</h1>
<!-- A lot of recipe-related stuff -->
</article>
<article id="recipe-3" itemscope itemtype="http://schema.org/Recipe">
<h1>Tarte Tatin</h1>
<!-- A lot of recipe-related stuff -->
</article>
</main>
You can provide the cuisine of a Recipe with its recipeCuisine property:
The cuisine of the recipe (for example, French or Ethiopian).
It expects a Text value.
There is also the locationCreated property, which expects a Place value (which includes Country), but it might be a stretch to use it in this context. Also it wouldn’t be clear if it refers to the location where the recipe is originally coming from, or to the location where the written form was created.
Schema.org doesn’t seem to offer a property to connect a Country and Recipe directly. But you could still connect the items via WebPage.
So for example, if you say WebPage about Country and WebPage mainEntity ItemList, and have each Recipe as itemListElement, there is at least some connection (a page about a specific country has a list of recipes as main content = the recipes are probably related to that country).

Selenium access a form field with bad id

Looking for the best approach to enter / read a value from a form field that lacks human readable ids / references.
The basic outline looks like
<div id="form-2143">
<div id="numberfield-1234">
<label id="numberfield-1234-label">
<span class="x-form-label">Field Name 1</span>
</label>
<div id="numberfield-1234-body">
<div id="numberfield-1234-wrap">
<input id="numberfield-1234-input" class="form-field" componentid="numberfield-1234">
</div>
</div>
</div>
...
</div>
There are more class defs and attributes involved, but above is the "basics" I have to work with.
This form has a number of entries, and there are more forms like it, so I am looking for a way to search for the label name, and access the input field within the same container.
I lack control of the site and cannot edit the HTML structure of the site; meaning I cannot give sensible names to the ids, but want to avoid hard referencing the poor names. Any suggestions on how to get Robot Framework & selenium to reference these elements?
Highlighting Andersson's answer in the comments
Using the XPath
//label[span[text()="Field Name 1"]]/following-sibling::div//input
Works for the above example.
The key part that answers the question of how to reference nearby elements is
/following-sibling

Can I add the custom fields to the product listing page in BigCommerce

Each product has the custom fields options. Can I output those custom fields on each product item in the product list page? If so, how? I have tried adding the ProductOtherDetails and the %%SNIPPET_ProductCustomFieldItem%% in the CategoryProductsItem.html, but got no output at all of any of the items I have tried. Any suggestions or pointers on how and if this is possible?
As of September 2015, you can now access %%GLOBAL_ProductCustomFields%% on any template file that renders a particular panel's individual items. For example:
-Snippets/CategoryProductsItem.html for category list pages
-Snippets/HomeFeaturedProductsItem.html for the featured products panel
I recommend adding the custom field name as a class to each field for easy hiding, and accessing of the value in case the custom fields ever change you won't be accessing them via :nth-child CSS which would break. You can do so by modify Snippets/ProductCustomFieldItem.html to add the custom field name to the CSS class or ID like this:
<div class="DetailRow %%GLOBAL_CustomFieldName%%">
<div class="Label">%%GLOBAL_CustomFieldName%%:</div>
<div class="Value">
%%GLOBAL_CustomFieldValue%%
</div>
</div>​
Doing so, will output like this in each item in the category list.
Above, I am using the custom fields to send through shipping time, as well as "Starting At" to prepend to the list page price if the item is a parent which has children of higher prices. In my opinion, these features greatly increase the user experience.
For Faceted Search (handlebars.js)
I recommend adding this to Panels/FacetedSearchProductGrid.html:
{{#each product.custom_fields}}
{{ id }} : {{ name }} : {{ value }}
{{/each}}
Those filters will be limited to the Product pages specifically. The only way around it is to hash together a solution using jQuery to go and fetch items in question from the product page. A pain, but do-able with unnecessary effort.