From 6ffc159255754ed3a9869a965eb40be331299b00 Mon Sep 17 00:00:00 2001 From: laurentbarontini Date: Sun, 7 Jun 2026 09:47:55 +0200 Subject: [PATCH] Planned --- modules/purchase_trade/lot.py | 66 ++++++++++++++---- modules/purchase_trade/tests/test_module.py | 75 +++++++++++++++++++++ 2 files changed, 129 insertions(+), 12 deletions(-) diff --git a/modules/purchase_trade/lot.py b/modules/purchase_trade/lot.py index 97e1cd8..e67921d 100755 --- a/modules/purchase_trade/lot.py +++ b/modules/purchase_trade/lot.py @@ -2970,14 +2970,24 @@ class LotShipping(Wizard): def default_ship(self, fields): values = {} + planned_from_locations = { + record.r_planned_from_location.id + for record in self.records + if record.r_planned_from_location + } + planned_to_locations = { + record.r_planned_to_location.id + for record in self.records + if record.r_planned_to_location + } + if len(planned_from_locations) == 1: + values['planned_from_location'] = next(iter( + planned_from_locations)) + if len(planned_to_locations) == 1: + values['planned_to_location'] = next(iter(planned_to_locations)) if len(self.records) != 1: return values record = self.records[0] - if record.r_planned_from_location: - values['planned_from_location'] = ( - record.r_planned_from_location.id) - if record.r_planned_to_location: - values['planned_to_location'] = record.r_planned_to_location.id return values @staticmethod @@ -3011,6 +3021,34 @@ class LotShipping(Wizard): shipment, = ShipmentIn.create([self._shipment_in_create_values()]) self.ship.shipment_in = shipment + def _scheduled_lotqt_values(self, lqt, quantity): + values = { + 'lot_p': self._record_id(lqt.lot_p), + 'lot_s': self._record_id(lqt.lot_s), + 'lot_quantity': quantity, + 'lot_unit': self._record_id(lqt.lot_unit), + 'lot_av': lqt.lot_av, + 'lot_status': lqt.lot_status, + 'lot_visible': lqt.lot_visible, + 'planned_from_location': self._record_id( + lqt.planned_from_location), + 'planned_to_location': self._record_id(lqt.planned_to_location), + 'planned_transport_type': lqt.planned_transport_type, + 'planned_from_date': lqt.planned_from_date, + 'planned_to_date': lqt.planned_to_date, + 'planned_note': lqt.planned_note, + } + if self.ship.shipment == 'in': + values['lot_shipment_in'] = self.ship.shipment_in.id + elif self.ship.shipment == 'out': + values['lot_shipment_out'] = self.ship.shipment_out.id + elif self.ship.shipment == 'int': + values['lot_shipment_internal'] = self.ship.shipment_internal.id + return values + + def _create_scheduled_lotqt(self, LotQt, lqt, quantity): + LotQt.create([self._scheduled_lotqt_values(lqt, quantity)]) + def transition_shipping(self): Lot = Pool().get('lot.lot') LotQt = Pool().get('lot.qt') @@ -3094,13 +3132,17 @@ class LotShipping(Wizard): if Decimal(str(lqt.lot_quantity or 0)) < 0: signed_shipped_quantity = -shipped_quantity #Increase forecasted virtual part shipped - if not plan_lot.updateVirtualPart( - signed_shipped_quantity, shipment_origin, - lqt.lot_s, plan_mode): - logger.info("LotShipping2:%s",shipped_quantity) - plan_lot.createVirtualPart( - signed_shipped_quantity, shipment_origin, - lqt.lot_s, plan_mode) + if lqt.is_planned_transport(): + self._create_scheduled_lotqt( + LotQt, lqt, signed_shipped_quantity) + else: + if not plan_lot.updateVirtualPart( + signed_shipped_quantity, shipment_origin, + lqt.lot_s, plan_mode): + logger.info("LotShipping2:%s", shipped_quantity) + plan_lot.createVirtualPart( + signed_shipped_quantity, shipment_origin, + lqt.lot_s, plan_mode) #Decrease forecasted virtual part non shipped if lqt.is_planned_transport(): lqt.lot_quantity -= signed_shipped_quantity diff --git a/modules/purchase_trade/tests/test_module.py b/modules/purchase_trade/tests/test_module.py index 56d5f0d..a714ad6 100644 --- a/modules/purchase_trade/tests/test_module.py +++ b/modules/purchase_trade/tests/test_module.py @@ -219,6 +219,37 @@ class PurchaseTradeTestCase(ModuleTestCase): 'planned_to_location': 20, }) + def test_lot_shipping_defaults_uniform_planned_locations(self): + 'link to transport carries uniform planned locations from selection' + wizard = lot_module.LotShipping() + wizard.records = [ + Mock( + r_planned_from_location=Mock(id=10), + r_planned_to_location=Mock(id=20)), + Mock( + r_planned_from_location=Mock(id=10), + r_planned_to_location=Mock(id=20)), + ] + + self.assertEqual(wizard.default_ship(None), { + 'planned_from_location': 10, + 'planned_to_location': 20, + }) + + def test_lot_shipping_ignores_mixed_planned_locations(self): + 'link to transport does not force mixed planned locations' + wizard = lot_module.LotShipping() + wizard.records = [ + Mock( + r_planned_from_location=Mock(id=10), + r_planned_to_location=Mock(id=20)), + Mock( + r_planned_from_location=Mock(id=11), + r_planned_to_location=Mock(id=21)), + ] + + self.assertEqual(wizard.default_ship(None), {}) + def test_lot_shipping_link_creates_shipment_from_dialog_supplier(self): 'link to transport creates shipment in from dialog supplier' wizard = lot_module.LotShipping() @@ -271,6 +302,50 @@ class PurchaseTradeTestCase(ModuleTestCase): with self.assertRaises(UserError): wizard.transition_shipping() + def test_lot_shipping_scheduled_lotqt_keeps_planned_locations(self): + 'scheduled lot.qt keeps the planned from and to locations' + wizard = lot_module.LotShipping() + wizard.ship = Mock( + shipment='in', + shipment_in=Mock(id=40), + shipment_out=None, + shipment_internal=None, + ) + lqt = Mock( + lot_p=Mock(id=1), + lot_s=Mock(id=2), + lot_unit=Mock(id=3), + lot_av='available', + lot_status='forecast', + lot_visible=True, + planned_from_location=Mock(id=10), + planned_to_location=Mock(id=20), + planned_transport_type='vessel', + planned_from_date=datetime.date(2026, 7, 1), + planned_to_date=datetime.date(2026, 7, 5), + planned_note='POL to POD', + ) + LotQt = Mock() + + wizard._create_scheduled_lotqt(LotQt, lqt, Decimal('125')) + + LotQt.create.assert_called_once_with([{ + 'lot_p': 1, + 'lot_s': 2, + 'lot_quantity': Decimal('125'), + 'lot_unit': 3, + 'lot_av': 'available', + 'lot_status': 'forecast', + 'lot_visible': True, + 'planned_from_location': 10, + 'planned_to_location': 20, + 'planned_transport_type': 'vessel', + 'planned_from_date': datetime.date(2026, 7, 1), + 'planned_to_date': datetime.date(2026, 7, 5), + 'planned_note': 'POL to POD', + 'lot_shipment_in': 40, + }]) + @with_transaction() def test_get_mtm_applies_component_ratio_as_percentage(self): 'get_mtm treats component ratio as a percentage'