repair/models/stock_move.py

191 lines
8.0 KiB
Python
Raw Permalink Normal View History

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, Command, fields, models
from odoo.tools.misc import groupby
MAP_REPAIR_LINE_TYPE_TO_MOVE_LOCATIONS_FROM_REPAIR = {
'add': {'location_id': 'location_id', 'location_dest_id': 'location_dest_id'},
'remove': {'location_id': 'location_dest_id', 'location_dest_id': 'parts_location_id'},
'recycle': {'location_id': 'location_dest_id', 'location_dest_id': 'recycle_location_id'},
}
class StockMove(models.Model):
_inherit = 'stock.move'
repair_id = fields.Many2one('repair.order', check_company=True)
repair_line_type = fields.Selection([
('add', 'Add'),
('remove', 'Remove'),
('recycle', 'Recycle')
], 'Type', store=True, index=True)
@api.depends('repair_line_type')
def _compute_forecast_information(self):
moves_to_compute = self.filtered(lambda move: not move.repair_line_type or move.repair_line_type == 'add')
for move in (self - moves_to_compute):
move.forecast_availability = move.product_qty
move.forecast_expected_date = False
return super(StockMove, moves_to_compute)._compute_forecast_information()
@api.depends('repair_id.picking_type_id')
def _compute_picking_type_id(self):
remaining_moves = self
for move in self:
if move.repair_id:
move.picking_type_id = move.repair_id.picking_type_id
remaining_moves -= move
return super(StockMove, remaining_moves)._compute_picking_type_id()
def copy_data(self, default=None):
default = dict(default or {})
if 'repair_id' in default or self.repair_id:
default['sale_line_id'] = False
return super().copy_data(default)
@api.ondelete(at_uninstall=False)
def _unlink_if_draft_or_cancel(self):
self.filtered('repair_id')._action_cancel()
return super()._unlink_if_draft_or_cancel()
def unlink(self):
self._clean_repair_sale_order_line()
return super().unlink()
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if not vals.get('repair_id') or 'repair_line_type' not in vals:
continue
repair_id = self.env['repair.order'].browse([vals['repair_id']])
vals['name'] = repair_id.name
src_location, dest_location = self._get_repair_locations(vals['repair_line_type'], repair_id)
if not vals.get('location_id'):
vals['location_id'] = src_location.id
if not vals.get('location_dest_id'):
vals['location_dest_id'] = dest_location.id
moves = super().create(vals_list)
repair_moves = self.env['stock.move']
for move in moves:
if not move.repair_id:
continue
move.group_id = move.repair_id.procurement_group_id.id
move.origin = move.name
move.picking_type_id = move.repair_id.picking_type_id.id
repair_moves |= move
if move.state == 'draft' and move.repair_id.state in ('confirmed', 'under_repair'):
move._check_company()
move._adjust_procure_method()
move._action_confirm()
move._trigger_scheduler()
repair_moves._create_repair_sale_order_line()
return moves
def write(self, vals):
res = super().write(vals)
repair_moves = self.env['stock.move']
moves_to_create_so_line = self.env['stock.move']
for move in self:
if not move.repair_id:
continue
# checks vals update
if 'repair_line_type' in vals or 'picking_type_id' in vals and move.product_id != move.repair_id.product_id:
move.location_id, move.location_dest_id = move._get_repair_locations(move.repair_line_type)
if not move.sale_line_id and 'sale_line_id' not in vals and move.repair_line_type == 'add':
moves_to_create_so_line |= move
if move.sale_line_id and ('repair_line_type' in vals or 'product_uom_qty' in vals):
repair_moves |= move
repair_moves._update_repair_sale_order_line()
moves_to_create_so_line._create_repair_sale_order_line()
return res
# Needed to also cancel the lastly added part
def _action_cancel(self):
self._clean_repair_sale_order_line()
return super()._action_cancel()
def _create_repair_sale_order_line(self):
if not self:
return
so_line_vals = []
for move in self:
if move.sale_line_id or move.repair_line_type != 'add' or not move.repair_id.sale_order_id:
continue
product_qty = move.product_uom_qty if move.repair_id.state != 'done' else move.quantity
so_line_vals.append({
'order_id': move.repair_id.sale_order_id.id,
'product_id': move.product_id.id,
'product_uom_qty': product_qty, # When relying only on so_line compute method, the sol quantity is only updated on next sol creation
'move_ids': [Command.link(move.id)],
})
if move.repair_id.under_warranty:
so_line_vals[-1]['price_unit'] = 0.0
elif move.price_unit:
so_line_vals[-1]['price_unit'] = move.price_unit
self.env['sale.order.line'].create(so_line_vals)
def _clean_repair_sale_order_line(self):
self.filtered(
lambda m: m.repair_id and m.sale_line_id
).mapped('sale_line_id').write({'product_uom_qty': 0.0})
def _update_repair_sale_order_line(self):
if not self:
return
moves_to_clean = self.env['stock.move']
moves_to_update = self.env['stock.move']
for move in self:
if not move.repair_id:
continue
if move.sale_line_id and move.repair_line_type != 'add':
moves_to_clean |= move
if move.sale_line_id and move.repair_line_type == 'add':
moves_to_update |= move
moves_to_clean._clean_repair_sale_order_line()
for sale_line, _ in groupby(moves_to_update, lambda m: m.sale_line_id):
sale_line.product_uom_qty = sum(sale_line.move_ids.mapped('product_uom_qty'))
def _is_consuming(self):
return super()._is_consuming() or (self.repair_id and self.repair_line_type == 'add')
def _get_repair_locations(self, repair_line_type, repair_id=False):
location_map = MAP_REPAIR_LINE_TYPE_TO_MOVE_LOCATIONS_FROM_REPAIR.get(repair_line_type)
if location_map:
if not repair_id:
self.repair_id.ensure_one()
repair_id = self.repair_id
location_id, location_dest_id = [repair_id[field] for field in location_map.values()]
else:
location_id, location_dest_id = False, False
return location_id, location_dest_id
def _get_source_document(self):
return self.repair_id or super()._get_source_document()
def _set_repair_locations(self):
moves_per_repair = self.filtered(lambda m: (m.repair_id and m.repair_line_type) is not False).grouped('repair_id')
if not moves_per_repair:
return
for moves in moves_per_repair.values():
grouped_moves = moves.grouped('repair_line_type')
for line_type, m in grouped_moves.items():
m.location_id, m.location_dest_id = m._get_repair_locations(line_type)
def _should_be_assigned(self):
res = super(StockMove, self)._should_be_assigned()
return bool(res and not (self.repair_id and self.repair_line_type))
def _create_extra_move(self):
if self.repair_id:
return self
return super(StockMove, self)._create_extra_move()
def _split(self, qty, restrict_partner_id=False):
# When setting the Repair Order as done with partially done moves, do not split these moves
if self.repair_id:
return []
return super(StockMove, self)._split(qty, restrict_partner_id)