From 93b90a3ff3b0c6f32ce1dd7e1c57fced8fcfb5a7 Mon Sep 17 00:00:00 2001 From: "Xavier Bol (xbo)" Date: Thu, 4 May 2023 06:39:19 +0000 Subject: [PATCH] [IMP] project: update closed_subtask_count when state of subtask changed Before this commit, when the user displays the subtasks list on a kanban card and change the state of a subtask to set to Done then the `closed_subtask_count` field does not changed in the kanban card. This commit updates the `closed_subtask_count` when the subtask will be closed or opened on the subtask list displayed in the kanban card. Also, the `this.props.record.activeFields` is no longer used to fill in the activeFields of Record component because the `activeFields` will change in the future. task-3248592 Part-of: odoo/odoo#120508 --- .../subtask_kanban_list.js | 89 ++++++++++--------- .../subtask_kanban_list.xml | 47 +++++----- .../project_subtask_kanban_list_tests.js | 50 +++++++++-- 3 files changed, 115 insertions(+), 71 deletions(-) diff --git a/addons/project/static/src/components/subtask_kanban_list/subtask_kanban_list.js b/addons/project/static/src/components/subtask_kanban_list/subtask_kanban_list.js index 5910d0222ec..b2586b1b76e 100644 --- a/addons/project/static/src/components/subtask_kanban_list/subtask_kanban_list.js +++ b/addons/project/static/src/components/subtask_kanban_list/subtask_kanban_list.js @@ -1,28 +1,33 @@ /** @odoo-module */ -import { useService } from "@web/core/utils/hooks"; - -import { registry } from "@web/core/registry"; import { Component, onWillStart } from "@odoo/owl"; + +import { useService } from "@web/core/utils/hooks"; +import { registry } from "@web/core/registry"; + import { Record } from "@web/views/record"; import { KanbanMany2ManyTagsAvatarUserField } from "@mail/web/fields/many2many_avatar_user_field/many2many_avatar_user_field"; -import { Field } from "@web/views/fields/field"; +import { Field, getFieldFromRegistry } from "@web/views/fields/field"; import { standardWidgetProps } from "@web/views/widgets/standard_widget_props"; export class SubtaskKanbanList extends Component { setup() { this.actionService = useService("action"); this.orm = useService("orm"); - this.subTaskIds = []; + this.subTasksRead = []; + this.subTaskClosed = new Set(); onWillStart(this.onWillStart); } async onWillStart() { - this.subTaskIds = await this.orm.search(this.props.record.resModel, [ - ["parent_id", "=", this.props.record.resId], - ["state", "not in", ["1_done", "1_canceled"]], - ]); + this.subTasksRead = await this.orm.searchRead( + this.props.record.resModel, [ + ["parent_id", "=", this.props.record.resId], + ["state", "not in", ["1_done", "1_canceled"]], + ], + this.fieldNames + ); } async goToSubtask(subtask_id) { @@ -38,44 +43,44 @@ export class SubtaskKanbanList extends Component { }); } - get fieldsInfo() { + get fieldNames() { + return Object.keys(this.fields); + } + + get fields() { + const { display_name, state, user_ids } = this.props.record.fields; return { - child_ids: { - type: "one2many", - relation: "project.task", - relatedFields: { - display_name: { type: "char" }, - state: { - selection: [ - ["01_in_progress", "In Progress"], - ["02_changes_requested", "Changes Requested"], - ["03_approved", "Approved"], - ["04_waiting_normal", "Waiting"], - ["1_done", "Done"], - ["1_canceled", "Canceled"], - ], - string: "Status", - type: "selection", - field: this.props.record.activeFields.state.field, - viewType: this.props.record.activeFields.state.viewType, - attrs: this.props.record.activeFields.state.attrs, - options: this.props.record.activeFields.state.options, - }, - user_ids: { - type: "many2many", - relation: "res.users", - field: this.props.record.activeFields.user_ids.field, - relatedFields: this.props.record.activeFields.user_ids.relatedFields, - attrs: this.props.record.activeFields.user_ids.attrs, - options: this.props.record.activeFields.user_ids.options, - }, - }, + display_name, + state, + user_ids, + }; + } + + get activeFields() { + return { + display_name: {}, + state: { + viewType: "kanban", + field: getFieldFromRegistry(this.fields.state.type, "project_task_state_selection", "kanban"), + }, + user_ids: { + field: getFieldFromRegistry(this.fields.user_ids.type, "many2many_avatar_user", "kanban"), }, }; } - get values() { - return { child_ids: this.subTaskIds }; + async onSubTaskSaved(subTask) { + const isKnownAsClosed = this.subTaskClosed.has(subTask.resId); + const isClosed = subTask.data.state.startsWith("1_"); + if (isKnownAsClosed && !isClosed) { + this.subTaskClosed.delete(subTask.resId); + } else if (!isKnownAsClosed && isClosed) { + this.subTaskClosed.add(subTask.resId); + } else { // nothing to do + return; + } + await this.props.record.load(); + this.props.record.model.notify(); } } diff --git a/addons/project/static/src/components/subtask_kanban_list/subtask_kanban_list.xml b/addons/project/static/src/components/subtask_kanban_list/subtask_kanban_list.xml index 5c2438513b3..61510df488a 100644 --- a/addons/project/static/src/components/subtask_kanban_list/subtask_kanban_list.xml +++ b/addons/project/static/src/components/subtask_kanban_list/subtask_kanban_list.xml @@ -3,31 +3,38 @@ - - diff --git a/addons/project/static/tests/project_subtask_kanban_list_tests.js b/addons/project/static/tests/project_subtask_kanban_list_tests.js index 3713538d94a..edbcace3cff 100644 --- a/addons/project/static/tests/project_subtask_kanban_list_tests.js +++ b/addons/project/static/tests/project_subtask_kanban_list_tests.js @@ -24,12 +24,13 @@ QUnit.module('Subtask Kanban List tests', { { name: "User One", login: 'one', password: 'one' }, ]); pyEnv['project.task'].create([ - { name: 'task one', project_id: projectId, closed_subtask_count: 2, subtask_count: 3, child_ids: [2, 3, 4], state: '01_in_progress', user_ids: [userId] }, + { name: 'task one', project_id: projectId, closed_subtask_count: 1, subtask_count: 4, child_ids: [2, 3, 4, 7], state: '01_in_progress', user_ids: [userId] }, { name: 'task two', parent_id: 1, closed_subtask_count: 0, subtask_count: 0, child_ids: [], state: '03_approved' }, { name: 'task three', parent_id: 1, closed_subtask_count: 0, subtask_count: 0, child_ids: [], state: '02_changes_requested' }, { name: 'task four', parent_id: 1, closed_subtask_count: 0, subtask_count: 0, child_ids: [], state: '1_done' }, { name: 'task five', closed_subtask_count: 0, subtask_count: 1, child_ids: [6], state: '03_approved' }, { name: 'task six', parent_id: 5, closed_subtask_count: 0, subtask_count: 0, child_ids: [], state: '1_canceled' }, + { name: 'task seven', parent_id: 1, closed_subtask_count: 0, subtask_count: 0, child_ids: [], state: '01_in_progress', user_ids: [userId] }, ]); this.views = { "project.task,false,kanban": @@ -39,15 +40,15 @@ QUnit.module('Subtask Kanban List tests', {
- - - + + + / +
-
`, @@ -72,13 +73,44 @@ QUnit.module('Subtask Kanban List tests', { await click(target, '.subtask_list_button'); assert.containsOnce(target, '.subtask_list', "Clicking on the button should make the subtask list render, in this case we are expectig 1 list"); - assert.containsN(target, '.subtask_list_row', 2, "The list rendered should show the open subtasks of the task, in this case 2"); - assert.containsN(target, '.subtask_state_widget_col', 2, "Each of the list's rows should have 1 state widget, thus we are looking for 2 in total"); - assert.containsN(target, '.subtask_user_widget_col', 2, "Each of the list's rows should have 1 user widgets, thus we are looking for 2 in total"); - assert.containsN(target, '.subtask_name_col', 2, "Each of the list's rows should display the subtask's name, thus we are looking for 2 in total"); + assert.containsN(target, '.subtask_list_row', 3, "The list rendered should show the open subtasks of the task, in this case 3"); + assert.containsN(target, '.subtask_state_widget_col', 3, "Each of the list's rows should have 1 state widget, thus we are looking for 3 in total"); + assert.containsN(target, '.subtask_user_widget_col', 3, "Each of the list's rows should have 1 user widgets, thus we are looking for 3 in total"); + assert.containsN(target, '.subtask_name_col', 3, "Each of the list's rows should display the subtask's name, thus we are looking for 3 in total"); await click(target, '.subtask_list_button'); assert.containsNone(target, '.subtask_list', "If the drawdown button is clicked again, the subtasks list should be hidden again"); }); + + QUnit.test("Update closed subtask count in the kanban card when the state of a subtask is set to Done.", async function (assert) { + let checkSteps = false; + const { openView } = await start({ + serverData: { views: this.views }, + mockRPC(route, { model, method }) { + if (checkSteps) { + assert.step(`${model}/${method}`); + } + }, + }); + await openView({ + res_model: "project.task", + views: [[false, "kanban"]], + }); + + assert.strictEqual(target.querySelector(".subtask_list_button").parentElement.textContent, "1/4"); + checkSteps = true; + await click(target, '.subtask_list_button'); + const subtaskEl = target.querySelector(".subtask_list"); + assert.containsOnce(subtaskEl, ".o_field_widget.o_field_project_task_state_selection.subtask_state_widget_col .o_status:not(.o_status_green,.o_status_bubble)", "The state of the subtask should be in progress"); + await click(subtaskEl, ".o_field_widget.o_field_project_task_state_selection.subtask_state_widget_col .o_status:not(.o_status_green,.o_status_bubble)"); + assert.containsNone(subtaskEl, ".o_field_widget.o_field_project_task_state_selection.subtask_state_widget_col .o_status:not(.o_status_green,.o_status_bubble)", "The state of the subtask should no longer be in progress"); + assert.containsOnce(subtaskEl, ".o_field_widget.o_field_project_task_state_selection.subtask_state_widget_col .fa-check-circle.text-success", "The state of the subtask should be Done"); + assert.verifySteps([ + "project.task/search_read", + "project.task/write", + "project.task/read", + "project.task/read", // read the parent task to recompute the subtask count + ]); + }); });