How To Find : If two different date ranges intersects

I was making a Odoo module today, it had a requirement to find the products that has offering within two ranges. Each of the product has different range for discounts/offers. It is sort of like the following:

For example, a shampoo discount starts from 01-06-2020 and ends at 20-06-2020, while another product like a soap discount starts from 10-06-2020 and ends at 22-06-2020. Now if I look for discounted credit notes to apply within a range, then we need to provide some time range, that these ranges intersect. For example, if I want to find discounts that were given within 02-06-2020 – 11 – 06 – 2020, then we should get both the discounts available here, as the discounts were still available for a day to the Soap and a few days for Shampoo. That means, we need to find if any of the given two ranges intersects with the ranges we have discounts for.

The easiest way to calculate this, is to check which date is max among the lower boundaries and which date is minimum in the lower boundaries. This should follow that the starting date would be less than or equal to the ending date, but never greater than the ending date. If greater than, then it does not intersect logically. In python, we can do this like the following:

# first we find all the vendor discounts, it has property start_date and end_date
vendor_discounts = self.env['vendor.discount'].search([])

# list to keep the vendor discount instances
vendor_discount_id_in_range = []

# from the form, we get date_from and date_to, where the dates intersect
for vendor_discount in vendor_discounts:
   # here is the logic to find intersects
    if max(self.date_from, vendor_discount.start_date) <= min(self.date_to, vendor_discount.end_date):
        vendor_discount_id_in_range.append(vendor_discount.id)

So, the logic going to be like the following:

if max(input_date_from, start_date_to_match) <= min(input_date_to, end_date_to_match)

How to get the string/part/word/text within brackets in Python using Regex

PROBLEM DEFINITION

For example, you have a string like the following:

[lagril] L.A. Girl Pro. Setting HD Matte Finish Spray

While you are scanning the line, you would like to extract the following word from it ‘lagril’, which you are interested in. How to do that?

GETTING TEXT WITHIN BRACKETS USING REGEX IN PYTHON

Our problem falls into a common string extraction problem we face in software engineering. We usually do this using Regular Expressions. Let’s build the regular expression logic first, using regex101.com

We need to find a string that starts with ‘[‘ bracket and ends with ‘]’ bracket, and in the middle, we expect alphanumeric word with small or capital letters, and they can be anything from 0 to any. So, this should be as simple as the following:

\[[A-Za-z0-9]*\]

Now, this should help us target the words that comes within bracket in a sentence/large string. But the trick to grab the text within the bracket is to group them. To use group in regex, we use () brackets without back slash in front. So if the regex is as following:

\[([A-Za-z0-9]*)\]

This will put the matching string in group 1. Now, how can you get what is in the group 1 of a regular expression engine? Let’s dive into python now:

# let's import regular expression engine first
import re

# our string
txt = '[lagril] L.A. Girl Pro. Setting HD Matte Finish Spray'

# our regex search would be as following:
x = re.search(r"\[([A-Za-z0-9]*)\]", txt)

# we know this will put the inner text in group 1. regex object that returned by re.search, has a method called 'group()' to catch the groups matches regex. You may use the following

x.group(1) # prints lagril

How to Hide a Column Dynamically in a Parent Model in Tree View on Odoo

We all know how to hide a column in a model based on parent value matching in Odoo installation. I have already written a post on this here:

How to hide a column ‘dynamically’ in Tree View on Odoo

Above case works when you are trying a hide a column in a sale order line, or move line or invoice line. But what if, you want to hide a column in stock picking delivery tree view or the receipt view? The above method, will not work, because column_invisible only works when the parent is referenced. But in a tree view list like ‘Deliver Orders’ under Inventory Overview, if you want to hide a column based on condition, how do we do that?

The answer is, we use data from context and set the attribute ‘invisible’ to True based on the context value. We can either do that by using any existing context value or we can add a context value using a computed field property and match it in the xml.

I will discuss on how can we use the existing context key:value pair in hiding a column on stock.picking model.

First, let’s imagine, we have a character field on stock.picking called ‘woocommerce_id’.

class stock_picking(models.Model):
_inhert = 'stock.picking'

woocommerce_id = fields.Char(string="Woocommerce ID")

Now the xml for Inventory overview stock.picking trees would be inheriting stock.vpicktree

<record id="inherit_delivery_picking" model="ir.ui.view">
<field name="name">stock.picking.inherit.delivery.picking</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.vpicktree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='location_dest_id']" position="after">
<field name="woocommerce_id"/>
</xpath>
</field>
</record>

Now, the above will show woocommerce_id in all vpicktree view, like ‘Delivery Orders’, ‘Receipts’, ‘Internal Transfers’. But What if, you want to show this field to only Delivery Orders?

To do that, we need to know the value for ‘default_picking_type_id’. This value is automatically set on stock.picking model view in the context by Odoo (Hints: You may view all the keys available to context in a model, by creating a computed field, that writes self.env.context.keys() to a file or show it in the tree view to find the keys, and self.env.context.get(‘key_name’) to find the value for it).

If you check the different values set by that key for different pages, you can see, it is set to 1 for delivery orders, 3 for receipts and 5 for internal transfers. Now if you want to show the woocommerce_id field to only delivery receipts, we set the attribute invisible for that field when the context key value is not 1 like following:

<field name="woocommerce_id" invisible="context.get('default_picking_type_id') != 1"/>

Save it, upgrade the module, and see the magic! cheers!

How to Update Context in Odoo

You may want to pass some data to a specific page in Odoo, and change the fields based on those data. In those cases, you want to use Context. Context is a data structure passed to the request object that contains hidden values, and can be used to change results of the page.

Context is a frozendict data type in Odoo. That’s why you can change it like you do in a dict data type, for example:

dict_object.update({
'test': 'test_value',
})

As the Context is frozendict, it won’t take such changes. Odoo provides a way to change values, it’s called ‘with_context’. The syntax is as following:

self = self.with_context({
'test': 'test_value',
})

This one should rewrite the context available in the self object by adding the new key:value pair you have mentioned. There are times, this might not work as expected, and you want a patch technique to update the context. This can be done by changing the data type to dict.

self.env.context = dict(self.env.context)
self.env.context.update({
'test': 'test_value',
})

This would also add the new key:value pair to your context and work as expected. There is almost no security concern here for converting the frozendict as the context will destroy once the page is left soon enough.