diff --git a/mgmtsystem_event/README.rst b/mgmtsystem_event/README.rst new file mode 100644 index 00000000000..a5382c32b9c --- /dev/null +++ b/mgmtsystem_event/README.rst @@ -0,0 +1,174 @@ +========================= +Management System - Event +========================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:ac5dfaf7ad4439088d78bb0c47a4746e49338a742ee2984019738e51dfaa2aa9 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanagement--system-lightgray.png?logo=github + :target: https://github.com/OCA/management-system/tree/18.0/mgmtsystem_event + :alt: OCA/management-system +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/management-system-18-0/management-system-18-0-mgmtsystem_event + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/management-system&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module enables you to manage the events of your management systems: + +- Quality (ISO 9001) +- Environment (ISO 14001) +- Information Security (ISO 27001) +- Health and Safety (ISO 45001) +- IT Services (ISO 20000) + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Users must be added to the appropriate groups within Odoo as follows: + +- Creators: Settings > Users > Groups > Management System / User + +Usage +===== + +To use this module: + +- Go to Management System > Events +- Click on Create to enter the following information: +- Partner : Customer, supplier or internal personnel +- Related to: Any reference pointing to the NC (order id, project id, + task id, etc.) +- Responsible: Person responsible for the NC +- Manager : Person managing the department or owner of the procedure +- Filled in by: Originator of NC report +- Origins: The source of the NC, how was it discover +- Documents: Against which document is the NC +- Description: Evidence, reference to the standards +- Click on Save and then set state from Draft to Analysis. + +Go to the newly created NC and fill in the following information: + +In the Causes and Analysis tab: + +- Causes: Add root causes +- Analysis: Describe the results of the investigation +- Severity: Select the severity among unfounded, minor and major + +In the Description tab: + +- Immediate action: Create or select an immediate action if appropriate + +Click on the Save button and then on the "Action Plan" button in the top +right corner. + +In the Actions tab, select or create new actions by entering the +following items: + +- Subject: What must be done - Return to Supplier, Use As Is, Scrap, + Rework, Re-grade, Repair +- Deadline: Date by which the action must be completed +- Responsible: Person in charge for implementing the action +- Response Type: Immediate, corrective or preventive actions or + improvement opportunity +- Description: Details of the action + +When the action is created, a notification is sent to the person +responsible for the action. + +Enter comments into the input field below the "Plan Review" section, +those comments are required to reach the next stage. + +To begin the work on the planned Actions change the stage of the NC to +open by clicking on the "In Progress" button in the top right corner. + +When all actions of the plan are done, their effectiveness must be +evaluated before closing the NC. + +Known issues / Roadmap +====================== + +- The custom emails should be replaced by Mail Tracking features and + Subtypes (like in Project Tasks and Project Issues) +- Automatically add responsible_user_id._uid, manager_user_id._uid, + author_user_id._uid to chatter + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Savoir-faire Linux + +Contributors +------------ + +- Daniel Reis +- Glen Dromgoole +- Loic Lacroix +- Sandy Carter +- Gervais Naoussi +- Eugen Don +- Jose Maria Alzaga +- `Tecnativa `__: + + - Ernesto Tejeda + +Trobz + +- Dung Tran +- Tuan Nguyen + +Other credits +------------- + +The development of this module has been financially supported by: + +- Camptocamp + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +This module is part of the `OCA/management-system `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mgmtsystem_event/__init__.py b/mgmtsystem_event/__init__.py new file mode 100644 index 00000000000..6eec492e88f --- /dev/null +++ b/mgmtsystem_event/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2013 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from . import models diff --git a/mgmtsystem_event/__manifest__.py b/mgmtsystem_event/__manifest__.py new file mode 100644 index 00000000000..e4f186f9de3 --- /dev/null +++ b/mgmtsystem_event/__manifest__.py @@ -0,0 +1,44 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Management System - Event", + "version": "18.0.0.0.0", + "author": "Savoir-faire Linux, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/management-system", + "license": "AGPL-3", + "category": "Management System", + "depends": ["mgmtsystem_action", "document_page"], + "data": [ + "security/ir.model.access.csv", + "security/mgmtsystem_event_security.xml", + "views/mgmtsystem_event.xml", + "views/mgmtsystem_origin.xml", + "views/mgmtsystem_cause.xml", + "views/mgmtsystem_severity.xml", + "views/mgmtsystem_action.xml", + "views/mgmtsystem_event_stage.xml", + "views/mgmtsystem_event_tag.xml", + "data/sequence.xml", + "data/mgmtsystem_event_severity.xml", + "data/mgmtsystem_event_origin.xml", + "data/mgmtsystem_event_cause.xml", + "data/mgmtsystem_event_stage.xml", + "data/mail_message_subtype.xml", + "reports/mgmtsystem_event_report.xml", + ], + "demo": [ + "demo/mgmtsystem_event_origin.xml", + "demo/mgmtsystem_event_cause.xml", + "demo/mgmtsystem_event.xml", + ], + "assets": { + "web.assets_backend": [ + "mgmtsystem_event/static/src/**/*.xml", + "mgmtsystem_event/static/src/**/*.esm.js", + ], + "web.assets_unit_tests": [ + "mgmtsystem_event/static/tests/**/*", + ], + }, + "installable": True, +} diff --git a/mgmtsystem_event/data/mail_message_subtype.xml b/mgmtsystem_event/data/mail_message_subtype.xml new file mode 100644 index 00000000000..f9c25f41d3c --- /dev/null +++ b/mgmtsystem_event/data/mail_message_subtype.xml @@ -0,0 +1,16 @@ + + + + Analysis + mgmtsystem.event + + Analysis + + + + Pending Approval + mgmtsystem.event + + Pending Approval + + diff --git a/mgmtsystem_event/data/mgmtsystem_event_cause.xml b/mgmtsystem_event/data/mgmtsystem_event_cause.xml new file mode 100644 index 00000000000..9f80b74fd6d --- /dev/null +++ b/mgmtsystem_event/data/mgmtsystem_event_cause.xml @@ -0,0 +1,41 @@ + + + Badly defined need + + + + Work conditions + + + + Information not available + + + + Bad communication + + + + Bad customer/supplier relation + + + + Human resource not available or inadequate + + + + Instruction not respected + + + + Procedure not respected + + + + Technical failure + + + + Procedure not defined + + diff --git a/mgmtsystem_event/data/mgmtsystem_event_origin.xml b/mgmtsystem_event/data/mgmtsystem_event_origin.xml new file mode 100644 index 00000000000..07d8a504d66 --- /dev/null +++ b/mgmtsystem_event/data/mgmtsystem_event_origin.xml @@ -0,0 +1,29 @@ + + + Quality Committee + + + + Direction Review + + + + Internal client complaint + + + + External client complaint + + + + Process Execution + + + + Internal Audit + + + + External Audit + + diff --git a/mgmtsystem_event/data/mgmtsystem_event_severity.xml b/mgmtsystem_event/data/mgmtsystem_event_severity.xml new file mode 100644 index 00000000000..247931ed183 --- /dev/null +++ b/mgmtsystem_event/data/mgmtsystem_event_severity.xml @@ -0,0 +1,16 @@ + + + Major + + + + + Minor + + + + + Unfounded + + + diff --git a/mgmtsystem_event/data/mgmtsystem_event_stage.xml b/mgmtsystem_event/data/mgmtsystem_event_stage.xml new file mode 100644 index 00000000000..6fa7321f936 --- /dev/null +++ b/mgmtsystem_event/data/mgmtsystem_event_stage.xml @@ -0,0 +1,37 @@ + + + Draft + 0 + draft + + + Analysis + 50 + analysis + + + Action Plan + 100 + pending + + + In Progress + 150 + open + + + Closed + 200 + done + + + Cancelled + 250 + cancel + + + Rejected + 300 + cancel + + diff --git a/mgmtsystem_event/data/sequence.xml b/mgmtsystem_event/data/sequence.xml new file mode 100644 index 00000000000..694fb9ba095 --- /dev/null +++ b/mgmtsystem_event/data/sequence.xml @@ -0,0 +1,8 @@ + + + Management System Event + mgmtsystem.event + 3 + + + diff --git a/mgmtsystem_event/demo/mgmtsystem_event.xml b/mgmtsystem_event/demo/mgmtsystem_event.xml new file mode 100644 index 00000000000..417250123db --- /dev/null +++ b/mgmtsystem_event/demo/mgmtsystem_event.xml @@ -0,0 +1,24 @@ + + + + 2010-12-03 + Demo Event + + + + The document has changed with no comments on the revision. + Administrator didn't know he had to add a comment when changing the document. + + + + + + + diff --git a/mgmtsystem_event/demo/mgmtsystem_event_cause.xml b/mgmtsystem_event/demo/mgmtsystem_event_cause.xml new file mode 100644 index 00000000000..7fc449b6f49 --- /dev/null +++ b/mgmtsystem_event/demo/mgmtsystem_event_cause.xml @@ -0,0 +1,8 @@ + + + Lack of expertise + The cause of the event is a lack of expertise of the person. + + diff --git a/mgmtsystem_event/demo/mgmtsystem_event_origin.xml b/mgmtsystem_event/demo/mgmtsystem_event_origin.xml new file mode 100644 index 00000000000..cc3e7626706 --- /dev/null +++ b/mgmtsystem_event/demo/mgmtsystem_event_origin.xml @@ -0,0 +1,8 @@ + + + Internal Audit + The event was discovered during an internal audit. + + diff --git a/mgmtsystem_event/models/__init__.py b/mgmtsystem_event/models/__init__.py new file mode 100644 index 00000000000..98fca6301ea --- /dev/null +++ b/mgmtsystem_event/models/__init__.py @@ -0,0 +1,12 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from . import mgmtsystem_event_stage +from . import mgmtsystem_event_cause +from . import mgmtsystem_event_origin +from . import mgmtsystem_event_severity +from . import mgmtsystem_event_tag +from . import mgmtsystem_event +from . import mgmtsystem_action +from . import mail_thread diff --git a/mgmtsystem_event/models/mail_thread.py b/mgmtsystem_event/models/mail_thread.py new file mode 100644 index 00000000000..660aa9a47e6 --- /dev/null +++ b/mgmtsystem_event/models/mail_thread.py @@ -0,0 +1,45 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + +from odoo.addons.mail.tools.discuss import Store + + +class MailThread(models.AbstractModel): + _inherit = "mail.thread" + + def _get_events_domain(self): + return [("res_model", "=", self._name), ("res_id", "=", self.id)] + + def _get_events_context(self): + return {} + + def action_view_events(self): + self.ensure_one() + action = self.env.ref("mgmtsystem_event.open_mgmtsystem_event_list").read()[0] + action["domain"] = self._get_events_domain() + action["context"] = self._get_events_context() + return action + + def _thread_to_store(self, store: Store, /, *, request_list=None, **kwargs): + result = super()._thread_to_store(store, request_list=request_list, **kwargs) + if self.env.user.has_group("mgmtsystem.group_mgmtsystem_viewer"): + event_count = { + res_id: res_count + for res_id, res_count in self.env["mgmtsystem.event"]._read_group( + [ + ("res_model", "=", self._name), + ("res_id", "in", self.ids), + ], + ["res_id"], + ["res_id:count"], + ) + } + for thread in self: + store.add( + thread, + {"event_count": event_count.get(thread.id, 0)}, + as_thread=True, + ) + return result diff --git a/mgmtsystem_event/models/mgmtsystem_action.py b/mgmtsystem_event/models/mgmtsystem_action.py new file mode 100644 index 00000000000..046f1745b0f --- /dev/null +++ b/mgmtsystem_event/models/mgmtsystem_action.py @@ -0,0 +1,17 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class MgmtsystemAction(models.Model): + _inherit = "mgmtsystem.action" + + event_immediate_id = fields.One2many("mgmtsystem.event", "immediate_action_id") + event_ids = fields.Many2many( + "mgmtsystem.event", + "mgmtsystem_event_action_rel", + "action_id", + "event_id", + "Events", + ) diff --git a/mgmtsystem_event/models/mgmtsystem_event.py b/mgmtsystem_event/models/mgmtsystem_event.py new file mode 100644 index 00000000000..41ca01793cc --- /dev/null +++ b/mgmtsystem_event/models/mgmtsystem_event.py @@ -0,0 +1,311 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import api, fields, models + + +class MgmtsystemEvent(models.Model): + _name = "mgmtsystem.event" + _description = "Event" + _inherit = ["mail.thread", "mail.activity.mixin"] + _order = "create_date desc" + + @api.model + def _stage_groups(self, stages, domain): + stage_ids = self.env["mgmtsystem.event.stage"].search([]) + return stage_ids + + # 1. Description + name = fields.Char(required=True, tracking=True) + ref = fields.Char("Reference", required=True, default="NEW") + # Compute data + number_of_events = fields.Integer("# of events", default=1) + opened_date = fields.Datetime("Date Opened", readonly=True, tracking=True) + deadline_date = fields.Datetime( + "Deadline", help="Deadline to handle and close event", tracking=True + ) + closed_date = fields.Datetime("Date Closed", readonly=True, tracking=True) + days_since_updated = fields.Integer( + compute="_compute_days_since_updated", store=True + ) + number_of_days_to_open = fields.Integer( + "# of days to open", compute="_compute_number_of_days_to_open", store=True + ) + number_of_days_to_close = fields.Integer( + "# of days to close", + compute="_compute_number_of_days_to_close", + store=True, + ) + + partner_id = fields.Many2one( + "res.partner", + "Partner", + required=True, + default=lambda self: self.env.company, + tracking=True, + ) + reference = fields.Char( + "Related to", default=lambda self: self._default_reference(), tracking=True + ) + event_type = fields.Selection( + [ + ("improvement", "Improvement"), + ("prevention", "Preventive"), + ("correction", "Corrective"), + ("immediate", "Immediate"), + ], + "Type", + required=True, + default="improvement", + tracking=True, + ) + responsible_user_id = fields.Many2one("res.users", "Responsible", tracking=True) + manager_user_id = fields.Many2one("res.users", "Manager", tracking=True) + user_id = fields.Many2one( + "res.users", + "Filled in by", + required=True, + default=lambda self: self.env.user, + tracking=True, + ) + origin_ids = fields.Many2many( + "mgmtsystem.event.origin", + "mgmtsystem_event_origin_rel", + "event_id", + "origin_id", + "Origin", + tracking=True, + ) + document_ids = fields.Many2many( + "document.page", + "mgmtsystem_event_document_rel", + "event_id", + "document_id", + "Documents", + help="Against which document is the event. Usually a Procedure.", + ) + description = fields.Text(required=True) + system_id = fields.Many2one("mgmtsystem.system", "System") + stage_id = fields.Many2one( + "mgmtsystem.event.stage", + "Stage", + tracking=True, + copy=False, + default=lambda self: self._default_stage(), + group_expand="_stage_groups", + ) + state = fields.Selection(related="stage_id.state", store=True) + kanban_state = fields.Selection( + [ + ("normal", "In Progress"), + ("done", "Ready for next stage"), + ("blocked", "Blocked"), + ], + default="normal", + tracking=True, + help="A kanban state indicates special situations affecting it:\n" + " * Normal is the default situation\n" + " * Blocked indicates something is preventing" + " the progress of this task\n" + " * Ready for next stage indicates the" + " task is ready to be pulled to the next stage", + required=True, + copy=False, + ) + + # 2. Root Cause Analysis + cause_ids = fields.Many2many( + "mgmtsystem.event.cause", + "mgmtsystem_event_cause_rel", + "event_id", + "cause_id", + "Cause", + ) + severity_id = fields.Many2one("mgmtsystem.event.severity", "Severity") + analysis = fields.Text() + immediate_action_id = fields.Many2one( + "mgmtsystem.action", + domain="[('event_ids', '=', id)]", + ) + + # 3. Action Plan + action_ids = fields.Many2many( + "mgmtsystem.action", + "mgmtsystem_event_action_rel", + "event_id", + "action_id", + "Actions", + ) + action_comments = fields.Text( + "Action Plan Comments", help="Comments on the action plan." + ) + + # 4. Effectiveness Evaluation + evaluation_comments = fields.Text( + help="Conclusions from the last effectiveness evaluation.", + ) + + # Multi-company + company_id = fields.Many2one( + "res.company", "Company", default=lambda self: self.env.company + ) + res_model = fields.Char(index=True) + res_id = fields.Integer(index=True) + res_ref = fields.Reference( + string="Related Record", + selection="_referenceable_models", + compute="_compute_res_ref", + ) + tag_ids = fields.Many2many("mgmtsystem.event.tag", string="Tags") + + @api.model + def _default_stage(self): + """Return the default stage.""" + return self.env.ref("mgmtsystem_event.stage_draft", False) or self.env[ + "mgmtsystem.event.stage" + ].search([("is_starting", "=", True)], limit=1) + + @api.model + def _referenceable_models(self): + return [ + (x.model, f"{x.name} ({x.model})") + for x in self.env["ir.model"] + .sudo() + .search( + [ + ("model", "!=", "mail.thread"), + ("model", "not ilike", "ir."), + ("transient", "=", False), + ], + order="name", + ) + ] + + @api.model + def _default_reference(self): + if self.env.context.get("mgmtsystem_event") and self.env.context.get("id"): + return ( + self.env[self.env.context["mgmtsystem_event"]] + .browse(self.env.context.get("id")) + .exists() + .display_name + ) + return "" + + def _get_all_actions(self): + self.ensure_one() + return self.action_ids + self.immediate_action_id + + @api.constrains("stage_id") + def _check_open_with_action_comments(self): + for nc in self: + if nc.state == "open" and not nc.action_comments: + raise models.ValidationError( + self.env._( + "Action plan comments are required " + "in order to put a event In Progress." + ) + ) + + @api.constrains("stage_id") + def _check_close_with_evaluation(self): + for nc in self: + if nc.state == "done": + if not nc.evaluation_comments: + raise models.ValidationError( + self.env._( + "Evaluation Comments are required " + "in order to close a Event." + ) + ) + actions_are_closed = nc._get_all_actions().mapped("stage_id.is_ending") + if not all(actions_are_closed): + raise models.ValidationError( + self.env._( + "All actions must be done " "before closing a Event." + ) + ) + + @api.model + def _elapsed_days(self, dt1, dt2): + return (dt2 - dt1).days if dt1 and dt2 else 0 + + @api.depends("create_date", "opened_date") + def _compute_number_of_days_to_open(self): + for event in self: + event.number_of_days_to_open = self._elapsed_days( + event.create_date, event.opened_date + ) + + @api.depends("opened_date", "closed_date") + def _compute_number_of_days_to_close(self): + for event in self: + event.number_of_days_to_close = self._elapsed_days( + event.opened_date, event.closed_date + ) + + @api.depends("write_date") + def _compute_days_since_updated(self): + for nc in self: + nc.days_since_updated = self._elapsed_days(nc.create_date, nc.write_date) + + @api.depends("res_model", "res_id") + def _compute_res_ref(self): + for rec in self: + if rec.res_model and rec.res_id: + rec.res_ref = f"{rec.res_model},{rec.res_id}" + else: + rec.res_ref = False + + @api.model_create_multi + def create(self, vals): + for value in vals: + value.update( + {"ref": self.env["ir.sequence"].next_by_code("mgmtsystem.event")} + ) + return super().create(vals) + + def write(self, vals): + is_writing = self.env.context.get("is_writing", False) + is_state_change = "stage_id" in vals or "state" in vals + # Reset Kanban State on Stage change + if is_state_change: + was_not_open = { + x.id: x.state in ("draft", "analysis", "pending") for x in self + } + if any(self.filtered(lambda x: x.kanban_state != "normal")): + vals["kanban_state"] = "normal" + + result = super().write(vals) + + # Set/reset the closing date + if not is_writing and is_state_change: + for nc in self.with_context(is_writing=True): + # On Close set Closing Date + if nc.state == "done" and not nc.closed_date: + nc.closed_date = fields.Datetime.now() + # On reopen resete Closing Date + elif nc.state != "done" and nc.closed_date: + nc.closed_date = None + # On action plan approval, Open the Actions + if nc.state == "open" and was_not_open[nc.id]: + for action in nc._get_all_actions(): + if action.stage_id.is_starting: + action.case_open() + + # Update followers + if "responsible_user_id" in vals: + for record in self: + if record.responsible_user_id and record.responsible_user_id.partner_id: + record.message_subscribe( + partner_ids=[record.responsible_user_id.partner_id.id] + ) + if "manager_user_id" in vals: + for record in self: + if record.manager_user_id and record.manager_user_id.partner_id: + record.message_subscribe( + partner_ids=[record.manager_user_id.partner_id.id] + ) + + return result diff --git a/mgmtsystem_event/models/mgmtsystem_event_cause.py b/mgmtsystem_event/models/mgmtsystem_event_cause.py new file mode 100644 index 00000000000..b4b6cda3adc --- /dev/null +++ b/mgmtsystem_event/models/mgmtsystem_event_cause.py @@ -0,0 +1,30 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class MgmtsystemEventCause(models.Model): + """Cause of the event of the management system.""" + + _name = "mgmtsystem.event.cause" + _description = "Cause of the event of the management system" + _order = "parent_id, sequence" + _parent_store = True + + name = fields.Char("Cause", required=True, translate=True) + description = fields.Text() + sequence = fields.Integer(help="Defines the order to present items") + parent_path = fields.Char(index=True) + parent_id = fields.Many2one("mgmtsystem.event.cause", "Group", ondelete="restrict") + child_ids = fields.One2many("mgmtsystem.event.cause", "parent_id", "Child Causes") + ref_code = fields.Char("Reference Code") + display_name = fields.Char(compute="_compute_display_name", recursive=True) + + @api.depends("name", "parent_id.display_name") + def _compute_display_name(self): + for obj in self: + if obj.parent_id: + obj.display_name = f"{obj.parent_id.display_name} / {obj.name}" + else: + obj.display_name = obj.name diff --git a/mgmtsystem_event/models/mgmtsystem_event_origin.py b/mgmtsystem_event/models/mgmtsystem_event_origin.py new file mode 100644 index 00000000000..8176cce1c2d --- /dev/null +++ b/mgmtsystem_event/models/mgmtsystem_event_origin.py @@ -0,0 +1,29 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class MgmtsystemEventOrigin(models.Model): + _name = "mgmtsystem.event.origin" + _description = "Origin of event of the management system" + _order = "parent_id, sequence" + _parent_store = True + + name = fields.Char("Origin", required=True, translate=True) + description = fields.Text() + sequence = fields.Integer(help="Defines the order to present items") + parent_path = fields.Char(index=True) + parent_id = fields.Many2one("mgmtsystem.event.origin", "Group", ondelete="restrict") + child_ids = fields.One2many("mgmtsystem.event.origin", "parent_id", "Childs") + ref_code = fields.Char("Reference Code") + + active = fields.Boolean(default=True) + + @api.depends("name", "parent_id.name") + def _compute_display_name(self): + for obj in self: + name = obj.name + if obj.parent_id: + name = f"{obj.parent_id.display_name} / {name}" + obj.display_name = name diff --git a/mgmtsystem_event/models/mgmtsystem_event_severity.py b/mgmtsystem_event/models/mgmtsystem_event_severity.py new file mode 100644 index 00000000000..b0433582f1e --- /dev/null +++ b/mgmtsystem_event/models/mgmtsystem_event_severity.py @@ -0,0 +1,16 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class MgmtsystemEventSeverity(models.Model): + """Event Severity - Critical, Major, Minor, Invalid, ...""" + + _name = "mgmtsystem.event.severity" + _description = "Severity of Complaints and Events" + + name = fields.Char("Title", required=True, translate=True) + sequence = fields.Integer() + description = fields.Text(translate=True) + active = fields.Boolean("Active?", default=True) diff --git a/mgmtsystem_event/models/mgmtsystem_event_stage.py b/mgmtsystem_event/models/mgmtsystem_event_stage.py new file mode 100644 index 00000000000..96d59d1575c --- /dev/null +++ b/mgmtsystem_event/models/mgmtsystem_event_stage.py @@ -0,0 +1,37 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class MgmtsystemEventStage(models.Model): + """This object is used to defined different state for Event.""" + + _name = "mgmtsystem.event.stage" + _description = "Event Stages" + _order = "sequence" + + def _get_states(self): + return [ + ("draft", self.env._("Draft")), + ("analysis", self.env._("Analysis")), + ("pending", self.env._("Action Plan")), + ("open", self.env._("In Progress")), + ("done", self.env._("Closed")), + ("cancel", self.env._("Cancelled")), + ] + + name = fields.Char("Stage Name", required=True, translate=True) + sequence = fields.Integer( + help="Used to order states. Lower is better.", default=100 + ) + state = fields.Selection(selection=_get_states, default="draft") + is_starting = fields.Boolean( + string="Is starting Stage", + help="select stis checkbox if this is the default stage \n" "for new events", + ) + fold = fields.Boolean( + string="Folded in Kanban", + help="This stage is folded in the kanban view when there are \n" + "no records in that stage to display.", + ) diff --git a/mgmtsystem_event/models/mgmtsystem_event_tag.py b/mgmtsystem_event/models/mgmtsystem_event_tag.py new file mode 100644 index 00000000000..e466c5d95e8 --- /dev/null +++ b/mgmtsystem_event/models/mgmtsystem_event_tag.py @@ -0,0 +1,14 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class MgmtsystemEventTag(models.Model): + _name = "mgmtsystem.event.tag" + _description = "Event Tags" + + name = fields.Char(required=True) + color = fields.Integer(string="Color Index", default=10) + + _sql_constraints = [("name_uniq", "unique (name)", "Tag name already exists !")] diff --git a/mgmtsystem_event/pyproject.toml b/mgmtsystem_event/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/mgmtsystem_event/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/mgmtsystem_event/readme/CONFIGURE.md b/mgmtsystem_event/readme/CONFIGURE.md new file mode 100644 index 00000000000..63435775c6c --- /dev/null +++ b/mgmtsystem_event/readme/CONFIGURE.md @@ -0,0 +1,3 @@ +Users must be added to the appropriate groups within Odoo as follows: + +- Creators: Settings \> Users \> Groups \> Management System / User \ No newline at end of file diff --git a/mgmtsystem_event/readme/CONTRIBUTORS.md b/mgmtsystem_event/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..6b1b4a30eb7 --- /dev/null +++ b/mgmtsystem_event/readme/CONTRIBUTORS.md @@ -0,0 +1,14 @@ +- Daniel Reis \<\> +- Glen Dromgoole \<\> +- Loic Lacroix \<\> +- Sandy Carter \<\> +- Gervais Naoussi \<\> +- Eugen Don \<\> +- Jose Maria Alzaga \<\> +- [Tecnativa](https://www.tecnativa.com): + - Ernesto Tejeda + +Trobz + +- Dung Tran \<\> +- Tuan Nguyen \<\> \ No newline at end of file diff --git a/mgmtsystem_event/readme/CREDITS.md b/mgmtsystem_event/readme/CREDITS.md new file mode 100644 index 00000000000..705d3b30cad --- /dev/null +++ b/mgmtsystem_event/readme/CREDITS.md @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +- Camptocamp diff --git a/mgmtsystem_event/readme/DESCRIPTION.md b/mgmtsystem_event/readme/DESCRIPTION.md new file mode 100644 index 00000000000..29e8e142351 --- /dev/null +++ b/mgmtsystem_event/readme/DESCRIPTION.md @@ -0,0 +1,8 @@ +This module enables you to manage the events of your management +systems: + +- Quality (ISO 9001) +- Environment (ISO 14001) +- Information Security (ISO 27001) +- Health and Safety (ISO 45001) +- IT Services (ISO 20000) diff --git a/mgmtsystem_event/readme/ROADMAP.md b/mgmtsystem_event/readme/ROADMAP.md new file mode 100644 index 00000000000..a01df886ea8 --- /dev/null +++ b/mgmtsystem_event/readme/ROADMAP.md @@ -0,0 +1,4 @@ +- The custom emails should be replaced by Mail Tracking features and + Subtypes (like in Project Tasks and Project Issues) +- Automatically add responsible_user_id.\_uid, manager_user_id.\_uid, + author_user_id.\_uid to chatter diff --git a/mgmtsystem_event/readme/USAGE.md b/mgmtsystem_event/readme/USAGE.md new file mode 100644 index 00000000000..9fc255970e8 --- /dev/null +++ b/mgmtsystem_event/readme/USAGE.md @@ -0,0 +1,50 @@ +To use this module: + +- Go to Management System \> Events +- Click on Create to enter the following information: +- Partner : Customer, supplier or internal personnel +- Related to: Any reference pointing to the NC (order id, project id, + task id, etc.) +- Responsible: Person responsible for the NC +- Manager : Person managing the department or owner of the procedure +- Filled in by: Originator of NC report +- Origins: The source of the NC, how was it discover +- Documents: Against which document is the NC +- Description: Evidence, reference to the standards +- Click on Save and then set state from Draft to Analysis. + +Go to the newly created NC and fill in the following information: + +In the Causes and Analysis tab: +- Causes: Add root causes +- Analysis: Describe the results of the investigation +- Severity: Select the severity among unfounded, minor and major + +In the Description tab: +- Immediate action: Create or select an immediate action if appropriate + +Click on the Save button and then on the "Action Plan" button in the top +right corner. + +In the Actions tab, select or create new actions by entering the +following items: + +- Subject: What must be done - Return to Supplier, Use As Is, Scrap, + Rework, Re-grade, Repair +- Deadline: Date by which the action must be completed +- Responsible: Person in charge for implementing the action +- Response Type: Immediate, corrective or preventive actions or improvement + opportunity +- Description: Details of the action + +When the action is created, a notification is sent to the person +responsible for the action. + +Enter comments into the input field below the "Plan Review" section, +those comments are required to reach the next stage. + +To begin the work on the planned Actions change the stage of the NC to +open by clicking on the "In Progress" button in the top right corner. + +When all actions of the plan are done, their effectiveness must be +evaluated before closing the NC. diff --git a/mgmtsystem_event/reports/mgmtsystem_event_report.xml b/mgmtsystem_event/reports/mgmtsystem_event_report.xml new file mode 100644 index 00000000000..4693153f9fc --- /dev/null +++ b/mgmtsystem_event/reports/mgmtsystem_event_report.xml @@ -0,0 +1,47 @@ + + + + mgmtsystem.event.report.pivot + mgmtsystem.event + + + + + + + + + + + + mgmtsystem.event.report.graph + mgmtsystem.event + + + + + + + + + + + + Management System Analysis + mgmtsystem.event + pivot,graph + + + Have a general overview of all events processed in the system by sorting them with specific criteria. + + + + diff --git a/mgmtsystem_event/security/ir.model.access.csv b/mgmtsystem_event/security/ir.model.access.csv new file mode 100644 index 00000000000..6b02d28f1d7 --- /dev/null +++ b/mgmtsystem_event/security/ir.model.access.csv @@ -0,0 +1,17 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_mgmtsystem_event_user","mgmtsystem.event.user","model_mgmtsystem_event","mgmtsystem.group_mgmtsystem_user",1,1,1,0 +"access_mgmtsystem_event_viewer","mgmtsystem.event.viewer","model_mgmtsystem_event","mgmtsystem.group_mgmtsystem_viewer",1,0,0,0 +"access_mgmtsystem_event_origin_user","mgmtsystem.event.origin.user","model_mgmtsystem_event_origin","mgmtsystem.group_mgmtsystem_user",1,0,0,0 +"access_mgmtsystem_event_origin_viewer","mgmtsystem.event.origin.viewer","model_mgmtsystem_event_origin","mgmtsystem.group_mgmtsystem_viewer",1,0,0,0 +"access_mgmtsystem_event_origin_manager","mgmtsystem.event.origin.manager","model_mgmtsystem_event_origin","mgmtsystem.group_mgmtsystem_manager",1,1,1,1 +"access_mgmtsystem_event_cause_user","mgmtsystem.event.cause.user","model_mgmtsystem_event_cause","mgmtsystem.group_mgmtsystem_user",1,0,0,0 +"access_mgmtsystem_event_cause_viewer","mgmtsystem.event.cause.viewer","model_mgmtsystem_event_cause","mgmtsystem.group_mgmtsystem_viewer",1,0,0,0 +"access_mgmtsystem_event_cause_manager","mgmtsystem.event.cause.manager","model_mgmtsystem_event_cause","mgmtsystem.group_mgmtsystem_manager",1,1,1,1 +"access_mgmtsystem_event_severity_user","mgmtsystem.event.severity.user","model_mgmtsystem_event_severity","mgmtsystem.group_mgmtsystem_user",1,0,0,0 +"access_mgmtsystem_event_severity_viewer","mgmtsystem.event.severity.viewer","model_mgmtsystem_event_severity","mgmtsystem.group_mgmtsystem_viewer",1,0,0,0 +"access_mgmtsystem_event_severity_manager","mgmtsystem.event.severity.manager","model_mgmtsystem_event_severity","mgmtsystem.group_mgmtsystem_manager",1,1,1,1 +"access_mgmtsystem_event_stage_user","access_mgmtsystem_event_stage","model_mgmtsystem_event_stage","mgmtsystem.group_mgmtsystem_user",1,0,0,0 +"access_mgmtsystem_event_stage_viewer","mgmtsystem.event.stage.viewer","model_mgmtsystem_event_stage","mgmtsystem.group_mgmtsystem_viewer",1,0,0,0 +"access_mgmtsystem_event_stage_manager","mgmtsystem.event.stage.manager","model_mgmtsystem_event_stage","mgmtsystem.group_mgmtsystem_manager",1,1,1,1 +"access_mgmtsystem_event_tag_user","access_mgmtsystem_event_tag_user","model_mgmtsystem_event_tag","mgmtsystem.group_mgmtsystem_user",1,0,0,0 +"access_mgmtsystem_event_tag_manager","access_mgmtsystem_event_tag_manager","model_mgmtsystem_event_tag","mgmtsystem.group_mgmtsystem_manager",1,1,1,1 diff --git a/mgmtsystem_event/security/mgmtsystem_event_security.xml b/mgmtsystem_event/security/mgmtsystem_event_security.xml new file mode 100644 index 00000000000..76a35b4ec5c --- /dev/null +++ b/mgmtsystem_event/security/mgmtsystem_event_security.xml @@ -0,0 +1,12 @@ + + + + + mgmtsystem_event multi-company + + + ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] + + diff --git a/mgmtsystem_event/static/description/icon.png b/mgmtsystem_event/static/description/icon.png new file mode 100644 index 00000000000..9e53b1e966a Binary files /dev/null and b/mgmtsystem_event/static/description/icon.png differ diff --git a/mgmtsystem_event/static/description/index.html b/mgmtsystem_event/static/description/index.html new file mode 100644 index 00000000000..8f8b24d6167 --- /dev/null +++ b/mgmtsystem_event/static/description/index.html @@ -0,0 +1,522 @@ + + + + + +Management System - Event + + + +
+

