1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.tools import float_compare
class SaleOrder(models.Model):
_inherit = 'sale.order'
repair_order_ids = fields.One2many(
comodel_name='repair.order', inverse_name='sale_order_id',
string='Repair Order', groups='stock.group_stock_user')
repair_count = fields.Integer(
"Repair Order(s)", compute='_compute_repair_count', groups='stock.group_stock_user')
@api.depends('repair_order_ids')
def _compute_repair_count(self):
for order in self:
order.repair_count = len(order.repair_order_ids)
def _action_cancel(self):
res = super()._action_cancel()
self.order_line._cancel_repair_order()
return res
def _action_confirm(self):
res = super()._action_confirm()
self.order_line._create_repair_order()
return res
def action_show_repair(self):
self.ensure_one()
if self.repair_count == 1:
return {
"type": "ir.actions.act_window",
"res_model": "repair.order",
"views": [[False, "form"]],
"res_id": self.repair_order_ids.id,
}
elif self.repair_count > 1:
return {
"name": _("Repair Orders"),
"type": "ir.actions.act_window",
"res_model": "repair.order",
"view_mode": "list,form",
"domain": [('sale_order_id', '=', self.id)],
}
class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
def _compute_qty_delivered(self):
remaining_so_lines = self
for so_line in self:
move = so_line.move_ids.sudo().filtered(lambda m: m.repair_id and m.state == 'done')
if len(move) != 1:
continue
remaining_so_lines -= so_line
so_line.qty_delivered = move.quantity
return super(SaleOrderLine, remaining_so_lines)._compute_qty_delivered()
@api.model_create_multi
def create(self, vals_list):
res = super().create(vals_list)
res.filtered(lambda line: line.state in ('sale', 'done'))._create_repair_order()
return res
def write(self, vals):
if 'product_uom_qty' in vals:
old_product_uom_qty = {line.id: line.product_uom_qty for line in self}
res = super().write(vals)
for line in self:
if line.state in ('sale', 'done') and line.product_id:
if float_compare(old_product_uom_qty[line.id], 0, precision_rounding=line.product_uom.rounding) <= 0 and float_compare(line.product_uom_qty, 0, precision_rounding=line.product_uom.rounding) > 0:
self._create_repair_order()
if float_compare(old_product_uom_qty[line.id], 0, precision_rounding=line.product_uom.rounding) > 0 and float_compare(line.product_uom_qty, 0, precision_rounding=line.product_uom.rounding) <= 0:
self._cancel_repair_order()
return res
return super().write(vals)
def _action_launch_stock_rule(self, previous_product_uom_qty=False):
# Picking must be generated for products created from the SO but not for parts added from the RO, as they're already handled there
lines_without_repair_move = self.filtered(lambda line: not line.move_ids.sudo().repair_id)
return super(SaleOrderLine, lines_without_repair_move)._action_launch_stock_rule(previous_product_uom_qty)
def _create_repair_order(self):
new_repair_vals = []
for line in self:
# One RO for each line with at least a quantity of 1, quantities > 1 don't create multiple ROs
if any(line.id == ro.sale_order_line_id.id for ro in line.order_id.sudo().repair_order_ids) and float_compare(line.product_uom_qty, 0, precision_rounding=line.product_uom.rounding) > 0:
binded_ro_ids = line.order_id.sudo().repair_order_ids.filtered(lambda ro: ro.sale_order_line_id.id == line.id and ro.state == 'cancel')
binded_ro_ids.action_repair_cancel_draft()
binded_ro_ids._action_repair_confirm()
continue
if not line.product_template_id.sudo().create_repair or line.move_ids.sudo().repair_id or float_compare(line.product_uom_qty, 0, precision_rounding=line.product_uom.rounding) <= 0:
continue
order = line.order_id
default_repair_vals = {
'state': 'confirmed',
'partner_id': order.partner_id.id,
'sale_order_id': order.id,
'sale_order_line_id': line.id,
'picking_type_id': order.warehouse_id.repair_type_id.id,
}
if line.product_id.tracking == 'serial':
vals = {
**default_repair_vals,
'product_id': line.product_id.id,
'product_qty': 1,
'product_uom': line.product_uom.id,
}
new_repair_vals.extend([vals] * int(line.product_uom_qty))
elif line.product_id.type == 'consu':
new_repair_vals.append({
**default_repair_vals,
'product_id': line.product_id.id,
'product_qty': line.product_uom_qty,
'product_uom': line.product_uom.id,
})
else:
new_repair_vals.append(default_repair_vals.copy())
if new_repair_vals:
self.env['repair.order'].sudo().create(new_repair_vals)
def _cancel_repair_order(self):
# Each RO binded to a SO line with Qty set to 0 or cancelled is set to 'Cancelled'
binded_ro_ids = self.env['repair.order']
for line in self:
binded_ro_ids |= line.order_id.sudo().repair_order_ids.filtered(lambda ro: ro.sale_order_line_id.id == line.id and ro.state != 'done')
binded_ro_ids.action_repair_cancel()
def has_valued_move_ids(self):
res = super().has_valued_move_ids()
return res and not self.move_ids.repair_id
|