From 3085186182f7dd2383d65ea9413fbe2f46abcf9f Mon Sep 17 00:00:00 2001 From: Valmik Jangla Date: Mon, 7 Mar 2016 18:41:05 +0530 Subject: [PATCH 1/6] Removed Fiscal Year from Holiday List and Calendar --- .../hr/doctype/holiday_list/holiday_list.json | 55 +++++++++++--- .../hr/doctype/holiday_list/holiday_list.py | 72 ++++++++++--------- .../holiday_list/holiday_list_calendar.js | 9 --- 3 files changed, 85 insertions(+), 51 deletions(-) diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.json b/erpnext/hr/doctype/holiday_list/holiday_list.json index 999709da327..ed6bea47818 100644 --- a/erpnext/hr/doctype/holiday_list/holiday_list.json +++ b/erpnext/hr/doctype/holiday_list/holiday_list.json @@ -17,6 +17,7 @@ "fieldtype": "Data", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, "label": "Holiday List Name", @@ -26,6 +27,7 @@ "oldfieldtype": "Data", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 1, @@ -41,6 +43,7 @@ "fieldtype": "Check", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, "label": "Default", @@ -48,6 +51,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -59,20 +63,45 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "fiscal_year", - "fieldtype": "Link", + "fieldname": "from_date", + "fieldtype": "Date", "hidden": 0, "ignore_user_permissions": 0, - "in_filter": 1, - "in_list_view": 1, - "label": "Fiscal Year", + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "From Date", "length": 0, "no_copy": 0, - "oldfieldname": "fiscal_year", - "oldfieldtype": "Link", - "options": "Fiscal Year", "permlevel": 0, + "precision": "", "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "to_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "To Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 1, @@ -88,6 +117,7 @@ "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, "label": "Weekly Off", @@ -96,6 +126,7 @@ "options": "\nSunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday", "permlevel": 0, "print_hide": 1, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 1, "reqd": 0, @@ -111,6 +142,7 @@ "fieldtype": "Button", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Get Weekly Off Dates", @@ -119,6 +151,7 @@ "options": "get_weekly_off_dates", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -134,6 +167,7 @@ "fieldtype": "Table", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Holidays", @@ -144,6 +178,7 @@ "options": "Holiday", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -159,6 +194,7 @@ "fieldtype": "Button", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Clear Table", @@ -167,6 +203,7 @@ "options": "clear_table", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -185,7 +222,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2015-11-16 06:29:47.519087", + "modified": "2016-03-07 05:54:39.627872", "modified_by": "Administrator", "module": "HR", "name": "Holiday List", diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.py b/erpnext/hr/doctype/holiday_list/holiday_list.py index b1cbca372a9..a796403f581 100644 --- a/erpnext/hr/doctype/holiday_list/holiday_list.py +++ b/erpnext/hr/doctype/holiday_list/holiday_list.py @@ -5,20 +5,21 @@ from __future__ import unicode_literals import frappe from frappe.utils import cint -from frappe.model.naming import make_autoname from frappe import throw, _ - from frappe.model.document import Document +class OverlapError(frappe.ValidationError): pass + class HolidayList(Document): def validate(self): self.update_default_holiday_list() + self.validate_time_period() + self.validate_days() def get_weekly_off_dates(self): self.validate_values() - self.validate_days() - yr_start_date, yr_end_date = get_fy_start_end_dates(self.fiscal_year) - date_list = self.get_weekly_off_date_list(yr_start_date, yr_end_date) + self.validate_weekly_days() + date_list = self.get_weekly_off_date_list(self.from_date, self.to_date) last_idx = max([cint(d.idx) for d in self.get("holidays")] or [0,]) for i, d in enumerate(date_list): ch = self.append('holidays', {}) @@ -27,19 +28,44 @@ class HolidayList(Document): ch.idx = last_idx + i + 1 def validate_values(self): - if not self.fiscal_year: - throw(_("Please select Fiscal Year")) if not self.weekly_off: throw(_("Please select weekly off day")) - def validate_days(self): + def validate_weekly_days(self): for day in self.get("holidays"): if (self.weekly_off or "").upper() == (day.description or "").upper(): frappe.throw("Records already exist for mentioned weekly off") - def get_weekly_off_date_list(self, year_start_date, year_end_date): + def validate_days(self): + for day in self.get("holidays"): + if not self.from_date <= day.holiday_date <= self.to_date: + frappe.throw("Date not between From Date and To Date") + + def validate_time_period(self): + if self.from_date > self.to_date: + throw(_("To Date cannot be before From Date")) + + existing = frappe.db.sql("""select holiday_list_name, from_date, to_date from `tabHoliday List` + where + ( + (%(from_date)s > from_date and %(from_date)s < to_date) or + (%(to_date)s > from_date and %(to_date)s < to_date) or + (%(from_date)s <= from_date and %(to_date)s >= to_date)) + and name!=%(name)s""", + { + "from_date": self.from_date, + "to_date": self.to_date, + "name": self.holiday_list_name + }, as_dict=True) + + if existing: + frappe.throw(_("This Time Period conflicts with {0} ({1} to {2})").format(existing[0].holiday_list_name, + existing[0].from_date, existing[0].to_date, OverlapError)) + + + def get_weekly_off_date_list(self, start_date, end_date): from frappe.utils import getdate - year_start_date, year_end_date = getdate(year_start_date), getdate(year_end_date) + year_start_date, year_end_date = getdate(start_date), getdate(end_date) from dateutil import relativedelta from datetime import timedelta @@ -60,40 +86,20 @@ class HolidayList(Document): def update_default_holiday_list(self): frappe.db.sql("""update `tabHoliday List` set is_default = 0 - where is_default = 1 and fiscal_year = %s""", (self.fiscal_year,)) + where is_default = 1""") @frappe.whitelist() def get_events(start, end, filters=None): - import json """Returns events for Gantt / Calendar view rendering. :param start: Start date-time. :param end: End date-time. :param filters: Filters (JSON). """ - from frappe.desk.calendar import get_event_conditions - conditions = get_event_conditions("Holiday List", filters) - - fiscal_year = None - if filters: - fiscal_year = json.loads(filters).get("fiscal_year") - - if not fiscal_year: - fiscal_year = frappe.db.get_value("Global Defaults", None, "current_fiscal_year") - - yr_start_date, yr_end_date = get_fy_start_end_dates(fiscal_year) data = frappe.db.sql("""select hl.name, hld.holiday_date, hld.description from `tabHoliday List` hl, tabHoliday hld where hld.parent = hl.name - and (ifnull(hld.holiday_date, "0000-00-00") != "0000-00-00" - and hld.holiday_date between %(start)s and %(end)s) - {conditions}""".format(conditions=conditions), { - "start": yr_start_date, - "end": yr_end_date - }, as_dict=True, update={"allDay": 1}) - + and ifnull(hld.holiday_date, "0000-00-00") != "0000-00-00" + """, as_dict=True, update={"allDay": 1}) return data - -def get_fy_start_end_dates(fiscal_year): - return frappe.db.get_value("Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]) diff --git a/erpnext/hr/doctype/holiday_list/holiday_list_calendar.js b/erpnext/hr/doctype/holiday_list/holiday_list_calendar.js index dee64de5259..cc24eb0000f 100644 --- a/erpnext/hr/doctype/holiday_list/holiday_list_calendar.js +++ b/erpnext/hr/doctype/holiday_list/holiday_list_calendar.js @@ -9,14 +9,5 @@ frappe.views.calendar["Holiday List"] = { "title": "description", "allDay": "allDay" }, - filters: [ - { - "fieldtype": "Link", - "fieldname": "fiscal_year", - "options": "Fiscal Year", - "label": __("Fiscal Year"), - "default": frappe.defaults.get_user_default("fiscal_year") - } - ], get_events_method: "erpnext.hr.doctype.holiday_list.holiday_list.get_events" } From c666030f34d89b99e36b471f0801425c3427096f Mon Sep 17 00:00:00 2001 From: Valmik Jangla Date: Wed, 9 Mar 2016 14:58:46 +0530 Subject: [PATCH 2/6] Get Weekly Off dates now gets dates dynamically Fixed Holiday List test records --- erpnext/hr/doctype/holiday_list/holiday_list.py | 16 ++++++++++------ .../hr/doctype/holiday_list/test_records.json | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.py b/erpnext/hr/doctype/holiday_list/holiday_list.py index a796403f581..de4f876d297 100644 --- a/erpnext/hr/doctype/holiday_list/holiday_list.py +++ b/erpnext/hr/doctype/holiday_list/holiday_list.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cint +from frappe.utils import cint, get_datetime from frappe import throw, _ from frappe.model.document import Document @@ -18,7 +18,6 @@ class HolidayList(Document): def get_weekly_off_dates(self): self.validate_values() - self.validate_weekly_days() date_list = self.get_weekly_off_date_list(self.from_date, self.to_date) last_idx = max([cint(d.idx) for d in self.get("holidays")] or [0,]) for i, d in enumerate(date_list): @@ -65,18 +64,23 @@ class HolidayList(Document): def get_weekly_off_date_list(self, start_date, end_date): from frappe.utils import getdate - year_start_date, year_end_date = getdate(start_date), getdate(end_date) + start_date, end_date = getdate(start_date), getdate(end_date) from dateutil import relativedelta from datetime import timedelta import calendar date_list = [] + existing_date_list = [] weekday = getattr(calendar, (self.weekly_off).upper()) - reference_date = year_start_date + relativedelta.relativedelta(weekday=weekday) + reference_date = start_date + relativedelta.relativedelta(weekday=weekday) + + for holiday in self.get("holidays"): + existing_date_list.append(get_datetime(holiday.holiday_date).date()) - while reference_date <= year_end_date: - date_list.append(reference_date) + while reference_date <= end_date: + if reference_date not in existing_date_list: + date_list.append(reference_date) reference_date += timedelta(days=7) return date_list diff --git a/erpnext/hr/doctype/holiday_list/test_records.json b/erpnext/hr/doctype/holiday_list/test_records.json index a8cf56b7717..0a0afe81758 100644 --- a/erpnext/hr/doctype/holiday_list/test_records.json +++ b/erpnext/hr/doctype/holiday_list/test_records.json @@ -1,7 +1,8 @@ [ { "doctype": "Holiday List", - "fiscal_year": "_Test Fiscal Year 2013", + "from_date": "2013-01-01", + "to_date":"2013-12-31", "holidays": [ { "description": "New Year", From 3427c939bd320bc114a4ed4dd9aa9407b6802550 Mon Sep 17 00:00:00 2001 From: Valmik Jangla Date: Wed, 9 Mar 2016 15:00:17 +0530 Subject: [PATCH 3/6] Removed unused code --- erpnext/hr/doctype/holiday_list/holiday_list.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.py b/erpnext/hr/doctype/holiday_list/holiday_list.py index de4f876d297..55d2468bc00 100644 --- a/erpnext/hr/doctype/holiday_list/holiday_list.py +++ b/erpnext/hr/doctype/holiday_list/holiday_list.py @@ -30,10 +30,6 @@ class HolidayList(Document): if not self.weekly_off: throw(_("Please select weekly off day")) - def validate_weekly_days(self): - for day in self.get("holidays"): - if (self.weekly_off or "").upper() == (day.description or "").upper(): - frappe.throw("Records already exist for mentioned weekly off") def validate_days(self): for day in self.get("holidays"): From ba54a43e3208e4847ccae2e753b7fef5574bc037 Mon Sep 17 00:00:00 2001 From: Valmik Jangla Date: Wed, 9 Mar 2016 16:09:53 +0530 Subject: [PATCH 4/6] Fixed Maintenance Schedule to work wit new Holiday List Improved code in Holiday List for speed --- .../hr/doctype/holiday_list/holiday_list.py | 6 +-- .../maintenance_schedule.py | 43 +++++++------------ 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.py b/erpnext/hr/doctype/holiday_list/holiday_list.py index 55d2468bc00..309e7e0f533 100644 --- a/erpnext/hr/doctype/holiday_list/holiday_list.py +++ b/erpnext/hr/doctype/holiday_list/holiday_list.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cint, get_datetime +from frappe.utils import cint, getdate from frappe import throw, _ from frappe.model.document import Document @@ -59,7 +59,6 @@ class HolidayList(Document): def get_weekly_off_date_list(self, start_date, end_date): - from frappe.utils import getdate start_date, end_date = getdate(start_date), getdate(end_date) from dateutil import relativedelta @@ -71,8 +70,7 @@ class HolidayList(Document): weekday = getattr(calendar, (self.weekly_off).upper()) reference_date = start_date + relativedelta.relativedelta(weekday=weekday) - for holiday in self.get("holidays"): - existing_date_list.append(get_datetime(holiday.holiday_date).date()) + existing_date_list = [getdate(holiday.holiday_date) for holiday in self.get("holidays")] while reference_date <= end_date: if reference_date not in existing_date_list: diff --git a/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py index d7ba1463d50..38a88c90746 100644 --- a/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py @@ -90,35 +90,24 @@ class MaintenanceSchedule(TransactionBase): return schedule_list def validate_schedule_date_for_holiday_list(self, schedule_date, sales_person): - from erpnext.accounts.utils import get_fiscal_year validated = False - fy_details = "" - try: - fy_details = get_fiscal_year(date=schedule_date, verbose=0) - except Exception: - pass - - if fy_details and fy_details[0]: - # check holiday list in employee master - holiday_list = frappe.db.sql_list("""select h.holiday_date from `tabEmployee` emp, - `tabSales Person` sp, `tabHoliday` h, `tabHoliday List` hl - where sp.name=%s and emp.name=sp.employee - and hl.name=emp.holiday_list and - h.parent=hl.name and - hl.fiscal_year=%s""", (sales_person, fy_details[0])) - if not holiday_list: - # check global holiday list - holiday_list = frappe.db.sql("""select h.holiday_date from - `tabHoliday` h, `tabHoliday List` hl - where h.parent=hl.name and hl.is_default = 1 - and hl.fiscal_year=%s""", fy_details[0]) - - if not validated and holiday_list: - if schedule_date in holiday_list: - schedule_date = add_days(schedule_date, -1) - else: - validated = True + # check holiday list in employee master + holiday_list = frappe.db.sql_list("""select h.holiday_date from `tabEmployee` emp, + `tabSales Person` sp, `tabHoliday` h, `tabHoliday List` hl + where h.parent=hl.name and sp.name=%s and emp.name=sp.employee + and hl.name=emp.holiday_list + """, (sales_person)) + if not holiday_list: + # check global holiday list + holiday_list = frappe.db.sql_list("""select h.holiday_date from + `tabHoliday` h, `tabHoliday List` hl + where h.parent=hl.name and hl.is_default = 1""") + if not validated and holiday_list: + if schedule_date in holiday_list: + schedule_date = add_days(schedule_date, -1) + else: + validated = True return schedule_date From e2b8af16ed75d2cca2670c96bbb85d19bf636977 Mon Sep 17 00:00:00 2001 From: Valmik Jangla Date: Thu, 10 Mar 2016 15:47:03 +0530 Subject: [PATCH 5/6] Fixed Salary Slip to work with new Holiday List --- erpnext/hr/doctype/salary_slip/salary_slip.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 671fbb888aa..21ee0ab7a62 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -118,9 +118,8 @@ class SalarySlip(TransactionBase): holidays = frappe.db.sql("""select t1.holiday_date from `tabHoliday` t1, `tabHoliday List` t2 where t1.parent = t2.name and t2.is_default = 1 - and t2.fiscal_year = %s and t1.holiday_date between %s and %s""", - (self.fiscal_year, start_date, end_date)) + (start_date, end_date)) holidays = [cstr(i[0]) for i in holidays] return holidays From 4284fcf2a622c91d97f58e30b3226d2b59db9be6 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 11 Mar 2016 17:16:30 +0530 Subject: [PATCH 6/6] [fix] additional fixes for removal of Fiscal Year form Holiday List --- erpnext/hr/doctype/employee/employee.py | 13 ++++ erpnext/hr/doctype/holiday/holiday.json | 55 ++++++++-------- .../hr/doctype/holiday_list/holiday_list.js | 14 ++++ .../hr/doctype/holiday_list/holiday_list.json | 26 +------- .../hr/doctype/holiday_list/holiday_list.py | 64 ++++++++----------- .../holiday_list/holiday_list_calendar.js | 10 ++- .../hr/doctype/holiday_list/test_records.json | 13 ++-- .../leave_application/leave_application.py | 3 +- erpnext/hr/doctype/salary_slip/salary_slip.py | 51 ++++++++------- .../doctype/salary_slip/test_salary_slip.py | 26 ++++---- erpnext/patches.txt | 1 + .../remove_fiscal_year_from_holiday_list.py | 17 +++++ .../setup/doctype/company/test_records.json | 9 ++- .../maintenance_schedule.py | 30 ++++----- 14 files changed, 178 insertions(+), 154 deletions(-) create mode 100644 erpnext/hr/doctype/holiday_list/holiday_list.js create mode 100644 erpnext/patches/v6_20x/remove_fiscal_year_from_holiday_list.py diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index a2b56fcb82a..db580f4d157 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -227,3 +227,16 @@ def get_employees_who_are_born_today(): from tabEmployee where day(date_of_birth) = day(%(date)s) and month(date_of_birth) = month(%(date)s) and status = 'Active'""", {"date": today()}, as_dict=True) + +def get_holiday_list_for_employee(employee, raise_exception=True): + employee = frappe.db.get_value("Employee", employee, ["holiday_list", "company"], as_dict=True) + holiday_list = employee.holiday_list + + if not holiday_list: + holiday_list = frappe.db.get_value("Company", employee.company, "default_holiday_list") + + if not holiday_list and raise_exception: + frappe.throw(_("Please set a Holiday List for either the Employee or the Company")) + + return holiday_list + diff --git a/erpnext/hr/doctype/holiday/holiday.json b/erpnext/hr/doctype/holiday/holiday.json index 091dd13ef1e..131215ca69a 100644 --- a/erpnext/hr/doctype/holiday/holiday.json +++ b/erpnext/hr/doctype/holiday/holiday.json @@ -6,32 +6,8 @@ "custom": 0, "docstatus": 0, "doctype": "DocType", + "document_type": "Setup", "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "description", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "300px", - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "300px" - }, { "allow_on_submit": 0, "bold": 0, @@ -40,6 +16,7 @@ "fieldtype": "Date", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, "label": "Date", @@ -56,6 +33,32 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "description", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "300px", + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "300px" } ], "hide_heading": 0, @@ -67,7 +70,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-01-27 11:52:46.864792", + "modified": "2016-03-11 06:39:10.913467", "modified_by": "Administrator", "module": "HR", "name": "Holiday", diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.js b/erpnext/hr/doctype/holiday_list/holiday_list.js new file mode 100644 index 00000000000..43ddfea493c --- /dev/null +++ b/erpnext/hr/doctype/holiday_list/holiday_list.js @@ -0,0 +1,14 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Holiday List', { + refresh: function(frm) { + + }, + from_date: function(frm) { + if (frm.doc.from_date && !frm.doc.to_date) { + var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12); + frm.set_value("to_date", frappe.datetime.add_days(a_year_from_start, -1)); + } + } +}); diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.json b/erpnext/hr/doctype/holiday_list/holiday_list.json index ed6bea47818..ea416543bdd 100644 --- a/erpnext/hr/doctype/holiday_list/holiday_list.json +++ b/erpnext/hr/doctype/holiday_list/holiday_list.json @@ -35,30 +35,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "is_default", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Default", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_on_submit": 0, "bold": 0, @@ -222,7 +198,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-03-07 05:54:39.627872", + "modified": "2016-03-11 05:26:24.819829", "modified_by": "Administrator", "module": "HR", "name": "Holiday List", diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.py b/erpnext/hr/doctype/holiday_list/holiday_list.py index 309e7e0f533..0412624d391 100644 --- a/erpnext/hr/doctype/holiday_list/holiday_list.py +++ b/erpnext/hr/doctype/holiday_list/holiday_list.py @@ -3,8 +3,8 @@ from __future__ import unicode_literals import frappe - -from frappe.utils import cint, getdate +import json +from frappe.utils import cint, getdate, formatdate from frappe import throw, _ from frappe.model.document import Document @@ -12,8 +12,6 @@ class OverlapError(frappe.ValidationError): pass class HolidayList(Document): def validate(self): - self.update_default_holiday_list() - self.validate_time_period() self.validate_days() def get_weekly_off_dates(self): @@ -32,31 +30,12 @@ class HolidayList(Document): def validate_days(self): - for day in self.get("holidays"): - if not self.from_date <= day.holiday_date <= self.to_date: - frappe.throw("Date not between From Date and To Date") - - def validate_time_period(self): if self.from_date > self.to_date: throw(_("To Date cannot be before From Date")) - existing = frappe.db.sql("""select holiday_list_name, from_date, to_date from `tabHoliday List` - where - ( - (%(from_date)s > from_date and %(from_date)s < to_date) or - (%(to_date)s > from_date and %(to_date)s < to_date) or - (%(from_date)s <= from_date and %(to_date)s >= to_date)) - and name!=%(name)s""", - { - "from_date": self.from_date, - "to_date": self.to_date, - "name": self.holiday_list_name - }, as_dict=True) - - if existing: - frappe.throw(_("This Time Period conflicts with {0} ({1} to {2})").format(existing[0].holiday_list_name, - existing[0].from_date, existing[0].to_date, OverlapError)) - + for day in self.get("holidays"): + if not (self.from_date <= day.holiday_date <= self.to_date): + frappe.throw(_("The holiday on {0} is not between From Date and To Date").format(formatdate(day.holiday_date))) def get_weekly_off_date_list(self, start_date, end_date): start_date, end_date = getdate(start_date), getdate(end_date) @@ -69,7 +48,7 @@ class HolidayList(Document): existing_date_list = [] weekday = getattr(calendar, (self.weekly_off).upper()) reference_date = start_date + relativedelta.relativedelta(weekday=weekday) - + existing_date_list = [getdate(holiday.holiday_date) for holiday in self.get("holidays")] while reference_date <= end_date: @@ -82,10 +61,6 @@ class HolidayList(Document): def clear_table(self): self.set('holidays', []) - def update_default_holiday_list(self): - frappe.db.sql("""update `tabHoliday List` set is_default = 0 - where is_default = 1""") - @frappe.whitelist() def get_events(start, end, filters=None): """Returns events for Gantt / Calendar view rendering. @@ -94,10 +69,27 @@ def get_events(start, end, filters=None): :param end: End date-time. :param filters: Filters (JSON). """ + condition = '' + values = { + "start_date": getdate(start), + "end_date": getdate(end) + } + + if filters: + if isinstance(filters, basestring): + filters = json.loads(filters) + + if filters.get('holiday_list'): + condition = 'and hlist.name=%(holiday_list)s' + values['holiday_list'] = filters['holiday_list'] + + data = frappe.db.sql("""select hlist.name, h.holiday_date, h.description + from `tabHoliday List` hlist, tabHoliday h + where h.parent = hlist.name + and h.holiday_date is not null + and h.holiday_date >= %(start_date)s + and h.holiday_date <= %(end_date)s + {condition}""".format(condition=condition), + values, as_dict=True, update={"allDay": 1}) - data = frappe.db.sql("""select hl.name, hld.holiday_date, hld.description - from `tabHoliday List` hl, tabHoliday hld - where hld.parent = hl.name - and ifnull(hld.holiday_date, "0000-00-00") != "0000-00-00" - """, as_dict=True, update={"allDay": 1}) return data diff --git a/erpnext/hr/doctype/holiday_list/holiday_list_calendar.js b/erpnext/hr/doctype/holiday_list/holiday_list_calendar.js index cc24eb0000f..3cc8dd5036f 100644 --- a/erpnext/hr/doctype/holiday_list/holiday_list_calendar.js +++ b/erpnext/hr/doctype/holiday_list/holiday_list_calendar.js @@ -9,5 +9,13 @@ frappe.views.calendar["Holiday List"] = { "title": "description", "allDay": "allDay" }, - get_events_method: "erpnext.hr.doctype.holiday_list.holiday_list.get_events" + get_events_method: "erpnext.hr.doctype.holiday_list.holiday_list.get_events", + filters: [ + { + 'fieldtype': 'Link', + 'fieldname': 'holiday_list', + 'options': 'Holiday List', + 'label': __('Holiday List') + } + ] } diff --git a/erpnext/hr/doctype/holiday_list/test_records.json b/erpnext/hr/doctype/holiday_list/test_records.json index 0a0afe81758..0bd096c415a 100644 --- a/erpnext/hr/doctype/holiday_list/test_records.json +++ b/erpnext/hr/doctype/holiday_list/test_records.json @@ -5,19 +5,18 @@ "to_date":"2013-12-31", "holidays": [ { - "description": "New Year", + "description": "New Year", "holiday_date": "2013-01-01" }, { - "description": "Republic Day", + "description": "Republic Day", "holiday_date": "2013-01-26" }, { - "description": "Test Holiday", + "description": "Test Holiday", "holiday_date": "2013-02-01" } - ], - "holiday_list_name": "_Test Holiday List", - "is_default": 1 + ], + "holiday_list_name": "_Test Holiday List" } -] \ No newline at end of file +] diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 705ace7c0dc..950400df444 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -8,6 +8,7 @@ from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_li comma_or, get_fullname from erpnext.hr.utils import set_employee_name from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates +from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee class LeaveDayBlockedError(frappe.ValidationError): pass @@ -422,7 +423,7 @@ def add_block_dates(events, start, end, employee, company): cnt+=1 def add_holidays(events, start, end, employee, company): - applicable_holiday_list = frappe.db.get_value("Employee", employee, "holiday_list") + applicable_holiday_list = get_holiday_list_for_employee(employee, company) if not applicable_holiday_list: return diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 21ee0ab7a62..f9f7377aa2b 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -11,7 +11,7 @@ from frappe import msgprint, _ from erpnext.setup.utils import get_company_currency from erpnext.hr.utils import set_employee_name from erpnext.hr.doctype.process_payroll.process_payroll import get_month_details - +from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.utilities.transaction_base import TransactionBase class SalarySlip(TransactionBase): @@ -20,11 +20,11 @@ class SalarySlip(TransactionBase): def get_emp_and_leave_details(self): if self.employee: - joining_date, relieving_date = frappe.db.get_value("Employee", self.employee, + joining_date, relieving_date = frappe.db.get_value("Employee", self.employee, ["date_of_joining", "relieving_date"]) - + self.get_leave_details(joining_date, relieving_date) - + struct = self.check_sal_struct(joining_date, relieving_date) if struct: self.set("earnings", []) @@ -33,10 +33,10 @@ class SalarySlip(TransactionBase): def check_sal_struct(self, joining_date, relieving_date): m = get_month_details(self.fiscal_year, self.month) - + struct = frappe.db.sql("""select name from `tabSalary Structure` where employee=%s and is_active = 'Yes' - and (from_date <= %s or from_date <= %s) + and (from_date <= %s or from_date <= %s) and (to_date is null or to_date >= %s or to_date >= %s)""", (self.employee, m.month_start_date, joining_date, m.month_end_date, relieving_date)) @@ -62,9 +62,9 @@ class SalarySlip(TransactionBase): self.fiscal_year = frappe.db.get_default("fiscal_year") if not self.month: self.month = "%02d" % getdate(nowdate()).month - + if not joining_date: - joining_date, relieving_date = frappe.db.get_value("Employee", self.employee, + joining_date, relieving_date = frappe.db.get_value("Employee", self.employee, ["date_of_joining", "relieving_date"]) m = get_month_details(self.fiscal_year, self.month) @@ -82,7 +82,7 @@ class SalarySlip(TransactionBase): self.leave_without_pay = lwp payment_days = flt(self.get_payment_days(m, joining_date, relieving_date)) - flt(lwp) self.payment_days = payment_days > 0 and payment_days or 0 - + def get_payment_days(self, month, joining_date, relieving_date): start_date = month['month_start_date'] if joining_date: @@ -90,15 +90,15 @@ class SalarySlip(TransactionBase): start_date = joining_date elif joining_date > month['month_end_date']: return - + end_date = month['month_end_date'] if relieving_date: if relieving_date > start_date and relieving_date < month['month_end_date']: end_date = relieving_date elif relieving_date < month['month_start_date']: frappe.throw(_("Employee relieved on {0} must be set as 'Left'") - .format(relieving_date)) - + .format(relieving_date)) + payment_days = date_diff(end_date, start_date) + 1 if not cint(frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")): @@ -108,20 +108,19 @@ class SalarySlip(TransactionBase): return payment_days def get_holidays_for_employee(self, start_date, end_date): - holidays = frappe.db.sql("""select t1.holiday_date - from `tabHoliday` t1, tabEmployee t2 - where t1.parent = t2.holiday_list and t2.name = %s - and t1.holiday_date between %s and %s""", - (self.employee, start_date, end_date)) - - if not holidays: - holidays = frappe.db.sql("""select t1.holiday_date - from `tabHoliday` t1, `tabHoliday List` t2 - where t1.parent = t2.name and t2.is_default = 1 - and t1.holiday_date between %s and %s""", - (start_date, end_date)) - - holidays = [cstr(i[0]) for i in holidays] + holiday_list = get_holiday_list_for_employee(self.employee) + holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday` + where + parent=%(holiday_list)s + and holiday_date >= %(start_date)s + and holiday_date <= %(end_date)s''', { + "holiday_list": holiday_list, + "start_date": start_date, + "end_date": end_date + }) + + holidays = [cstr(i) for i in holidays] + return holidays def calculate_lwp(self, holidays, m): diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index 387c5931290..2383affa0af 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -13,11 +13,11 @@ class TestSalarySlip(unittest.TestCase): def setUp(self): for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]: frappe.db.sql("delete from `tab%s`" % dt) - + make_allocation_record(leave_type="_Test Leave Type LWP") - - frappe.db.set_value("Holiday List", "_Test Holiday List", "is_default", 1) - + + frappe.db.set_value("Company", "_Test Company", "default_holiday_list", "_Test Holiday List") + from erpnext.hr.doctype.leave_application.test_leave_application import _test_records as leave_applications la = frappe.copy_doc(leave_applications[2]) la.insert() @@ -32,7 +32,7 @@ class TestSalarySlip(unittest.TestCase): frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1) ss = frappe.copy_doc(test_records[0]) ss.insert() - + self.assertEquals(ss.total_days_in_month, 31) self.assertEquals(ss.payment_days, 30) self.assertEquals(ss.earnings[0].e_modified_amount, 14516.13) @@ -46,7 +46,7 @@ class TestSalarySlip(unittest.TestCase): frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) ss = frappe.copy_doc(test_records[0]) ss.insert() - + self.assertEquals(ss.total_days_in_month, 29) self.assertEquals(ss.payment_days, 28) self.assertEquals(ss.earnings[0].e_modified_amount, 14482.76) @@ -55,32 +55,32 @@ class TestSalarySlip(unittest.TestCase): self.assertEquals(ss.deductions[1].d_modified_amount, 48.28) self.assertEquals(ss.gross_pay, 14982.76) self.assertEquals(ss.net_pay, 14834.48) - + def test_payment_days(self): # Holidays not included in working days frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) - + # set joinng date in the same month frappe.db.set_value("Employee", "_T-Employee-0001", "date_of_joining", "2013-01-11") - + ss = frappe.copy_doc(test_records[0]) ss.insert() - + self.assertEquals(ss.total_days_in_month, 29) self.assertEquals(ss.payment_days, 19) - + # set relieving date in the same month frappe.db.set_value("Employee", "_T-Employee-0001", "relieving_date", "2013-01-28") ss.save() self.assertEquals(ss.total_days_in_month, 29) self.assertEquals(ss.payment_days, 16) - + # Holidays included in working days frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1) ss.save() self.assertEquals(ss.total_days_in_month, 31) self.assertEquals(ss.payment_days, 17) - + frappe.db.set_value("Employee", "_T-Employee-0001", "date_of_joining", "2001-01-11") frappe.db.set_value("Employee", "_T-Employee-0001", "relieving_date", None) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d6ebf5bd65a..e53a506ac49 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -254,3 +254,4 @@ erpnext.patches.v6_24.repost_valuation_rate_for_serialized_items erpnext.patches.v6_24.set_recurring_id erpnext.patches.v6_20x.set_compact_print execute:frappe.delete_doc_if_exists("Web Form", "contact") #2016-03-10 +erpnext.patches.v6_20x.remove_fiscal_year_from_holiday_list diff --git a/erpnext/patches/v6_20x/remove_fiscal_year_from_holiday_list.py b/erpnext/patches/v6_20x/remove_fiscal_year_from_holiday_list.py new file mode 100644 index 00000000000..c8c62036e2d --- /dev/null +++ b/erpnext/patches/v6_20x/remove_fiscal_year_from_holiday_list.py @@ -0,0 +1,17 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + default_holiday_list = frappe.db.get_value("Holiday List", {"is_default": 1}) + if default_holiday_list: + for company in frappe.get_all("Company", fields=["name", "default_holiday_list"]): + if not company.default_holiday_list: + frappe.db.set_value("Company", company.name, "default_holiday_list", default_holiday_list) + + + fiscal_years = frappe._dict((fy.name, fy) for fy in frappe.get_all("Fiscal Year", fields=["name", "year_start_date", "year_end_date"])) + + for holiday_list in frappe.get_all("Holiday List", fields=["name", "fiscal_year"]): + fy = fiscal_years[holiday_list.fiscal_year] + frappe.db.set_value("Holiday List", holiday_list.name, "from_date", fy.year_start_date) + frappe.db.set_value("Holiday List", holiday_list.name, "to_date", fy.year_end_date) diff --git a/erpnext/setup/doctype/company/test_records.json b/erpnext/setup/doctype/company/test_records.json index b6918b3afaa..7e26ca32074 100644 --- a/erpnext/setup/doctype/company/test_records.json +++ b/erpnext/setup/doctype/company/test_records.json @@ -6,7 +6,8 @@ "default_currency": "INR", "doctype": "Company", "domain": "Manufacturing", - "chart_of_accounts": "Standard" + "chart_of_accounts": "Standard", + "default_holiday_list": "_Test Holiday List" }, { "abbr": "_TC1", @@ -15,7 +16,8 @@ "default_currency": "USD", "doctype": "Company", "domain": "Retail", - "chart_of_accounts": "Standard" + "chart_of_accounts": "Standard", + "default_holiday_list": "_Test Holiday List" }, { "abbr": "_TC2", @@ -24,6 +26,7 @@ "country": "Germany", "doctype": "Company", "domain": "Retail", - "chart_of_accounts": "Standard" + "chart_of_accounts": "Standard", + "default_holiday_list": "_Test Holiday List" } ] diff --git a/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py index 38a88c90746..04699729352 100644 --- a/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py @@ -9,6 +9,7 @@ from frappe.utils import add_days, getdate, cint, cstr from frappe import throw, _ from erpnext.utilities.transaction_base import TransactionBase, delete_events from erpnext.stock.utils import get_valid_serial_nos +from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee class MaintenanceSchedule(TransactionBase): def generate_schedule(self): @@ -92,22 +93,19 @@ class MaintenanceSchedule(TransactionBase): def validate_schedule_date_for_holiday_list(self, schedule_date, sales_person): validated = False - # check holiday list in employee master - holiday_list = frappe.db.sql_list("""select h.holiday_date from `tabEmployee` emp, - `tabSales Person` sp, `tabHoliday` h, `tabHoliday List` hl - where h.parent=hl.name and sp.name=%s and emp.name=sp.employee - and hl.name=emp.holiday_list - """, (sales_person)) - if not holiday_list: - # check global holiday list - holiday_list = frappe.db.sql_list("""select h.holiday_date from - `tabHoliday` h, `tabHoliday List` hl - where h.parent=hl.name and hl.is_default = 1""") - if not validated and holiday_list: - if schedule_date in holiday_list: - schedule_date = add_days(schedule_date, -1) - else: - validated = True + employee = frappe.db.get_value("Sales Person", sales_person, "employee") + holiday_list = get_holiday_list_for_employee(employee) + holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday` where parent=%s''', holiday_list) + + if not validated and holidays: + + # max iterations = len(holidays) + for i in xrange(len(holidays)): + if schedule_date in holidays: + schedule_date = add_days(schedule_date, -1) + else: + validated = True + break return schedule_date