From e88ae39cc25a21340ceda652fd73aa76eeb884f4 Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Thu, 16 Nov 2023 13:52:07 +0100 Subject: [PATCH 01/14] fix(pm): Filter timelogs by employee --- erpnext/public/js/projects/eventSources.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/projects/eventSources.js b/erpnext/public/js/projects/eventSources.js index d5cbf13e8fc..9539e9a6456 100644 --- a/erpnext/public/js/projects/eventSources.js +++ b/erpnext/public/js/projects/eventSources.js @@ -123,7 +123,7 @@ export class PMSource_TimeLogs extends withHoursDiplay(PMViewEventSource) { async getArgs(params) { const args = await super.getArgs(params); - // args.filters.push(["Timesheet", "employee", "=", this.getCurrentEmployee()]); + args.filters.push(["Timesheet", "employee", "=", await this.getCurrentEmployee()]); return args; } -- GitLab From 22a57e8c04c0441c159c88500e34b85dbe3ebf21 Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 19:15:16 +0100 Subject: [PATCH 02/14] refactor(pm): Find correct Timesheet for time log --- .../projects/doctype/timesheet/timesheet.py | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index b37775fc69a..468b384f73c 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -514,29 +514,39 @@ def get_list_context(context=None): } +def find_ts_for_time_log(values: dict) -> Timesheet | None: + if values.get("parent"): + return frappe.get_doc("Timesheet", values.parent) # type: ignore + + ts_filters = { + "employee": ["=", values.get("employee")], + "docstatus": ["=", 0], + "parent_project": ["=", values.get("project")], + } + + if m := frappe.get_list("Timesheet", filters=ts_filters, fields=["name"], limit=1): + return frappe.get_doc("Timesheet", m[0].name) # type: ignore + + ts_filters["parent_project"] = ["is", "not set"] + if m := frappe.get_list("Timesheet", filters=ts_filters, fields=["name"], limit=1): + return frappe.get_doc("Timesheet", m[0].name) # type: ignore + + @frappe.whitelist() def add_time_log(values: str | dict): - values = frappe.parse_json(values) + values: dict = frappe.parse_json(values) # type: ignore if ( - values.doctype == "Timesheet Details" + values.doctype == "Timesheet Detail" and values.name and frappe.db.exists("Timesheet Detail", values.name) ): return update_time_log(values) # Find a suitable timesheet - ts_filters = { - "employee": ["=", values.employee], - "parent_project": ["is", "not set"], - "docstatus": ["=", 0], - } + ts = find_ts_for_time_log(values) - ts: Timesheet - matches = frappe.get_list("Timesheet", filters=ts_filters, fields=["name"], limit=1) - if matches: - ts = frappe.get_doc("Timesheet", matches[0].name) # type: ignore - else: + if not ts: ts = frappe.new_doc("Timesheet") # type: ignore ts.update( { -- GitLab From 25585632ffb24bced2f7d5e264af768d3419ff2b Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 19:19:53 +0100 Subject: [PATCH 03/14] fix(pm): Create a new time log when updating deleted one --- erpnext/projects/doctype/timesheet/timesheet.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 468b384f73c..24ab1f6e595 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -567,10 +567,17 @@ def add_time_log(values: str | dict): @frappe.whitelist() def update_time_log(values: str | dict, delete=False): values = frappe.parse_json(values) + try: + tl = frappe.get_doc("Timesheet Detail", values.name) + if tl.parenttype != "Timesheet": + return + except frappe.DoesNotExistError: + if delete: + return # The timelog is already deleted, silently ignore + else: + values.name = None + return add_time_log(values) - tl = frappe.get_doc("Timesheet Detail", values.name) - if tl.parenttype != "Timesheet": - return ts: Timesheet = frappe.get_doc("Timesheet", tl.parent) # type: ignore # Update timelog -- GitLab From c0ec405b39593a272826733e784afe5ac1d5d6f0 Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 19:20:15 +0100 Subject: [PATCH 04/14] fix(pm): Delete Timesheet when removing last time log --- erpnext/projects/doctype/timesheet/timesheet.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 24ab1f6e595..a061d6289fd 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -585,6 +585,9 @@ def update_time_log(values: str | dict, delete=False): row = rows[0] if delete: ts.remove(row) + if not ts.time_logs: + ts.delete() + return None else: row.update(values) ts.save() -- GitLab From 5b8c74052ac62a04b53b6c65344d3a6f56740377 Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 19:21:41 +0100 Subject: [PATCH 05/14] fix(pm): Check compatibilty with timesheet when updating log --- erpnext/projects/doctype/timesheet/timesheet.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index a061d6289fd..71a2063541c 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -580,6 +580,20 @@ def update_time_log(values: str | dict, delete=False): ts: Timesheet = frappe.get_doc("Timesheet", tl.parent) # type: ignore + if not delete and not ts.is_new(): + # Check that the existing time log is compatible with the new values. + # More precisely, check that the time log is still compatible with its parent. + must_be_moved = False + if values.project and ts.parent_project and (values.project != ts.parent_project): + must_be_moved = True + if values.employee and ts.employee and (values.employee != ts.employee): + must_be_moved = True + + if must_be_moved: + update_time_log(values, delete=True) + values.name = None + return add_time_log(values) + # Update timelog if rows := ts.get("time_logs", {"name": values.name}): row = rows[0] -- GitLab From 0ec1d5f047a2275a3641ad0bd69265e5c712cb1f Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 19:23:34 +0100 Subject: [PATCH 06/14] fix(pm): Filter by user's employee --- .../projects/doctype/timesheet/timesheet.py | 1 + .../public/js/projects/PMViewEventSource.js | 2 +- erpnext/public/js/projects/eventSources.js | 39 ++++++++++++------- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 71a2063541c..cf8d8f9522e 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -633,6 +633,7 @@ def get_time_logs(start, end, filters=None): "time_logs.activity_type", "time_logs.project", "time_logs.task", + "employee", ] events = frappe.get_list("Timesheet", fields=fields, filters=filters) diff --git a/erpnext/public/js/projects/PMViewEventSource.js b/erpnext/public/js/projects/PMViewEventSource.js index d3da8da76b8..3f23d733217 100644 --- a/erpnext/public/js/projects/PMViewEventSource.js +++ b/erpnext/public/js/projects/PMViewEventSource.js @@ -125,7 +125,7 @@ export class PMViewEventSource { doctype: this.doctype, start: format_ymd(start), end: format_ymd(frappe.datetime.add_days(end, -1)), - filters: this.getFilters(), + filters: await this.getFilters(), field_map: this.field_map, fields: Object.values(this.field_map), }; diff --git a/erpnext/public/js/projects/eventSources.js b/erpnext/public/js/projects/eventSources.js index 9539e9a6456..b08c78a8c1d 100644 --- a/erpnext/public/js/projects/eventSources.js +++ b/erpnext/public/js/projects/eventSources.js @@ -1,6 +1,14 @@ import { PMTimeLogDialog } from "./PMTimeLogDialog"; import { PMViewEventSource } from "./PMViewEventSource"; +let _currentEmployee = ""; +async function getCurrentEmployee() { + if (!_currentEmployee) { + const res = await frappe.db.get_value("Employee", { user_id: frappe.session.user }, "name"); + _currentEmployee = res?.message?.name; + } + return _currentEmployee; +} const withHoursDiplay = (/** @type {PMViewEventSource} */ C) => { /** @type {PMViewEventSource} */ @@ -49,6 +57,20 @@ export class PMSource_TaskAssigmentsLog extends PMSource_TaskAssigments { container.append(this.makeLegendItem(__("Assigned"), { color: "orange", doctype: "Task Assignment" })); } + /** @protected */ async getFilters() { + const filters = await super.getFilters(); + filters.push(["Task Assignment Row", "employee", "=", await getCurrentEmployee()]); + return filters; + } + + async getArgs(params) { + const args = await super.getArgs(params); + const fm = { ...this.field_map, employee: "assigned_to.employee as employee" }; + args.field_map = fm; + args.fields = Object.values(fm); + return args; + } + async prepareEvent(event) { event = await super.prepareEvent(event); event.editable = false; @@ -95,15 +117,6 @@ export class PMSource_TaskAssigmentsLog extends PMSource_TaskAssigments { } export class PMSource_TimeLogs extends withHoursDiplay(PMViewEventSource) { - _currentEmployee = ""; - async getCurrentEmployee() { - if (this._currentEmployee) { - return this._currentEmployee; - } - const res = await frappe.db.get_value("Employee", { user_id: frappe.session.user }, "name"); - this._currentEmployee = res?.message?.name; - } - setupLegend(/** @type {HTMLElement} */ container) { container.append(this.makeLegendItem(__("Saved"), { color: "blue", doctype: "Timesheet Detail" })); } @@ -121,10 +134,10 @@ export class PMSource_TimeLogs extends withHoursDiplay(PMViewEventSource) { activity_type: "activity_type", }; - async getArgs(params) { - const args = await super.getArgs(params); - args.filters.push(["Timesheet", "employee", "=", await this.getCurrentEmployee()]); - return args; + /** @protected */ async getFilters() { + const filters = await super.getFilters(); + filters.push(["Timesheet", "employee", "=", await getCurrentEmployee()]); + return filters; } grabEventStyle() { -- GitLab From 3e3c5e5bfaec78518a728b44cdf3ecd3f0121041 Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 19:24:04 +0100 Subject: [PATCH 07/14] feat(pm): Add description field to time log dialog --- erpnext/public/js/projects/PMTimeLogDialog.js | 8 +++++--- erpnext/public/js/projects/eventSources.js | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/public/js/projects/PMTimeLogDialog.js b/erpnext/public/js/projects/PMTimeLogDialog.js index 56c193b8722..80ebba7a7a1 100644 --- a/erpnext/public/js/projects/PMTimeLogDialog.js +++ b/erpnext/public/js/projects/PMTimeLogDialog.js @@ -105,6 +105,7 @@ export class PMTimeLogDialog { const res = await frappe.db.get_value("Employee", { user_id: frappe.session.user }, "name"); const currentEmployee = res?.message?.name; + const NO_DEPENDS_ON = { depends_on: "", read_only_depends_on: "", mandatory_depends_on: "" }; return [ { @@ -131,8 +132,8 @@ export class PMTimeLogDialog { reqd: 1, }, { fieldtype: "Section Break", label: "Details", hide_border: 1, collapsible: 1 }, - field("project", { depends_on: "", read_only_depends_on: "", mandatory_depends_on: "" }), - field("activity_type", { depends_on: "", read_only_depends_on: "", mandatory_depends_on: "" }), + field("project", NO_DEPENDS_ON), + field("activity_type", NO_DEPENDS_ON), // { // fieldtype: "Link", // fieldname: "user", @@ -160,7 +161,8 @@ export class PMTimeLogDialog { default: currentEmployee, // depends_on: "eval:!doc.employee", }, - { fieldtype: "Section Break" }, + { fieldtype: "Section Break", label: __("Description"), hide_border: 1, collapsible: 1 }, + field("description", NO_DEPENDS_ON), ]; } } \ No newline at end of file diff --git a/erpnext/public/js/projects/eventSources.js b/erpnext/public/js/projects/eventSources.js index b08c78a8c1d..dccd773a5a8 100644 --- a/erpnext/public/js/projects/eventSources.js +++ b/erpnext/public/js/projects/eventSources.js @@ -37,7 +37,7 @@ const extractTime = (x) => { export class PMSource_TaskAssigments extends withHoursDiplay(PMViewEventSource) { doctype = "Task Assignment"; - mainKeys = ["task", "project", "employee", "_hours"]; + mainKeys = ["project", "task", "employee", "_hours"]; field_map = { name: "name", start: "start_date", @@ -123,7 +123,7 @@ export class PMSource_TimeLogs extends withHoursDiplay(PMViewEventSource) { doctype = "Timesheet Detail"; method = "erpnext.projects.doctype.timesheet.timesheet.get_time_logs"; - mainKeys = ["task", "project", "activity_type", "_hours"]; + mainKeys = ["project", "task", "activity_type", "_hours"]; field_map = { name: "name", start: "from_time", @@ -132,6 +132,7 @@ export class PMSource_TimeLogs extends withHoursDiplay(PMViewEventSource) { task: "task", project: "project", activity_type: "activity_type", + description: "description", }; /** @protected */ async getFilters() { -- GitLab From 4c3a6a76f970700c3601ad2ec1bf830ac8313d22 Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 19:25:18 +0100 Subject: [PATCH 08/14] fix(pm): Fix incorrect datetime when dragging time log --- erpnext/public/js/projects/eventSources.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/projects/eventSources.js b/erpnext/public/js/projects/eventSources.js index dccd773a5a8..5f0fc4256e2 100644 --- a/erpnext/public/js/projects/eventSources.js +++ b/erpnext/public/js/projects/eventSources.js @@ -151,7 +151,7 @@ export class PMSource_TimeLogs extends withHoursDiplay(PMViewEventSource) { exportEventTime(targetDocument, info) { const time = extractTime(info.event.extendedProps.sourceData.from_time); - targetDocument.from_time = info.startStr + " " + time; + targetDocument.from_time = info.event.startStr + " " + time; targetDocument.to_time = null; targetDocument.hours = info.event.extendedProps.sourceData.hours; return targetDocument; -- GitLab From 64822015867cb7d746b350c95e9fb56469a59b95 Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 19:29:24 +0100 Subject: [PATCH 09/14] fix(pm): Fix end date should be exclusive --- erpnext/public/js/projects/PMViewEventSource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/projects/PMViewEventSource.js b/erpnext/public/js/projects/PMViewEventSource.js index 3f23d733217..07e2b6dccb0 100644 --- a/erpnext/public/js/projects/PMViewEventSource.js +++ b/erpnext/public/js/projects/PMViewEventSource.js @@ -124,7 +124,7 @@ export class PMViewEventSource { return { doctype: this.doctype, start: format_ymd(start), - end: format_ymd(frappe.datetime.add_days(end, -1)), + end: format_ymd(end), filters: await this.getFilters(), field_map: this.field_map, fields: Object.values(this.field_map), -- GitLab From df47140a30a6d44b2a653c706cf2590272b5fcb6 Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 19:30:24 +0100 Subject: [PATCH 10/14] chore(pm): Clean-up --- .../js/projects/PMViewCalendar_Assignments.js | 4 ++-- erpnext/public/js/projects/PMViewEventSource.js | 15 +++++++-------- .../public/js/projects/ProjectManagementView.js | 5 ++--- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/erpnext/public/js/projects/PMViewCalendar_Assignments.js b/erpnext/public/js/projects/PMViewCalendar_Assignments.js index efb3549ea53..60f8ec836d6 100644 --- a/erpnext/public/js/projects/PMViewCalendar_Assignments.js +++ b/erpnext/public/js/projects/PMViewCalendar_Assignments.js @@ -91,12 +91,12 @@ export class PMViewCalendar_Assignments extends PMViewBaseCalendar { }; } - make() { + async make() { // add links to other calendars this.page.clear_user_actions(); $(this.parent).on("show", this.refresh.bind(this)); - super.make(); + await super.make(); this.footnote_area = frappe.utils.set_footnote( this.footnote_area, diff --git a/erpnext/public/js/projects/PMViewEventSource.js b/erpnext/public/js/projects/PMViewEventSource.js index 07e2b6dccb0..5ff7a4b0cae 100644 --- a/erpnext/public/js/projects/PMViewEventSource.js +++ b/erpnext/public/js/projects/PMViewEventSource.js @@ -116,7 +116,8 @@ export class PMViewEventSource { } async fetch({ start, end }) { - const events = await frappe.xcall(this.method, await this.getArgs({ start, end })); + const args = await this.getArgs({ start, end }); + const events = await frappe.xcall(this.method, args); return await this.prepareEvents(events); } @@ -477,14 +478,12 @@ export class PMViewEventSource { } const getValue = (k) => String(info.extendedProps?.[k] ?? ""); - const key = this.mainKeys.slice(0, 2).map(getValue).join(""); - const hash1 = stringHash32(key) % 360; - // const startStr = String(String(info.startStr ?? info.start)); - // const hash2 = 5 * (Number(startStr.replace(/[^0-9]/g, "")) % 7) || 0; - - let hue = hash1; - hue = Math.round((hue + 360) % 360); + const key1 = getValue(this.mainKeys[0]); + const hash1 = stringHash32(key1) % 360; + // const key2 = getValue(this.mainKeys[1]); + // const hash2 = stringHash32(key2) % 60; + const hue = Math.round((hash1) % 360); const saturation = 50; const adjust = 20 + .5 * hue - (6.2e-2 * hue)**2 + (1.895e-2 * hue)**3; const lightness = 60 - adjust / 2; diff --git a/erpnext/public/js/projects/ProjectManagementView.js b/erpnext/public/js/projects/ProjectManagementView.js index ddeb58fc190..3b71c5ed9cc 100644 --- a/erpnext/public/js/projects/ProjectManagementView.js +++ b/erpnext/public/js/projects/ProjectManagementView.js @@ -72,6 +72,7 @@ export class ProjectManagementView extends frappe.views.ListView { async rebuildCalendar() { this.calendar?.destroy?.(); + this.legendContainer.innerHTML = ""; // Always hide filters this.page.hide_form(); @@ -94,10 +95,8 @@ export class ProjectManagementView extends frappe.views.ListView { view: this, parent: this.$result, }); - this.calendar.make(); - - this.legendContainer.innerHTML = ""; this.calendar.setupLegend?.(this.legendContainer); + await this.calendar.make(); } pm_setup_sidebar() { -- GitLab From 4bbc5b49ba4ae8b213935b679377860bde455f75 Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 19:34:34 +0100 Subject: [PATCH 11/14] feat(pm): Save and restore view params to/from URL --- .../js/projects/ProjectManagementView.js | 48 +++++++++++++++++-- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/erpnext/public/js/projects/ProjectManagementView.js b/erpnext/public/js/projects/ProjectManagementView.js index 3b71c5ed9cc..1e40c9e8b80 100644 --- a/erpnext/public/js/projects/ProjectManagementView.js +++ b/erpnext/public/js/projects/ProjectManagementView.js @@ -7,15 +7,43 @@ import { PMViewCalendar_Timelog } from "./PMViewCalendar_Timelog"; /** @typedef {"schedule" | "assign" | "log"} pm_mode_t */ export class ProjectManagementView extends frappe.views.ListView { - /** @type {pm_mode_t} */ _mode = "assign" + /** @type {pm_mode_t} */ _mode = "log" get mode() { - return this._mode + return this._mode; } set mode(/** @type {pm_mode_t} */ v) { - this._mode = v; + this._mode = v || "log"; this.setActiveCalendar(); } + /** @type {string} */ _date = "" + get date() { + const dateObj = this.calendar?.fullcalendar?.getDate() // in UTC, which might be the previous day + const dateStr = dateObj && frappe.datetime.get_datetime_as_string(dateObj); + return dateStr?.slice(0,10) ?? this._date; + } + set date(v) { + this._date = v; + this.calendar?.fullcalendar?.gotoDate(v); + } + + get_search_params() { + const params = super.get_search_params() + params.append("view", this.mode); + params.append("date", this.date); + return params; + } + + parse_filters_from_route_options() { + if (frappe.route_options) { + const { view, date, ...rest } = frappe.route_options; + frappe.route_options = rest; + this.mode = view ?? ""; + this.date = date ?? ""; + } + return super.parse_filters_from_route_options(); + } + static load_last_view() {} show_skeleton() {} hide_skeleton() {} @@ -35,8 +63,9 @@ export class ProjectManagementView extends frappe.views.ListView { this.pm_setup_sidebar(); } - refresh() { - this.render() + async refresh() { + await this.render(); + this.update_url_with_filters(); } process_document_refreshes() { @@ -71,6 +100,7 @@ export class ProjectManagementView extends frappe.views.ListView { } async rebuildCalendar() { + this.update_url_with_filters(); this.calendar?.destroy?.(); this.legendContainer.innerHTML = ""; @@ -97,6 +127,14 @@ export class ProjectManagementView extends frappe.views.ListView { }); this.calendar.setupLegend?.(this.legendContainer); await this.calendar.make(); + + if (this._date) { + this.calendar.fullcalendar.gotoDate(this._date); + this.calendar.fullcalendar.on("datesSet", (info) => { + this._date = info.startStr.slice(0,10); + this.update_url_with_filters(); + }); + } } pm_setup_sidebar() { -- GitLab From 940c10a7285f66631750f58b8478b49bfd395e5d Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 20:04:48 +0100 Subject: [PATCH 12/14] feat(pm): Handle custom fields (reqd or in_list_view) --- erpnext/projects/doctype/timesheet/timesheet.py | 13 +++++++++++-- erpnext/public/js/projects/PMTimeLogDialog.js | 17 ++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index cf8d8f9522e..a6c9fecd2fc 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -624,7 +624,7 @@ def get_time_logs(start, end, filters=None): ["Timesheet Detail", end_date, ">=", start], ] - fields = [ + fields = { "time_logs.parent", "time_logs.name", "time_logs.from_time", @@ -633,8 +633,17 @@ def get_time_logs(start, end, filters=None): "time_logs.activity_type", "time_logs.project", "time_logs.task", + "time_logs.description", "employee", - ] + } + + meta = frappe.get_meta("Timesheet Detail") + extra_dfs = [] + extra_dfs += meta.get("fields", {"reqd": ("=", 1)}) + extra_dfs += meta.get("fields", {"in_list_view": ("=", 1)}) + for df in extra_dfs: + fields.add("time_logs." + df.fieldname) + events = frappe.get_list("Timesheet", fields=fields, filters=filters) return events diff --git a/erpnext/public/js/projects/PMTimeLogDialog.js b/erpnext/public/js/projects/PMTimeLogDialog.js index 80ebba7a7a1..7b0e7f1ac2f 100644 --- a/erpnext/public/js/projects/PMTimeLogDialog.js +++ b/erpnext/public/js/projects/PMTimeLogDialog.js @@ -1,3 +1,8 @@ +import { getCurrentEmployee } from "./PMUtils"; + +// These fields are computed from the dialog values, we don't want to show them in the dialog. +const COMPUTED_FIELDS = ["hours", "from_time", "to_time"]; + export class PMTimeLogDialog { constructor(values, callback) { this._values = Object.assign({}, values || {}); @@ -85,11 +90,17 @@ export class PMTimeLogDialog { if (this._all_fields) { return this._all_fields; } - const isFieldAlreadySpecified = (fieldname) => base_fields.some(df => df.fieldname === fieldname); - const required_fields = this.meta.fields.filter((df) => df.reqd); + const isFieldAlreadySpecified = (fieldname) => { + return base_fields.some(df => df.fieldname === fieldname) || COMPUTED_FIELDS.includes(fieldname); + } + const required_fields = this.meta.fields.filter((df) => df.reqd || df.in_list_view); const base_fields = await this._get_custom_fields(); const missing_fields = required_fields.filter((df) => !isFieldAlreadySpecified(df.fieldname)); - this._all_fields = [...base_fields, ...missing_fields]; + this._all_fields = [...base_fields]; + if (missing_fields.length) { + this._all_fields.push({ fieldtype: "Section Break", label: __("More Information"), hide_border: 1, collapsible: 1 }); + this._all_fields.push(...missing_fields); + } return this._all_fields; } -- GitLab From 69851248dc19de25bb697f718150c3465566dcfa Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 20:08:24 +0100 Subject: [PATCH 13/14] refactor(pm): Use getCurrentEmployee in dialog --- erpnext/public/js/projects/PMTimeLogDialog.js | 3 +-- erpnext/public/js/projects/PMUtils.js | 17 +++++++++++++++++ erpnext/public/js/projects/eventSources.js | 10 +--------- 3 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 erpnext/public/js/projects/PMUtils.js diff --git a/erpnext/public/js/projects/PMTimeLogDialog.js b/erpnext/public/js/projects/PMTimeLogDialog.js index 7b0e7f1ac2f..fab05f8acb3 100644 --- a/erpnext/public/js/projects/PMTimeLogDialog.js +++ b/erpnext/public/js/projects/PMTimeLogDialog.js @@ -114,8 +114,7 @@ export class PMTimeLogDialog { ...overrides, }); - const res = await frappe.db.get_value("Employee", { user_id: frappe.session.user }, "name"); - const currentEmployee = res?.message?.name; + const currentEmployee = await getCurrentEmployee(); const NO_DEPENDS_ON = { depends_on: "", read_only_depends_on: "", mandatory_depends_on: "" }; return [ diff --git a/erpnext/public/js/projects/PMUtils.js b/erpnext/public/js/projects/PMUtils.js new file mode 100644 index 00000000000..51c5db6e7d5 --- /dev/null +++ b/erpnext/public/js/projects/PMUtils.js @@ -0,0 +1,17 @@ +let myOwnEmployee = ""; +let overridenEmployee = ""; + +export async function getCurrentEmployee() { + if (overridenEmployee) { + return overridenEmployee; + } + if (!myOwnEmployee) { + const res = await frappe.db.get_value("Employee", { user_id: frappe.session.user }, "name"); + myOwnEmployee = res?.message?.name; + } + return myOwnEmployee; +} + +export function viewAsAnotherEmployee(employee = "") { + overridenEmployee = employee; +} diff --git a/erpnext/public/js/projects/eventSources.js b/erpnext/public/js/projects/eventSources.js index 5f0fc4256e2..709a9ea4ddf 100644 --- a/erpnext/public/js/projects/eventSources.js +++ b/erpnext/public/js/projects/eventSources.js @@ -1,15 +1,7 @@ import { PMTimeLogDialog } from "./PMTimeLogDialog"; +import { getCurrentEmployee } from "./PMUtils"; import { PMViewEventSource } from "./PMViewEventSource"; -let _currentEmployee = ""; -async function getCurrentEmployee() { - if (!_currentEmployee) { - const res = await frappe.db.get_value("Employee", { user_id: frappe.session.user }, "name"); - _currentEmployee = res?.message?.name; - } - return _currentEmployee; -} - const withHoursDiplay = (/** @type {PMViewEventSource} */ C) => { /** @type {PMViewEventSource} */ const WithHoursDiplay = class extends C { -- GitLab From 3227a5c18da271fcbc7a6c7cbb475817224bd71a Mon Sep 17 00:00:00 2001 From: Corentin Forler <8860073-cforler_dokos@users.noreply.gitlab.com> Date: Wed, 29 Nov 2023 20:09:23 +0100 Subject: [PATCH 14/14] perf(pm): Fetch time log first in TL dialog --- erpnext/public/js/projects/eventSources.js | 28 ++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/erpnext/public/js/projects/eventSources.js b/erpnext/public/js/projects/eventSources.js index 709a9ea4ddf..d06cb2d2d34 100644 --- a/erpnext/public/js/projects/eventSources.js +++ b/erpnext/public/js/projects/eventSources.js @@ -191,6 +191,23 @@ export class PMSource_TimeLogs extends withHoursDiplay(PMViewEventSource) { if (evt?.extendedProps?.doctype === "Timesheet Detail") { return evt.extendedProps; } + // if (evt?.extendedProps?.doctype === "Timesheet Detail") { + // return new Promise((resolve, reject) => { + // frappe.call({ + // method: "frappe.client.get", + // type: "GET", + // args: { + // doctype: "Timesheet Detail", + // name: evt?.extendedProps?.name, + // parent: "Timesheet", + // }, + // callback: (r) => { + // frappe.model.sync(r.message); + // resolve(r.message); + // }, + // }).fail(reject); + // }) + // } } doGuiCreateDocument(info) { @@ -203,10 +220,13 @@ export class PMSource_TimeLogs extends withHoursDiplay(PMViewEventSource) { async show_dialog_for_event(info) { const event = info?.event; - const [ta, tl] = await Promise.all([ - this.get_task_assignment_for_event(event), - this.get_time_log_for_event(event), - ]); + + let tl = await this.get_time_log_for_event(event); + let ta = null; + if (!tl) { + ta = await this.get_task_assignment_for_event(event); + } + let duration = (tl?.["hours"] * 3600) || (ta?.["duration"]) || 0; duration = Math.round(duration / 60) * 60; const start_time = extractTime(tl?.start_time ?? tl?.start_time); -- GitLab