From 3851d15360e6a84a6a3f9e4f8ffeb629375f0988 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 2 Jun 2021 15:57:58 +0530 Subject: [PATCH] feat:overtime --- .../employee_checkin/employee_checkin.py | 34 ++-- erpnext/hr/doctype/shift_type/shift_type.py | 26 ++- erpnext/payroll/doctype/gratuity/gratuity.py | 9 +- .../doctype/overtime_details/__init__.py | 0 .../overtime_details/overtime_details.js | 8 + .../overtime_details/overtime_details.json | 101 ++++++++++ .../overtime_details/overtime_details.py | 8 + .../overtime_details/test_overtime_details.py | 8 + .../overtime_salary_component/__init__.py | 0 .../overtime_salary_component.json | 33 ++++ .../overtime_salary_component.py | 8 + .../payroll/doctype/overtime_slip/__init__.py | 0 .../doctype/overtime_slip/overtime_slip.js | 54 ++++++ .../doctype/overtime_slip/overtime_slip.json | 181 ++++++++++++++++++ .../doctype/overtime_slip/overtime_slip.py | 61 ++++++ .../overtime_slip/test_overtime_slip.py | 8 + .../payroll/doctype/overtime_type/__init__.py | 0 .../doctype/overtime_type/overtime_type.js | 15 ++ .../doctype/overtime_type/overtime_type.json | 116 +++++++++++ .../doctype/overtime_type/overtime_type.py | 8 + .../overtime_type/test_overtime_type.py | 8 + 21 files changed, 656 insertions(+), 30 deletions(-) create mode 100644 erpnext/payroll/doctype/overtime_details/__init__.py create mode 100644 erpnext/payroll/doctype/overtime_details/overtime_details.js create mode 100644 erpnext/payroll/doctype/overtime_details/overtime_details.json create mode 100644 erpnext/payroll/doctype/overtime_details/overtime_details.py create mode 100644 erpnext/payroll/doctype/overtime_details/test_overtime_details.py create mode 100644 erpnext/payroll/doctype/overtime_salary_component/__init__.py create mode 100644 erpnext/payroll/doctype/overtime_salary_component/overtime_salary_component.json create mode 100644 erpnext/payroll/doctype/overtime_salary_component/overtime_salary_component.py create mode 100644 erpnext/payroll/doctype/overtime_slip/__init__.py create mode 100644 erpnext/payroll/doctype/overtime_slip/overtime_slip.js create mode 100644 erpnext/payroll/doctype/overtime_slip/overtime_slip.json create mode 100644 erpnext/payroll/doctype/overtime_slip/overtime_slip.py create mode 100644 erpnext/payroll/doctype/overtime_slip/test_overtime_slip.py create mode 100644 erpnext/payroll/doctype/overtime_type/__init__.py create mode 100644 erpnext/payroll/doctype/overtime_type/overtime_type.js create mode 100644 erpnext/payroll/doctype/overtime_type/overtime_type.json create mode 100644 erpnext/payroll/doctype/overtime_type/overtime_type.py create mode 100644 erpnext/payroll/doctype/overtime_type/test_overtime_type.py diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py index 4d731c79f8c..791b32e9c0e 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py @@ -31,18 +31,19 @@ class EmployeeCheckin(Document): def fetch_shift(self): shift_actual_timings = get_actual_start_end_datetime_of_shift(self.employee, get_datetime(self.time), True) - if shift_actual_timings[0] and shift_actual_timings[1]: - if shift_actual_timings[2].shift_type.determine_check_in_and_check_out == 'Strictly based on Log Type in Employee Checkin' and not self.log_type and not self.skip_auto_attendance: - frappe.throw(_('Log Type is required for check-ins falling in the shift: {0}.').format(shift_actual_timings[2].shift_type.name)) - if not self.attendance: - self.shift = shift_actual_timings[2].shift_type.name - self.shift_actual_start = shift_actual_timings[0] - self.shift_actual_end = shift_actual_timings[1] - self.shift_start = shift_actual_timings[2].start_datetime - self.shift_end = shift_actual_timings[2].end_datetime + if not frappe.db.get_value("Shift Type", shift_actual_timings[2].shift_type.name, "allow_overtime"): + if shift_actual_timings[0] and shift_actual_timings[1]: + if shift_actual_timings[2].shift_type.determine_check_in_and_check_out == 'Strictly based on Log Type in Employee Checkin' and not self.log_type and not self.skip_auto_attendance: + frappe.throw(_('Log Type is required for check-ins falling in the shift: {0}.').format(shift_actual_timings[2].shift_type.name)) + if not self.attendance: + self.shift = shift_actual_timings[2].shift_type.name + self.shift_actual_start = shift_actual_timings[0] + self.shift_actual_end = shift_actual_timings[1] + self.shift_start = shift_actual_timings[2].start_datetime + self.shift_end = shift_actual_timings[2].end_datetime elif frappe.db.get_value("Shift Type", shift_actual_timings[2].shift_type.name, "allow_overtime"): - #because after Actual time it takes check-in/out invalid - #if employee checkout late or check-in before before shift timing adding time buffer. + # #because after Actual time it takes check-in/out invalid + # #if employee checkout late or check-in before before shift timing adding time buffer. self.shift = shift_actual_timings[2].shift_type.name self.shift_start = shift_actual_timings[2].start_datetime self.shift_end = shift_actual_timings[2].end_datetime @@ -101,12 +102,18 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki employee_doc = frappe.get_doc('Employee', employee) if not frappe.db.exists('Attendance', {'employee':employee, 'attendance_date':attendance_date, 'docstatus':('!=', '2')}): + print(working_hours) working_timedelta = '00:00:00' working_time = None working_time = modf(working_hours) if working_time[1] or working_time[0]: working_timedelta = timedelta(hours =int(working_time[1]), minutes = int(working_time[0] * 60)) - working_time = str(int(working_time[1])) + ' Hours ' + str(int(working_time[0] * 60)) + ' Minutes' + from erpnext.hr.doctype.shift_type.shift_type import convert_time_into_duration + working_time = convert_time_into_duration(working_timedelta) + + print("working") + print(working_timedelta) + print(working_time) doc_dict = { 'doctype': 'Attendance', @@ -114,7 +121,6 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki 'attendance_date': attendance_date, 'status': attendance_status, 'working_time': working_time, - 'working_timedelta': working_timedelta, 'company': employee_doc.company, 'shift': shift, 'late_entry': late_entry, @@ -151,6 +157,8 @@ def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type): """ total_hours = 0 in_time = out_time = None + print("Madar Chod") + print(logs) if check_in_out_type == 'Alternating entries as IN and OUT during the same shift': in_time = logs[0].time if len(logs) >= 2: diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py index 916289de593..f3c6c055989 100644 --- a/erpnext/hr/doctype/shift_type/shift_type.py +++ b/erpnext/hr/doctype/shift_type/shift_type.py @@ -19,7 +19,6 @@ from datetime import timedelta class ShiftType(Document): def validate(self): - self.validate_overtime() self.set_working_hours() @@ -35,12 +34,7 @@ class ShiftType(Document): else: # for night shift time_difference = shift_start - shift_end - - self.working_time_delta = str(time_difference) - time_difference = str(time_difference).split(":") - - if int(time_difference[0]) or int(time_difference[1]): - self.standard_working_time = time_difference[0] + " Hours " + time_difference[1] + " Minutes" + self.standard_working_time = convert_time_into_duration(time_difference) @@ -65,12 +59,17 @@ class ShiftType(Document): logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time") from pprint import pprint pprint(logs) - for key, group in itertools.groupby(logs, key=lambda x: (x['employee'], x['shift_actual_start'])): + if self.allow_overtime == 1: + print("chumma") + checkins_log = itertools.groupby(logs, key=lambda x: (x['employee'], x['shift_start'])) + else: + checkins_log = itertools.groupby(logs, key=lambda x: (x['employee'], x['shift_actual_start'])) + + for key, group in checkins_log: single_shift_logs = list(group) attendance_status, working_hours, late_entry, early_exit, in_time, out_time = self.get_attendance(single_shift_logs) - - print("_______>>>>>>>>>>>>>",attendance_status, working_hours, late_entry, early_exit, in_time, out_time) + print(attendance_status, working_hours, late_entry, early_exit, in_time, out_time) mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, in_time, out_time, self.name) @@ -87,6 +86,7 @@ class ShiftType(Document): late_entry = early_exit = False total_working_hours, in_time, out_time = calculate_working_hours(logs, self.determine_check_in_and_check_out, self.working_hours_calculation_based_on) + print(total_working_hours) if cint(self.enable_entry_grace_period) and in_time and in_time > logs[0].shift_start + timedelta(minutes=cint(self.late_entry_grace_period)): late_entry = True @@ -95,6 +95,7 @@ class ShiftType(Document): early_exit = True if self.working_hours_threshold_for_absent and total_working_hours < self.working_hours_threshold_for_absent: + print("------->>", 'Here', print(self.working_hours_threshold_for_absent)) return 'Absent', total_working_hours, late_entry, early_exit, in_time, out_time if self.working_hours_threshold_for_half_day and total_working_hours < self.working_hours_threshold_for_half_day: @@ -173,3 +174,8 @@ def get_filtered_date_list(employee, start_date, end_date, filter_attendance=Tru {"employee":employee, "start_date":start_date, "end_date":end_date, "holiday_list":holiday_list}, as_list=True) return [getdate(date[0]) for date in dates] + + +def convert_time_into_duration(time_difference): + time_difference = str(time_difference).split(":") + return (int(time_difference[0]) * 3600) + (int(time_difference[1]) * 60) + int(time_difference[2]) diff --git a/erpnext/payroll/doctype/gratuity/gratuity.py b/erpnext/payroll/doctype/gratuity/gratuity.py index 971ec523c97..f085ac90a35 100644 --- a/erpnext/payroll/doctype/gratuity/gratuity.py +++ b/erpnext/payroll/doctype/gratuity/gratuity.py @@ -235,17 +235,12 @@ def get_gratuity_rule_slabs(gratuity_rule): return frappe.get_all("Gratuity Rule Slab", filters= {'parent': gratuity_rule}, fields = ["*"], order_by="idx") def get_salary_structure(employee): - salary_struct = frappe.get_list("Salary Structure Assignment", filters = { + salary_structure_assignment = frappe.get_list("Salary Structure Assignment", filters = { "employee": employee, 'docstatus': 1 }, fields=["from_date", "salary_structure"], order_by = "from_date desc") - - if len(salary_struct): - return salary_struct[0].salary_structure - else: - frappe.throw(_("No Salary Structure Assignment found for employee: {0}").format(employee)) - + return salary_structure_assignment[0].salary_structure if len(salary_structure_assignment) else None def get_last_salary_slip(employee): return frappe.get_list("Salary Slip", filters = { diff --git a/erpnext/payroll/doctype/overtime_details/__init__.py b/erpnext/payroll/doctype/overtime_details/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/payroll/doctype/overtime_details/overtime_details.js b/erpnext/payroll/doctype/overtime_details/overtime_details.js new file mode 100644 index 00000000000..be0b9fa5b4c --- /dev/null +++ b/erpnext/payroll/doctype/overtime_details/overtime_details.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Overtime Details', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/payroll/doctype/overtime_details/overtime_details.json b/erpnext/payroll/doctype/overtime_details/overtime_details.json new file mode 100644 index 00000000000..0964e4d60db --- /dev/null +++ b/erpnext/payroll/doctype/overtime_details/overtime_details.json @@ -0,0 +1,101 @@ +{ + "actions": [], + "creation": "2021-05-27 13:39:56.788736", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "reference_document_type", + "reference_document", + "date", + "start_time", + "end_time", + "overtime_type", + "total_working_time", + "working_timedelta", + "overtime_duration", + "overtime_durationtime", + "overtime_amount" + ], + "fields": [ + { + "fieldname": "reference_document_type", + "fieldtype": "Link", + "label": "Reference Document Type ", + "options": "DocType", + "read_only": 1 + }, + { + "fieldname": "date", + "fieldtype": "Date", + "label": "Date" + }, + { + "fieldname": "start_time", + "fieldtype": "Datetime", + "label": "Start Time " + }, + { + "fieldname": "end_time", + "fieldtype": "Datetime", + "label": "End Time" + }, + { + "fieldname": "overtime_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Overtime Type ", + "options": "Overtime Type", + "reqd": 1 + }, + { + "fieldname": "total_working_time", + "fieldtype": "Data", + "label": "Total Working Time" + }, + { + "default": "00:00:00", + "fieldname": "working_timedelta", + "fieldtype": "Time", + "label": "Working Time(Delta)" + }, + { + "fieldname": "overtime_duration", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Overtime Duration", + "reqd": 1 + }, + { + "default": "00:00:00", + "fieldname": "overtime_durationtime", + "fieldtype": "Time", + "hidden": 1, + "label": "Overtime Duration(Time)" + }, + { + "fieldname": "overtime_amount", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Overtime Amount", + "reqd": 1 + }, + { + "fieldname": "reference_document", + "fieldtype": "Dynamic Link", + "label": "Reference Document", + "options": "reference_document_type" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-05-27 13:43:11.578682", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Overtime Details", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/payroll/doctype/overtime_details/overtime_details.py b/erpnext/payroll/doctype/overtime_details/overtime_details.py new file mode 100644 index 00000000000..32f9a7b8ebb --- /dev/null +++ b/erpnext/payroll/doctype/overtime_details/overtime_details.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class OvertimeDetails(Document): + pass diff --git a/erpnext/payroll/doctype/overtime_details/test_overtime_details.py b/erpnext/payroll/doctype/overtime_details/test_overtime_details.py new file mode 100644 index 00000000000..3064ef4017f --- /dev/null +++ b/erpnext/payroll/doctype/overtime_details/test_overtime_details.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + +class TestOvertimeDetails(unittest.TestCase): + pass diff --git a/erpnext/payroll/doctype/overtime_salary_component/__init__.py b/erpnext/payroll/doctype/overtime_salary_component/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/payroll/doctype/overtime_salary_component/overtime_salary_component.json b/erpnext/payroll/doctype/overtime_salary_component/overtime_salary_component.json new file mode 100644 index 00000000000..3276bb3f5c7 --- /dev/null +++ b/erpnext/payroll/doctype/overtime_salary_component/overtime_salary_component.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "creation": "2021-05-25 12:49:03.287694", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "salary_component" + ], + "fields": [ + { + "fieldname": "salary_component", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Salary Component ", + "options": "Salary Component", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-05-25 12:49:03.287694", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Overtime Salary Component", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/doctype/overtime_salary_component/overtime_salary_component.py b/erpnext/payroll/doctype/overtime_salary_component/overtime_salary_component.py new file mode 100644 index 00000000000..00868503e6b --- /dev/null +++ b/erpnext/payroll/doctype/overtime_salary_component/overtime_salary_component.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class OvertimeSalaryComponent(Document): + pass diff --git a/erpnext/payroll/doctype/overtime_slip/__init__.py b/erpnext/payroll/doctype/overtime_slip/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/payroll/doctype/overtime_slip/overtime_slip.js b/erpnext/payroll/doctype/overtime_slip/overtime_slip.js new file mode 100644 index 00000000000..642a9581dbc --- /dev/null +++ b/erpnext/payroll/doctype/overtime_slip/overtime_slip.js @@ -0,0 +1,54 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Overtime Slip', { + onload: function() { + + }, + employee: function(frm) { + if (frm.doc.employee) { + frm.events.set_frequency_and_dates(frm); + frm.events.get_emp_details_and_overtime_duration(frm); + } + }, + + + from_date: function(frm) { + if (frm.doc.employee) { + frm.events.set_frequency_and_dates(frm); + frm.events.get_emp_details_and_overtime_duration(frm); + } + }, + + set_frequency_and_dates: function(frm) { + frappe.call({ + method: "erpnext.payroll.doctype.overtime_slip.overtime_slip.get_frequency_and_dates", + args: { + employee: frm.doc.employee, + date: frm.doc.from_date || frm.doc.posting_date, + }, + callback: function(r) { + frm.set_value("payroll_frequency", r.message[1]); + frm.doc.from_date = r.message[0].start_date; + frm.doc.to_date = r.message[0].end_date; + frm.refresh(); + } + }); + }, + + get_emp_details_and_overtime_duration: function(frm) { + if (frm.doc.employee) { + return frappe.call({ + method: 'get_emp_and_overtime_details', + doc: frm.doc, + callback: function(r) { + + } + }); + } + }, + + reset_value: function(frm) { + + } +}); diff --git a/erpnext/payroll/doctype/overtime_slip/overtime_slip.json b/erpnext/payroll/doctype/overtime_slip/overtime_slip.json new file mode 100644 index 00000000000..228ca7da3e9 --- /dev/null +++ b/erpnext/payroll/doctype/overtime_slip/overtime_slip.json @@ -0,0 +1,181 @@ +{ + "actions": [], + "creation": "2021-05-27 12:47:32.372698", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "posting_date", + "employee", + "employee_name", + "column_break_4", + "status", + "company", + "department", + "section_break_7", + "from_date", + "to_date", + "column_break_10", + "payroll_frequency", + "section_break_12", + "overtime_details", + "section_break_13", + "total_overtime_duration", + "total_overtime_durationtime", + "column_break_17", + "amount", + "name1", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1 + }, + { + "default": "Pending", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Pending\nApproved\nRejected", + "reqd": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Overtime Slip", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "overtime_details", + "fieldtype": "Table", + "label": "Overtime Details", + "options": "Overtime Details", + "reqd": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date" + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "read_only": 1 + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "payroll_frequency", + "fieldtype": "Select", + "label": "Payroll Frequency", + "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" + }, + { + "fieldname": "section_break_13", + "fieldtype": "Section Break" + }, + { + "fieldname": "total_overtime_duration", + "fieldtype": "Data", + "label": "Total Overtime Duration" + }, + { + "fieldname": "total_overtime_durationtime", + "fieldtype": "Time", + "label": "Total Overtime Duration(Time)" + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount" + }, + { + "fieldname": "section_break_12", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "name1", + "fieldtype": "Duration", + "label": "name" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2021-05-31 15:07:39.485473", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Overtime Slip", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/doctype/overtime_slip/overtime_slip.py b/erpnext/payroll/doctype/overtime_slip/overtime_slip.py new file mode 100644 index 00000000000..0f29c23817d --- /dev/null +++ b/erpnext/payroll/doctype/overtime_slip/overtime_slip.py @@ -0,0 +1,61 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.utils import get_datetime +from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates +from erpnext.payroll.doctype.gratuity.gratuity import get_salary_structure +from frappe.model.document import Document +from pprint import pprint + +class OvertimeSlip(Document): + + @frappe.whitelist() + def get_emp_and_overtime_details(self): + overtime_based_on = frappe.db.get_single_value("Payroll Settings", "overtime_based_on") + records = [] + if overtime_based_on == "Attendance": + records = self.get_attendance_record() + elif overtime_based_on == "Timesheet": + records = self.get_timesheet_record() + else: + frappe.throw(_('Select "Calculate Overtime Hours Based On" in Payroll Settings')) + + if len(records): + self.create_overtime_details_row(records) + else: + frappe.throw(_("No {0} records found for Overtime").format(overtime_based_on)) + + def create_overtime_details_row(self, records): + pprint(records) + + + def get_attendance_record(self): + records = frappe.db.sql("""SELECT overtime_duration, employee, name, attendance_date, overtime_type + FROM `TabAttendance` + WHERE + attendance_date >= %s AND attendance_date <= %s + AND employee = %s + AND docstatus = 1 AND status= 'Present' + AND ( + overtime_duration IS NOT NULL OR overtime_duration != '00:00:00.000000' + ) + """, (get_datetime(self.from_date), get_datetime(self.to_date), self.employee), as_dict=1) + + return records + + +@frappe.whitelist() +def get_frequency_and_dates(employee, date): + print(date) + salary_structure = get_salary_structure(employee) + if salary_structure: + payroll_frequency = frappe.db.get_value('Salary Structure', salary_structure, 'payroll_frequency') + date_details = get_start_end_dates(payroll_frequency, date, frappe.db.get_value('Employee', employee, 'company')) + print(date_details) + return [date_details, payroll_frequency] + else: + frappe.throw(_("No Salary Structure Assignment found for Employee: {0}").format(employee)) + + diff --git a/erpnext/payroll/doctype/overtime_slip/test_overtime_slip.py b/erpnext/payroll/doctype/overtime_slip/test_overtime_slip.py new file mode 100644 index 00000000000..915c56e0514 --- /dev/null +++ b/erpnext/payroll/doctype/overtime_slip/test_overtime_slip.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + +class TestOvertimeSlip(unittest.TestCase): + pass diff --git a/erpnext/payroll/doctype/overtime_type/__init__.py b/erpnext/payroll/doctype/overtime_type/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/payroll/doctype/overtime_type/overtime_type.js b/erpnext/payroll/doctype/overtime_type/overtime_type.js new file mode 100644 index 00000000000..c1b86db27a9 --- /dev/null +++ b/erpnext/payroll/doctype/overtime_type/overtime_type.js @@ -0,0 +1,15 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Overtime Type', { + setup: function(frm) { + frm.set_query("party_type", () => { + let party_type = ["Employee", "Department", "Employee Grade"]; + return { + filters: { + name: ["in", party_type] + } + }; + }); + } +}); diff --git a/erpnext/payroll/doctype/overtime_type/overtime_type.json b/erpnext/payroll/doctype/overtime_type/overtime_type.json new file mode 100644 index 00000000000..c403b00efb7 --- /dev/null +++ b/erpnext/payroll/doctype/overtime_type/overtime_type.json @@ -0,0 +1,116 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2021-05-25 12:49:09.178306", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "party_type", + "party", + "column_break_3", + "applicable_salary_component", + "pay_rate_multipliers_section", + "standard_multiplier", + "applicable_for_weekend", + "weekend_multiplier", + "column_break_9", + "applicable_for_public_holiday", + "public_holiday_multipliers" + ], + "fields": [ + { + "fieldname": "party_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Party Type", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "party", + "fieldtype": "Dynamic Link", + "label": "Party", + "options": "party_type" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "applicable_salary_component", + "fieldtype": "Table MultiSelect", + "label": "Applicable Salary Component", + "options": "Overtime Salary Component" + }, + { + "description": "Pay Rate Multipliers apply to the hourly wage for the position you\u2019re working during the overtime hours.", + "fieldname": "pay_rate_multipliers_section", + "fieldtype": "Section Break", + "label": "Pay Rate Multipliers" + }, + { + "fieldname": "standard_multiplier", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Standard Multiplier", + "reqd": 1 + }, + { + "default": "0", + "description": "If unchecked, the standard multiplier will be taken as default for the weekend.\n", + "fieldname": "applicable_for_weekend", + "fieldtype": "Check", + "label": "Applicable for Weekend" + }, + { + "depends_on": "eval: doc.applicable_for_weekend == 1", + "fieldname": "weekend_multiplier", + "fieldtype": "Float", + "label": "Weekend Multiplier", + "mandatory_depends_on": "eval: doc.applicable_for_weekend == 1" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "If unchecked, the standard multiplier will be taken as default for Public Holiday.", + "fieldname": "applicable_for_public_holiday", + "fieldtype": "Check", + "label": "Applicable for Public Holiday" + }, + { + "depends_on": "eval: doc.applicable_for_public_holiday == 1", + "fieldname": "public_holiday_multipliers", + "fieldtype": "Float", + "label": "Public Holiday Multipliers", + "mandatory_depends_on": "eval: doc.applicable_for_public_holiday == 1" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-05-25 13:21:11.318945", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Overtime Type", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/doctype/overtime_type/overtime_type.py b/erpnext/payroll/doctype/overtime_type/overtime_type.py new file mode 100644 index 00000000000..75cd3149cef --- /dev/null +++ b/erpnext/payroll/doctype/overtime_type/overtime_type.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class OvertimeType(Document): + pass diff --git a/erpnext/payroll/doctype/overtime_type/test_overtime_type.py b/erpnext/payroll/doctype/overtime_type/test_overtime_type.py new file mode 100644 index 00000000000..ba5f62fc6dc --- /dev/null +++ b/erpnext/payroll/doctype/overtime_type/test_overtime_type.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + +class TestOvertimeType(unittest.TestCase): + pass