增加质检模块
This commit is contained in:
7
quality_mrp/models/__init__.py
Normal file
7
quality_mrp/models/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import mrp_production
|
||||
from . import quality
|
||||
from . import stock_move
|
||||
from . import stock_move_line
|
||||
82
quality_mrp/models/mrp_production.py
Normal file
82
quality_mrp/models/mrp_production.py
Normal file
@@ -0,0 +1,82 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class MrpProduction(models.Model):
|
||||
_inherit = "mrp.production"
|
||||
|
||||
check_ids = fields.One2many('quality.check', 'production_id', string="Checks")
|
||||
quality_check_todo = fields.Boolean(compute='_compute_check')
|
||||
quality_check_fail = fields.Boolean(compute='_compute_check')
|
||||
quality_alert_ids = fields.One2many('quality.alert', "production_id", string="Alerts")
|
||||
quality_alert_count = fields.Integer(compute='_compute_quality_alert_count')
|
||||
|
||||
def _compute_quality_alert_count(self):
|
||||
for production in self:
|
||||
production.quality_alert_count = len(production.quality_alert_ids)
|
||||
|
||||
def _compute_check(self):
|
||||
for production in self:
|
||||
todo = False
|
||||
fail = False
|
||||
for check in production.check_ids:
|
||||
if check.quality_state == 'none':
|
||||
todo = True
|
||||
elif check.quality_state == 'fail':
|
||||
fail = True
|
||||
if fail and todo:
|
||||
break
|
||||
production.quality_check_fail = fail
|
||||
production.quality_check_todo = todo
|
||||
|
||||
def button_quality_alert(self):
|
||||
self.ensure_one()
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("quality_control.quality_alert_action_check")
|
||||
action['views'] = [(False, 'form')]
|
||||
action['context'] = {
|
||||
'default_company_id': self.company_id.id,
|
||||
'default_product_id': self.product_id.id,
|
||||
'default_product_tmpl_id': self.product_id.product_tmpl_id.id,
|
||||
'default_production_id': self.id,
|
||||
}
|
||||
return action
|
||||
|
||||
def button_mark_done(self):
|
||||
for order in self:
|
||||
if any(x.quality_state == 'none' for x in order.check_ids):
|
||||
raise UserError(_('You still need to do the quality checks!'))
|
||||
return super(MrpProduction, self).button_mark_done()
|
||||
|
||||
def open_quality_alert_mo(self):
|
||||
self.ensure_one()
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("quality_control.quality_alert_action_check")
|
||||
action['context'] = {
|
||||
'default_company_id': self.company_id.id,
|
||||
'default_product_id': self.product_id.id,
|
||||
'default_product_tmpl_id': self.product_id.product_tmpl_id.id,
|
||||
'default_production_id': self.id,
|
||||
}
|
||||
action['domain'] = [('id', 'in', self.quality_alert_ids.ids)]
|
||||
action['views'] = [(False, 'tree'),(False,'form')]
|
||||
if self.quality_alert_count == 1:
|
||||
action['views'] = [(False, 'form')]
|
||||
action['res_id'] = self.quality_alert_ids.id
|
||||
return action
|
||||
|
||||
def check_quality(self):
|
||||
self.ensure_one()
|
||||
checks = self.check_ids.filtered(lambda x: x.quality_state == 'none')
|
||||
if checks:
|
||||
return checks.action_open_quality_check_wizard()
|
||||
|
||||
def action_cancel(self):
|
||||
res = super(MrpProduction, self).action_cancel()
|
||||
self.sudo().mapped('check_ids').filtered(lambda x: x.quality_state == 'none').unlink()
|
||||
return res
|
||||
|
||||
def _action_confirm_mo_backorders(self):
|
||||
super()._action_confirm_mo_backorders()
|
||||
(self.move_raw_ids | self.move_finished_ids)._create_quality_checks_for_mo()
|
||||
49
quality_mrp/models/quality.py
Normal file
49
quality_mrp/models/quality.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class QualityPoint(models.Model):
|
||||
_inherit = "quality.point"
|
||||
|
||||
@api.model
|
||||
def _get_domain_for_production(self, quality_points_domain):
|
||||
return quality_points_domain
|
||||
|
||||
|
||||
class QualityCheck(models.Model):
|
||||
_inherit = "quality.check"
|
||||
|
||||
production_id = fields.Many2one(
|
||||
'mrp.production', 'Production Order', check_company=True)
|
||||
|
||||
@api.depends('move_line_id.qty_done')
|
||||
def _compute_qty_line(self):
|
||||
record_without_production = self.env['quality.check']
|
||||
for qc in self:
|
||||
if qc.production_id:
|
||||
qc.qty_line = qc.production_id.qty_producing
|
||||
else:
|
||||
record_without_production |= qc
|
||||
return super(QualityCheck, record_without_production)._compute_qty_line()
|
||||
|
||||
@api.depends('production_id.lot_producing_id')
|
||||
def _compute_lot_line_id(self):
|
||||
op_level_comp_qc = self.env['quality.check']
|
||||
for qc in self:
|
||||
if qc.test_type in ('register_consumed_materials', 'register_byproducts'):
|
||||
continue
|
||||
if qc.product_id == qc.production_id.product_id and qc.production_id.lot_producing_id:
|
||||
qc.lot_line_id = qc.production_id.lot_producing_id
|
||||
qc.lot_id = qc.lot_line_id
|
||||
continue
|
||||
op_level_comp_qc |= qc
|
||||
return super(QualityCheck, op_level_comp_qc)._compute_lot_line_id()
|
||||
|
||||
|
||||
class QualityAlert(models.Model):
|
||||
_inherit = "quality.alert"
|
||||
|
||||
production_id = fields.Many2one(
|
||||
'mrp.production', "Production Order", check_company=True)
|
||||
62
quality_mrp/models/stock_move.py
Normal file
62
quality_mrp/models/stock_move.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class StockMove(models.Model):
|
||||
_inherit = "stock.move"
|
||||
|
||||
def _action_confirm(self, merge=True, merge_into=False):
|
||||
moves = super(StockMove, self)._action_confirm(merge=merge, merge_into=merge_into)
|
||||
moves._create_quality_checks_for_mo()
|
||||
|
||||
return moves
|
||||
|
||||
def _search_quality_points(self, product_id, picking_type_id, measure_on):
|
||||
quality_points_domain = self.env['quality.point']._get_domain(product_id, picking_type_id, measure_on=measure_on)
|
||||
quality_points_domain = self.env['quality.point']._get_domain_for_production(quality_points_domain)
|
||||
return self.env['quality.point'].sudo().search(quality_points_domain)
|
||||
|
||||
def _create_quality_checks_for_mo(self):
|
||||
# Groupby move by production order. Use it in order to generate missing quality checks.
|
||||
mo_moves = defaultdict(lambda: self.env['stock.move'])
|
||||
check_vals_list = []
|
||||
for move in self:
|
||||
if move.production_id and not move.scrapped:
|
||||
mo_moves[move.production_id] |= move
|
||||
|
||||
# QC of product type
|
||||
for production, moves in mo_moves.items():
|
||||
quality_points = self._search_quality_points(moves.product_id, production.picking_type_id, 'product')
|
||||
|
||||
|
||||
# Since move lines are created too late for the manufactured product, we create the QC of move_line type directly here instead, excluding by-products
|
||||
quality_points_lot_type = self._search_quality_points(production.product_id, production.picking_type_id, 'move_line')
|
||||
|
||||
quality_points = quality_points | quality_points_lot_type
|
||||
if not quality_points:
|
||||
continue
|
||||
mo_check_vals_list = quality_points._get_checks_values(moves.product_id, production.company_id.id, existing_checks=production.sudo().check_ids)
|
||||
for check_value in mo_check_vals_list:
|
||||
check_value.update({
|
||||
'production_id': production.id,
|
||||
})
|
||||
check_vals_list += mo_check_vals_list
|
||||
|
||||
# QC of operation type
|
||||
for production, moves in mo_moves.items():
|
||||
quality_points_operation = self._search_quality_points(self.env['product.product'], production.picking_type_id, 'operation')
|
||||
|
||||
for point in quality_points_operation:
|
||||
if point.check_execute_now():
|
||||
check_vals_list.append({
|
||||
'point_id': point.id,
|
||||
'team_id': point.team_id.id,
|
||||
'measure_on': 'operation',
|
||||
'production_id': production.id,
|
||||
})
|
||||
|
||||
self.env['quality.check'].sudo().create(check_vals_list)
|
||||
34
quality_mrp/models/stock_move_line.py
Normal file
34
quality_mrp/models/stock_move_line.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import models
|
||||
|
||||
|
||||
class StockMoveLine(models.Model):
|
||||
_inherit = "stock.move.line"
|
||||
|
||||
def write(self, vals):
|
||||
res = super().write(vals)
|
||||
if vals.get('lot_id') and self.check_ids:
|
||||
self.check_ids.filtered(lambda qc: qc.test_type in ('register_consumed_materials', 'register_byproducts')).lot_id = vals['lot_id']
|
||||
return res
|
||||
|
||||
def _get_check_values(self, quality_point):
|
||||
vals = super(StockMoveLine, self)._get_check_values(quality_point)
|
||||
vals.update({'production_id': self.move_id.production_id.id or self.move_id.raw_material_production_id.id})
|
||||
return vals
|
||||
|
||||
def _get_quality_points_all_products(self, quality_points_by_product_picking_type):
|
||||
if self.move_id.raw_material_production_id:
|
||||
return set()
|
||||
else:
|
||||
return super()._get_quality_points_all_products(quality_points_by_product_picking_type)
|
||||
|
||||
def _create_quality_check_at_write(self, vals):
|
||||
if self.move_id.production_id or self.move_id.raw_material_production_id:
|
||||
return False
|
||||
return super()._create_quality_check_at_write(vals)
|
||||
|
||||
def _filter_move_lines_applicable_for_quality_check(self):
|
||||
ok_lines = self.filtered(lambda sml: sml.move_id.raw_material_production_id)
|
||||
done_lines = self.filtered(lambda sml: sml.product_id == sml.move_id.production_id.product_id)
|
||||
return ok_lines | super(StockMoveLine, self - ok_lines - done_lines)._filter_move_lines_applicable_for_quality_check()
|
||||
Reference in New Issue
Block a user