1

We are working in an Industrial Factory, the purchase's responsible must to inform to the CEO about any purchase for procuring manufacturing materials for a Sale Order product.

For example, we have a Sale Order SO00005 with the product [856A3779G02] PULLER – PLATFORM, FAN BLADE, it has its own BoM and it also has the MTO and Manufacture route.

The system creates a new PO, the responsible edit and handles the route, and at confirm, we must send to the CEO a message like this:

SO00005 adlı [856A3779G02] PULLER – PLATFORM, FAN BLADE projesi için toplam maliyet 100.0₺'dir.

SO00005 adlı [856A3779G02] PULLER – PLATFORM, FAN BLADE şindiye kadarki toplam maliyet 1175.0₺.

Where the first one says the current material price, and the second one says the total per Sale Order.

How can we handle this?

1 Answer 1

4

First, we will begin with the notification process. We can use here an automated action in order to make this action the simplest possible.

Automated Action

We go to Settings > Technical > Automation > Automated Actions (don't forget install the base_automation module), and we create a new action with the next parameters:

Model: purchase.order, we want to notify when the CONFIRM ORDER button is clicked, changing the current Purchase Order state to purchase. Trigger Condition: On Update, we want to send this notification when the Purchase Order is confirmed, it means, when the Purchase Order change its state to purchase. Before Update Domain: So, the prior domain will be all current draft, to approve or e-mail sent purchase orders, ["|","|",["state","=","draft"],["state","=","sent"],["state","=","to approve"]]. Apply on: We'll send this mail when the Purchase Order's state change to purchase, [["state","=","purchase"]]. Action To Do: finally, we will apply a python code in order to get the required information and send it to the boss.

Python Code

We decided to use python code because it is easier to iterate over objects in code; we mean, we can also send emails as Action To Do, but in this case, we would require taking purchase.order.line as hour model and it could be harder.

Now, the simplest way consists in sending a message to a channel inside Odoo, keeping within the system as many procedures as we can. So, before to create this action we must create a private or public channel in the discuss module.

Continuing with the code, we will obtain this channel for sending messages to it:

channel = env['mail.channel'].search([('name', '=', 'purchases-notify')])
channel.ensure_one()

Also, we need the CEO manager partner:

boss = env['res.partner'].search([('function', '=', 'CEO')])

Now, we will iterate over each line in the Purchase Order processing the information; we have to options here, by default, tracking purchases and other expenses in Odoo uses the accounting module and process, it means, we must to add analytical accounts to each line in the Purchase Order, and, if the PO has many different products, it could be very annoying. We will use other approaching.

Manufacturing order and sale orders generateProcurement Groups based in the Procurement Rules, and the Procurement Group has a unique Sale Order origin, it is a double advantage for us; first one is that we can get the product's name through this relationship; this PG has the SO name as its name too.

By default Odoo does not split each purchase line by PG groups, it just merges the lines if the product or product variant and the uom are the same, and also we cannot know which is the PG origin for it line; to solve this we must to install the purchase_line_procurement_group module from OCA.

We have the next code then:

for line in record.order_line:
  procurement_group = line.procurement_group_id
  product = env['sale.order'].search([('id', '=', procurement_group.sale_id.id)]).order_line[0].name
  sub_total = line.price_subtotal

Getting the line's product cost from the price_subtotal field.

But we want to get the total cost for SO: we get first all purchase order lines related to the current line's PG, then, we iterate over them summing just line which PO is also confirmed:

purchase_order_lines_list = env['purchase.order.line'].search([('procurement_group_id','=',procurement_group.id)])
  total = 0
  for line in purchase_order_lines_list:
    if line.order_id.state == 'purchase':
      total += line.price_subtotal

The second advantage, as we can see in the above code is: as each PG has just one SO origin, we don't need necessarily to search with the procurement_group_id.sale_id.id field, because of PG ID will be associate just to one SO and no other.

We have all the information that we need, then, we will send a new message for each line in the PO:

post_vars = {
    'subject': "Purchase {}".format(record.name),
    'body': '''<p>Mr. <strong>{0}</strong>,</p>
        <p><strong>{1}</strong> adlı <strong>{2}</strong> projesi için toplam maliyet <strong>{3}{4}</strong>'dir.</p>
        <p><strong>{1}</strong> adlı <strong>{2}</strong> şindiye kadarki toplam maliyet <strong>{5}{4}</strong>.</p>'''.format(boss.name, procurement_group.name, product, sub_total, line.currency_id.symbol, total)
  }
  channel.message_post(type="notification", subtype="mt_comment", **post_vars)

We must add the currency symbol, existing as a field in the line, which is line.currency_id.symbol.

Finally, our full code will be:

channel = env['mail.channel'].search([('name', '=', 'purchases-notify')])
channel.ensure_one()


boss = env['res.partner'].search([('function', '=', 'CEO')])

for line in record.order_line:
  procurement_group = line.procurement_group_id
  product = env['sale.order'].search([('id', '=', procurement_group.sale_id.id)]).order_line[0].name
  sub_total = line.price_subtotal
  purchase_order_lines_list = env['purchase.order.line'].search([('procurement_group_id','=',procurement_group.id)])
  total = 0
  for line in purchase_order_lines_list:
    if line.order_id.state == 'purchase':
      total += line.price_subtotal
  post_vars = {
    'subject': "Purchase {}".format(record.name),
    'body': '''<p>Mr. <strong>{0}</strong>,</p>
        <p><strong>{1}</strong> adlı <strong>{2}</strong> projesi için toplam maliyet <strong>{3}{4}</strong>'dir.</p>
        <p><strong>{1}</strong> adlı <strong>{2}</strong> şindiye kadarki toplam maliyet <strong>{5}{4}</strong>.</p>'''.format(boss.name, procurement_group.name, product, sub_total, line.currency_id.symbol, total)
  }
  channel.message_post(type="notification", subtype="mt_comment", **post_vars)

And we will get the next each time we confirm a PO: enter image description here

Not the answer you're looking for? Browse other questions tagged or ask your own question.