diff --git a/base_tier_validation/__manifest__.py b/base_tier_validation/__manifest__.py index 3508f5dc05..000ebe7556 100644 --- a/base_tier_validation/__manifest__.py +++ b/base_tier_validation/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Base Tier Validation", "summary": "Implement a validation process based on tiers.", - "version": "15.0.1.2.0", + "version": "16.0.0.0.0", "development_status": "Mature", "maintainers": ["LoisRForgeFlow"], "category": "Tools", @@ -25,14 +25,19 @@ ], "assets": { "web.assets_backend": [ - "/base_tier_validation/static/src/js/systray.js", - "/base_tier_validation/static/src/js/tier_review_widget.js", "/base_tier_validation/static/src/scss/systray.scss", "/base_tier_validation/static/src/scss/review.scss", - ], - "web.assets_qweb": [ - "base_tier_validation/static/src/xml/systray.xml", - "base_tier_validation/static/src/xml/tier_review_template.xml", + "/base_tier_validation/static/src/js/main.esm.js", + "/base_tier_validation/static/src/js/ir_model.esm.js", + "/base_tier_validation/static/src/js/systray_service.esm.js", + "/base_tier_validation/static/src/js/systray.esm.js", + "/base_tier_validation/static/src/js/review_groups.esm.js", + "/base_tier_validation/static/src/js/reviewer_menu_view.esm.js", + "/base_tier_validation/static/src/js/tier_review_widget.esm.js", + "/base_tier_validation/static/src/js/review_group_view.esm.js", + "/base_tier_validation/static/src/js/reviewer_menu_container.esm.js", + "/base_tier_validation/static/src/js/review_notification_handler.esm.js", + "/base_tier_validation/static/src/xml/**/*", ], }, } diff --git a/base_tier_validation/models/res_config_settings.py b/base_tier_validation/models/res_config_settings.py index f90a875ce0..c9fd1adbb4 100644 --- a/base_tier_validation/models/res_config_settings.py +++ b/base_tier_validation/models/res_config_settings.py @@ -1,12 +1,14 @@ # Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import models class ResConfigSettings(models.TransientModel): _inherit = "res.config.settings" - module_base_tier_validation_formula = fields.Boolean(string="Tier Formula") - module_base_tier_validation_forward = fields.Boolean("Tier Forward & Backward") - module_base_tier_validation_server_action = fields.Boolean("Tier Server Action") - module_base_tier_validation_report = fields.Boolean("Tier Reports") + # Activate me back when modules are migrated + + # module_base_tier_validation_formula = fields.Boolean(string="Tier Formula") + # module_base_tier_validation_forward = fields.Boolean("Tier Forward & Backward") + # module_base_tier_validation_server_action = fields.Boolean("Tier Server Action") + # module_base_tier_validation_report = fields.Boolean("Tier Reports") diff --git a/base_tier_validation/models/res_users.py b/base_tier_validation/models/res_users.py index c6253849ed..056aae4826 100644 --- a/base_tier_validation/models/res_users.py +++ b/base_tier_validation/models/res_users.py @@ -28,12 +28,15 @@ def review_user_count(self): .search([("id", "in", reviews.mapped("res_id"))]) .filtered(lambda x: not x.rejected and x.can_review) ) - if len(records): + # if len(records): + for rec in records: record = self.env[model] user_reviews[model] = { + "id": rec.id, "name": record._description, "model": model, "icon": modules.module.get_module_icon(record._original_module), + "type": "tier_review", "pending_count": len(records), } return list(user_reviews.values()) diff --git a/base_tier_validation/models/tier_review.py b/base_tier_validation/models/tier_review.py index 95e4f757c8..33fd586bc3 100644 --- a/base_tier_validation/models/tier_review.py +++ b/base_tier_validation/models/tier_review.py @@ -1,6 +1,8 @@ # Copyright 2017-19 ForgeFlow S.L. (https://www.forgeflow.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import pytz + from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -44,6 +46,9 @@ class TierReview(models.Model): done_by = fields.Many2one(comodel_name="res.users") requested_by = fields.Many2one(comodel_name="res.users") reviewed_date = fields.Datetime(string="Validation Date") + reviewed_formated_date = fields.Char( + string="Validation Formated Date", compute="_compute_reviewed_formated_date" + ) has_comment = fields.Boolean(related="definition_id.has_comment", readonly=True) comment = fields.Char(string="Comments") can_review = fields.Boolean( @@ -56,6 +61,17 @@ class TierReview(models.Model): related="definition_id.approve_sequence", readonly=True ) + @api.depends_context("tz") + def _compute_reviewed_formated_date(self): + timezone = self._context.get("tz") or self.env.user.partner_id.tz or "UTC" + for review in self: + if not review.reviewed_date: + review.reviewed_formated_date = False + continue + requested_date_utc = pytz.timezone(timezone).localize(review.reviewed_date) + requested_date = requested_date_utc.astimezone(pytz.timezone(timezone)) + review.reviewed_formated_date = requested_date.replace(tzinfo=None) + @api.depends("definition_id.approve_sequence") def _compute_can_review(self): for record in self: diff --git a/base_tier_validation/models/tier_validation.py b/base_tier_validation/models/tier_validation.py index 771ce7b437..6dd80133c7 100644 --- a/base_tier_validation/models/tier_validation.py +++ b/base_tier_validation/models/tier_validation.py @@ -7,6 +7,7 @@ from odoo import _, api, fields, models from odoo.exceptions import ValidationError +from odoo.tools.misc import frozendict class TierValidation(models.AbstractModel): @@ -333,7 +334,7 @@ def validate_tier(self): if self.has_comment: return self._add_comment("validate", reviews) self._validate_tier(reviews) - self._update_counter() + self._update_counter({"review_deleted": True}) def reject_tier(self): self.ensure_one() @@ -342,7 +343,7 @@ def reject_tier(self): if self.has_comment: return self._add_comment("reject", reviews) self._rejected_tier(reviews) - self._update_counter() + self._update_counter({"review_deleted": True}) def _notify_rejected_review_body(self): has_comment = self.review_ids.filtered( @@ -424,7 +425,7 @@ def request_validation(self): "requested_by": self.env.uid, } ) - self._update_counter() + self._update_counter({"review_created": True}) self._notify_review_requested(created_trs) return created_trs @@ -442,16 +443,22 @@ def _notify_restarted_review(self): def restart_validation(self): for rec in self: if getattr(rec, self._state_field) in self._state_from: + to_update_counter = ( + rec.mapped("review_ids").filtered(lambda a: a.status == "pending") + and True + or False + ) rec.mapped("review_ids").unlink() - self._update_counter() + if to_update_counter: + self._update_counter({"review_deleted": True}) rec._notify_restarted_review() @api.model - def _update_counter(self): + def _update_counter(self, review_counter): self.review_ids._compute_can_review() notifications = [] - channel = "base.tier.validation" - notifications.append([self.env.user.partner_id, channel, {}]) + channel = "base.tier.validation/updated" + notifications.append([self.env.user.partner_id, channel, review_counter]) self.env["bus.bus"]._sendmany(notifications) def unlink(self): @@ -459,24 +466,29 @@ def unlink(self): return super().unlink() @api.model - def fields_view_get( - self, view_id=None, view_type="form", toolbar=False, submenu=False - ): - res = super().fields_view_get( - view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu - ) + def get_view(self, view_id=None, view_type="form", **options): + res = super().get_view(view_id=view_id, view_type=view_type, **options) + + View = self.env["ir.ui.view"] + + # Override context for postprocessing + if view_id and res.get("base_model", self._name) != self._name: + View = View.with_context(base_model_name=res["base_model"]) if view_type == "form" and not self._tier_validation_manual_config: doc = etree.XML(res["arch"]) params = { "state_field": self._state_field, "state_from": ",".join("'%s'" % state for state in self._state_from), } + all_models = res["models"].copy() for node in doc.xpath(self._tier_validation_buttons_xpath): # By default, after the last button of the header str_element = self.env["ir.qweb"]._render( "base_tier_validation.tier_validation_buttons", params ) new_node = etree.fromstring(str_element) + new_arch, new_models = View.postprocess_and_fields(new_node, self._name) + new_node = etree.fromstring(new_arch) for new_element in new_node: node.addnext(new_element) for node in doc.xpath("/form/sheet"): @@ -484,21 +496,24 @@ def fields_view_get( "base_tier_validation.tier_validation_label", params ) new_node = etree.fromstring(str_element) + new_arch, new_models = View.postprocess_and_fields(new_node, self._name) + new_node = etree.fromstring(new_arch) for new_element in new_node: node.addprevious(new_element) str_element = self.env["ir.qweb"]._render( "base_tier_validation.tier_validation_reviews", params ) - node.addnext(etree.fromstring(str_element)) - View = self.env["ir.ui.view"] - - # Override context for postprocessing - if view_id and res.get("base_model", self._name) != self._name: - View = View.with_context(base_model_name=res["base_model"]) - new_arch, new_fields = View.postprocess_and_fields(doc, self._name) - res["arch"] = new_arch - # We don't want to loose previous configuration, so, we only want to add - # the new fields - new_fields.update(res["fields"]) - res["fields"] = new_fields + new_node = etree.fromstring(str_element) + new_arch, new_models = View.postprocess_and_fields(new_node, self._name) + for model in new_models: + if model in all_models: + continue + if model not in res["models"]: + all_models[model] = new_models[model] + else: + all_models[model] = res["models"][model] + new_node = etree.fromstring(new_arch) + node.append(new_node) + res["arch"] = etree.tostring(doc) + res["models"] = frozendict(all_models) return res diff --git a/base_tier_validation/static/src/js/ir_model.esm.js b/base_tier_validation/static/src/js/ir_model.esm.js new file mode 100644 index 0000000000..f4c1215ae6 --- /dev/null +++ b/base_tier_validation/static/src/js/ir_model.esm.js @@ -0,0 +1,29 @@ +/** @odoo-module **/ + +import {registerModel} from "@mail/model/model_core"; +import {attr, one} from "@mail/model/model_field"; + +registerModel({ + name: "ir.model.review", + fields: { + /** + * Determines the name of the views that are available for this model. + */ + availableWebViews: attr({ + compute() { + return ["kanban", "list", "form", "activity"]; + }, + }), + reviewGroup: one("ReviewGroup", { + inverse: "irModel", + }), + iconUrl: attr(), + id: attr({ + identifying: true, + }), + model: attr({ + required: true, + }), + name: attr(), + }, +}); diff --git a/base_tier_validation/static/src/js/main.esm.js b/base_tier_validation/static/src/js/main.esm.js new file mode 100644 index 0000000000..c945e50a40 --- /dev/null +++ b/base_tier_validation/static/src/js/main.esm.js @@ -0,0 +1,8 @@ +/** @odoo-module **/ + +import {systrayService} from "@base_tier_validation/js/systray_service.esm"; + +import {registry} from "@web/core/registry"; + +const serviceRegistry = registry.category("services"); +serviceRegistry.add("review_systray_service", systrayService); diff --git a/base_tier_validation/static/src/js/review_group_view.esm.js b/base_tier_validation/static/src/js/review_group_view.esm.js new file mode 100644 index 0000000000..1fbd704556 --- /dev/null +++ b/base_tier_validation/static/src/js/review_group_view.esm.js @@ -0,0 +1,46 @@ +/** @odoo-module **/ + +import {registerModel} from "@mail/model/model_core"; +import {one} from "@mail/model/model_field"; + +registerModel({ + name: "ReviewGroupView", + recordMethods: { + /** + * @param {MouseEvent} ev + */ + onClickFilterButton(ev) { + this.reviewMenuViewOwner.update({isOpen: false}); + // Fetch the data from the button otherwise fetch the ones from the parent (.o_ActivityMenuView_activityGroup). + const data = _.extend({}, $(ev.currentTarget).data(), $(ev.target).data()); + const context = {}; + + this.env.services.action.doAction( + { + context, + name: data.model_name, + res_model: data.res_model, + search_view_id: [false], + type: "ir.actions.act_window", + domain: [["can_review", "=", true]], + views: this.reviewGroup.irModel.availableWebViews.map( + (viewName) => [false, viewName] + ), + }, + { + clearBreadcrumbs: true, + } + ); + }, + }, + fields: { + reviewGroup: one("ReviewGroup", { + identifying: true, + inverse: "reviewGroupViews", + }), + reviewMenuViewOwner: one("ReviewerMenuView", { + identifying: true, + inverse: "reviewGroupViews", + }), + }, +}); diff --git a/base_tier_validation/static/src/js/review_groups.esm.js b/base_tier_validation/static/src/js/review_groups.esm.js new file mode 100644 index 0000000000..0d95baee3e --- /dev/null +++ b/base_tier_validation/static/src/js/review_groups.esm.js @@ -0,0 +1,52 @@ +/** @odoo-module **/ + +import {registerModel} from "@mail/model/model_core"; +import {attr, many, one} from "@mail/model/model_field"; + +registerModel({ + name: "ReviewGroup", + modelMethods: { + convertData(data) { + return { + domain: data.domain, + irModel: { + iconUrl: data.icon, + id: data.id, + model: data.model, + name: data.name, + }, + pending_count: data.pending_count, + }; + }, + }, + recordMethods: { + /** + * @private + */ + _onChangePendingCount() { + if (this.type === "tier_review" && this.pending_count === 0) { + this.delete(); + } + }, + }, + fields: { + reviewGroupViews: many("ReviewGroupView", { + inverse: "reviewGroup", + }), + domain: attr(), + irModel: one("ir.model.review", { + identifying: true, + inverse: "reviewGroup", + }), + pending_count: attr({ + default: 0, + }), + type: attr(), + }, + onChanges: [ + { + dependencies: ["pending_count", "type"], + methodName: "_onChangePendingCount", + }, + ], +}); diff --git a/base_tier_validation/static/src/js/review_notification_handler.esm.js b/base_tier_validation/static/src/js/review_notification_handler.esm.js new file mode 100644 index 0000000000..5f66a89bda --- /dev/null +++ b/base_tier_validation/static/src/js/review_notification_handler.esm.js @@ -0,0 +1,26 @@ +/** @odoo-module **/ + +import {registerPatch} from "@mail/model/model_core"; +import {decrement, increment} from "@mail/model/model_field_command"; + +registerPatch({ + name: "MessagingNotificationHandler", + recordMethods: { + /** + * @override + */ + async _handleNotification(message) { + if (message.type === "base.tier.validation/updated") { + for (const reviewMenuView of this.messaging.models.ReviewerMenuView.all()) { + if (message.payload.review_created) { + reviewMenuView.update({extraCount: increment()}); + } + if (message.payload.review_deleted) { + reviewMenuView.update({extraCount: decrement()}); + } + } + } + return this._super(message); + }, + }, +}); diff --git a/base_tier_validation/static/src/js/reviewer_menu_container.esm.js b/base_tier_validation/static/src/js/reviewer_menu_container.esm.js new file mode 100644 index 0000000000..cfe23bd9a1 --- /dev/null +++ b/base_tier_validation/static/src/js/reviewer_menu_container.esm.js @@ -0,0 +1,25 @@ +/** @odoo-module **/ + +// ensure components are registered beforehand. +import {getMessagingComponent} from "@mail/utils/messaging_component"; + +const {Component} = owl; + +export class ReviewerMenuContainer extends Component { + /** + * @override + */ + setup() { + super.setup(); + this.env.services.messaging.modelManager.messagingCreatedPromise.then(() => { + this.reviewerMenuView = + this.env.services.messaging.modelManager.messaging.models.ReviewerMenuView.insert(); + this.render(); + }); + } +} + +Object.assign(ReviewerMenuContainer, { + components: {ReviewerMenuView: getMessagingComponent("ReviewerMenuView")}, + template: "base_tier_validation.ReviewerMenuContainer", +}); diff --git a/base_tier_validation/static/src/js/reviewer_menu_view.esm.js b/base_tier_validation/static/src/js/reviewer_menu_view.esm.js new file mode 100644 index 0000000000..9ba42e76bc --- /dev/null +++ b/base_tier_validation/static/src/js/reviewer_menu_view.esm.js @@ -0,0 +1,29 @@ +/** @odoo-module **/ + +import {useComponentToModel} from "@mail/component_hooks/use_component_to_model"; +import {registerMessagingComponent} from "@mail/utils/messaging_component"; + +const {Component} = owl; + +export class ReviewerMenuView extends Component { + /** + * @override + */ + setup() { + super.setup(); + useComponentToModel({fieldName: "component"}); + } + /** + * @returns {ReviewerMenuView} + */ + get reviewerMenuView() { + return this.props.record; + } +} + +Object.assign(ReviewerMenuView, { + props: {record: Object}, + template: "base_tier_validation.ReviewerMenuView", +}); + +registerMessagingComponent(ReviewerMenuView); diff --git a/base_tier_validation/static/src/js/systray.esm.js b/base_tier_validation/static/src/js/systray.esm.js new file mode 100644 index 0000000000..3d78714d27 --- /dev/null +++ b/base_tier_validation/static/src/js/systray.esm.js @@ -0,0 +1,104 @@ +/** @odoo-module **/ + +import {registerModel} from "@mail/model/model_core"; +import {attr, many} from "@mail/model/model_field"; + +import session from "web.session"; + +registerModel({ + name: "ReviewerMenuView", + lifecycleHooks: { + _created() { + this.fetchData(); + document.addEventListener("click", this._onClickCaptureGlobal, true); + }, + _willDelete() { + document.removeEventListener("click", this._onClickCaptureGlobal, true); + }, + }, + recordMethods: { + close() { + this.update({isOpen: false}); + }, + async fetchData() { + const data = await this.messaging.rpc({ + model: "res.users", + method: "review_user_count", + args: [], + kwargs: {context: session.user_context}, + }); + + this.update({ + reviewGroups: data.map((vals) => + this.messaging.models.ReviewGroup.convertData(vals) + ), + extraCount: 0, + }); + }, + /** + * @param {MouseEvent} ev + */ + onClickDropdownToggle(ev) { + ev.preventDefault(); + if (this.isOpen) { + this.update({isOpen: false}); + } else { + this.update({isOpen: true}); + this.fetchData(); + } + }, + /** + * Closes the menu when clicking outside, if appropriate. + * + * @private + * @param {MouseEvent} ev + */ + _onClickCaptureGlobal(ev) { + if (!this.exists()) { + return; + } + if (!this.component || !this.component.root.el) { + return; + } + if (this.component.root.el.contains(ev.target)) { + return; + } + this.close(); + }, + }, + fields: { + reviewGroups: many("ReviewGroup", { + sort: [["smaller-first", "irModel.id"]], + }), + reviewGroupViews: many("ReviewGroupView", { + compute() { + return this.reviewGroups.map((reviewGroup) => { + return { + reviewGroup, + }; + }); + }, + inverse: "reviewMenuViewOwner", + }), + component: attr(), + counter: attr({ + compute() { + return this.reviewGroups.reduce( + (total, group) => total + group.pending_count, + this.extraCount + ); + }, + }), + /** + * Determines the number of activities that have been added in the + * system but not yet taken into account in each activity group counter. + * + * @deprecated this field should be replaced by directly updating the + * counter of each group. + */ + extraCount: attr(), + isOpen: attr({ + default: false, + }), + }, +}); diff --git a/base_tier_validation/static/src/js/systray.js b/base_tier_validation/static/src/js/systray.js deleted file mode 100644 index 827982e006..0000000000 --- a/base_tier_validation/static/src/js/systray.js +++ /dev/null @@ -1,165 +0,0 @@ -odoo.define("tier_validation.systray", function (require) { - "use strict"; - - var core = require("web.core"); - var session = require("web.session"); - var SystrayMenu = require("web.SystrayMenu"); - var Widget = require("web.Widget"); - - var QWeb = core.qweb; - - var ReviewMenu = Widget.extend({ - template: "tier.validation.ReviewMenu", - events: { - "show.bs.dropdown": "_onReviewMenuShow", - "click .o_mail_activity_action": "_onReviewActionClick", - "click .o_mail_preview": "_onReviewFilterClick", - }, - start: function () { - this.$reviews_preview = this.$(".o_mail_systray_dropdown_items"); - this._updateReviewPreview(); - var channel = "base.tier.validation"; - this.call("bus_service", "addChannel", channel); - this.call("bus_service", "startPolling"); - this.call("bus_service", "onNotification", this, this._updateReviewPreview); - return this._super(); - }, - - // Private - - /** - * Make RPC and get current user's activity details - * @private - * @returns {integer} - */ - _getReviewData: function () { - var self = this; - - return self - ._rpc({ - model: "res.users", - method: "review_user_count", - kwargs: { - context: session.user_context, - }, - }) - .then(function (data) { - self.reviews = data; - self.reviewCounter = _.reduce( - data, - function (total_count, p_data) { - return total_count + p_data.pending_count; - }, - 0 - ); - self.$(".o_notification_counter").text(self.reviewCounter); - self.$el.toggleClass("o_no_notification", !self.reviewCounter); - }); - }, - - /** - * Get particular model view to redirect on click of review on that model. - * @private - * @param {String} model - * @returns {integer} - */ - _getReviewModelViewID: function (model) { - return this._rpc({ - model: model, - method: "get_activity_view_id", - }); - }, - - /** - * Update(render) activity system tray view on activity updation. - * @private - */ - _updateReviewPreview: function () { - var self = this; - self._getReviewData().then(function () { - self.$reviews_preview.html( - QWeb.render("tier.validation.ReviewMenuPreview", { - reviews: self.reviews, - }) - ); - }); - }, - - /** - * Update counter based on activity status(created or Done) - * @private - * @param {Object} [data] key, value to decide activity created or deleted - * @param {String} [data.type] notification type - * @param {Boolean} [data.activity_deleted] when activity deleted - * @param {Boolean} [data.activity_created] when activity created - */ - _updateCounter: function (data) { - if (data) { - if (data.review_created) { - this.reviewCounter++; - } - if (data.review_deleted && this.reviewCounter > 0) { - this.reviewCounter--; - } - this.$(".o_notification_counter").text(this.reviewCounter); - this.$el.toggleClass("o_no_notification", !this.reviewCounter); - } - }, - - // ------------------------------------------------------------ - // Handlers - // ------------------------------------------------------------ - - /** - * Redirect to specific action given its xml id - * @private - * @param {MouseEvent} ev - */ - _onReviewActionClick: function (ev) { - ev.stopPropagation(); - var actionXmlid = $(ev.currentTarget).data("action_xmlid"); - this.do_action(actionXmlid); - }, - - /** - * Redirect to particular model view - * @private - * @param {MouseEvent} event - */ - _onReviewFilterClick: function (event) { - // Fetch the data from the button otherwise fetch the ones from the - // parent (.o_tier_channel_preview). - var data = _.extend( - {}, - $(event.currentTarget).data(), - $(event.target).data() - ); - var context = {}; - this.do_action({ - type: "ir.actions.act_window", - name: data.model_name, - res_model: data.res_model, - views: [ - [false, "list"], - [false, "form"], - ], - search_view_id: [false], - domain: [["can_review", "=", true]], - context: context, - }); - }, - - /** - * When menu clicked update activity preview if counter updated - * @private - * @param {MouseEvent} event - */ - _onReviewMenuShow: function () { - this._updateReviewPreview(); - }, - }); - - SystrayMenu.Items.push(ReviewMenu); - - return ReviewMenu; -}); diff --git a/base_tier_validation/static/src/js/systray_service.esm.js b/base_tier_validation/static/src/js/systray_service.esm.js new file mode 100644 index 0000000000..78a1101c06 --- /dev/null +++ b/base_tier_validation/static/src/js/systray_service.esm.js @@ -0,0 +1,17 @@ +/** @odoo-module **/ + +import {ReviewerMenuContainer} from "./reviewer_menu_container.esm"; + +import {registry} from "@web/core/registry"; + +const systrayRegistry = registry.category("systray"); + +export const systrayService = { + start() { + systrayRegistry.add( + "base_tier_validation.ReviewerMenu", + {Component: ReviewerMenuContainer}, + {sequence: 99} + ); + }, +}; diff --git a/base_tier_validation/static/src/js/tier_review_widget.esm.js b/base_tier_validation/static/src/js/tier_review_widget.esm.js new file mode 100644 index 0000000000..a5ab0aa5d9 --- /dev/null +++ b/base_tier_validation/static/src/js/tier_review_widget.esm.js @@ -0,0 +1,36 @@ +/** @odoo-module **/ + +import {registry} from "@web/core/registry"; + +import {useService} from "@web/core/utils/hooks"; + +const {Component, useState} = owl; + +export class ReviewsTable extends Component { + setup() { + this.docs = useState({}); + this.collapse = false; + this.orm = useService("orm"); + this.reviews = []; + } + _getReviewData() { + const records = this.env.model.root.data.review_ids.records; + const reviews = []; + for (var i = 0; i < records.length; i++) { + reviews.push(records[i].data); + } + return reviews; + } + onToggleCollapse(ev) { + var $panelHeading = $(ev.currentTarget).closest(".panel-heading"); + if (this.collapse) { + $panelHeading.next("div#collapse1").hide(); + } else { + $panelHeading.next("div#collapse1").show(); + } + this.collapse = !this.collapse; + } +} + +ReviewsTable.template = "base_tier_validation.Collapse"; +registry.category("fields").add("form.tier_validation", ReviewsTable); diff --git a/base_tier_validation/static/src/js/tier_review_widget.js b/base_tier_validation/static/src/js/tier_review_widget.js deleted file mode 100644 index 672dfcf151..0000000000 --- a/base_tier_validation/static/src/js/tier_review_widget.js +++ /dev/null @@ -1,66 +0,0 @@ -odoo.define("base_tier_validation.ReviewField", function (require) { - "use strict"; - - var AbstractField = require("web.AbstractField"); - var core = require("web.core"); - var field_registry = require("web.field_registry"); - - var QWeb = core.qweb; - - var ReviewField = AbstractField.extend({ - template: "tier.review.Collapse", - events: { - "click .o_info_btn": "_onButtonClicked", - "show.bs.collapse": "_showCollapse", - "hide.bs.collapse": "_hideCollapse", - }, - start: function () { - var self = this; - self._renderDropdown(); - }, - - /** - * Make RPC and get current user's activity details - * @private - * @param {Object} res_ids - * @returns {integer} - */ - _getReviewData: function (res_ids) { - var self = this; - - return this._rpc({ - model: "res.users", - method: "get_reviews", - args: [res_ids], - }).then(function (data) { - self.reviews = data; - }); - }, - _renderDropdown: function () { - var self = this; - return this._getReviewData(self.value).then(function () { - self.$(".o_review").html( - QWeb.render("tier.review.ReviewsTable", { - reviews: self.reviews, - }) - ); - }); - }, - _onButtonClicked: function (event) { - event.preventDefault(); - if (!this.$el.hasClass("open")) { - this._renderDropdown(); - } - }, - _showCollapse: function () { - this.$el.find(".panel-heading").addClass("active"); - }, - _hideCollapse: function () { - this.$el.find(".panel-heading").removeClass("active"); - }, - }); - - field_registry.add("tier_validation", ReviewField); - - return ReviewField; -}); diff --git a/base_tier_validation/static/src/xml/reviewer_menu_container.xml b/base_tier_validation/static/src/xml/reviewer_menu_container.xml new file mode 100644 index 0000000000..358ff6bc9a --- /dev/null +++ b/base_tier_validation/static/src/xml/reviewer_menu_container.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/base_tier_validation/static/src/xml/systray.xml b/base_tier_validation/static/src/xml/systray.xml index 5faa47c2be..4a72ae47db 100644 --- a/base_tier_validation/static/src/xml/systray.xml +++ b/base_tier_validation/static/src/xml/systray.xml @@ -1,82 +1,95 @@ - - - - - -
-
- Review -
-
-
- - - -
- - - -
-
-
- - - 0 Pending - -
-
-
-
-
- -
+ +