diff --git a/oca_dependencies.txt b/oca_dependencies.txt index e9e2f94a54b..862337b7266 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -2,3 +2,4 @@ # Add a repository url and branch if you need a forked version product-attribute stock-logistics-warehouse +stock-logistics-workflow diff --git a/quality_control_issue/README.rst b/quality_control_issue/README.rst new file mode 100644 index 00000000000..8581832f781 --- /dev/null +++ b/quality_control_issue/README.rst @@ -0,0 +1,110 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +===================== +Quality Control Issue +===================== + +This module extends the functionality of quality Control to allow you to +report and manage quality control issues. + +Configuration +============= + +To configure this module in order to take advantage of the kanban views you +need to create the stages for *issues* and *problems*. To **create** stages in +any kanban view click on *Add New Column*. Then you can **reorder** the stages +just dragging them. + +In created stages you can **configure** them clicking on the gear button that +appears at the right of the stage name and clicking on *Edit*. Note the +following behaviors: + +* You can set a *Quality Control Team*. + + - Stages with no team set will be shared by all teams. + - Stages with a team associated will be only available for that specific + team. + +* In Issue Stages you can also relate a *QC State* to the stage. + + - When you move to a different stage an issue with *QC state* defined the + state of the issue will also change according to it. + - The other way around, if you change the state, the system will look for + an appropriate stage and if existing the issue will be move to that stage. + - If you change the *QC team* of an issue, the system will get the default + stage for that team and apply it to the issue. + +Usage +===== + +To use Quality Control Issues, you need to: + +#. Go to *Quality Control > Issues > QC Issues* or to *Quality Control > + Dashboard* and click on *Issues* in any of your teams. +#. Click on create to report an issue. +#. Select the product and quantity for the issue. Optionally you can specify + a location and relate the issue to some *Problem*. + +To manage your Quality Control Problems, you have to: + +#. Go to *Quality Control > Problem Tracking > Problems* or to *Quality + Control > Dashboard* and click on *Problems* in any of your teams. + +Issue Dispositions: +------------------- + +You can perform the following actions in quality control issues 'in progress': + +* Scrap: Click on *Scrap Products* button. +* Create RMA: Install `rma_quality_control_issue` and see instructions there. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/129/9.0 + +Known issues / Roadmap +====================== + +Todo: +----- + +* Add more dispositions: repair, refurbish... + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Lois Rilo +* Jordi Ballester Alomar + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/quality_control_issue/__init__.py b/quality_control_issue/__init__.py new file mode 100644 index 00000000000..7201c080519 --- /dev/null +++ b/quality_control_issue/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/quality_control_issue/__openerp__.py b/quality_control_issue/__openerp__.py new file mode 100644 index 00000000000..71872c5b27a --- /dev/null +++ b/quality_control_issue/__openerp__.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017 Aleph Objects, Inc. (https://www.alephobjects.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Quality Control Issue", + "summary": "Allow to manage and report Quality Control Issues.", + "version": "9.0.1.0.0", + "category": "Quality Control", + "website": "https://odoo-community.org/", + "author": "Eficent , Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": [ + "quality_control", + "quality_control_team", + "stock", + "stock_scrap", + ], + "data": [ + "security/ir.model.access.csv", + "security/quality_control_issue_security.xml", + "data/qc_issue_sequence.xml", + "data/qc_stage_data.xml", + "views/qc_issue_view.xml", + "views/qc_problem_view.xml", + "views/qc_problem_group_view.xml", + "views/qc_team_dashboard_view.xml", + "views/stock_scrap_view.xml", + ], +} diff --git a/quality_control_issue/data/qc_issue_sequence.xml b/quality_control_issue/data/qc_issue_sequence.xml new file mode 100644 index 00000000000..d779d8a8715 --- /dev/null +++ b/quality_control_issue/data/qc_issue_sequence.xml @@ -0,0 +1,15 @@ + + + + + + + QC Issues + qc.issue + QCI/%(range_year)s/ + 5 + + + + diff --git a/quality_control_issue/data/qc_stage_data.xml b/quality_control_issue/data/qc_stage_data.xml new file mode 100644 index 00000000000..25617235cf6 --- /dev/null +++ b/quality_control_issue/data/qc_stage_data.xml @@ -0,0 +1,23 @@ + + + + + + + + New + 1 + False + + + + + + Done + 100 + True + + + + diff --git a/quality_control_issue/models/__init__.py b/quality_control_issue/models/__init__.py new file mode 100644 index 00000000000..64bcb241bd0 --- /dev/null +++ b/quality_control_issue/models/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import stock_scrap +from . import qc_stage +from . import qc_problem +from . import qc_problem_group +from . import qc_issue_stage +from . import qc_issue diff --git a/quality_control_issue/models/qc_issue.py b/quality_control_issue/models/qc_issue.py new file mode 100644 index 00000000000..c0311e5c426 --- /dev/null +++ b/quality_control_issue/models/qc_issue.py @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017 Aleph Objects, Inc. (https://www.alephobjects.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, fields, models, _ +from openerp.exceptions import UserError +import openerp.addons.decimal_precision as dp + + +class QualityControlIssue(models.Model): + _name = "qc.issue" + _description = "Quality Control Issue" + _inherit = "mail.thread" + + @api.multi + def _compute_stock_scrap_qty(self): + for rec in self: + rec.stock_scrap_qty = sum( + self.stock_scrap_ids.mapped('scrap_qty')) + + @api.model + def create(self, vals): + vals['name'] = self.env['ir.sequence'].next_by_code( + 'qc.issue') or '' + return super(QualityControlIssue, self).create(vals) + + @api.one + def _get_uom(self): + self.product_uom = self.product_id.product_tmpl_id.uom_id + + def _get_default_stage_id(self): + """ Gives default stage_id """ + team = self.env['qc.team']._get_default_qc_team_id( + user_id=self.env.uid) + return self.issue_stage_find([], team, [('fold', '=', False)]) + + @api.multi + def _read_group_stage_ids(self, domain, read_group_order=None, + access_rights_uid=None): + access_rights_uid = access_rights_uid or self._uid + stage_obj = self.env['qc.issue.stage'] + search_domain = [] + qc_team_id = self.env.context.get('default_qc_team_id') or False + if qc_team_id: + search_domain += ['|', ('id', 'in', self.ids)] + search_domain += ['|', ('qc_team_id', '=', qc_team_id)] + search_domain += [('qc_team_id', '=', False)] + else: + search_domain += ['|', ('id', 'in', self.ids)] + search_domain += [('qc_team_id', '=', False)] + # perform search + stage_ids = stage_obj._search(search_domain, + access_rights_uid=access_rights_uid) + result = [stage.name_get()[0] for stage in + stage_obj.browse(stage_ids)] + # restore order of the search + result.sort( + lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) + + fold = {} + for stage in stage_obj.browse(stage_ids): + fold[stage.id] = stage.fold or False + return result, fold + + name = fields.Char(readonly=True) + state = fields.Selection( + selection=[("new", "New"), + ("progress", "In Progress"), + ("done", "Done"), + ("cancel", "Cancel")], default="new", + track_visibility='onchange', readonly=True) + product_id = fields.Many2one( + comodel_name="product.product", string="Product", + readonly=True, states={"new": [("readonly", False)]}, required=True) + product_tracking = fields.Selection(related="product_id.tracking") + product_qty = fields.Float( + string="Product Quantity", required=True, default=1.0, + readonly=True, states={"new": [("readonly", False)]}, + digits_compute=dp.get_precision("Product Unit of Measure")) + product_uom = fields.Many2one( + comodel_name="product.uom", string="Product Unit of Measure", + default=_get_uom, required=True, readonly=True, + states={"new": [("readonly", False)]}) + lot_id = fields.Many2one( + comodel_name="stock.production.lot", string="Lot/Serial Number", + readonly=True, states={"new": [("readonly", False)]},) + location_id = fields.Many2one( + comodel_name="stock.location", string="Location", + readonly=True, states={"new": [("readonly", False)]},) + inspector_id = fields.Many2one( + comodel_name="res.users", string="Inspector", + track_visibility="onchange", + readonly=True, states={"new": [("readonly", False)]}, + default=lambda self: self.env.user, required=True) + responsible_id = fields.Many2one( + comodel_name="res.users", string="Assigned to", + track_visibility="onchange", + states={"done": [("readonly", True)]},) + description = fields.Text( + states={"done": [("readonly", True)]},) + qc_problem_ids = fields.Many2many( + comodel_name="qc.problem", string="Problems", + relation="qc_issue_problem_rel", column1="qc_issue_id", + column2="qc_problem_id", + states={"done": [("readonly", True)]},) + color = fields.Integer(string='Color Index') + stage_id = fields.Many2one( + comodel_name="qc.issue.stage", string='Stage', + track_visibility='onchange', + select=True, default=_get_default_stage_id, + domain="['|', ('qc_team_id', '=', False), " + "('qc_team_id', '=', qc_team_id)]") + qc_team_id = fields.Many2one( + comodel_name='qc.team', string='QC Team', + default=lambda self: self.env[ + 'qc.team'].sudo()._get_default_qc_team_id(user_id=self.env.uid), + index=True, track_visibility='onchange') + company_id = fields.Many2one( + comodel_name='res.company', string='Company', required=True, + default=lambda self: self.env.user.company_id) + stock_scrap_ids = fields.One2many( + comodel_name='stock.scrap', string='Scraps', + inverse_name='qc_issue_id') + stock_scrap_qty = fields.Integer(compute=_compute_stock_scrap_qty) + + _group_by_full = { + 'stage_id': _read_group_stage_ids + } + + def issue_stage_find(self, cases, team, domain=None, order='sequence'): + """ Override of the base.stage method + Parameter of the stage search taken from the problem: + - team_id: if set, stages must belong to this team or + be a default stage; if not set, stages must be default + stages + """ + team_ids = set() + if team: + team_ids.add(team.id) + for issue in cases: + if issue.team_id: + team_ids.add(issue.team_id.id) + search_domain = [] + if team_ids: + search_domain += [('|')] * (len(team_ids)) + search_domain.append(('qc_team_id', '=', False)) + for team_id in team_ids: + search_domain.append(('qc_team_id', '=', team_id)) + else: + search_domain.append(('qc_team_id', '=', False)) + search_domain += list(domain) + # perform search, return the first found + stage = self.env['qc.issue.stage'].search( + search_domain, order=order, limit=1) + return stage + + @api.multi + def write(self, vals): + stage_obj = self.env['qc.issue.stage'] + state = vals.get('state') + if state: + if len(self.mapped('qc_team_id')) > 1: + raise UserError(_( + "Every issue must have the same QC team to perform this " + "action.")) + team = self[0].qc_team_id + stage = self.issue_stage_find([], team, [('state', '=', state)]) + if stage: + vals.update({'stage_id': stage.id}) + return super(QualityControlIssue, self).write(vals) + team_id = vals.get('qc_team_id') + if team_id is not None: + team = self.env['qc.team'].browse(team_id) + stage = self.issue_stage_find([], team, [('fold', '=', False)]) + if stage: + vals.update({'stage_id': stage.id}) + stage_id = vals.get('stage_id') + if stage_id: + state = stage_obj.browse(stage_id).state + if state: + vals.update({'state': state}) + return super(QualityControlIssue, self).write(vals) + + @api.multi + def action_confirm(self): + self.write({'state': 'progress'}) + + @api.multi + def action_done(self): + self.write({'state': 'done'}) + + @api.multi + def action_cancel(self): + self.write({'state': 'cancel'}) + + @api.onchange('product_id') + def _onchange_product_id(self): + self.product_uom = self.product_id.product_tmpl_id.uom_id + if self.lot_id.product_id != self.product_id: + self.lot_id = False + if self.product_id: + return {'domain': { + 'lot_id': [('product_id', '=', self.product_id.id)]}} + return {'domain': {'lot_id': []}} + + @api.onchange("lot_id") + def _onchange_lot_id(self): + product = self.lot_id.product_id + if product: + self.product_id = product + self.product_uom = product.product_tmpl_id.uom_id + + @api.multi + def scrap_products(self): + self.ensure_one() + return { + 'name': _('Scrap'), + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'stock.scrap', + 'view_id': self.env.ref('stock_scrap.stock_scrap_form_view2').id, + 'type': 'ir.actions.act_window', + 'context': { + 'default_qc_issue_id': self.id, + 'default_location_id': self.location_id.id, + 'default_product_id': self.product_id.id, + 'default_scrap_qty': self.product_qty, + 'default_product_uom_id': self.product_uom.id, + 'default_lot_id': self.lot_id.id, + }, + 'target': 'new', + } + + @api.multi + def action_view_stock_scrap(self): + action = self.env.ref('stock_scrap.action_stock_scrap') + result = action.read()[0] + lines = self.stock_scrap_ids + # choose the view_mode accordingly + if len(lines) != 1: + result['domain'] = "[('id', 'in', " + \ + str(lines.ids) + ")]" + elif len(lines) == 1: + res = self.env.ref('stock_scrap.stock_scrap_form_view', False) + result['views'] = [(res and res.id or False, 'form')] + result['res_id'] = lines.id + return result diff --git a/quality_control_issue/models/qc_issue_stage.py b/quality_control_issue/models/qc_issue_stage.py new file mode 100644 index 00000000000..7342ec369d8 --- /dev/null +++ b/quality_control_issue/models/qc_issue_stage.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017 Aleph Objects, Inc. (https://www.alephobjects.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, fields, models + +AVAILABLE_PRIORITIES = [ + ('0', 'Normal'), + ('1', 'Low'), + ('2', 'High'), + ('3', 'Very High'), +] + + +class QualityControlIssueStage(models.Model): + _name = "qc.issue.stage" + _rec_name = 'name' + _order = "sequence, name, id" + + @api.model + def default_get(self, fields): + """ Hack : when going from the kanban view, creating a stage with a + qc team in context should not create a stage for the current team only. + """ + ctx = dict(self.env.context) + if ctx.get('default_qc_team_id') and not ctx.get('some_context'): + ctx.pop('default_qc_team_id') + return super(QualityControlIssueStage, + self.with_context(ctx)).default_get(fields) + + name = fields.Char('Stage Name', required=True) + sequence = fields.Integer( + string='Sequence', help="Used to order stages. Lower is better.", + default=1) + qc_team_id = fields.Many2one( + comodel_name='qc.team', string='Quality Control Team', + ondelete='set null') + fold = fields.Boolean( + string='Folded in Pipeline', default=False, + help='This stage is folded in the kanban view when there are no ' + 'records in that stage to display.') + state = fields.Selection( + string="QC State", + selection=lambda self: self.env['qc.issue']._fields['state'].selection) diff --git a/quality_control_issue/models/qc_problem.py b/quality_control_issue/models/qc_problem.py new file mode 100644 index 00000000000..5390ca9ae93 --- /dev/null +++ b/quality_control_issue/models/qc_problem.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017 Aleph Objects, Inc. (https://www.alephobjects.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, fields, models + +AVAILABLE_PRIORITIES = [ + ('0', 'Normal'), + ('1', 'Low'), + ('2', 'High'), + ('3', 'Very High'), +] + + +class QcProblem(models.Model): + _name = "qc.problem" + _description = "Quality Control Problem Tracking" + _inherit = "mail.thread" + + def _get_default_stage_id(self): + """ Gives default stage_id """ + team = self.env['qc.team']._get_default_qc_team_id( + user_id=self.env.uid) + return self.stage_find([], team, [('fold', '=', False)]) + + @api.multi + def _read_group_stage_ids(self, domain, read_group_order=None, + access_rights_uid=None): + access_rights_uid = access_rights_uid or self._uid + stage_obj = self.env['qc.stage'] + search_domain = [] + qc_team_id = self.env.context.get('default_qc_team_id') or False + if qc_team_id: + search_domain += ['|', ('id', 'in', self.ids)] + search_domain += ['|', ('qc_team_id', '=', qc_team_id)] + search_domain += [('qc_team_id', '=', False)] + else: + search_domain += ['|', ('id', 'in', self.ids)] + search_domain += [('qc_team_id', '=', False)] + # perform search + stage_ids = stage_obj._search(search_domain, + access_rights_uid=access_rights_uid) + result = [stage.name_get()[0] for stage in + stage_obj.browse(stage_ids)] + # restore order of the search + result.sort( + lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) + + fold = {} + for stage in stage_obj.browse(stage_ids): + fold[stage.id] = stage.fold or False + return result, fold + + @api.one + @api.depends('issue_ids') + def _compute_count(self): + self.issue_count = len(self.issue_ids) + + name = fields.Char() + notes = fields.Text() + issue_ids = fields.Many2many( + comodel_name="qc.issue", string="QC Issues", + relation="qc_issue_problem_rel", column1="qc_problem_id", + column2="qc_issue_id") + problem_group_id = fields.Many2one( + comodel_name="qc.problem.group", string="Problem Group") + issue_count = fields.Integer( + string="Issues", compute=_compute_count, store=True) + color = fields.Integer(string='Color Index') + priority = fields.Selection( + selection=AVAILABLE_PRIORITIES, string='Rating', index=True) + stage_id = fields.Many2one( + comodel_name="qc.stage", string='Stage', track_visibility='onchange', + select=True, default=_get_default_stage_id, + domain="['|', ('qc_team_id', '=', False), " + "('qc_team_id', '=', qc_team_id)]") + qc_team_id = fields.Many2one( + comodel_name='qc.team', string='QC Team', + default=lambda self: self.env[ + 'qc.team'].sudo()._get_default_qc_team_id(user_id=self.env.uid), + index=True, track_visibility='onchange') + company_id = fields.Many2one( + comodel_name='res.company', string='Company', required=True, + default=lambda self: self.env.user.company_id) + _group_by_full = { + 'stage_id': _read_group_stage_ids + } + + def stage_find(self, cases, team, domain=None, order='sequence'): + """ Override of the base.stage method + Parameter of the stage search taken from the problem: + - team_id: if set, stages must belong to this team or + be a default stage; if not set, stages must be default + stages + """ + team_ids = set() + if team: + team_ids.add(team.id) + for problem in cases: + if problem.team_id: + team_ids.add(problem.team_id.id) + search_domain = [] + if team_ids: + search_domain += [('|')] * (len(team_ids)) + search_domain.append(('qc_team_id', '=', False)) + for team_id in team_ids: + search_domain.append(('qc_team_id', '=', team_id)) + else: + search_domain.append(('qc_team_id', '=', False)) + search_domain += list(domain) + # perform search, return the first found + stage = self.env['qc.stage'].search( + search_domain, order=order, limit=1) + return stage diff --git a/quality_control_issue/models/qc_problem_group.py b/quality_control_issue/models/qc_problem_group.py new file mode 100644 index 00000000000..b4e2322c7f0 --- /dev/null +++ b/quality_control_issue/models/qc_problem_group.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017 Aleph Objects, Inc. (https://www.alephobjects.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import fields, models + + +class QcProblemGroup(models.Model): + _name = "qc.problem.group" + _description = "Quality Control Problem Tracking Groups" + + name = fields.Char() + problem_ids = fields.One2many( + comodel_name="qc.problem", inverse_name="problem_group_id") + company_id = fields.Many2one( + comodel_name='res.company', string='Company', required=True, + default=lambda self: self.env.user.company_id) diff --git a/quality_control_issue/models/qc_stage.py b/quality_control_issue/models/qc_stage.py new file mode 100644 index 00000000000..a1592339d8d --- /dev/null +++ b/quality_control_issue/models/qc_stage.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017 Aleph Objects, Inc. (https://www.alephobjects.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, fields, models + +AVAILABLE_PRIORITIES = [ + ('0', 'Normal'), + ('1', 'Low'), + ('2', 'High'), + ('3', 'Very High'), +] + + +class QualityControlStage(models.Model): + _name = "qc.stage" + _rec_name = 'name' + _order = "sequence, name, id" + + @api.model + def default_get(self, fields): + """ Hack : when going from the kanban view, creating a stage with a + qc team in context should not create a stage for the current team only. + """ + ctx = dict(self.env.context) + if ctx.get('default_qc_team_id') and not ctx.get('some_context'): + ctx.pop('default_qc_team_id') + return super(QualityControlStage, + self.with_context(ctx)).default_get(fields) + + name = fields.Char('Stage Name', required=True) + sequence = fields.Integer( + string='Sequence', help="Used to order stages. Lower is better.", + default=1) + qc_team_id = fields.Many2one( + comodel_name='qc.team', string='Quality Control Team', + ondelete='set null') + fold = fields.Boolean( + string='Folded in Pipeline', default=False, + help='This stage is folded in the kanban view when there are no ' + 'records in that stage to display.') diff --git a/quality_control_issue/models/stock_scrap.py b/quality_control_issue/models/stock_scrap.py new file mode 100644 index 00000000000..adaef5f2bf8 --- /dev/null +++ b/quality_control_issue/models/stock_scrap.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017 Aleph Objects, Inc. (https://www.alephobjects.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import fields, models + + +class QualityControlIssueStage(models.Model): + _inherit = "stock.scrap" + + qc_issue_id = fields.Many2one( + comodel_name="qc.issue", string="Quality Control Issue") diff --git a/quality_control_issue/security/ir.model.access.csv b/quality_control_issue/security/ir.model.access.csv new file mode 100644 index 00000000000..eaf2e35118b --- /dev/null +++ b/quality_control_issue/security/ir.model.access.csv @@ -0,0 +1,11 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_user_qc_issue,qc.issue.user,quality_control_issue.model_qc_issue,quality_control.group_quality_control_user,1,1,1,0 +access_manager_qc_issue,qc.issue.manager,quality_control_issue.model_qc_issue,quality_control.group_quality_control_manager,1,1,1,1 +access_user_qc_problem_group,qc.problem.group.user,quality_control_issue.model_qc_problem_group,quality_control.group_quality_control_user,1,0,0,0 +access_manager_qc_problem_group,qc.problem.group.manager,quality_control_issue.model_qc_problem_group,quality_control.group_quality_control_manager,1,1,1,1 +access_user_qc_problem,qc.problem.user,quality_control_issue.model_qc_problem,quality_control.group_quality_control_user,1,1,1,0 +access_manager_qc_team,qc.problem.manager,quality_control_issue.model_qc_problem,quality_control.group_quality_control_manager,1,1,1,1 +access_user_qc_stage,qc.stage.user,quality_control_issue.model_qc_stage,quality_control.group_quality_control_user,1,0,0,0 +access_manager_qc_stage,qc.stage.manager,quality_control_issue.model_qc_stage,quality_control.group_quality_control_manager,1,1,1,1 +access_user_qc_issue_stage,qc.issue.stage.user,quality_control_issue.model_qc_issue_stage,quality_control.group_quality_control_user,1,0,0,0 +access_manager_qc_issue_stage,qc.issue.stage.manager,quality_control_issue.model_qc_issue_stage,quality_control.group_quality_control_manager,1,1,1,1 diff --git a/quality_control_issue/security/quality_control_issue_security.xml b/quality_control_issue/security/quality_control_issue_security.xml new file mode 100644 index 00000000000..014abff4ede --- /dev/null +++ b/quality_control_issue/security/quality_control_issue_security.xml @@ -0,0 +1,22 @@ + + + + + + qc.issue multi-company + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + + + qc.issue multi-company + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + + + diff --git a/quality_control_issue/static/description/icon.png b/quality_control_issue/static/description/icon.png new file mode 100644 index 00000000000..e0aa80bcee2 Binary files /dev/null and b/quality_control_issue/static/description/icon.png differ diff --git a/quality_control_issue/static/description/icon.svg b/quality_control_issue/static/description/icon.svg new file mode 100644 index 00000000000..1c275d282ad --- /dev/null +++ b/quality_control_issue/static/description/icon.svg @@ -0,0 +1,95 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/quality_control_issue/views/qc_issue_view.xml b/quality_control_issue/views/qc_issue_view.xml new file mode 100644 index 00000000000..e9809597416 --- /dev/null +++ b/quality_control_issue/views/qc_issue_view.xml @@ -0,0 +1,190 @@ + + + + + + + qc.issue.tree + qc.issue + + + + + + + + + + + + + qc.issue.form + qc.issue + +
+
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+
+
+ + + qc.issue.kanban + qc.issue + + + + + + +
+ +
+
+ +
+ Product:

