From bc8193bf130248fe5e89e0f593e606965b611c19 Mon Sep 17 00:00:00 2001 From: Michael Tietz Date: Wed, 10 Apr 2024 17:02:57 +0200 Subject: [PATCH] [FIX] delivery_send_to_shipper_at_operation: Keep delivery_notification_sent set on backorder --- .../models/stock_picking.py | 58 +++++++++++----- .../readme/CONTRIBUTORS.rst | 1 + .../tests/test_send_to_shipper.py | 68 +++++++++++++++++-- 3 files changed, 106 insertions(+), 21 deletions(-) diff --git a/delivery_send_to_shipper_at_operation/models/stock_picking.py b/delivery_send_to_shipper_at_operation/models/stock_picking.py index b2ba2b8024..8e81300966 100644 --- a/delivery_send_to_shipper_at_operation/models/stock_picking.py +++ b/delivery_send_to_shipper_at_operation/models/stock_picking.py @@ -1,5 +1,6 @@ # Copyright 2021 Camptocamp SA # Copyright 2023 Jacques-Etienne Baudoux (BCIM) +# Copyright 2024 Michael Tietz (MT Software) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from lxml import etree @@ -20,19 +21,31 @@ class StockPicking(models.Model): delivery_notification_sent = fields.Boolean(default=False, copy=False) def _send_confirmation_email(self): + picking_ids_skip_costs = [] for picking in self: - skip_delivery_cost = picking._handle_send_to_shipper_at_operation() - picking = picking.with_context(skip_delivery_cost=skip_delivery_cost) - super(StockPicking, picking)._send_confirmation_email() + if not picking._is_send_to_shipper_at_operation(): + continue + picking_ids_skip_costs.append(picking.id) + picking.carrier_id = picking.ship_picking_id.carrier_id + pickings_skip_costs = self.browse(picking_ids_skip_costs) + if pickings_skip_costs: + pickings_skip_costs._handle_send_to_shipper_at_operation() + super(StockPicking, self - pickings_skip_costs)._send_confirmation_email() def _handle_send_to_shipper_at_operation(self): """Send the delivery notice to the carrier from a specific operation type. We are only interested by sending the delivery notice, the delivery fee still have to be added to the SO by the ship operation. - - Return True if the operation has send the delivery notice. """ + super().with_context(skip_delivery_cost=True)._send_confirmation_email() + for picking in self: + related_ship = picking.ship_picking_id + values = picking._prepare_values_send_to_ship_at_operation(related_ship) + related_ship.write(values) + + def _is_send_to_shipper_at_operation(self): + """Return True if the operation needs to send the delivery notice.""" self.ensure_one() if not self.carrier_id: # If the current operation has no carrier defined, but a carrier @@ -47,20 +60,23 @@ def _handle_send_to_shipper_at_operation(self): and self.picking_type_id in carrier.send_delivery_notice_picking_type_ids ): - self.carrier_id = carrier - self.with_context(skip_delivery_cost=True).send_to_shipper() - # Flag the current operation and the ship one. - # Mandatory to not execute twice 'send_to_shipper' method - self.delivery_notification_sent = True - related_ship.delivery_notification_sent = True - related_ship.carrier_price = self.carrier_price - if not related_ship.carrier_tracking_ref: - related_ship.carrier_tracking_ref = self.carrier_tracking_ref - else: - related_ship.carrier_tracking_ref += "," + self.carrier_tracking_ref return True return False + def _prepare_values_send_to_ship_at_operation(self, related_ship): + self.ensure_one() + related_ship.ensure_one() + carrier_tracking_ref = related_ship.carrier_tracking_ref + if carrier_tracking_ref: + carrier_tracking_ref += "," + self.carrier_tracking_ref + else: + carrier_tracking_ref = self.carrier_tracking_ref + return { + "delivery_notification_sent": True, + "carrier_price": self.carrier_price, + "carrier_tracking_ref": carrier_tracking_ref, + } + def send_to_shipper(self): # Do not send delivery notice to the carrier if it has already been sent # through a previous operation (like a pack) @@ -111,3 +127,13 @@ def _fields_view_get_adapt_send_to_shipper_attrs(self, view_arch): ) transfer_modifiers_to_node(modifiers, field) return etree.tostring(doc, encoding="unicode") + + def _create_backorder(self): + backorders = super()._create_backorder() + for backorder in backorders: + delivery_notification_sent = ( + backorder.backorder_id.delivery_notification_sent + ) + if delivery_notification_sent: + backorder.delivery_notification_sent = delivery_notification_sent + return backorders diff --git a/delivery_send_to_shipper_at_operation/readme/CONTRIBUTORS.rst b/delivery_send_to_shipper_at_operation/readme/CONTRIBUTORS.rst index 8c1a61275b..9bf5d68d4c 100644 --- a/delivery_send_to_shipper_at_operation/readme/CONTRIBUTORS.rst +++ b/delivery_send_to_shipper_at_operation/readme/CONTRIBUTORS.rst @@ -2,3 +2,4 @@ * `Trobz `_: * Nguyen Hoang Hiep * Jacques-Etienne Baudoux (BCIM) +* Michael Tietz (MT Software) diff --git a/delivery_send_to_shipper_at_operation/tests/test_send_to_shipper.py b/delivery_send_to_shipper_at_operation/tests/test_send_to_shipper.py index 7fb38ab62d..a2e3001038 100644 --- a/delivery_send_to_shipper_at_operation/tests/test_send_to_shipper.py +++ b/delivery_send_to_shipper_at_operation/tests/test_send_to_shipper.py @@ -1,11 +1,12 @@ # Copyright 2021 Camptocamp SA +# Copyright 2024 Michael Tietz (MT Software) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from unittest import mock from lxml import etree -from odoo.tests.common import SavepointCase +from odoo.tests.common import Form, SavepointCase from odoo.tools.safe_eval import safe_eval SEND_SHIPPING_RETURN_VALUE = [{"exact_price": 10.0, "tracking_number": "TEST"}] @@ -15,7 +16,11 @@ class TestDeliverySendToShipper(SavepointCase): @classmethod def setUpClass(cls): super().setUpClass() - cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.env = cls.env( + context=dict( + cls.env.context, tracking_disable=True, set_default_package=False + ) + ) cls.wh = cls.env.ref("stock.warehouse0") cls.wh.delivery_steps = "pick_pack_ship" cls.stock_location = cls.env.ref("stock.stock_location_stock") @@ -82,10 +87,19 @@ def setUpClass(cls): ) (cls.picking | cls.packing | cls.shipping).sale_id = cls.order - def _validate_picking(self, picking): + def _validate_picking(self, picking, qty_done=None): for ml in picking.move_line_ids: - ml.qty_done = ml.product_uom_qty - picking._action_done() + ml.qty_done = qty_done or ml.product_uom_qty + action_data = picking.button_validate() + if not action_data or action_data is True: + return picking.browse() + backorder_wizard = Form( + self.env["stock.backorder.confirmation"].with_context( + action_data["context"] + ) + ).save() + backorder_wizard.process() + return self.env["stock.picking"].search([("backorder_id", "=", picking.id)]) def test_send_to_shipper_on_ship(self): """Check sending of delivery notification on ship. @@ -195,3 +209,47 @@ def test_picking_fields_view_get(self): attrs_str = button_send_to_shipper.attrib["attrs"] attrs = safe_eval(attrs_str) self.assertIn(("delivery_notification_sent", "=", True), attrs["invisible"]) + + def test_send_to_shipper_on_partial_pack(self): + """Check that the field delivery_notification_sent + is not set on a pack backorder + but on the ship transfer + """ + with mock.patch.object( + type(self.carrier_on_pack), + "send_shipping", + return_value=SEND_SHIPPING_RETURN_VALUE, + ): + self.shipping.carrier_id = self.carrier_on_pack + self._validate_picking(self.picking) + pack_backorder = self._validate_picking(self.packing, 5) + self.assertTrue(self.shipping.delivery_notification_sent) + self.assertFalse(pack_backorder.delivery_notification_sent) + self._validate_picking(pack_backorder, 5) + self.assertTrue(self.shipping.delivery_notification_sent) + backorder = self._validate_picking(self.shipping, 5) + self.assertEqual(self.shipping.state, "done") + self.assertTrue(self.shipping.delivery_notification_sent) + self.assertTrue(backorder.delivery_notification_sent) + backorder2 = self._validate_picking(backorder, 5) + self.assertFalse(backorder2) + self.assertTrue(backorder.delivery_notification_sent) + + def test_send_to_shipper_on_pack_partial_shipping(self): + """Check that delivery_notification_sent is set on a ship backorder""" + with mock.patch.object( + type(self.carrier_on_pack), + "send_shipping", + return_value=SEND_SHIPPING_RETURN_VALUE, + ): + self.shipping.carrier_id = self.carrier_on_pack + self._validate_picking(self.picking) + self._validate_picking(self.packing) + self.assertTrue(self.shipping.delivery_notification_sent) + backorder = self._validate_picking(self.shipping, 5) + self.assertEqual(self.shipping.state, "done") + self.assertTrue(self.shipping.delivery_notification_sent) + self.assertTrue(backorder.delivery_notification_sent) + backorder2 = self._validate_picking(backorder, 5) + self.assertFalse(backorder2) + self.assertTrue(backorder.delivery_notification_sent)