mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
[ADD] new module quality_control_plan
This commit is contained in:
3
quality_control_plan/models/__init__.py
Normal file
3
quality_control_plan/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
from . import mgmtsystem_nonconformity_inspection, partner_quality, \
|
||||
qc_inspection_plan, qc_plan, qc_trigger, stock_picking_inspection
|
||||
@@ -0,0 +1,17 @@
|
||||
# Copyright 2019 Marcelo Frare (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# Copyright 2019 Stefano Consolaro (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class MgmtsystemMgmInspection(models.Model):
|
||||
"""
|
||||
Extends nonconformity adding related inspection
|
||||
"""
|
||||
|
||||
_inherit = ['mgmtsystem.nonconformity']
|
||||
|
||||
# new field
|
||||
# inspection reference
|
||||
inspection_id = fields.Many2one('qc.inspection', 'Inspection')
|
||||
21
quality_control_plan/models/partner_quality.py
Normal file
21
quality_control_plan/models/partner_quality.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# Copyright 2019 Marcelo Frare (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# Copyright 2019 Stefano Consolaro (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class MgmtsystemMgmPartner(models.Model):
|
||||
"""
|
||||
Extends partner with quality control triggers
|
||||
"""
|
||||
|
||||
_inherit = ['res.partner']
|
||||
|
||||
# new fields
|
||||
# trigger to activate inspection
|
||||
qc_triggers = fields.One2many(
|
||||
comodel_name="qc.trigger.partner_line",
|
||||
inverse_name="partner",
|
||||
string="Quality control triggers"
|
||||
)
|
||||
29
quality_control_plan/models/product_trigger.py
Normal file
29
quality_control_plan/models/product_trigger.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# Copyright 2019 Marcelo Frare (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# Copyright 2019 Stefano Consolaro (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class QcProduct(models.Model):
|
||||
"""
|
||||
Extends product model with a field to store quality control plan assigned
|
||||
"""
|
||||
|
||||
_inherit = ['qc.trigger.product_template_line']
|
||||
|
||||
# new filed
|
||||
# product's control plan
|
||||
plan_id = fields.Many2one('qc.plan', 'Plan')
|
||||
|
||||
|
||||
class QcCategory(models.Model):
|
||||
"""
|
||||
Extends product category model with a field to store quality control plan assigned
|
||||
"""
|
||||
|
||||
_inherit = ['qc.trigger.product_category_line']
|
||||
|
||||
# new filed
|
||||
# sets product category's control plan
|
||||
plan_id = fields.Many2one('qc.plan', 'Plan')
|
||||
211
quality_control_plan/models/qc_inspection_plan.py
Normal file
211
quality_control_plan/models/qc_inspection_plan.py
Normal file
@@ -0,0 +1,211 @@
|
||||
# Copyright 2019 Marcelo Frare (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# Copyright 2019 Stefano Consolaro (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models, api, _
|
||||
|
||||
|
||||
class QcInspection(models.Model):
|
||||
"""
|
||||
Extends inspection with:
|
||||
- plan control
|
||||
- nonconformity relations
|
||||
- method to fill inspection with control plan data
|
||||
- method create nonconformity from inspection
|
||||
"""
|
||||
|
||||
_inherit = ['qc.inspection']
|
||||
|
||||
# new fields
|
||||
# the control plan to be used
|
||||
plan_id = fields.Many2one('qc.plan', 'Control Plan')
|
||||
# quantity to be checked
|
||||
qty_checked = fields.Float('Quantity checked')
|
||||
# nonconformity reference
|
||||
inspection_ids = fields.One2many('mgmtsystem.nonconformity',
|
||||
'inspection_id',
|
||||
'Nonconformity'
|
||||
)
|
||||
|
||||
@api.model
|
||||
def create(self, values):
|
||||
"""
|
||||
Extends inspection method by integrating logic to determine the control plan
|
||||
to be used and the calculation of the quantity to be checked
|
||||
"""
|
||||
|
||||
# calls original method
|
||||
new_record = super(QcInspection, self).create(values)
|
||||
|
||||
# gets product of the inspection
|
||||
product_id = new_record.product_id
|
||||
|
||||
# gets partner from picking if exists
|
||||
if new_record.picking_id:
|
||||
partner_id = self.env['stock.picking'
|
||||
].search([('id', '=', new_record.picking_id.id)],
|
||||
limit=1
|
||||
).partner_id.id
|
||||
else:
|
||||
partner_id = False
|
||||
|
||||
# temporary presetted solutions
|
||||
solution_art_prt = '' # plan for product (article) and partner
|
||||
solution_cat_prt = '' # plan for product category and partner
|
||||
solution_art = '' # plan for product (article)
|
||||
solution_cat = '' # plan for product category
|
||||
solution_prt = '' # plan for partner
|
||||
|
||||
# tries to get plan for product and partner
|
||||
solution_art_prt = self.env['qc.trigger.product_template_line'
|
||||
].search([('product_template',
|
||||
'=',
|
||||
product_id.product_tmpl_id.id),
|
||||
('partners', '!=', False),
|
||||
('partners', '=', partner_id)
|
||||
],
|
||||
limit=1
|
||||
)
|
||||
|
||||
if (len(solution_art_prt)
|
||||
== 0):
|
||||
# tries to gets plan for category and partner
|
||||
solution_cat_prt = self.env['qc.trigger.product_category_line'
|
||||
].search([('product_category',
|
||||
'=',
|
||||
product_id.categ_id.id),
|
||||
('partners', '!=', False),
|
||||
('partners', '=', partner_id)
|
||||
],
|
||||
limit=1
|
||||
)
|
||||
|
||||
if (len(solution_art_prt)
|
||||
+ len(solution_cat_prt)
|
||||
== 0):
|
||||
# tries to get plan for product
|
||||
solution_art = self.env['qc.trigger.product_template_line'
|
||||
].search([('product_template',
|
||||
'=',
|
||||
product_id.product_tmpl_id.id
|
||||
),
|
||||
('partners', '=', False)
|
||||
],
|
||||
limit=1
|
||||
)
|
||||
|
||||
if (len(solution_art_prt)
|
||||
+ len(solution_cat_prt)
|
||||
+ len(solution_art)
|
||||
== 0):
|
||||
# tries to get plan for category
|
||||
solution_cat = self.env['qc.trigger.product_category_line'
|
||||
].search([('product_category',
|
||||
'=',
|
||||
product_id.categ_id.id
|
||||
),
|
||||
('partners', '=', False)
|
||||
],
|
||||
limit=1
|
||||
)
|
||||
|
||||
if (len(solution_art_prt)
|
||||
+ len(solution_cat_prt)
|
||||
+ len(solution_art)
|
||||
+ len(solution_cat)
|
||||
== 0):
|
||||
# tries to get plan for partner
|
||||
solution_prt = self.env['qc.trigger.partner_line'
|
||||
].search([('partner', '=', partner_id)
|
||||
],
|
||||
limit=1
|
||||
)
|
||||
|
||||
# gets the plan from the first positive try
|
||||
if len(solution_art_prt):
|
||||
plan_id = solution_art_prt.plan_id
|
||||
elif len(solution_cat_prt):
|
||||
plan_id = solution_cat_prt.plan_id
|
||||
elif len(solution_art):
|
||||
plan_id = solution_art.plan_id
|
||||
elif len(solution_cat):
|
||||
plan_id = solution_cat.plan_id
|
||||
elif len(solution_prt):
|
||||
plan_id = solution_prt.plan_id
|
||||
else:
|
||||
new_record.qty_checked = '1'
|
||||
new_record.plan_id = ''
|
||||
return new_record
|
||||
|
||||
if plan_id:
|
||||
# assigns plan to be used
|
||||
new_record.plan_id = plan_id
|
||||
|
||||
if new_record.plan_id.free_pass:
|
||||
# for free pass doesn't check product
|
||||
new_record.qty_checked = 0
|
||||
|
||||
else:
|
||||
# gets check informations from levels
|
||||
qty_related = self.env['qc.level'
|
||||
].search([('plan_id', '=', plan_id.id),
|
||||
('qty_received', '<', new_record.qty)
|
||||
],
|
||||
limit=1,
|
||||
order='qty_received desc'
|
||||
)
|
||||
# assigns qty to check
|
||||
if qty_related:
|
||||
if qty_related.chk_type == 'percent':
|
||||
# as percent of qty to check
|
||||
new_record.qty_checked = int(new_record.qty
|
||||
* qty_related.qty_checked
|
||||
/ 100
|
||||
)
|
||||
else:
|
||||
# as absolute value
|
||||
new_record.qty_checked = qty_related.qty_checked
|
||||
|
||||
# verifies if enough pcs to check
|
||||
if new_record.qty_checked > new_record.qty:
|
||||
new_record.qty_checked = new_record.qty
|
||||
|
||||
# checks and fix absolute minimum value lower to 1
|
||||
if new_record.qty_checked < 1:
|
||||
new_record.qty_checked = 1
|
||||
|
||||
return new_record
|
||||
|
||||
@api.multi
|
||||
def create_nonconformity(self, **kwargs):
|
||||
"""
|
||||
Opens nonconformity form view prefilled with inspection data
|
||||
"""
|
||||
|
||||
# gets partner if exists
|
||||
if self.picking_id.partner_id.id:
|
||||
partner = self.picking_id.partner_id.id
|
||||
else:
|
||||
partner = False
|
||||
|
||||
tmp_form_name = "mgmtsystem_nonconformity.view_mgmtsystem_nonconformity_form"
|
||||
return {
|
||||
# opens nonconformity form view
|
||||
'name' : _('Create Nonconformity on not compliant Inspection'),
|
||||
'view_type' : 'form',
|
||||
'view_mode' : 'form',
|
||||
'res_model' : 'mgmtsystem.nonconformity',
|
||||
'view_id' : self.env.ref(tmp_form_name).id,
|
||||
'type' : 'ir.actions.act_window',
|
||||
|
||||
# fills fields with inspection data
|
||||
'context': {
|
||||
'default_name' : _('Inspection not compliant'),
|
||||
'default_product_id' : self.product_id.id,
|
||||
'default_partner_id' : partner,
|
||||
'default_qty_checked' : self.qty_checked,
|
||||
'default_inspection_id' : self.id
|
||||
},
|
||||
|
||||
'target': 'new'
|
||||
}
|
||||
86
quality_control_plan/models/qc_plan.py
Normal file
86
quality_control_plan/models/qc_plan.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# Copyright 2019 Marcelo Frare (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# Copyright 2019 Stefano Consolaro (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models, api, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class QcPlan(models.Model):
|
||||
"""
|
||||
Manages quality control plans
|
||||
"""
|
||||
|
||||
# model
|
||||
_name = 'qc.plan'
|
||||
_description = 'Quality Control Plan'
|
||||
|
||||
_inherit = ['mail.thread']
|
||||
|
||||
# fields
|
||||
# alphanumeric identification code
|
||||
name = fields.Char('Name', required=True)
|
||||
# description of plan calculation
|
||||
description = fields.Char('Description')
|
||||
# free pass option
|
||||
free_pass = fields.Boolean('Free pass')
|
||||
# control levels of the plan
|
||||
plan_ids = fields.One2many('qc.level', 'plan_id', 'Plan')
|
||||
|
||||
@api.onchange('free_pass')
|
||||
def on_change_free_pass(self):
|
||||
"""
|
||||
Checks if there is only one free pass plan
|
||||
Sets the last changed to false if another one exists
|
||||
"""
|
||||
|
||||
if self.free_pass:
|
||||
free_p = self.env['qc.plan'].search([('free_pass', '=', True)])[0]
|
||||
if free_p:
|
||||
self.free_pass = False
|
||||
raise ValidationError(_("A free pass plan already exists: %s.")
|
||||
% free_p.name)
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
"""
|
||||
Avoids multiple free pass plans
|
||||
"""
|
||||
if vals['free_pass']:
|
||||
if self.env['qc.plan'].search([('free_pass', '=', True)]):
|
||||
return False
|
||||
return super(QcPlan, self).create(vals)
|
||||
|
||||
|
||||
class QcLevel(models.Model):
|
||||
"""
|
||||
Manages the lelevs for a control plan
|
||||
"""
|
||||
|
||||
# model
|
||||
_name = 'qc.level'
|
||||
_description = 'Quality Control Plan Levels'
|
||||
|
||||
# fields
|
||||
# plan reference
|
||||
plan_id = fields.Many2one('qc.plan', 'Plan', required=True)
|
||||
# minimum ingoing quantity
|
||||
qty_received = fields.Float('Quantity to inspect',
|
||||
help='Minimum received quantity reference'
|
||||
)
|
||||
# quantity value to check
|
||||
qty_checked = fields.Float(
|
||||
'Quantity checked',
|
||||
help='Quantity to check if the received goods is higher than the reference'
|
||||
)
|
||||
# chek value type: absolute or percent of ingoing quantity
|
||||
chk_type = fields.Selection([('absolute', 'Absolute value'),
|
||||
('percent', 'Percent'),
|
||||
],
|
||||
'Measure',
|
||||
default='absolute',
|
||||
help='Indicate how to use quantity checked value'
|
||||
)
|
||||
|
||||
# defines record name to display in form view
|
||||
_rec_name = 'id'
|
||||
51
quality_control_plan/models/qc_trigger.py
Normal file
51
quality_control_plan/models/qc_trigger.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# Copyright 2010 NaN Projectes de Programari Lliure, S.L.
|
||||
# Copyright 2014 Serv. Tec. Avanzados - Pedro M. Baeza
|
||||
# Copyright 2014 Oihane Crucelaegui - AvanzOSC
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# Copyright 2017 Simone Rubino - Agile Business Group
|
||||
# Copyright 2019 Marcelo Frare (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# Copyright 2019 Stefano Consolaro (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class QcTriggerPartnerLine(models.Model):
|
||||
"""
|
||||
Extends quality trigger with partner control plan
|
||||
"""
|
||||
|
||||
_inherit = "qc.trigger.line"
|
||||
|
||||
# model
|
||||
_name = "qc.trigger.partner_line"
|
||||
|
||||
# new fields
|
||||
# reference partner
|
||||
partner = fields.Many2one(comodel_name="res.partner")
|
||||
# control plan to use
|
||||
plan_id = fields.Many2one('qc.plan', 'Plan', required=True)
|
||||
|
||||
|
||||
class QcTriggerProductLine(models.Model):
|
||||
"""
|
||||
Extends product model with a field to store quality control plan assigned
|
||||
"""
|
||||
|
||||
_inherit = ['qc.trigger.product_template_line']
|
||||
|
||||
# new filed
|
||||
# product's control plan
|
||||
plan_id = fields.Many2one('qc.plan', 'Plan')
|
||||
|
||||
|
||||
class QcTriggerCategoryLine(models.Model):
|
||||
"""
|
||||
Extends product category model with a field to store quality control plan assigned
|
||||
"""
|
||||
|
||||
_inherit = ['qc.trigger.product_category_line']
|
||||
|
||||
# new filed
|
||||
# sets product category's control plan
|
||||
plan_id = fields.Many2one('qc.plan', 'Plan')
|
||||
27
quality_control_plan/models/qc_trigger_partner_line.py
Normal file
27
quality_control_plan/models/qc_trigger_partner_line.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Copyright 2010 NaN Projectes de Programari Lliure, S.L.
|
||||
# Copyright 2014 Serv. Tec. Avanzados - Pedro M. Baeza
|
||||
# Copyright 2014 Oihane Crucelaegui - AvanzOSC
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# Copyright 2017 Simone Rubino - Agile Business Group
|
||||
# Copyright 2019 Marcelo Frare (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# Copyright 2019 Stefano Consolaro (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class QcTriggerPartnerLine(models.Model):
|
||||
"""
|
||||
Extend quality trigger with partner control plan
|
||||
"""
|
||||
|
||||
_inherit = "qc.trigger.line"
|
||||
|
||||
# model
|
||||
_name = "qc.trigger.partner_line"
|
||||
|
||||
# new fields
|
||||
# reference partner
|
||||
partner = fields.Many2one(comodel_name="res.partner")
|
||||
# control plan to use
|
||||
plan_id = fields.Many2one('qc.plan', 'Plan', required=True)
|
||||
51
quality_control_plan/models/stock_picking_inspection.py
Normal file
51
quality_control_plan/models/stock_picking_inspection.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# Copyright 2019 Marcelo Frare (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# Copyright 2019 Stefano Consolaro (Ass. PNLUG - Gruppo Odoo <http://odoo.pnlug.it>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import models, api
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
"""
|
||||
Extend picking method
|
||||
"""
|
||||
|
||||
_inherit = 'stock.picking'
|
||||
|
||||
@api.multi
|
||||
def action_done(self):
|
||||
"""
|
||||
Extend actions on done stock move adding inspection defined on Partner
|
||||
"""
|
||||
|
||||
# does original action and memorize result
|
||||
result = super(StockPicking, self).action_done()
|
||||
|
||||
# gets model of the inspection
|
||||
inspection_model = self.env['qc.inspection']
|
||||
|
||||
# for each line moved
|
||||
for operation in self.move_lines:
|
||||
# gets quality trigger associate to movement type
|
||||
qc_trigger = self.env['qc.trigger'
|
||||
].search([('picking_type_id',
|
||||
'=',
|
||||
self.picking_type_id.id)
|
||||
])
|
||||
|
||||
# gets partner associate to movement
|
||||
partner = (self.partner_id)
|
||||
|
||||
# gets trigger for movement type on partner
|
||||
trigger_line = self.env['qc.trigger.partner_line'
|
||||
].search([('partner', '=', partner.id),
|
||||
('trigger', '=', qc_trigger.id)
|
||||
],
|
||||
limit=1
|
||||
)
|
||||
|
||||
# adds new ispection
|
||||
if trigger_line:
|
||||
inspection_model._make_inspection(operation, trigger_line)
|
||||
|
||||
return result
|
||||
Reference in New Issue
Block a user