+ Qty:

+ Location: +

+ Problems: + + ; + + +
+
+
+
+
+
+
+
+ + + qc.issue.search + qc.issue + + + + + + + + + + + + + + + + + + + + + + + + QC Issues + ir.actions.act_window + qc.issue + form + kanban,tree,form + + + + + + +
diff --git a/quality_control_issue/views/qc_problem_group_view.xml b/quality_control_issue/views/qc_problem_group_view.xml new file mode 100644 index 00000000000..7a5a0364fa2 --- /dev/null +++ b/quality_control_issue/views/qc_problem_group_view.xml @@ -0,0 +1,48 @@ + + + + + + + qc.problem.group.tree + qc.problem.group + + + + + + + + + qc.problem.group.form + qc.problem.group + +
+ + + + + + + + + + +
+
+
+ + + Problem groups + ir.actions.act_window + qc.problem.group + form + + + + +
diff --git a/quality_control_issue/views/qc_problem_view.xml b/quality_control_issue/views/qc_problem_view.xml new file mode 100644 index 00000000000..503d610921e --- /dev/null +++ b/quality_control_issue/views/qc_problem_view.xml @@ -0,0 +1,136 @@ + + + + + + + qc.problem.tree + qc.problem + + + + + + + + + + + qc.problem.form + qc.problem + +
+
+ + +
+ + + + + + + + + + + + + + + + + + +
+ + +
+
+
+
+ + + qc.problem.kanban + qc.problem + + + + + + + + +
+ +
+
+ +
+