Management System - Event

+ + +

Beta License: AGPL-3 OCA/management-system Translate me on Weblate Try me on Runboat

+

This module enables you to manage the events of your management systems:

+
    +
  • Quality (ISO 9001)
  • +
  • Environment (ISO 14001)
  • +
  • Information Security (ISO 27001)
  • +
  • Health and Safety (ISO 45001)
  • +
  • IT Services (ISO 20000)
  • +
+

Table of contents

+ +
+

Configuration

+

Users must be added to the appropriate groups within Odoo as follows:

+
    +
  • Creators: Settings > Users > Groups > Management System / User
  • +
+
+
+

Usage

+

To use this module:

+
    +
  • Go to Management System > Events
  • +
  • Click on Create to enter the following information:
  • +
  • Partner : Customer, supplier or internal personnel
  • +
  • Related to: Any reference pointing to the NC (order id, project id, +task id, etc.)
  • +
  • Responsible: Person responsible for the NC
  • +
  • Manager : Person managing the department or owner of the procedure
  • +
  • Filled in by: Originator of NC report
  • +
  • Origins: The source of the NC, how was it discover
  • +
  • Documents: Against which document is the NC
  • +
  • Description: Evidence, reference to the standards
  • +
  • Click on Save and then set state from Draft to Analysis.
  • +
