Skip to content

Commit

Permalink
[IMP] purchase_sale_inter_company: Activity instead of hard raise
Browse files Browse the repository at this point in the history
A number of things can go wrong in trying to confirm the PO picking
when the SO picking is confirmed. Instead of raising an error and
thereby blocking the confirm of the SO picking, provide an option by
which the exception can be caught. The SO picking will still be
confirmed, but an activity will be posted for someone to deal with the
PO picking manually.
  • Loading branch information
thomaspaulb committed Aug 21, 2024
1 parent d2e6146 commit 6757ff0
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 5 deletions.
12 changes: 12 additions & 0 deletions purchase_sale_inter_company/models/res_company.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@

from odoo import fields, models

SELECTION_SYNC_FAILURE_ACTIONS = [
("raise", "Block and raise error"),
("notify", "Continue, but create activity to notify someone"),
]


class ResCompany(models.Model):
_inherit = "res.company"
Expand Down Expand Up @@ -36,6 +41,13 @@ class ResCompany(models.Model):
help="Sync the receipt from the destination company with the "
"delivery from the source company",
)
sync_picking_failure_action = fields.Selection(
SELECTION_SYNC_FAILURE_ACTIONS,
string="On sync picking failure",
default="raise",
help="Pick action to perform on sync picking failure",
)
block_po_manual_picking_validation = fields.Boolean(
string="Block manual validation of picking in the destination company",
)
notify_user_id = fields.Many2one("res.users", "User to Notify")
10 changes: 10 additions & 0 deletions purchase_sale_inter_company/models/res_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,17 @@ class InterCompanyRulesConfig(models.TransientModel):
"the delivery from the source company",
readonly=False,
)
sync_picking_failure_action = fields.Selection(
related="company_id.sync_picking_failure_action",
readonly=False,
)
block_po_manual_picking_validation = fields.Boolean(
related="company_id.block_po_manual_picking_validation",
readonly=False,
)
notify_user_id = fields.Many2one(
"res.users",
related="company_id.notify_user_id",
help="User to notify incase of sync picking failure.",
readonly=False,
)
51 changes: 46 additions & 5 deletions purchase_sale_inter_company/models/stock_picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ def _action_done(self):
purchase.picking_ids.write({"intercompany_picking_id": pick.id})
if not pick.intercompany_picking_id and purchase.picking_ids[0]:
pick.write({"intercompany_picking_id": purchase.picking_ids[0]})
pick._action_done_intercompany_actions(purchase)
return super()._action_done()

def _action_done_intercompany_actions(self, purchase):
self.ensure_one()
try:
pick = self
for move in pick.move_lines:
move_lines = move.move_line_ids
po_move_lines = (
Expand Down Expand Up @@ -89,7 +96,32 @@ def _action_done(self):
# if it doesn't exist, create it by copying from original company
dest_lot_id = lot_id.copy({"company_id": po_ml.company_id.id})
po_ml.lot_id = dest_lot_id
return super()._action_done()

except Exception:

Check warning on line 100 in purchase_sale_inter_company/models/stock_picking.py

View check run for this annotation

Codecov / codecov/patch

purchase_sale_inter_company/models/stock_picking.py#L100

Added line #L100 was not covered by tests
if self.env.company_id.sync_picking_failure_action == "raise":
raise

Check warning on line 102 in purchase_sale_inter_company/models/stock_picking.py

View check run for this annotation

Codecov / codecov/patch

purchase_sale_inter_company/models/stock_picking.py#L102

Added line #L102 was not covered by tests
else:
self._notify_picking_problem(purchase)

Check warning on line 104 in purchase_sale_inter_company/models/stock_picking.py

View check run for this annotation

Codecov / codecov/patch

purchase_sale_inter_company/models/stock_picking.py#L104

Added line #L104 was not covered by tests

def _notify_picking_problem(self, purchase):
self.ensure_one()
note = _(
"Failure to confirm picking for PO %s. "
"Original picking %s still confirmed, please check "
"the other side manually."
) % (purchase.name, self.name)
self.activity_schedule(
"mail.mail_activity_data_warning",
fields.Date.today(),
note=note,
# Try to notify someone relevant
user_id=(
self.company_id.notify_user_id.id
or self.sale_id.user_id.id
or self.sale_id.team_id.user_id.id
or SUPERUSER_ID,
),
)

def button_validate(self):
res = super().button_validate()
Expand All @@ -104,10 +136,19 @@ def button_validate(self):
and record.picking_type_code == "outgoing"
):
if record.intercompany_picking_id:
record._sync_receipt_with_delivery(
dest_company,
record.sale_id,
)
try:
record._sync_receipt_with_delivery(
dest_company,
record.sale_id,
)
except Exception:
if record.company_id.sync_picking_failure_action == "raise":
raise
else:
record._notify_picking_problem(
record.sale_id.auto_purchase_order_id
)