+ issues + +
+
+
+
+
+
+
+
+ + + qc.problem.search + qc.problem + + + + + + + + + + + + + + + + + + + + Problem Tracking + ir.actions.act_window + qc.problem + form + kanban,tree,form + + + + + + +
diff --git a/quality_control_issue/views/qc_team_dashboard_view.xml b/quality_control_issue/views/qc_team_dashboard_view.xml new file mode 100644 index 00000000000..99bc0376cd2 --- /dev/null +++ b/quality_control_issue/views/qc_team_dashboard_view.xml @@ -0,0 +1,56 @@ + + + + + + Issues by team + qc.issue + kanban,tree,form,pivot + { + 'search_default_qc_team_id': [active_id], + 'default_qc_team_id': active_id, + 'default_user_id': uid, + } + + +

Manage Quality Control Issues through your team flow.

+
+
+ + + Problem tracking + qc.problem + kanban,tree,form,pivot + { + 'search_default_qc_team_id': [active_id], + 'default_qc_team_id': active_id, + 'default_user_id': uid, + } + + +

Track problems through your team flow.

+
+
+ + + qc.team.dashboard - quality_control_issue + qc.team + + + + + + + + + + + + + +
diff --git a/quality_control_issue/views/stock_scrap_view.xml b/quality_control_issue/views/stock_scrap_view.xml new file mode 100644 index 00000000000..28e135d69ec --- /dev/null +++ b/quality_control_issue/views/stock_scrap_view.xml @@ -0,0 +1,29 @@ + + + + + + + stock.scrap.form - quality_control_issue + stock.scrap + + + + + + + + + + stock.scrap.form2 - quality_control_issue + stock.scrap + + + + + + + + +