Link to transport

This commit is contained in:
2026-06-07 07:53:18 +02:00
parent 126225ea6a
commit 700adb87f9
3 changed files with 68 additions and 33 deletions

View File

@@ -2958,11 +2958,9 @@ class LotShipping(Wizard):
'lot.shipping.start',
'purchase_trade.shipping_view_form', [
Button("Cancel", 'end', 'tryton-cancel'),
Button("New shipment & link", 'create_shipment', 'tryton-add'),
Button("Link", 'shipping', 'tryton-ok', default=True),
])
create_shipment = StateTransition()
shipping = StateTransition()
def transition_start(self):
@@ -2986,25 +2984,21 @@ class LotShipping(Wizard):
def _record_id(record):
return record.id if record else None
def _unique_supplier(self):
suppliers = {}
for record in self.records:
supplier = getattr(record, 'r_supplier', None)
if supplier:
suppliers[supplier.id] = supplier
if len(suppliers) == 1:
return next(iter(suppliers.values()))
if not suppliers:
raise UserError("Supplier is required to create a shipment.")
raise UserError(
"Please select lots with the same supplier to create a shipment.")
def _selected_shipment(self):
if self.ship.shipment == 'in':
return self.ship.shipment_in
if self.ship.shipment == 'out':
return self.ship.shipment_out
if self.ship.shipment == 'int':
return self.ship.shipment_internal
def _shipment_in_create_values(self):
if self.ship.shipment != 'in':
raise UserError("Shipment creation is only available for Shipment In.")
supplier = self._unique_supplier()
if not self.ship.shipment_supplier:
raise UserError("Shipment supplier is required.")
values = {
'supplier': self._record_id(supplier),
'supplier': self._record_id(self.ship.shipment_supplier),
}
if self.ship.planned_from_location:
values['from_location'] = self.ship.planned_from_location.id
@@ -3012,13 +3006,10 @@ class LotShipping(Wizard):
values['to_location'] = self.ship.planned_to_location.id
return values
def transition_create_shipment(self):
def _create_shipment_in(self):
ShipmentIn = Pool().get('stock.shipment.in')
if self.ship.shipment_in:
return self.transition_shipping()
shipment, = ShipmentIn.create([self._shipment_in_create_values()])
self.ship.shipment_in = shipment
return self.transition_shipping()
def transition_shipping(self):
Lot = Pool().get('lot.lot')
@@ -3027,6 +3018,11 @@ class LotShipping(Wizard):
affected_lines = []
if not self.ship.shipment:
pass
if self.ship.create_new_shipment and self._selected_shipment():
raise UserError(
"Choose either an existing shipment or create a new shipment.")
if self.ship.create_new_shipment:
self._create_shipment_in()
with Lot.skip_quantity_consistency():
for r in self.records:
if r.r_lot_shipment_in:
@@ -3041,15 +3037,15 @@ class LotShipping(Wizard):
shipped_quantity = Decimal(str(r.r_lot_matched)).quantize(Decimal("0.00001"))
if self.ship.shipment == 'in':
if not self.ship.shipment_in:
UserError("Shipment not known!")
raise UserError("Shipment not known!")
shipment_origin = 'stock.shipment.in,'+str(self.ship.shipment_in.id)
elif self.ship.shipment == 'out':
if not self.ship.shipment_out:
UserError("Shipment not known!")
raise UserError("Shipment not known!")
shipment_origin = 'stock.shipment.out,'+str(self.ship.shipment_out.id)
elif self.ship.shipment == 'int':
if not self.ship.shipment_internal:
UserError("Shipment not known!")
raise UserError("Shipment not known!")
shipment_origin = 'stock.shipment.internal,'+str(self.ship.shipment_internal.id)
if r.id < 10000000 :
l = Lot(r.id)
@@ -3137,6 +3133,16 @@ class LotShippingStart(ModelView):
shipment_in = fields.Many2One('stock.shipment.in',"Shipment In", states={'invisible': (Eval('shipment') != 'in')})
shipment_out = fields.Many2One('stock.shipment.out',"Shipment Out", states={'invisible': (Eval('shipment') != 'out')})
shipment_internal = fields.Many2One('stock.shipment.internal',"Shipment Internal", states={'invisible': (Eval('shipment') != 'int')})
create_new_shipment = fields.Boolean(
"Create a new shipment",
states={'invisible': Eval('shipment') != 'in'})
shipment_supplier = fields.Many2One(
'party.party', "Shipment supplier",
states={
'invisible': ~Bool(Eval('create_new_shipment')),
'required': Bool(Eval('create_new_shipment')),
},
depends=['create_new_shipment'])
planned_from_location = fields.Many2One(
'stock.location', "Planned From", readonly=True)
planned_to_location = fields.Many2One(
@@ -3152,6 +3158,10 @@ class LotShippingStart(ModelView):
def default_shipment(cls):
return 'in'
@classmethod
def default_create_new_shipment(cls):
return False
class LotPlanTransport(Wizard):
"Plan lot to transport"

View File

@@ -219,27 +219,33 @@ class PurchaseTradeTestCase(ModuleTestCase):
'planned_to_location': 20,
})
def test_lot_shipping_create_shipment_links_planned_locations(self):
'link to transport creates shipment in and links planned locations'
def test_lot_shipping_link_creates_shipment_from_dialog_supplier(self):
'link to transport creates shipment in from dialog supplier'
wizard = lot_module.LotShipping()
supplier = Mock(id=30)
created_shipment = Mock(id=40)
ShipmentIn = Mock()
ShipmentIn.create.return_value = [created_shipment]
wizard.records = [Mock(r_supplier=supplier)]
wizard.records = []
wizard.ship = Mock(
shipment='in',
shipment_in=None,
shipment_out=None,
shipment_internal=None,
create_new_shipment=True,
shipment_supplier=supplier,
planned_from_location=Mock(id=10),
planned_to_location=Mock(id=20),
)
with patch('trytond.modules.purchase_trade.lot.Pool') as PoolMock:
PoolMock.return_value.get.return_value = ShipmentIn
with patch.object(
wizard, 'transition_shipping', return_value='end'
) as transition_shipping:
state = wizard.transition_create_shipment()
Lot = Mock()
Lot.skip_quantity_consistency.return_value.__enter__ = Mock()
Lot.skip_quantity_consistency.return_value.__exit__ = Mock()
pool = Mock()
pool.get.side_effect = [Lot, Mock(), Mock(), ShipmentIn]
with patch('trytond.modules.purchase_trade.lot.Pool',
return_value=pool):
state = wizard.transition_shipping()
self.assertEqual(state, 'end')
ShipmentIn.create.assert_called_once_with([{
@@ -248,7 +254,22 @@ class PurchaseTradeTestCase(ModuleTestCase):
'to_location': 20,
}])
self.assertEqual(wizard.ship.shipment_in, created_shipment)
transition_shipping.assert_called_once_with()
Lot.assert_lines_quantity_consistency.assert_called_once_with([])
def test_lot_shipping_rejects_existing_and_new_shipment(self):
'link to transport rejects choosing existing shipment and create new'
wizard = lot_module.LotShipping()
wizard.ship = Mock(
shipment='in',
shipment_in=Mock(id=40),
shipment_out=None,
shipment_internal=None,
create_new_shipment=True,
shipment_supplier=Mock(id=30),
)
with self.assertRaises(UserError):
wizard.transition_shipping()
@with_transaction()
def test_get_mtm_applies_component_ratio_as_percentage(self):

View File

@@ -4,6 +4,10 @@
<field name="shipment"/>
<label name="shipment_in"/>
<field name="shipment_in" context="{'lot_shipping_planned_from_location': Eval('planned_from_location'), 'lot_shipping_planned_to_location': Eval('planned_to_location')}"/>
<label name="create_new_shipment"/>
<field name="create_new_shipment"/>
<label name="shipment_supplier"/>
<field name="shipment_supplier"/>
<label name="shipment_out"/>
<field name="shipment_out"/>
<label name="shipment_internal"/>