+

Go to the newly created NC and fill in the following information:

+

In the Causes and Analysis tab:

+
    +
  • Causes: Add root causes
  • +
  • Analysis: Describe the results of the investigation
  • +
  • Severity: Select the severity among unfounded, minor and major
  • +
+

In the Description tab:

+
    +
  • Immediate action: Create or select an immediate action if appropriate
  • +
+

Click on the Save button and then on the “Action Plan” button in the top +right corner.

+

In the Actions tab, select or create new actions by entering the +following items:

+
    +
  • Subject: What must be done - Return to Supplier, Use As Is, Scrap, +Rework, Re-grade, Repair
  • +
  • Deadline: Date by which the action must be completed
  • +
  • Responsible: Person in charge for implementing the action
  • +
  • Response Type: Immediate, corrective or preventive actions or +improvement opportunity
  • +
  • Description: Details of the action
  • +
+

When the action is created, a notification is sent to the person +responsible for the action.

+

Enter comments into the input field below the “Plan Review” section, +those comments are required to reach the next stage.

+

To begin the work on the planned Actions change the stage of the NC to +open by clicking on the “In Progress” button in the top right corner.

+

When all actions of the plan are done, their effectiveness must be +evaluated before closing the NC.

+
+
+

Known issues / Roadmap

+
    +
  • The custom emails should be replaced by Mail Tracking features and +Subtypes (like in Project Tasks and Project Issues)
  • +
  • Automatically add responsible_user_id._uid, manager_user_id._uid, +author_user_id._uid to chatter
  • +
