From 27d558c9ef0088afff33aa0c44bf331df87a4158 Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Thu, 22 Feb 2018 06:35:49 +0100 Subject: [PATCH] Optionally allow overlap on Timesheet #12893 (#13018) * give user option to ignore time overlap validation * move settings to new Projects Settings * clean up - remove trailing space --- .../accounts_settings/accounts_settings.json | 18 +- .../doctype/projects_settings/__init__.py | 0 .../projects_settings/projects_settings.js | 8 + .../projects_settings/projects_settings.json | 189 ++++++++++++++++++ .../projects_settings/projects_settings.py | 10 + .../test_projects_settings.js | 23 +++ .../test_projects_settings.py | 10 + .../doctype/timesheet/test_timesheet.py | 40 ++++ .../projects/doctype/timesheet/timesheet.py | 12 +- 9 files changed, 304 insertions(+), 6 deletions(-) create mode 100644 erpnext/projects/doctype/projects_settings/__init__.py create mode 100644 erpnext/projects/doctype/projects_settings/projects_settings.js create mode 100644 erpnext/projects/doctype/projects_settings/projects_settings.json create mode 100644 erpnext/projects/doctype/projects_settings/projects_settings.py create mode 100644 erpnext/projects/doctype/projects_settings/test_projects_settings.js create mode 100644 erpnext/projects/doctype/projects_settings/test_projects_settings.py diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 30b1eed2f5c..b19bb2bfeb6 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -42,6 +42,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -72,6 +73,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -103,6 +105,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -132,6 +135,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -163,6 +167,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -193,6 +198,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -223,6 +229,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -254,6 +261,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -285,6 +293,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -315,6 +324,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -345,6 +355,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -374,6 +385,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -404,6 +416,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -434,6 +447,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -465,6 +479,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -497,6 +512,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -511,7 +527,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-01-05 15:26:10.357085", + "modified": "2018-02-21 16:47:38.043115", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/projects/doctype/projects_settings/__init__.py b/erpnext/projects/doctype/projects_settings/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/projects/doctype/projects_settings/projects_settings.js b/erpnext/projects/doctype/projects_settings/projects_settings.js new file mode 100644 index 00000000000..9902b834920 --- /dev/null +++ b/erpnext/projects/doctype/projects_settings/projects_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Projects Settings', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/projects/doctype/projects_settings/projects_settings.json b/erpnext/projects/doctype/projects_settings/projects_settings.json new file mode 100644 index 00000000000..7fa1558a76f --- /dev/null +++ b/erpnext/projects/doctype/projects_settings/projects_settings.json @@ -0,0 +1,189 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-02-21 16:42:13.882879", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "timesheet_sb", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Timesheets", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "ignore_workstation_time_overlap", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Ignore Workstation Time Overlap", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "ignore_user_time_overlap", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Ignore User Time Overlap", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "ignore_employee_time_overlap", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Ignore Employee Time Overlap", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2018-02-21 16:42:42.357209", + "modified_by": "Administrator", + "module": "Projects", + "name": "Projects Settings", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/projects/doctype/projects_settings/projects_settings.py b/erpnext/projects/doctype/projects_settings/projects_settings.py new file mode 100644 index 00000000000..9dcac14f9ad --- /dev/null +++ b/erpnext/projects/doctype/projects_settings/projects_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class ProjectsSettings(Document): + pass diff --git a/erpnext/projects/doctype/projects_settings/test_projects_settings.js b/erpnext/projects/doctype/projects_settings/test_projects_settings.js new file mode 100644 index 00000000000..f6feaa48049 --- /dev/null +++ b/erpnext/projects/doctype/projects_settings/test_projects_settings.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Projects Settings", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Projects Settings + () => frappe.tests.make('Projects Settings', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/projects/doctype/projects_settings/test_projects_settings.py b/erpnext/projects/doctype/projects_settings/test_projects_settings.py new file mode 100644 index 00000000000..d671da73b77 --- /dev/null +++ b/erpnext/projects/doctype/projects_settings/test_projects_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestProjectsSettings(unittest.TestCase): + pass diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py index 2588d566d56..42c2308409c 100644 --- a/erpnext/projects/doctype/timesheet/test_timesheet.py +++ b/erpnext/projects/doctype/timesheet/test_timesheet.py @@ -77,6 +77,46 @@ class TestTimesheet(unittest.TestCase): self.assertEquals(ts.per_billed, 100) self.assertEquals(ts.time_logs[0].sales_invoice, sales_invoice.name) + def test_timesheet_time_overlap(self): + settings = frappe.get_single('Projects Settings') + initial_setting = settings.ignore_employee_time_overlap + settings.ignore_employee_time_overlap = 0 + settings.save() + + update_activity_type("_Test Activity Type") + timesheet = frappe.new_doc("Timesheet") + timesheet.employee = "_T-Employee-0001" + timesheet.append( + 'time_logs', + { + "billable": 1, + "activity_type": "_Test Activity Type", + "from_type": now_datetime(), + "hours": 3, + "company": "_Test Company" + } + ) + timesheet.append( + 'time_logs', + { + "billable": 1, + "activity_type": "_Test Activity Type", + "from_type": now_datetime(), + "hours": 3, + "company": "_Test Company" + } + ) + + self.assertRaises(frappe.ValidationError, timesheet.save) + + settings.ignore_employee_time_overlap = 1 + settings.save() + timesheet.save() # should not throw an error + + settings.ignore_employee_time_overlap = initial_setting + settings.save() + + def make_salary_structure(employee): name = frappe.db.get_value('Salary Structure Employee', {'employee': employee}, 'parent') if name: diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 3d94b5ce001..57a51d21454 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -180,14 +180,16 @@ class Timesheet(Document): self.validate_overlap(data) def validate_overlap(self, data): + settings = frappe.get_single('Projects Settings') if self.production_order: - self.validate_overlap_for("workstation", data, data.workstation) + self.validate_overlap_for("workstation", data, data.workstation, settings.ignore_workstation_time_overlap) else: - self.validate_overlap_for("user", data, self.user) - self.validate_overlap_for("employee", data, self.employee) + self.validate_overlap_for("user", data, self.user, settings.ignore_user_time_overlap) + self.validate_overlap_for("employee", data, self.employee, settings.ignore_employee_time_overlap) - def validate_overlap_for(self, fieldname, args, value): - if not value: return + def validate_overlap_for(self, fieldname, args, value, ignore_validation=False): + if not value or ignore_validation: + return existing = self.get_overlap_for(fieldname, args, value) if existing: