TDD: Date Assertions – Laravel 7, PHP 7.* – Carbon 2 – Changes

If you follow a TDD approach to develop your software, and also a Laravel user, working with a JSON API, you might have experienced some date assertion issues while asserting Json with Laravel 7. Previously, when Laravel was using Carbon 1 for date management, it would not return the whole Carbon object for assertJson. Which is why, the following would work in a sample PHPUnit Test:

$contact = factory(Contact::class)->create();
$response = $this->get('/api/contacts' . $contact->id);

$response->assertJson([
'name' => $contact->name,
'email' => $contact->email,
'meeting_date' => $contact->meeting_date,
'company' => $contact->company,
]);

But as of now, Carbon 2, returns the whole Carbon object for $contact->meeting_date here, the assertion will fair, because the $response, didn’t get a Carbon object, instead a Json string here.

If you go through the Carbon documentation here, you can see the following under the section ‘Migrate to Carbon 2’:

$date->jsonSerialize() and json_encode($date) no longer returns arrays but simple strings: "2017-06-27T13:14:15.000000Z". This allows to create dates from it easier in JavaScript. 

This is basically what you need to use if you are on Laravel 7 with Carbon 2. You need to serialize the data as Laravel is not doing it for you automatically here to fix this up. Just change the ‘meeting_date’ to the following:

'meeting_date' => $contact->meeting_date->jsonSerialize(),

and this should let your assertion pass.

Remember, you might not need to explicitly do this in your controller return until you are following a TDD based development where an assertion to pass is important to continue the process. Laravel resource would implicitly serialize your data before returning them from controller.

Dirty Odoo Hacks: How to Know If a Scheduled Action is Running

This blog post goes to my ‘Dirty Hack’ series, where I try to open the gross hacking attempts I practically use in several of my projects.

Odoo is my favorite piece of framework. Even though it has evolved as OpenERP, but the extend ability that Odoo gives, probably can take it anywhere, any other framework unable to, without massive change to their base architecture. This goes to the pros of Odoo, while the biggest cons of Odoo, is that it is not owned/developed by a major ‘English’ speaking company/people. If you go through the Odoo documentation, you can clearly understand the difference I am talking about. One key reason, Odoo is not often seen everywhere, probably because of poor documentation. Most of the things are not explained in details, as Odoo is a large piece of software, makes it difficult for developers to dig it down and extend.

Odoo has it’s own scheduling system, which they call ‘Scheduled Actions’. Odoo manages a model ‘ir.cron’ to manage the Scheduled Actions. If you look at the model details from (on debug mode)
Odoo >> Settings >> Technical >> Database Structure >> Model
you will see, there is no field it uses to detect if the scheduled action is running or stopped. It has a ‘state’ property though, which does not mean the status like many other odoo models. Here is a output of state property from Odoo shell:

>>> cron_id = self.env['ir.cron'].browse(18)
>>> cron_id.state

'code'

That says, it is telling you what kind of operation it is going to execute, here it is saying, this scheduled action is executing a ‘Python Code’ here.

There are times, when you might require to determine whether the scheduled action is running or not. In my case, it was to track down the some syncing issues, like to find out whether the syncing is doing it’s job properly through some custom methods. As Odoo doesn’t provide a way to do this, I had to find a dirty way to do it. Here is how I did it.

Odoo runs in Postgres SQL, which is basically an open source version of Oracle like Object Oriented Database system. Like many other OODS, Postgres also provide an operation called the following:

FOR UPDATE NOWAIT

From the oracle documentation, it means:

Oracle provides the FOR UPDATE NOWAIT clause in SQL syntax to allow the developer to lock a set of Oracle rows for the duration of a transaction.

When the scheduled action runs, it locks the specific row of the ir_cron table, of course it would because it has to update that specific row with nextcall and other field data. So if a cron is running, and you try to put a lock with another process, will basically fail, which means the cron is running, otherwise the opposite is true. Viola, that should work for us, isn’t it? Simple! Then again, you have to remember, the lock will put in place for the amount of time you specify, or you have to throw an early rollback (why not a commit? to be in safety to avoid any unwanted data being committed to the database during that session). Here is the simple method to do the whole process:

def _check_if_cron_running(self):
cron_id = self.env['ir.cron'].browse(18)
""" assuming the cron id is 18, you can do any kind of search, like searching by name of the scheduled action etc """
if cron_id:
try:
self._cr.execute("""SELECT id FROM "%s" WHERE id IN %%s FOR UPDATE NOWAIT""" % cron_id._table, [tuple(cron_id.ids)], log_exceptions=False)
""" self._cr.execute is used to execute a direct sql command, each ir.cron model data will have 'ids' to tell you which ids are selected, and _table to tell you what is the table name for the model, here it should return ir_cron """
self._cr.rollback() # we need to rollback and give the database cursor back to the other process ASAP
_logger.info("log and operate whatever here, this section is reached when the scheduled action is stopped or not running")
except psycopg2.OperationalError:
self._cr.rollback() # we need to rollback the errors and give control to the other process
_logger.info("log and operate whatever here, this section is reached when the scheduled action is running")

Remember, this is dirty, you are hitting direct SQL, on a near around 500 model based framework. So, please take care of your things before placing this in production. Rest assured, keep using Odoo for future! A brilliant piece of art is Odoo!

How to Empty a Model In Odoo / Mass Delete in Odoo / Mass Unlink

In Odoo, we use unlink() ORM method to delete a record. But if you are trying to empty a model or wants to delete multiple records, the best way to do it, is to use two steps.

First, search the records:

record_set = self.env['your.model'].search([])

Second, unlink them all at once instead of looping through them:

record_set.unlink()

If you have a list of ids, search them using the list:

ids = [1, 2, 3, 10, 11, 12]
record_set = self.env['your.model'].search([('id', 'in', ids)])

and unlink:

record_set.unlink()

Remember, there is no need to loop through this iterable object, odoo unlink does it for you.

TIPS: If you want to get all the ids in a model directly in a list, you can use the following:

record_list = self.env['your.model'].search([]).ids

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.

How to Validate Odoo Invoice Automatically

You might want to validate your odoo invoices automatically, without bothering your users to go to each vendor/customer bill and validate them, instead triggering based on certain conditions. It’s a 4 step code:

First, we get the invoice:

invoice_id = self.env['account.invoice'].browse(your_invoice_id)

Assign the date to it:

invoice_id.action_date_assign()

Create the invoice moves:

invoice_id.action_move_create()

Now you can validate the invoice:

invoice_id.invoice_validate()

How to Add Invoice to Purchase Order in Odoo Automatically

I was facing an issue in two way matching algorithm for Incoming Receipts & Vendor bills in my Odoo installation. As vendor bills are tagged in Purchase Orders only through use of ‘Create Bill’, I had to make an algorithm to calculate the matching. Remember, the match would only work if the supply chain members follows the right way of doing it, otherwise, it would fail. That’s what happened, it fails almost 3 times out of 10. Then I realized, the invoice needs to be pinned automatically in the stock.picking model, instead of purchase.order model at the time a Receipt is validated, to keep the tracking for my matching.

While doing so, first challenge is to pin an invoice to a purchase id. By default, the model for invoicing, ‘account.invoice’ has a foreign read only key for purchase, which is purchase_id. But remember, this is a read only field, and can’t be written/created in a new or existing record using record. I dig down the Odoo codes a little under purchase/models/account_invoice.py and could find the Odoo function that can do the job for us. It’s called ‘purchase_order_change()’.

Here is a snippet of adding an invoice automatically to a purchase ID:

invoice_id = self.env['account.invoice'].create({
'type': 'in_invoice',
'purchase_id': self.purchase_id.id,
'partner_id': self.purchase_id.partner_id.id,
})

invoice_id.purchase_order_change()

This will create an invoice in Draft state. Remember, you need to validate this invoice, if you would like to create the invoice number, not just an invoice entry. You may do it manually or you may do it automatically ‘How to Automatically Validate an Invoice in Odoo

Remember, I am triggering the snippet from the stock.picking, not from purchase.order, which is why, the ‘self’, is related to stock.picking and calling self.purchase_id to link the original purchase_id that triggered the stock.picking record.

Now, a small tip. What if, if you want to align the invoice to only have the products you have received in this particular receipt? Easy as pie:

for invoice_line in invoice_id.mapped('invoice_line_ids'):
check = 1
for move_line in self.mapped('move_line_ids'):
if invoice_line.product_id.id == move_line.porduct_id.id:
check = 0

if check:
invoice_line.unlink()

Simple! Isn’t it? Happy coding!

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

Odoo field has an attribute called ‘attrs’. This adds custom attribute to the Odoo form/tree views. You may use different kind of attributes with attrs like ‘required’, ‘readonly’ or ‘invisible’. For basic, to hide a field regardless of it be an individual entity or parent relation, we use the following:

<field name='field_name' invisible='1'/>

This is useful when we are triggering a computed field that we don’t want to show on a tree/form view. But what if we want to dynamically decide whether to invisible the field or not? For such cases, we usually use invisible with the ‘attrs’ attribute on fields, for example:

<field name='product_id' attrs="{ 'invisible': [('is_set', '=', True)]}"/>

You might set the field 'is_set' itself as invisible and a boolean computed field like the following:

is_set = fields.Boolean(compute="_set_is_set")

def _set_is_set(self):
if self.lot_id.life_date:
self.is_set = True
else:
self.is_set = False

Seems easy, right? But there is a catch, the above won’t hide the whole column if you are on a place like ‘purchase.order.line’ or ‘stock.move.line’. How can you work on such cases? It only works on individual entry, but not on a relational column. There is an attribute called ‘column_invisible’ same like ‘invisible’ we have used above. But the difference is, you need to set this based on parent value, thus hides a relational column. Here is an example to hide a column called ‘show_expiry’ in a picking operation:

<field name="show_expiry" attrs="{ 'column_invisible' : [('parent.picking_type_code', 'in', ['incoming'])]}" />

Above code checks the ‘picking_type_code’ value from ‘stock.picking’ model, and hides the column if it’s an incoming shipment and shows when it’s an outgoing shipment. That means, the above, would show the column (show_expiry) if it’s a Delivery to customer location or you already have the product, but won’t show if you are doing a GRN, means you don’t have the stock yet, just arriving.

Pretty simple, isn’t it? Good luck.

How to use dynamic ‘domain’ in Odoo 12 Many2one relation

There are two ways you can have a model data selection in Odoo, one is to use Selection field and the Other is Many2one relational field. To dynamically create a Selection field, all you need to do is to pass the list of (value, string) tuple as the first parameter or use a reference of a function to get a return list of same. Creating dynamic selection field is quite simple, with one drawback! It won’t work on api onchange. As the values on the selection fields are already loaded, api onchange can not reverse and reload the selection fields like it can do on ‘relational objects’. The only way left is to use a Many2one relational field.

Now Many2one relational field now make it a bit complicated as, you can’t generate the list in a straightforward way like you can do in selection field. Most likely, you would like to pass a domain, that matches some attribute of ‘self’, and some from another relation. Here is how you can achieve this!

I will create a simple model with two fields, one is Char and the other is Many2one:

_name = 'my.product.manager'

sku = fields.Char(string='SKU', required=True)
basket = fields.Many2one('basket.location', string='Product Basket Location', required=True)

Now, for example, you would like to select these SKUs during the creation of purchase order, and would like the field to come up when a change is made on the purchase order line.

First, we create a new Many2one relation field on ‘purchase.order.line’ model in relation to ‘my.product.manager’

_inherit = 'purchase.order.line'
basket_location_id = fields.Many2one( 'my.product.manager', 'Basket Location')

Cool, now you don’t want to share all the baskets in the purchase order form for a product, instead you want to show only those baskets that are allocated for that specific SKU. Here is how, you can pass a dynamic domain to the basket_location_id and filter it

@api.onchange('product_id')
def onchange_basket(self):
res = {
'domain' : {
'basket_dest_id' : [('sku', '=', self.product_id.default_code)],
}
}
return res

Neat! Pretty straight forward, isn’t it? Odoo 12 doesn’t give you the opportunity to choose a predefined location for a product. If you combine change.qty with the above, you can soon have a great module to use to let Odoo choose a product from default location. Cool Idea?

Happy coding!

How to setup Postfix relay with authentication in CentOS 7

To configure postfix to relay mail using another MTA, you may do the following steps:

postconf -e 'relayhost = smtp.to.relay.com'
postconf -e 'smtp_sasl_auth_enable = yes'
postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
postconf -e 'smtp_sasl_security_options='

Replace smtp.to.relay.com with the original MTA hostname that you going to use for relaying. Now, create the sasl_passwd file in /etc/postfix with the following inside:

smtp.to.relay.com smtp_username:smtp_password

Now, use postmap to generate postfix hash db:

postmap /etc/postfix/sasl_passwd

You can verify if it’s working with the following:

postmap -q smtp.to.relay.com /etc/postfix/sasl_passwd

This will return the username and password for your smtp relay host.

Now all you need to do is to restart the postfix

service postfix restart