Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[14.0][IMP] purchase_sale_inter_company: the sale order is updated when the purchase order is modified #391

Open
wants to merge 1 commit into
base: 14.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion purchase_sale_inter_company/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Inter Company Module for Purchase to Sale Order
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:3899bc2db53f1d1d16a8c0d9b0f75002813b2e3095815b55949efad59958f1a3
!! source digest: sha256:ed23c6c7f375be3c7730c810d0af05c1373ef8d8dbbde6e793b16b999f97979d
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
Expand Down
2 changes: 2 additions & 0 deletions purchase_sale_inter_company/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
"sale_stock",
"purchase_stock",
"account_invoice_inter_company",
"base_view_inheritance_extension",
],
"data": [
"views/res_config_view.xml",
"views/purchase_view.xml",
"wizard/stock_backorder_confirmation_views.xml",
],
"demo": [
Expand Down
44 changes: 32 additions & 12 deletions purchase_sale_inter_company/models/purchase_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ class PurchaseOrder(models.Model):
compute_sudo=True,
)

@api.depends("state")
def _compute_intercompany_sale_order_id(self):
"""A One2many would be simpler, but the record rules make unaccesible for
regular users so the logic doesn't work properly"""
ids_dict_list = self.env["sale.order"].search_read(
[("auto_purchase_order_id", "in", self.ids)],
[("auto_purchase_order_id", "in", self.ids), ("state", "!=", "cancel")],
["id", "auto_purchase_order_id"],
)
ids_dict = {d["auto_purchase_order_id"][0]: d["id"] for d in ids_dict_list}
Expand Down Expand Up @@ -192,16 +193,33 @@ def _prepare_sale_order_line_data(self, purchase_line, dest_company, sale_order)
return new_line._convert_to_write(new_line._cache)

def button_cancel(self):
sale_orders = (
self.env["sale.order"]
.sudo()
.search([("auto_purchase_order_id", "in", self.ids)])
)
for so in sale_orders:
if so.state not in ["draft", "sent", "cancel"]:
raise UserError(_("You can't cancel an order that is %s") % so.state)
sale_orders.action_cancel()
self.write({"partner_ref": False})
if self.intercompany_sale_order_id:
if self.intercompany_sale_order_id.sudo().state not in [
"draft",
"sent",
"cancel",
]:
raise UserError(
_(
"You can not cancel your purchase order %s.\n"
"The sale order %s at your supplier %s that is linked "
"to your purchase order, is in the state %s.\n"
"In order to cancel your purchase order, you must first "
"ask your supplier to cancel his sale order."
)
% (
self.name,
self.intercompany_sale_order_id.sudo().name,
self.partner_id.name,
self.intercompany_sale_order_id.sudo().state,
)
)
if self.intercompany_sale_order_id.sudo().state in [
"draft",
"sent",
]:
self.intercompany_sale_order_id.sudo().action_cancel()
self.write({"partner_ref": False})
return super().button_cancel()


Expand All @@ -214,11 +232,13 @@ class PurchaseOrderLine(models.Model):
compute_sudo=True,
)

@api.depends("state")
def _compute_intercompany_sale_line_id(self):
"""A One2many would be simpler, but the record rules make unaccesible for
regular users so the logic doesn't work properly"""
ids_dict_list = self.env["sale.order.line"].search_read(
[("auto_purchase_line_id", "in", self.ids)], ["id", "auto_purchase_line_id"]
[("auto_purchase_line_id", "in", self.ids), ("state", "!=", "cancel")],
["id", "auto_purchase_line_id"],
)
ids_dict = {d["auto_purchase_line_id"][0]: d["id"] for d in ids_dict_list}
for line in self:
Expand Down
3 changes: 2 additions & 1 deletion purchase_sale_inter_company/static/description/index.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
Expand Down Expand Up @@ -366,7 +367,7 @@ <h1 class="title">Inter Company Module for Purchase to Sale Order</h1>
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:3899bc2db53f1d1d16a8c0d9b0f75002813b2e3095815b55949efad59958f1a3
!! source digest: sha256:ed23c6c7f375be3c7730c810d0af05c1373ef8d8dbbde6e793b16b999f97979d
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/multi-company/tree/14.0/purchase_sale_inter_company"><img alt="OCA/multi-company" src="https://img.shields.io/badge/github-OCA%2Fmulti--company-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/multi-company-14-0/multi-company-14-0-purchase_sale_inter_company"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/multi-company&amp;target_branch=14.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module is useful if there are multiple companies in the same Odoo database and those companies sell goods or services among themselves.</p>
Expand Down
103 changes: 23 additions & 80 deletions purchase_sale_inter_company/tests/test_inter_company_purchase_sale.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# Copyright 2018-2019 Tecnativa - Carlos Dauden
# Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import re

