diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py
index 235ea0ee642..b5bfb6b2e40 100644
--- a/addons/mrp/models/mrp_production.py
+++ b/addons/mrp/models/mrp_production.py
@@ -651,8 +651,6 @@ class MrpProduction(models.Model):
location.check_access_rule('read')
except (AttributeError, AccessError):
location = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1).lot_stock_id
- self.move_raw_ids.update({'picking_type_id': self.picking_type_id})
- self.move_finished_ids.update({'picking_type_id': self.picking_type_id})
self.location_src_id = self.picking_type_id.default_location_src_id.id or location.id
self.location_dest_id = self.picking_type_id.default_location_dest_id.id or location.id
diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py
index c2a61b97090..75150cae3a0 100644
--- a/addons/mrp/models/stock_move.py
+++ b/addons/mrp/models/stock_move.py
@@ -115,6 +115,13 @@ class StockMove(models.Model):
for move in self:
move.priority = move.raw_material_production_id.priority or move.priority or '0'
+ @api.depends('raw_material_production_id.picking_type_id', 'production_id.picking_type_id')
+ def _compute_picking_type_id(self):
+ super()._compute_picking_type_id()
+ for move in self:
+ if move.raw_material_production_id or move.production_id:
+ move.picking_type_id = (move.raw_material_production_id or move.production_id).picking_type_id
+
@api.depends('raw_material_production_id.lot_producing_id')
def _compute_order_finished_lot_ids(self):
for move in self:
diff --git a/addons/mrp/tests/test_procurement.py b/addons/mrp/tests/test_procurement.py
index 297adfcf455..0932da42624 100644
--- a/addons/mrp/tests/test_procurement.py
+++ b/addons/mrp/tests/test_procurement.py
@@ -453,6 +453,8 @@ class TestProcurement(TestMrpCommon):
'product_uom': product_1.uom_id.id,
'product_uom_qty': 10.00,
'procure_method': 'make_to_stock',
+ 'location_id': self.warehouse.lot_stock_id.id,
+ 'location_dest_id': self.ref('stock.stock_location_customers'),
})],
})
pick_output.action_confirm() # should trigger orderpoint to create and confirm 1st MO
diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index dfeaa578e1e..0699dde62a4 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -132,7 +132,7 @@ class StockMove(models.Model):
'Propagate cancel and split', default=True,
help='If checked, when this move is cancelled, cancel the linked move too')
delay_alert_date = fields.Datetime('Delay Alert Date', help='Process at this date to be on time', compute="_compute_delay_alert_date", store=True)
- picking_type_id = fields.Many2one('stock.picking.type', 'Operation Type', check_company=True)
+ picking_type_id = fields.Many2one('stock.picking.type', 'Operation Type', compute='_compute_picking_type_id', store=True, check_company=True)
inventory_id = fields.Many2one('stock.inventory', 'Inventory', check_company=True)
move_line_ids = fields.One2many('stock.move.line', 'move_id')
move_line_nosuggest_ids = fields.One2many('stock.move.line', 'move_id', domain=['|', ('product_qty', '=', 0.0), ('qty_done', '!=', 0.0)])
@@ -156,10 +156,10 @@ class StockMove(models.Model):
warehouse_id = fields.Many2one('stock.warehouse', 'Warehouse', help="Technical field depicting the warehouse to consider for the route selection on the next procurement (if any).")
has_tracking = fields.Selection(related='product_id.tracking', string='Product with Tracking')
quantity_done = fields.Float('Quantity Done', compute='_quantity_done_compute', digits='Product Unit of Measure', inverse='_quantity_done_set')
- show_operations = fields.Boolean(related='picking_id.picking_type_id.show_operations', readonly=False)
+ show_operations = fields.Boolean(related='picking_id.picking_type_id.show_operations')
+ picking_code = fields.Selection(related='picking_id.picking_type_id.code', readonly=True)
show_details_visible = fields.Boolean('Details Visible', compute='_compute_show_details_visible')
show_reserved_availability = fields.Boolean('From Supplier', compute='_compute_show_reserved_availability')
- picking_code = fields.Selection(related='picking_id.picking_type_id.code', readonly=True)
product_type = fields.Selection(related='product_id.type', readonly=True)
additional = fields.Boolean("Whether the move was added after the picking's confirmation", default=False)
is_locked = fields.Boolean(compute='_compute_is_locked', readonly=True)
@@ -200,6 +200,12 @@ class StockMove(models.Model):
for move in self:
move.priority = move.picking_id.priority or '0'
+ @api.depends('picking_id.picking_type_id')
+ def _compute_picking_type_id(self):
+ for move in self:
+ if move.picking_id:
+ move.picking_type_id = move.picking_id.picking_type_id
+
@api.depends('picking_id.is_locked')
def _compute_is_locked(self):
for move in self:
@@ -225,10 +231,10 @@ class StockMove(models.Model):
elif len(move.move_line_ids) > 1:
move.show_details_visible = True
else:
- move.show_details_visible = (((consignment_enabled and move.picking_id.picking_type_id.code != 'incoming') or
+ move.show_details_visible = (((consignment_enabled and move.picking_code != 'incoming') or
show_details_visible or move.has_tracking != 'none') and
move._show_details_in_draft() and
- move.picking_id.picking_type_id.show_operations is False)
+ move.show_operations is False)
def _compute_show_reserved_availability(self):
""" This field is only of use in an attrs in the picking view, in order to hide the
@@ -300,7 +306,7 @@ class StockMove(models.Model):
else:
move.delay_alert_date = False
- @api.depends('move_line_ids.qty_done', 'move_line_ids.product_uom_id', 'move_line_nosuggest_ids.qty_done', 'picking_type_id')
+ @api.depends('move_line_ids.qty_done', 'move_line_ids.product_uom_id', 'move_line_nosuggest_ids.qty_done', 'picking_type_id.show_reserved')
def _quantity_done_compute(self):
""" This field represents the sum of the move lines `qty_done`. It allows the user to know
if there is still work to do.
@@ -400,7 +406,7 @@ class StockMove(models.Model):
total_availability = self.env['stock.quant']._get_available_quantity(move.product_id, move.location_id) if move.product_id else 0.0
move.availability = min(move.product_qty, total_availability)
- @api.depends('product_id', 'picking_type_id', 'picking_id', 'reserved_availability', 'priority', 'state', 'product_uom_qty', 'location_id')
+ @api.depends('product_id', 'picking_type_id', 'reserved_availability', 'priority', 'state', 'product_uom_qty', 'location_id')
def _compute_forecast_information(self):
""" Compute forecasted information of the related product by warehouse."""
self.forecast_availability = False
@@ -415,11 +421,10 @@ class StockMove(models.Model):
outgoing_unreserved_moves_per_warehouse = defaultdict(lambda: self.env['stock.move'])
for move in product_moves:
- picking_type = move.picking_type_id or move.picking_id.picking_type_id
is_unreserved = move.state in ('waiting', 'confirmed', 'partially_available')
- if picking_type.code in self._consuming_picking_types() and is_unreserved:
+ if move.picking_type_id.code in self._consuming_picking_types() and is_unreserved:
outgoing_unreserved_moves_per_warehouse[warehouse_by_location[move.location_id]] |= move
- elif picking_type.code in self._consuming_picking_types():
+ elif move.picking_type_id.code in self._consuming_picking_types():
move.forecast_availability = move.reserved_availability
for warehouse, moves in outgoing_unreserved_moves_per_warehouse.items():
@@ -497,17 +502,13 @@ class StockMove(models.Model):
move_lines_commands.append((0, 0, move_line_vals))
move.write({'move_line_ids': move_lines_commands})
- @api.depends('picking_id', 'picking_type_id', 'date')
+ @api.depends('picking_type_id', 'date')
def _compute_reservation_date(self):
for move in self:
- if move.state in ['draft', 'confirmed', 'waiting', 'partially_available']:
- picking_type_id = False
- if move.picking_type_id:
- picking_type_id = move.picking_type_id
- elif move.picking_id:
- picking_type_id = move.picking_id.picking_type_id
- if picking_type_id and picking_type_id.reservation_method == 'by_date':
- move.reservation_date = fields.Date.to_date(move.date) - timedelta(days=move.picking_type_id.reservation_days_before)
+ if move.state not in ['draft', 'confirmed', 'waiting', 'partially_available']:
+ continue
+ if move.picking_type_id and move.picking_type_id.reservation_method == 'by_date':
+ move.reservation_date = fields.Date.to_date(move.date) - timedelta(days=move.picking_type_id.reservation_days_before)
@api.constrains('product_uom')
def _check_uom(self):
@@ -614,13 +615,11 @@ class StockMove(models.Model):
"""
self.ensure_one()
- picking_type_id = self.picking_type_id or self.picking_id.picking_type_id
-
# If "show suggestions" is not checked on the picking type, we have to filter out the
# reserved move lines. We do this by displaying `move_line_nosuggest_ids`. We use
# different views to display one field or another so that the webclient doesn't have to
# fetch both.
- if picking_type_id.show_reserved:
+ if self.picking_type_id.show_reserved:
view = self.env.ref('stock.view_stock_move_operations')
else:
view = self.env.ref('stock.view_stock_move_nosuggest_operations')
@@ -637,8 +636,8 @@ class StockMove(models.Model):
'context': dict(
self.env.context,
show_owner=self.picking_type_id.code != 'incoming',
- show_lots_m2o=self.has_tracking != 'none' and (picking_type_id.use_existing_lots or self.state == 'done' or self.origin_returned_move_id.id), # able to create lots, whatever the value of ` use_create_lots`.
- show_lots_text=self.has_tracking != 'none' and picking_type_id.use_create_lots and not picking_type_id.use_existing_lots and self.state != 'done' and not self.origin_returned_move_id.id,
+ show_lots_m2o=self.has_tracking != 'none' and (self.picking_type_id.use_existing_lots or self.state == 'done' or self.origin_returned_move_id.id), # able to create lots, whatever the value of ` use_create_lots`.
+ show_lots_text=self.has_tracking != 'none' and self.picking_type_id.use_create_lots and not self.picking_type_id.use_existing_lots and self.state != 'done' and not self.origin_returned_move_id.id,
show_source_location=self.picking_type_id.code != 'incoming',
show_destination_location=self.picking_type_id.code != 'outgoing',
show_package=not self.location_id.usage == 'supplier',
@@ -897,7 +896,7 @@ class StockMove(models.Model):
'warning': {'title': _('Warning'), 'message': _('Existing Serial numbers (%s). Please correct the serial numbers encoded.') % ','.join(used_lots.lot_id.mapped('display_name'))}
}
- @api.onchange('move_line_ids', 'move_line_nosuggest_ids')
+ @api.onchange('move_line_ids', 'move_line_nosuggest_ids', 'picking_type_id')
def onchange_move_line_ids(self):
if not self.picking_type_id.use_create_lots:
# This onchange manages the creation of multiple lot name. We don't
@@ -1141,7 +1140,6 @@ class StockMove(models.Model):
and move.state == 'confirmed'
and (move._should_bypass_reservation()
or move.picking_type_id.reservation_method == 'at_confirm'
- or move.picking_id.picking_type_id.reservation_method == 'at_confirm'
or (move.reservation_date and move.reservation_date <= fields.Date.today())))\
._action_assign()
if new_push_moves:
@@ -1169,7 +1167,7 @@ class StockMove(models.Model):
'move_dest_ids': self,
'group_id': group_id,
'route_ids': self.route_ids,
- 'warehouse_id': self.warehouse_id or self.picking_id.picking_type_id.warehouse_id or self.picking_type_id.warehouse_id,
+ 'warehouse_id': self.warehouse_id or self.picking_type_id.warehouse_id,
'priority': self.priority,
'orderpoint_id': self.orderpoint_id,
}
@@ -1800,8 +1798,7 @@ class StockMove(models.Model):
for move in self:
domains.append([('product_id', '=', move.product_id.id), ('location_id', '=', move.location_dest_id.id)])
static_domain = [('state', 'in', ['confirmed', 'partially_available']), ('procure_method', '=', 'make_to_stock')]
- reservation_domain = ['|', '|', ('picking_type_id.reservation_method', '=', 'at_confirm'),
- ('picking_id.picking_type_id.reservation_method', '=', 'at_confirm'),
- ('reservation_date', '<=', fields.Date.today())]
+ reservation_domain = ['|', ('picking_type_id.reservation_method', '=', 'at_confirm'),
+ ('reservation_date', '<=', fields.Date.today())]
moves_to_reserve = self.env['stock.move'].search(expression.AND([static_domain, expression.OR(domains), reservation_domain]))
moves_to_reserve._action_assign()
diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py
index 1b64256df4f..4ebbccecb2d 100644
--- a/addons/stock/models/stock_move_line.py
+++ b/addons/stock/models/stock_move_line.py
@@ -59,6 +59,7 @@ class StockMoveLine(models.Model):
picking_code = fields.Selection(related='picking_id.picking_type_id.code', readonly=True)
picking_type_use_create_lots = fields.Boolean(related='picking_id.picking_type_id.use_create_lots', readonly=True)
picking_type_use_existing_lots = fields.Boolean(related='picking_id.picking_type_id.use_existing_lots', readonly=True)
+ picking_type_entire_packs = fields.Boolean(related='picking_id.picking_type_id.show_entire_packs', readonly=True)
state = fields.Selection(related='move_id.state', store=True, related_sudo=False)
is_initial_demand_editable = fields.Boolean(related='move_id.is_initial_demand_editable', readonly=False)
is_locked = fields.Boolean(related='move_id.is_locked', default=True, readonly=True)
@@ -67,7 +68,6 @@ class StockMoveLine(models.Model):
reference = fields.Char(related='move_id.reference', store=True, related_sudo=False, readonly=False)
tracking = fields.Selection(related='product_id.tracking', readonly=True)
origin = fields.Char(related='move_id.origin', string='Source')
- picking_type_entire_packs = fields.Boolean(related='picking_id.picking_type_id.show_entire_packs', readonly=True)
description_picking = fields.Text(string="Description picking")
@api.depends('picking_id.picking_type_id', 'product_id.tracking')
diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py
index 783523594b9..139fa870cdb 100644
--- a/addons/stock/models/stock_picking.py
+++ b/addons/stock/models/stock_picking.py
@@ -595,9 +595,11 @@ class Picking(models.Model):
self.location_id = location_id
self.location_dest_id = location_dest_id
(self.move_lines | self.move_ids_without_package).update({
- "picking_type_id": self.picking_type_id,
+ "picking_type_id": self.picking_type_id, # The compute store doesn't work in case of One2many inverse (move_ids_without_package)
"company_id": self.company_id,
})
+ for move in (self.move_lines | self.move_ids_without_package):
+ move.description_picking = move.product_id._get_description(move.picking_type_id)
if self.partner_id and self.partner_id.picking_warn:
if self.partner_id.picking_warn == 'no-message' and self.partner_id.parent_id:
@@ -614,6 +616,13 @@ class Picking(models.Model):
'message': partner.picking_warn_msg
}}
+ @api.onchange('location_id', 'location_dest_id')
+ def _onchange_locations(self):
+ (self.move_lines | self.move_ids_without_package).update({
+ "location_id": self.location_id,
+ "location_dest_id": self.location_dest_id
+ })
+
@api.model
def create(self, vals):
defaults = self.default_get(['name', 'picking_type_id'])
@@ -622,22 +631,6 @@ class Picking(models.Model):
if picking_type.sequence_id:
vals['name'] = picking_type.sequence_id.next_by_id()
- # As the on_change in one2many list is WIP, we will overwrite the locations on the stock moves here
- # As it is a create the format will be a list of (0, 0, dict)
- moves = vals.get('move_lines', []) + vals.get('move_ids_without_package', [])
- if moves and vals.get('location_id') and vals.get('location_dest_id'):
- for move in moves:
- if len(move) == 3 and move[0] == 0:
- move[2]['location_id'] = vals['location_id']
- move[2]['location_dest_id'] = vals['location_dest_id']
- # When creating a new picking, a move can have no `company_id` (create before
- # picking type was defined) or a different `company_id` (the picking type was
- # changed for an another company picking type after the move was created).
- # So, we define the `company_id` in one of these cases.
- picking_type = self.env['stock.picking.type'].browse(vals['picking_type_id'])
- if 'picking_type_id' not in move[2] or move[2]['picking_type_id'] != picking_type.id:
- move[2]['picking_type_id'] = picking_type.id
- move[2]['company_id'] = picking_type.company_id.id
# make sure to write `schedule_date` *after* the `stock.move` creation in
# order to get a determinist execution of `_set_scheduled_date`
scheduled_date = vals.pop('scheduled_date', False)
diff --git a/addons/stock/models/stock_rule.py b/addons/stock/models/stock_rule.py
index f9e2d273d91..4491185142b 100644
--- a/addons/stock/models/stock_rule.py
+++ b/addons/stock/models/stock_rule.py
@@ -489,10 +489,8 @@ class ProcurementGroup(models.Model):
('state', 'in', ['confirmed', 'partially_available']),
('product_uom_qty', '!=', 0.0)
]
- # TODO: make picking_type_id/reservation method one value on stock.move
- moves_domain = expression.AND([moves_domain, ['|', '|', ('picking_type_id.reservation_method', '=', 'at_confirm'),
- ('picking_id.picking_type_id.reservation_method', '=', 'at_confirm'),
- ('reservation_date', '<=', fields.Date.today())]])
+ moves_domain = expression.AND([moves_domain, ['|', ('picking_type_id.reservation_method', '=', 'at_confirm'),
+ ('reservation_date', '<=', fields.Date.today())]])
if company_id:
moves_domain = expression.AND([[('company_id', '=', company_id)], moves_domain])
return moves_domain
diff --git a/addons/stock/tests/test_proc_rule.py b/addons/stock/tests/test_proc_rule.py
index 85d52d2e288..e6435dcaa4b 100644
--- a/addons/stock/tests/test_proc_rule.py
+++ b/addons/stock/tests/test_proc_rule.py
@@ -52,6 +52,8 @@ class TestProcRule(TransactionCase):
'product_uom': product.uom_id.id,
'product_uom_qty': 10.00,
'procure_method': 'make_to_order',
+ 'location_id': self.ref('stock.stock_location_output'),
+ 'location_dest_id': self.ref('stock.stock_location_customers'),
})],
}
pick_output = self.env['stock.picking'].create(vals)
diff --git a/addons/stock/views/stock_move_views.xml b/addons/stock/views/stock_move_views.xml
index 1a40df4f688..b3d53c08c9a 100644
--- a/addons/stock/views/stock_move_views.xml
+++ b/addons/stock/views/stock_move_views.xml
@@ -135,6 +135,7 @@
+