Use a single assign wizard

issue9422
review299961011
This commit is contained in:
Cédric Krier committed 2020-07-25 08:19:31 +02:00
1 parent 0915d89979
commit f6eb3790f3
6 files changed
+152 -214

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* Use a single assign wizard for all shipments
* Add access rules to product quantities by warehouse
* Add moves for product quantities by warehouse
* Enforce filling cost price of move
+2 -6
View File
@@ -30,11 +30,9 @@ def register():
shipment.ShipmentInReturn,
shipment.ShipmentOut,
shipment.ShipmentOutReturn,
shipment.AssignShipmentOutAssignFailed,
shipment.ShipmentInternal,
shipment.AssignPartial,
shipment.Address,
shipment.AssignShipmentInternalAssignFailed,
shipment.AssignShipmentInReturnAssignFailed,
period.Period,
period.Cache,
product.Template,
@@ -57,9 +55,7 @@ def register():
res.User,
module='stock', type_='model')
Pool.register(
shipment.AssignShipmentOut,
shipment.AssignShipmentInternal,
shipment.AssignShipmentInReturn,
shipment.Assign,
product.OpenProductQuantitiesByWarehouse,
product.RecomputeCostPrice,
product.ModifyCostPrice,
+107 -161
View File
@@ -8,7 +8,7 @@ from functools import partial
from sql import Null
from trytond.i18n import gettext
from trytond.model import Workflow, ModelView, ModelSQL, fields
from trytond.model import Workflow, ModelView, ModelSQL, fields, dualmethod
from trytond.model.exceptions import AccessError
from trytond.modules.company import CompanyReport
from trytond.wizard import Wizard, StateTransition, StateView, Button
@@ -28,6 +28,43 @@ class ShipmentMixin:
]
class ShipmentAssignMixin(ShipmentMixin):
@classmethod
def assign_wizard(cls, shipments):
raise NotImplementedError
@property
def assign_moves(self):
raise NotImplementedError
@dualmethod
@ModelView.button
def assign_try(cls, shipments):
raise NotImplementedError
@dualmethod
def assign_reset(cls, shipments):
cls.wait(shipments)
@dualmethod
@ModelView.button
def assign_force(cls, shipments):
cls.assign(shipments)
@dualmethod
def assign_ignore(cls, shipments):
pool = Pool()
Move = pool.get('stock.move')
Move.write([
m for s in shipments for m in s.assign_moves
if m.assignation_required
and m.state in {'staging', 'draft'}], {
'quantity': 0,
})
cls.assign(shipments)
class ShipmentIn(ShipmentMixin, Workflow, ModelSQL, ModelView):
"Supplier Shipment"
__name__ = 'stock.shipment.in'
@@ -457,7 +494,7 @@ class ShipmentIn(ShipmentMixin, Workflow, ModelSQL, ModelView):
})
class ShipmentInReturn(ShipmentMixin, Workflow, ModelSQL, ModelView):
class ShipmentInReturn(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
"Supplier Return Shipment"
__name__ = 'stock.shipment.in.return'
_rec_name = 'number'
@@ -736,7 +773,7 @@ class ShipmentInReturn(ShipmentMixin, Workflow, ModelSQL, ModelView):
@set_employee('assigned_by')
def assign(cls, shipments):
Move = Pool().get('stock.move')
Move.assign([m for s in shipments for m in s.moves])
Move.assign([m for s in shipments for m in s.assign_moves])
@classmethod
@ModelView.button
@@ -764,7 +801,11 @@ class ShipmentInReturn(ShipmentMixin, Workflow, ModelSQL, ModelView):
def assign_wizard(cls, shipments):
pass
@classmethod
@property
def assign_moves(self):
return self.moves
@dualmethod
@ModelView.button
def assign_try(cls, shipments, with_childs=None):
pool = Pool()
@@ -772,7 +813,7 @@ class ShipmentInReturn(ShipmentMixin, Workflow, ModelSQL, ModelView):
to_assign = defaultdict(list)
for shipment in shipments:
location_type = shipment.from_location.type
for move in shipment.moves:
for move in shipment.assign_moves:
if move.assignation_required:
to_assign[location_type].append(move)
success = True
@@ -789,13 +830,8 @@ class ShipmentInReturn(ShipmentMixin, Workflow, ModelSQL, ModelView):
cls.assign(shipments)
return success
@classmethod
@ModelView.button
def assign_force(cls, shipments):
cls.assign(shipments)
class ShipmentOut(ShipmentMixin, Workflow, ModelSQL, ModelView):
class ShipmentOut(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
"Customer Shipment"
__name__ = 'stock.shipment.out'
_rec_name = 'number'
@@ -1134,6 +1170,9 @@ class ShipmentOut(ShipmentMixin, Workflow, ModelSQL, ModelView):
@Workflow.transition('assigned')
@set_employee('assigned_by')
def assign(cls, shipments):
pool = Pool()
Move = pool.get('stock.move')
Move.assign([m for s in shipments for m in s.assign_moves])
cls._sync_inventory_to_outgoing(shipments, quantity=False)
@classmethod
@@ -1347,11 +1386,16 @@ class ShipmentOut(ShipmentMixin, Workflow, ModelSQL, ModelView):
def assign_wizard(cls, shipments):
pass
@classmethod
@property
def assign_moves(self):
return self.inventory_moves
@dualmethod
@ModelView.button
def assign_try(cls, shipments):
Move = Pool().get('stock.move')
to_assign = [m for s in shipments for m in s.inventory_moves
to_assign = [
m for s in shipments for m in s.assign_moves
if m.assignation_required]
if Move.assign_try(to_assign):
cls.assign(shipments)
@@ -1359,13 +1403,6 @@ class ShipmentOut(ShipmentMixin, Workflow, ModelSQL, ModelView):
else:
return False
@classmethod
@ModelView.button
def assign_force(cls, shipments):
Move = Pool().get('stock.move')
Move.assign([m for s in shipments for m in s.inventory_moves])
cls.assign(shipments)
class ShipmentOutReturn(ShipmentMixin, Workflow, ModelSQL, ModelView):
"Customer Return Shipment"
@@ -1774,56 +1811,7 @@ class ShipmentOutReturn(ShipmentMixin, Workflow, ModelSQL, ModelView):
cls.save(shipments)
class AssignShipmentOutAssignFailed(ModelView):
'Assign Customer Shipment'
__name__ = 'stock.shipment.out.assign.failed'
inventory_moves = fields.Many2Many('stock.move', None, None,
'Inventory Moves', readonly=True,
help="The inventory moves that were not assigned.")
@staticmethod
def default_inventory_moves():
ShipmentOut = Pool().get('stock.shipment.out')
shipment_id = Transaction().context.get('active_id')
if not shipment_id:
return []
shipment = ShipmentOut(shipment_id)
return [x.id for x in shipment.inventory_moves if x.state == 'draft']
class AssignShipmentOut(Wizard):
'Assign Customer Shipment'
__name__ = 'stock.shipment.out.assign'
start = StateTransition()
failed = StateView('stock.shipment.out.assign.failed',
'stock.shipment_out_assign_failed_view_form', [
Button('Force Assign', 'force', 'tryton-forward',
states={
'invisible': ~Id('stock',
'group_stock_force_assignment').in_(
Eval('context', {}).get('groups', [])),
}),
Button('OK', 'end', 'tryton-ok', True),
])
force = StateTransition()
def transition_start(self):
pool = Pool()
Shipment = pool.get('stock.shipment.out')
if Shipment.assign_try([Shipment(Transaction().context['active_id'])]):
return 'end'
else:
return 'failed'
def transition_force(self):
Shipment = Pool().get('stock.shipment.out')
Shipment.assign_force([Shipment(Transaction().context['active_id'])])
return 'end'
class ShipmentInternal(ShipmentMixin, Workflow, ModelSQL, ModelView):
class ShipmentInternal(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
"Internal Shipment"
__name__ = 'stock.shipment.internal'
_rec_name = 'number'
@@ -2353,7 +2341,9 @@ class ShipmentInternal(ShipmentMixin, Workflow, ModelSQL, ModelView):
@Workflow.transition('assigned')
@set_employee('assigned_by')
def assign(cls, shipments):
pass
pool = Pool()
Move = pool.get('stock.move')
Move.assign([m for s in shipments for m in s.assign_moves])
@classmethod
@ModelView.button
@@ -2394,11 +2384,16 @@ class ShipmentInternal(ShipmentMixin, Workflow, ModelSQL, ModelView):
def assign_wizard(cls, shipments):
pass
@classmethod
@property
def assign_moves(self):
return self.outgoing_moves
@dualmethod
@ModelView.button
def assign_try(cls, shipments):
Move = Pool().get('stock.move')
to_assign = [m for s in shipments for m in s.outgoing_moves
to_assign = [
m for s in shipments for m in s.assign_moves
if m.assignation_required]
if not to_assign or Move.assign_try(to_assign):
cls.assign(shipments)
@@ -2406,13 +2401,6 @@ class ShipmentInternal(ShipmentMixin, Workflow, ModelSQL, ModelView):
else:
return False
@classmethod
@ModelView.button
def assign_force(cls, shipments):
Move = Pool().get('stock.move')
Move.assign([m for s in shipments for m in s.outgoing_moves])
cls.assign(shipments)
@property
def _move_planned_date(self):
'''
@@ -2457,103 +2445,61 @@ class Address(metaclass=PoolMeta):
help="Check to send deliveries to the address.")
class AssignShipmentInternalAssignFailed(ModelView):
'Assign Shipment Internal'
__name__ = 'stock.shipment.internal.assign.failed'
moves = fields.Many2Many('stock.move', None, None, 'Moves',
readonly=True,
help="The moves that were not assigned.")
@staticmethod
def default_moves():
ShipmentInternal = Pool().get('stock.shipment.internal')
shipment_id = Transaction().context.get('active_id')
if not shipment_id:
return []
shipment = ShipmentInternal(shipment_id)
return [x.id for x in shipment.outgoing_moves if x.state == 'draft']
class AssignShipmentInternal(Wizard):
'Assign Shipment Internal'
__name__ = 'stock.shipment.internal.assign'
class Assign(Wizard):
"Assign Shipment"
__name__ = 'stock.shipment.assign'
start = StateTransition()
failed = StateView('stock.shipment.internal.assign.failed',
'stock.shipment_internal_assign_failed_view_form', [
Button('Force Assign', 'force', 'tryton-forward',
states={
'invisible': ~Id('stock',
'group_stock_force_assignment').in_(
Eval('context', {}).get('groups', [])),
}),
Button('OK', 'end', 'tryton-ok', True),
])
force = StateTransition()
def transition_start(self):
pool = Pool()
Shipment = pool.get('stock.shipment.internal')
if Shipment.assign_try([Shipment(Transaction().context['active_id'])]):
return 'end'
else:
return 'failed'
def transition_force(self):
Shipment = Pool().get('stock.shipment.internal')
Shipment.assign_force([Shipment(Transaction().context['active_id'])])
return 'end'
class AssignShipmentInReturnAssignFailed(ModelView):
'Assign Supplier Return Shipment'
__name__ = 'stock.shipment.in.return.assign.failed'
moves = fields.Many2Many('stock.move', None, None, 'Moves',
readonly=True,
help="The moves that were not assigned.")
@staticmethod
def default_moves():
ShipmentInternal = Pool().get('stock.shipment.in.return')
shipment_id = Transaction().context.get('active_id')
if not shipment_id:
return []
shipment = ShipmentInternal(shipment_id)
return [x.id for x in shipment.moves if x.state == 'draft']
class AssignShipmentInReturn(Wizard):
'Assign Supplier Return Shipment'
__name__ = 'stock.shipment.in.return.assign'
start = StateTransition()
failed = StateView('stock.shipment.in.return.assign.failed',
'stock.shipment_in_return_assign_failed_view_form', [
Button('Force Assign', 'force', 'tryton-forward',
partial = StateView(
'stock.shipment.assign.partial',
'stock.shipment_assign_partial_view_form', [
Button("Cancel", 'cancel', 'tryton-cancel'),
Button("Wait", 'end', 'tryton-ok', True),
Button("Ignore", 'ignore', 'tryton-forward'),
Button("Force", 'force', 'tryton-forward',
states={
'invisible': ~Id('stock',
'group_stock_force_assignment').in_(
Eval('context', {}).get('groups', [])),
}),
Button('OK', 'end', 'tryton-ok', True),
])
cancel = StateTransition()
force = StateTransition()
ignore = StateTransition()
def transition_start(self):
pool = Pool()
Shipment = pool.get('stock.shipment.in.return')
if Shipment.assign_try([Shipment(Transaction().context['active_id'])]):
if self.record.assign_try():
return 'end'
else:
return 'failed'
return 'partial'
def default_partial(self, fields):
values = {}
if 'moves' in fields:
values['moves'] = [
m.id for m in self.record.assign_moves
if m.state in {'staging', 'draft'}]
return values
def transition_cancel(self):
self.record.assign_reset()
return 'end'
def transition_force(self):
Shipment = Pool().get('stock.shipment.in.return')
Shipment.assign_force([Shipment(Transaction().context['active_id'])])
self.record.assign_force()
return 'end'
def transition_ignore(self):
self.record.assign_ignore()
return 'end'
class AssignPartial(ModelView):
"Assign Shipment"
__name__ = 'stock.shipment.assign.partial'
moves = fields.Many2Many(
'stock.move', None, None, "Moves", readonly=True,
help="The moves that were not assigned.")
class DeliveryNote(CompanyReport):
'Delivery Note'
+23 -36
View File
@@ -54,12 +54,6 @@ this repository contains the full copyright notices and license terms. -->
<menuitem parent="menu_stock" sequence="20"
action="act_shipment_in_form" id="menu_shipment_in_form"/>
<record model="ir.action.wizard" id="wizard_shipment_in_return_assign">
<field name="name">Assign Purchase Return Shipment</field>
<field name="wiz_name">stock.shipment.in.return.assign</field>
<field name="model">stock.shipment.in.return</field>
</record>
<record model="ir.ui.view" id="shipment_in_return_view_form">
<field name="model">stock.shipment.in.return</field>
<field name="type">form</field>
@@ -122,18 +116,6 @@ this repository contains the full copyright notices and license terms. -->
action="act_shipment_in_return_form"
id="menu_shipment_in_return_form"/>
<record model="ir.ui.view" id="shipment_in_return_assign_failed_view_form">
<field name="model">stock.shipment.in.return.assign.failed</field>
<field name="type">form</field>
<field name="name">shipment_in_return_assign_failed_form</field>
</record>
<record model="ir.action.wizard" id="wizard_shipment_out_assign">
<field name="name">Assign Customer Shipment</field>
<field name="wiz_name">stock.shipment.out.assign</field>
<field name="model">stock.shipment.out</field>
</record>
<record model="ir.ui.view" id="shipment_out_view_form">
<field name="model">stock.shipment.out</field>
<field name="type">form</field>
@@ -199,18 +181,6 @@ this repository contains the full copyright notices and license terms. -->
<menuitem parent="menu_stock" sequence="30"
action="act_shipment_out_form" id="menu_shipment_out_form"/>
<record model="ir.ui.view" id="shipment_out_assign_failed_view_form">
<field name="model">stock.shipment.out.assign.failed</field>
<field name="type">form</field>
<field name="name">shipment_out_assign_failed_form</field>
</record>
<record model="ir.action.wizard" id="wizard_shipment_internal_assign">
<field name="name">Assign Shipment Internal</field>
<field name="wiz_name">stock.shipment.internal.assign</field>
<field name="model">stock.shipment.internal</field>
</record>
<record model="ir.ui.view" id="shipment_internal_view_form">
<field name="model">stock.shipment.internal</field>
<field name="type">form</field>
@@ -288,12 +258,6 @@ this repository contains the full copyright notices and license terms. -->
action="act_shipment_internal_form"
id="menu_shipment_internal_form"/>
<record model="ir.ui.view" id="shipment_internal_assign_failed_view_form">
<field name="model">stock.shipment.internal.assign.failed</field>
<field name="type">form</field>
<field name="name">shipment_internal_assign_failed_form</field>
</record>
<record model="ir.ui.view" id="shipment_out_return_view_form">
<field name="model">stock.shipment.out.return</field>
<field name="type">form</field>
@@ -1069,5 +1033,28 @@ this repository contains the full copyright notices and license terms. -->
pyson="1"/>
<field name="rule_group" ref="rule_group_shipment_internal"/>
</record>
<record model="ir.ui.view" id="shipment_assign_partial_view_form">
<field name="model">stock.shipment.assign.partial</field>
<field name="type">form</field>
<field name="name">shipment_assign_partial_form</field>
</record>
<record model="ir.action.wizard" id="wizard_shipment_in_return_assign">
<field name="name">Assign Supplier Return Shipment</field>
<field name="wiz_name">stock.shipment.assign</field>
<field name="model">stock.shipment.in.return</field>
</record>
<record model="ir.action.wizard" id="wizard_shipment_out_assign">
<field name="name">Assign Customer Shipment</field>
<field name="wiz_name">stock.shipment.assign</field>
<field name="model">stock.shipment.out</field>
</record>
<record model="ir.action.wizard" id="wizard_shipment_internal_assign">
<field name="name">Assign Internal Shipment</field>
<field name="wiz_name">stock.shipment.assign</field>
<field name="model">stock.shipment.internal</field>
</record>
</data>
</tryton>
+11 -11
View File
@@ -132,8 +132,10 @@ Make 1 unit of the product available::
Assign the shipment now::
>>> shipment_out.click('assign_try')
False
>>> shipment_assign = Wizard('stock.shipment.assign', [shipment_out])
>>> len(shipment_assign.form.moves)
1
>>> shipment_assign.execute('end')
>>> shipment_out.reload()
>>> len(shipment_out.outgoing_moves)
2
@@ -152,14 +154,12 @@ Assign the shipment now::
>>> len(set(planned_dates))
1
Delete the draft move, assign and pack shipment::
Ignore non assigned moves and pack shipment::
>>> for move in shipment_out.inventory_moves:
... if move.state == 'draft':
... break
>>> shipment_out.inventory_moves.remove(move)
>>> shipment_out.click('assign_try')
True
>>> shipment_assign = Wizard('stock.shipment.assign', [shipment_out])
>>> shipment_assign.execute('ignore')
>>> sorted([m.quantity for m in shipment_out.inventory_moves])
[0.0, 1.0]
>>> shipment_out.assigned_by == employee
True
>>> shipment_out.packed_by
@@ -174,7 +174,7 @@ Delete the draft move, assign and pack shipment::
>>> len(shipment_out.outgoing_moves)
2
>>> len(shipment_out.inventory_moves)
1
2
>>> shipment_out.inventory_moves[0].state
'done'
>>> sum([m.quantity for m in shipment_out.inventory_moves]) == \
@@ -199,7 +199,7 @@ Set the state as Done::
>>> len(shipment_out.outgoing_moves)
2
>>> len(shipment_out.inventory_moves)
1
2
>>> shipment_out.inventory_moves[0].state
'done'
>>> sum([m.quantity for m in shipment_out.inventory_moves]) == \
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form col="2">
<image name="tryton-warning" xexpand="0" xfill="0"/>
<separator string="Unable to assign these products:" id="unable"/>
<field name="moves" colspan="2" view_ids="stock.move_view_tree_simple"/>
</form>