+
+
+

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 to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Savoir-faire Linux
  • +
+
+
+

Contributors

+ +

Trobz

+ +
+
+

Other credits

+

The development of this module has been financially supported by:

+
    +
  • Camptocamp
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

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.

+

This module is part of the OCA/management-system project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/mgmtsystem_event/static/src/components/chatter_topbar/chatter_topbar.esm.js b/mgmtsystem_event/static/src/components/chatter_topbar/chatter_topbar.esm.js new file mode 100644 index 00000000000..808524ab4df --- /dev/null +++ b/mgmtsystem_event/static/src/components/chatter_topbar/chatter_topbar.esm.js @@ -0,0 +1,22 @@ +import {Chatter} from "@mail/chatter/web_portal/chatter"; +import {patch} from "@web/core/utils/patch"; + +patch(Chatter.prototype, { + async onClickShowEvents() { + if (this.isTemporary) { + const saved = await this.doSaveRecord(); + if (!saved) { + return; + } + } + this.env.services.action.doAction( + "mgmtsystem_event.open_mgmtsystem_event_thread_list", + { + additionalContext: { + id: this.props.threadId, + mgmtsystem_event: this.props.threadModel, + }, + } + ); + }, +}); diff --git a/mgmtsystem_event/static/src/components/chatter_topbar/chatter_topbar.xml b/mgmtsystem_event/static/src/components/chatter_topbar/chatter_topbar.xml new file mode 100644 index 00000000000..a4adfb692c2 --- /dev/null +++ b/mgmtsystem_event/static/src/components/chatter_topbar/chatter_topbar.xml @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/mgmtsystem_event/static/tests/test_chatter.test.js b/mgmtsystem_event/static/tests/test_chatter.test.js new file mode 100644 index 00000000000..93659c6cc8c --- /dev/null +++ b/mgmtsystem_event/static/tests/test_chatter.test.js @@ -0,0 +1,71 @@ +import { + assertSteps, + contains, + mailModels, + onRpcBefore, + openFormView, + start, + startServer, + step, +} from "@mail/../tests/mail_test_helpers"; + +import { + defineModels, + getKwArgs, + makeKwArgs, + serverState, +} from "@web/../tests/web_test_helpers"; +import {describe, test} from "@odoo/hoot"; + +describe.current.tags("desktop"); + +class MailThreadEvent extends mailModels.MailThread { + _thread_to_store() { + const result = super._thread_to_store(...arguments); + const kwargs = getKwArgs(arguments, "ids", "store", "fields", "request_list"); + const store = kwargs.store; + const id = kwargs.ids[0]; + store.add( + this.env[this._name].browse(id), + { + event_count: 0, + }, + makeKwArgs({as_thread: true}) + ); + return result; + } +} + +defineModels({...mailModels, MailThread: MailThreadEvent}); + +test("simple chatter on a record", async () => { + const pyEnv = await startServer(); + onRpcBefore((route, args) => { + if (route.startsWith("/mail") || route.startsWith("/discuss")) { + step(`${route} - ${JSON.stringify(args)}`); + } + }); + await start(); + await assertSteps([ + `/mail/data - ${JSON.stringify({ + init_messaging: {}, + failures: true, + systray_get_activities: true, + context: { + lang: "en", + tz: "taht", + uid: serverState.userId, + allowed_company_ids: [1], + }, + })}`, + ]); + const partnerId = pyEnv["res.partner"].create({name: "John Doe"}); + await openFormView("res.partner", partnerId); + await contains(".o-mail-Chatter-topbar"); + await contains(".o-mail-Thread"); + await assertSteps([ + `/mail/thread/data - {"request_list":["activities","attachments","followers","scheduledMessages","suggestedRecipients"],"thread_id":${partnerId},"thread_model":"res.partner"}`, + `/mail/thread/messages - {"thread_id":${partnerId},"thread_model":"res.partner","limit":30}`, + ]); + await contains(".o_ChatterTopbar_buttonEvents"); +}); diff --git a/mgmtsystem_event/tests/__init__.py b/mgmtsystem_event/tests/__init__.py new file mode 100644 index 00000000000..d03d8e839e7 --- /dev/null +++ b/mgmtsystem_event/tests/__init__.py @@ -0,0 +1,6 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_cause +from . import test_origin +from . import test_event +from . import test_js diff --git a/mgmtsystem_event/tests/test_cause.py b/mgmtsystem_event/tests/test_cause.py new file mode 100644 index 00000000000..05da8a468a9 --- /dev/null +++ b/mgmtsystem_event/tests/test_cause.py @@ -0,0 +1,47 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import exceptions +from odoo.tests import common + + +class TestModelCause(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.record = cls.env["mgmtsystem.event.cause"].create({"name": "TestCause"}) + cls.record2 = cls.env["mgmtsystem.event.cause"].create( + {"name": "test2", "parent_id": cls.record.id} + ) + cls.record3 = cls.env["mgmtsystem.event.cause"].create( + {"name": "test3", "parent_id": cls.record2.id} + ) + + def test_create_cause(self): + self.assertNotEqual(self.record.id, 0) + self.assertNotEqual(self.record.id, None) + + def test_display_name(self): + name_assoc = self.record + name_assoc._compute_display_name() + self.assertEqual(name_assoc.display_name, "TestCause") + self.assertEqual(name_assoc.id, self.record.id) + + name_assoc = self.record2 + name_assoc._compute_display_name() + self.assertEqual(name_assoc.display_name, "TestCause / test2") + self.assertEqual(name_assoc.id, self.record2.id) + + name_assoc = self.record3 + name_assoc._compute_display_name() + self.assertEqual(name_assoc.display_name, "TestCause / test2 / test3") + self.assertEqual(name_assoc.id, self.record3.id) + + def test_recursion(self): + parent = self.env["mgmtsystem.event.cause"].create({"name": "ParentCause"}) + child = self.env["mgmtsystem.event.cause"].create( + {"name": "ChildCause", "parent_id": parent.id} + ) + # no recursion + with self.assertRaises(exceptions.UserError), self.cr.savepoint(): + parent.write({"parent_id": child.id}) diff --git a/mgmtsystem_event/tests/test_event.py b/mgmtsystem_event/tests/test_event.py new file mode 100644 index 00000000000..ea5512868ea --- /dev/null +++ b/mgmtsystem_event/tests/test_event.py @@ -0,0 +1,86 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.exceptions import ValidationError +from odoo.tests import common + + +class TestModelEvent(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.nc_model = cls.env["mgmtsystem.event"] + cls.partner = cls.env["res.partner"].search([])[0] + cls.nc_test = cls.nc_model.create( + { + "partner_id": cls.partner.id, + "manager_user_id": cls.env.user.id, + "description": "description", + "responsible_user_id": cls.env.user.id, + } + ) + action_vals = {"name": "An Action", "type_action": "immediate"} + action1 = cls.nc_model.action_ids.create(action_vals) + cls.nc_test.immediate_action_id = action1 + + def test_stage_group(self): + """Group by Stage shows all stages""" + group_stages = self.nc_test.read_group( + domain=[], fields=["stage_id"], groupby=["stage_id"] + ) + num_stages = len(self.nc_model.stage_id.search([])) + self.assertEqual(len(group_stages), num_stages) + + def test_reset_kanban_state(self): + """Reset Kanban State on Stage change""" + self.nc_test.kanban_state = "done" + self.nc_test.stage_id = self.env.ref("mgmtsystem_event.stage_analysis") + self.assertEqual(self.nc_test.kanban_state, "normal") + + def test_open_validation(self): + """Don't allow approving/In Progress action comments""" + open_stage = self.env.ref("mgmtsystem_event.stage_open") + with self.assertRaises(ValidationError): + self.nc_test.stage_id = open_stage + + def test_done_validation(self): + """Don't allow closing an NC without evaluation comments""" + done_stage = self.env.ref("mgmtsystem_event.stage_done") + self.nc_test.action_comments = "OK!" + with self.assertRaises(ValidationError): + self.nc_test.stage_id = done_stage + + def test_done_actions_validation(self): + """Don't allow closing an NC with open actions""" + done_stage = self.env.ref("mgmtsystem_event.stage_done") + self.nc_test.immediate_action_id.stage_id = self.env.ref( + "mgmtsystem_action.stage_open" + ) + self.nc_test.evaluation_comments = "OK!" + with self.assertRaises(ValidationError): + self.nc_test.stage_id = done_stage + + def test_state_transition(self): + """Close and reopen Event""" + self.nc_test.action_comments = "OK!" + self.nc_test.stage_id = self.env.ref("mgmtsystem_event.stage_open") + self.assertEqual( + self.nc_test.immediate_action_id.stage_id, + self.env.ref("mgmtsystem_action.stage_open"), + "Plan Approval starts Actions", + ) + + self.nc_test.immediate_action_id.stage_id = self.env.ref( + "mgmtsystem_action.stage_open" + ) + self.nc_test.evaluation_comments = "OK!" + self.nc_test.immediate_action_id.stage_id = self.env.ref( + "mgmtsystem_action.stage_close" + ) + self.nc_test.stage_id = self.env.ref("mgmtsystem_event.stage_done") + self.assertEqual(self.nc_test.state, "done") + self.assertTrue(self.nc_test.closed_date, "Set close date on Done") + + self.nc_test.stage_id = self.env.ref("mgmtsystem_event.stage_open") + self.assertEqual(self.nc_test.state, "open") + self.assertFalse(self.nc_test.closed_date, "Reset close date on reopen") diff --git a/mgmtsystem_event/tests/test_js.py b/mgmtsystem_event/tests/test_js.py new file mode 100644 index 00000000000..d2514929593 --- /dev/null +++ b/mgmtsystem_event/tests/test_js.py @@ -0,0 +1,17 @@ +# Copyright 2025 Dixmit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import odoo + +from odoo.addons.web.tests.test_js import WebSuite + + +@odoo.tests.tagged("post_install", "-at_install") +class TestEventFrontend(WebSuite): + """Test Event OCA Frontend""" + + def get_hoot_filters(self): + self._test_params = [("+", "@mgmtsystem_event")] + return super().get_hoot_filters() + + def test_event(self): + self.test_unit_desktop() diff --git a/mgmtsystem_event/tests/test_origin.py b/mgmtsystem_event/tests/test_origin.py new file mode 100644 index 00000000000..a4475761bd3 --- /dev/null +++ b/mgmtsystem_event/tests/test_origin.py @@ -0,0 +1,37 @@ +# Copyright (C) 2010 Savoir-faire Linux (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests import common + + +class TestModelOrigin(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.record = cls.env["mgmtsystem.event.origin"].create({"name": "TestOrigin"}) + cls.record2 = cls.env["mgmtsystem.event.origin"].create( + {"name": "test2", "parent_id": cls.record.id} + ) + cls.record3 = cls.env["mgmtsystem.event.origin"].create( + {"name": "test3", "parent_id": cls.record2.id} + ) + + def test_create_origin(self): + self.assertNotEqual(self.record.id, 0) + self.assertNotEqual(self.record.id, None) + + def test_display_name(self): + name_assoc = self.record + name_assoc._compute_display_name() + self.assertEqual(name_assoc.display_name, "TestOrigin") + self.assertEqual(name_assoc.id, self.record.id) + + name_assoc = self.record2 + name_assoc._compute_display_name() + self.assertEqual(name_assoc.display_name, "TestOrigin / test2") + self.assertEqual(name_assoc.id, self.record2.id) + + name_assoc = self.record3 + name_assoc._compute_display_name() + self.assertEqual(name_assoc.display_name, "TestOrigin / test2 / test3") + self.assertEqual(name_assoc.id, self.record3.id) diff --git a/mgmtsystem_event/views/mgmtsystem_action.xml b/mgmtsystem_event/views/mgmtsystem_action.xml new file mode 100644 index 00000000000..ca2523b1d4e --- /dev/null +++ b/mgmtsystem_event/views/mgmtsystem_action.xml @@ -0,0 +1,20 @@ + + + + mgmtsystem.action.form + mgmtsystem.action + + + + + + + + + diff --git a/mgmtsystem_event/views/mgmtsystem_cause.xml b/mgmtsystem_event/views/mgmtsystem_cause.xml new file mode 100644 index 00000000000..bf906e1210f --- /dev/null +++ b/mgmtsystem_event/views/mgmtsystem_cause.xml @@ -0,0 +1,61 @@ + + + + + mgmtsystem.event.cause.form + form + mgmtsystem.event.cause + +
+ + + + + + + +
+
+
+ + + mgmtsystem.event.cause.list + mgmtsystem.event.cause + list + + + + + + + + + + + + Causes + mgmtsystem.event.cause + + + + + + + + + + Causes + ir.actions.act_window + mgmtsystem.event.cause + + + + + +
diff --git a/mgmtsystem_event/views/mgmtsystem_event.xml b/mgmtsystem_event/views/mgmtsystem_event.xml new file mode 100644 index 00000000000..484a62ffa9d --- /dev/null +++ b/mgmtsystem_event/views/mgmtsystem_event.xml @@ -0,0 +1,414 @@ + + + + + mgmtsystem.event.list + mgmtsystem.event + + + + + + + + + + + + + + + + + + + + + + mgmtsystem.event.filter + mgmtsystem.event + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mgmtsystem.event.kanban + mgmtsystem.event + + + + + + + + + +
+
+ + + - + + + +
+
+ +
+
+ + + + + +
+
+
+
+
+
+
+ + + mgmtsystem.event.form + mgmtsystem.event + +
+
+ +
+ +
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

You cannot edit this page if the event is in Draft state

+
+ +
+ + +
+

You cannot edit this page if the event is in Draft or Cancel state

+
+ + + + + + + + + + + + + + + + + + + + +
+ + +
+

You cannot edit this page if the event is in Draft, Analysis or Cancel state

+
+ + + + + + + + + + + +
+
+ + + + +
+ + + + Events + mgmtsystem.event + kanban,list,form + + + {"search_default_user_id":uid} + + + + Events + mgmtsystem.event + kanban,list,form + [('res_id', '=', id), ('res_model', '=', mgmtsystem_event)] + + + + {"search_default_user_id":uid, "default_res_model": mgmtsystem_event, "default_res_id": id} + + + + + diff --git a/mgmtsystem_event/views/mgmtsystem_event_stage.xml b/mgmtsystem_event/views/mgmtsystem_event_stage.xml new file mode 100644 index 00000000000..0036360f81b --- /dev/null +++ b/mgmtsystem_event/views/mgmtsystem_event_stage.xml @@ -0,0 +1,49 @@ + + + Management System Event Stage List + mgmtsystem.event.stage + + + + + + + + + + + mgmtsystem.event.stage.form.view + mgmtsystem.event.stage + +
+ + + + + + + + + + + +
+
+
+ + + Stages + mgmtsystem.event.stage + list,form + {} + + + +
diff --git a/mgmtsystem_event/views/mgmtsystem_event_tag.xml b/mgmtsystem_event/views/mgmtsystem_event_tag.xml new file mode 100644 index 00000000000..fafa4b7571e --- /dev/null +++ b/mgmtsystem_event/views/mgmtsystem_event_tag.xml @@ -0,0 +1,60 @@ + + + + Tags + mgmtsystem.event.tag + + + + + + + + + Tags + mgmtsystem.event.tag + + + + + + + + + + Tags + mgmtsystem.event.tag + +
+ + + +
+
+
+ + + Tags + mgmtsystem.event.tag + +

+ Click to add a new tag. +

+
+
+ + + Tags + mgmtsystem.event.tag + list,form + {} + + + +
diff --git a/mgmtsystem_event/views/mgmtsystem_origin.xml b/mgmtsystem_event/views/mgmtsystem_origin.xml new file mode 100644 index 00000000000..cb3292c7c68 --- /dev/null +++ b/mgmtsystem_event/views/mgmtsystem_origin.xml @@ -0,0 +1,70 @@ + + + + + mgmtsystem.event.origin.form + form + mgmtsystem.event.origin + +
+ + + + + + + + + + +
+
+
+ + + mgmtsystem.event.origin.list + mgmtsystem.event.origin + list + + + + + + + + + + + + + Origins + mgmtsystem.event.origin + + + + + + + + + + Origins + mgmtsystem.event.origin + list,form + + + + + +
diff --git a/mgmtsystem_event/views/mgmtsystem_severity.xml b/mgmtsystem_event/views/mgmtsystem_severity.xml new file mode 100644 index 00000000000..7329110c369 --- /dev/null +++ b/mgmtsystem_event/views/mgmtsystem_severity.xml @@ -0,0 +1,39 @@ + + + + mgmtsystem.event.severity.form + form + mgmtsystem.event.severity + +
+ + + + + + + + + +
+
+
+ + + Severity + mgmtsystem.event.severity + list,form + + + +