from odoo.exceptions import UserError
from odoo.tests.common import Form
Expand Down Expand Up @@ -194,7 +193,12 @@ def _approve_po(self, purchase_id):
return (
self.env["sale.order"]
.with_user(self.user_company_b)
.search([("auto_purchase_order_id", "=", purchase_id.id)])
.search(
[
("auto_purchase_order_id", "=", purchase_id.id),
("state", "!=", "cancel"),
]
)
)

def test_purchase_sale_inter_company(self):
Expand Down Expand Up @@ -519,81 +523,20 @@ def test_sync_picking_different_product_multiple_lines(self):
sale.sudo().write({"picking_ids": [(4, new_picking.id)]})
sale.action_confirm()

def test_update_open_sale_order(self):
"""
When the purchase user request extra product, the sale order gets synched if
it's open.
"""
purchase = self._create_purchase_order(
self.partner_company_b, self.consumable_product
)
purchase.picking_type_id = self.warehouse_a.in_type_id
sale = self._approve_po(purchase)
sale.action_confirm()
# Now we add an extra product to the PO and it will show up in the SO
po_form = Form(purchase)
with po_form.order_line.new() as line:
line.product_id = self.consumable_product_2
line.product_qty = 6
po_form.save()
# It's synched and the values match
synched_order_line = sale.order_line.filtered(
lambda x: x.product_id == self.consumable_product_2
)
self.assertTrue(
bool(synched_order_line),
"The line should have been created in the sale order",
)
self.assertEqual(
synched_order_line.product_uom_qty,
6,
"The quantity should be equal to the one set in the purchase order",
)
# Also the moves match as well
so_picking_id = sale.picking_ids
synched_move = so_picking_id.move_lines.filtered(
lambda x: x.product_id == self.consumable_product_2
)
self.assertTrue(
bool(synched_move),
"The move should have been created in the delivery order",
)
self.assertEqual(
synched_move.product_uom_qty,
6,
"The quantity should be equal to the one set in the purchase order",
)
# The quantity is synched as well
purchase_line = purchase.order_line.filtered(
lambda x: x.product_id == self.consumable_product_2
).sudo()
purchase_line.product_qty = 8
self.assertEqual(
synched_order_line.product_uom_qty,
8,
"The quantity should be equal to the one set in the purchase order",
)
self.assertEqual(
synched_move.product_uom_qty,
8,
"The quantity should synched to the one set in the purchase order",
)
# Let's decrease the quantity
purchase_line.product_qty = 3
self.assertEqual(
synched_order_line.product_uom_qty,
3,
"The quantity should decrease as it was in the purchase order",
)
self.assertEqual(
synched_move.product_uom_qty,
8,
"The quantity should remain as it was as it can't be decreased from the SO",
)
# A warning activity is scheduled in the picking
self.assertRegex(
so_picking_id.activity_ids.note,
re.compile(
"3.0 Units of Consumable Product 2.+instead of 8.0 Units", re.DOTALL
),
)
def test_update_po(self):
# create intercompany sale order
self.company_b.sale_auto_validation = False
old_sale = self._approve_po(self.purchase_company_a)
# cancel and back to draft the PO before update
self.purchase_company_a.button_cancel()
self.purchase_company_a.button_draft()
# update PO
self.purchase_company_a.order_line.product_qty = 5.0
# create new intercompany sale order after confirm updated PO
self.company_b.sale_auto_validation = True
new_sale = self._approve_po(self.purchase_company_a)
# test update
self.assertEqual(new_sale.order_line.product_uom_qty, 5.0)
self.assertEqual(new_sale.state, "sale")
self.assertEqual(self.purchase_company_a.intercompany_sale_order_id, new_sale)
self.assertEqual(old_sale.auto_purchase_order_id, self.purchase_company_a)
17 changes: 17 additions & 0 deletions purchase_sale_inter_company/views/purchase_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="purchase_order_form" model="ir.ui.view">
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form" />
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="intercompany_sale_order_id" invisible="True" />
</field>
<field name="order_line" position="attributes">
<attribute name="attrs" operation="python_dict" key="readonly">
['|', ('state', 'in', ('done', 'cancel')), '&amp;', ('intercompany_sale_order_id', '!=', False), ('state', '=', 'purchase')]
</attribute>
</field>
</field>
</record>
</odoo>
Loading