# if the flag is set, block the validation of the picking in the destination company
if self.env.company.block_po_manual_picking_validation:
for record in self:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -679,3 +679,75 @@ def test_block_manual_validation(self):
# The manual validation should be blocked
with self.assertRaises(UserError):
po_picking_id.with_user(self.user_company_a).button_validate()

def test_notify_picking_problem(self):
self.company_a.sync_picking = True
self.company_b.sync_picking = True
self.company_a.sync_picking_failure_action = "notify"
self.company_b.sync_picking_failure_action = "notify"
self.company_a.notify_user_id = self.user_company_a
self.company_b.notify_user_id = self.user_company_b

purchase = self._create_purchase_order(
self.partner_company_b, self.consumable_product
)
purchase_2 = self._create_purchase_order(
self.partner_company_b, self.consumable_product
)
purchase.order_line += purchase.order_line.copy({"product_qty": 2})
sale = self._approve_po(purchase)
sale.action_confirm()

# validate the SO picking
so_picking_id = sale.picking_ids

# Link to a new purchase order so it can trigger
# `PO does not exist or has no receipts` in _sync_receipt_with_delivery
sale.auto_purchase_order_id = purchase_2

# Set quantities done on the picking and validate
for move in so_picking_id.move_lines:
move.quantity_done = move.product_uom_qty
so_picking_id.button_validate()

# Test that picking has an activity now
self.assertTrue(len(so_picking_id.activity_ids) > 0)
activity_warning = self.env.ref("mail.mail_activity_data_warning")
warning_activity = so_picking_id.activity_ids.filtered(
lambda a: a.activity_type_id == activity_warning
)
self.assertEqual(len(warning_activity), 1)

# Test the user assigned to the activity
self.assertEqual(
warning_activity.user_id, so_picking_id.company_id.notify_user_id
)

def test_raise_picking_problem(self):
self.company_a.sync_picking = True
self.company_b.sync_picking = True
self.company_a.sync_picking_failure_action = "raise"
self.company_b.sync_picking_failure_action = "raise"

purchase = self._create_purchase_order(
self.partner_company_b, self.consumable_product
)
purchase_2 = self._create_purchase_order(
self.partner_company_b, self.consumable_product
)
purchase.order_line += purchase.order_line.copy({"product_qty": 2})
sale = self._approve_po(purchase)
sale.action_confirm()

# validate the SO picking
so_picking_id = sale.picking_ids

# Link to a new purchase order so it can trigger
# `PO does not exist or has no receipts` in _sync_receipt_with_delivery
sale.auto_purchase_order_id = purchase_2

# Set quantities done on the picking and validate
for move in so_picking_id.move_lines:
move.quantity_done = move.product_uom_qty
with self.assertRaises(UserError):
so_picking_id.button_validate()
18 changes: 18 additions & 0 deletions purchase_sale_inter_company/views/res_config_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@
for="sync_picking"
/>
<br />
<field name="sync_picking_failure_action" class="oe_inline" />
<label
string="On sync picking failure"
class="o_light_label"
for="sync_picking_failure_action"
/>
<br />
<label
for="notify_user_id"
class="o_light_label"
attrs="{'invisible': [('sync_picking_failure_action', '!=', 'notify')]}"
/>
<field
name="notify_user_id"
attrs="{'invisible': [('sync_picking_failure_action', '!=', 'notify')], 'required': [('sync_picking_failure_action', '=', 'notify')]}"
class="oe_inline"
/>
<br />
<field
name="block_po_manual_picking_validation"
class="oe_inline"
Expand Down

0 comments on commit 6757ff0

Please sign in to comment.