diff --git a/erpnext/hr/doctype/attendance/attendance.js b/erpnext/hr/doctype/attendance/attendance.js index 794664add43..ee83e8d90af 100644 --- a/erpnext/hr/doctype/attendance/attendance.js +++ b/erpnext/hr/doctype/attendance/attendance.js @@ -45,15 +45,21 @@ frappe.ui.form.on('Attendance', { }, set_overtime_type: function(frm) { - frappe.call({ - method: "erpnext.hr.doctype.attendance.attendance.get_overtime_type", - args: { - employee: frm.doc.employee, - }, - callback: function(r) { - if (r.message) { - frm.set_value("overtime_type", r.message); - } + frappe.db.get_single_value("Payroll Settings", "overtime_based_on").then((r)=>{ + if (r == "Attendance") { + frappe.call({ + method: "erpnext.hr.doctype.attendance.attendance.get_overtime_type", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value("overtime_type", r.message); + } + } + }); + } else { + frm.set_value("overtime_type", ''); } }); }, diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index d96b8fd944b..a5a18be302e 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -4,11 +4,10 @@ from __future__ import unicode_literals import frappe -from frappe.utils import getdate, nowdate from frappe import _ from frappe.model.document import Document from erpnext.hr.utils import validate_active_employee -from frappe.utils import cstr, get_datetime, formatdate, getdate +from frappe.utils import cstr, get_datetime, formatdate, getdate, nowdate class Attendance(Document): def validate(self): @@ -58,7 +57,7 @@ class Attendance(Document): self.overtime_type = get_overtime_type(self.employee) if self.overtime_type: - if frappe.db.get_single_value("Payroll Settings", "overtime_based_on") != "Attendance": + if frappe.db.get_single_value("Payroll Settings", "overtime_based_on") != "Attendance": frappe.msgprint(_('Set "Calculate Overtime Based On Attendance" to Attendance for Overtime Slip Creation')) maximum_overtime_hours_allowed = frappe.db.get_single_value("Payroll Settings", "maximum_overtime_hours_allowed") @@ -136,25 +135,28 @@ def get_shift_type(employee, attendance_date): @frappe.whitelist() def get_overtime_type(employee): - emp_department = frappe.db.get_value("Employee", employee, "department") - if emp_department: - overtime_type = frappe.get_list("Overtime Type", filters={"party_type": "Department", "party": emp_department}, fields=['name']) - if len(overtime_type): - overtime_type = overtime_type[0].name - - emp_grade = frappe.db.get_value("Employee", employee, "grade") - if emp_grade: - overtime_type = frappe.get_list("Overtime Type", filters={"party_type": "Employee Grade", "party": emp_grade}, - fields=['name']) - if len(overtime_type): - - overtime_type = overtime_type[0].name - - overtime_type = frappe.get_list("Overtime Type", filters={"party_type": "Employee", "party": employee}, fields=['name']) + emp_department = frappe.db.get_value("Employee", employee, "department") + if emp_department: + overtime_type = frappe.get_list("Overtime Type", filters={ + "applicable_for": "Department", "department": emp_department}, fields=['name']) if len(overtime_type): overtime_type = overtime_type[0].name - return overtime_type + emp_grade = frappe.db.get_value("Employee", employee, "grade") + if emp_grade: + overtime_type = frappe.get_list("Overtime Type", filters={ + "applicable_for": "Employee Grade", "employee_grade": emp_grade}, + fields=['name']) + if len(overtime_type): + + overtime_type = overtime_type[0].name + + overtime_type = frappe.get_list("Overtime Type", filters={ + "applicable_for": "Employee", "employee": employee}, fields=['name']) + if len(overtime_type): + overtime_type = overtime_type[0].name + + return overtime_type @frappe.whitelist() def get_events(start, end, filters=None): diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py index 272ab6d36a8..b2153b79db3 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py @@ -42,11 +42,11 @@ class EmployeeCheckin(Document): 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. - 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 + #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 @frappe.whitelist() def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=None, log_type=None, skip_auto_attendance=0, employee_fieldname='attendance_device_id'): diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py index d733d051963..5047076fc14 100644 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py +++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import cint, cstr, getdate, now_datetime, nowdate +from frappe.utils import cstr, getdate, now_datetime, nowdate from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday from erpnext.hr.utils import validate_active_employee @@ -61,12 +61,12 @@ class ShiftAssignment(Document): def throw_overlap_error(self, shift_details): shift_details = frappe._dict(shift_details) if shift_details.docstatus == 1 and shift_details.status == "Active": - msg = _("Employee {0} already has Active Shift {1}: {2}").format(frappe.bold(self.employee), frappe.bold(self.shift_type), frappe.bold(shift_details.name)) + msg = _("Employee {0} already has Active Shift {1}: {2}").format(frappe.bold(self.employee), frappe.bold(self.shift_type), frappe.bold(shift_details.name)) + " " if shift_details.start_date: - msg += _(" from {0}").format(getdate(self.start_date).strftime("%d-%m-%Y")) + msg += _("from {0}").format(getdate(self.start_date).strftime("%d-%m-%Y")) + " " title = "Ongoing Shift" if shift_details.end_date: - msg += _(" to {0}").format(getdate(self.end_date).strftime("%d-%m-%Y")) + msg += _("to {0}").format(getdate(self.end_date).strftime("%d-%m-%Y")) title = "Active Shift" if msg: frappe.throw(msg, title=title) @@ -273,7 +273,7 @@ def get_actual_start_end_datetime_of_shift(employee, for_datetime, consider_defa if timestamp and for_datetime <= timestamp: timestamp_index = index break - if timestamp_index and timestamp_index%2 == 1: + if timestamp_index and timestamp_index % 2 == 1: shift_details = shift_timings_as_per_timestamp[int((timestamp_index-1)/2)] actual_shift_start = shift_details.actual_start actual_shift_end = shift_details.actual_end diff --git a/erpnext/hr/doctype/shift_type/shift_type.json b/erpnext/hr/doctype/shift_type/shift_type.json index 7aaa508c692..23935511cee 100644 --- a/erpnext/hr/doctype/shift_type/shift_type.json +++ b/erpnext/hr/doctype/shift_type/shift_type.json @@ -25,7 +25,7 @@ "column_break_19", "process_attendance_after", "last_sync_of_checkin", - "grace_period_settings_auto_attendance_section", + "grace_period_settings_section", "enable_entry_grace_period", "late_entry_grace_period", "column_break_18", @@ -141,12 +141,6 @@ "fieldtype": "Section Break", "label": "Auto Attendance Settings" }, - { - "depends_on": "enable_auto_attendance", - "fieldname": "grace_period_settings_auto_attendance_section", - "fieldtype": "Section Break", - "label": "Grace Period Settings For Auto Attendance" - }, { "default": "0", "description": "Mark attendance based on 'Employee Checkin' for Employees assigned to this shift.", @@ -188,10 +182,16 @@ { "fieldname": "column_break_19", "fieldtype": "Column Break" + }, + { + "depends_on": "enable_auto_attendance", + "fieldname": "grace_period_settings_section", + "fieldtype": "Section Break", + "label": "Grace Period Settings" } ], "links": [], - "modified": "2021-06-09 13:38:25.697100", + "modified": "2021-06-15 15:22:55.756078", "modified_by": "Administrator", "module": "HR", "name": "Shift Type", diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py index 2b3af3d3b0b..e6d3dd7db9c 100644 --- a/erpnext/hr/doctype/shift_type/shift_type.py +++ b/erpnext/hr/doctype/shift_type/shift_type.py @@ -4,11 +4,8 @@ from __future__ import unicode_literals import itertools -from datetime import timedelta - import frappe from frappe import _ -from math import modf from frappe.model.document import Document from frappe.utils import cint, getdate, get_datetime from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift, get_employee_shift @@ -26,7 +23,7 @@ class ShiftType(Document): end_time = self.end_time.split(":") start_time = self.start_time.split(":") - shift_end = timedelta(hours = int(end_time[0]), minutes = int(end_time[1]), seconds = int(end_time[2])) + shift_end = timedelta(hours = int(end_time[0]), minutes = int(end_time[1]), seconds = int(end_time[2])) shift_start = timedelta(hours =int(start_time[0]), minutes = int(start_time[1]), seconds = int(start_time[2])) if shift_end > shift_start: diff --git a/erpnext/hr/doctype/shift_type/shift_type_dashboard.py b/erpnext/hr/doctype/shift_type/shift_type_dashboard.py index aedd190bcb4..c9d2286b6b6 100644 --- a/erpnext/hr/doctype/shift_type/shift_type_dashboard.py +++ b/erpnext/hr/doctype/shift_type/shift_type_dashboard.py @@ -10,7 +10,10 @@ def get_data(): }, 'transactions': [ { - 'items': ['Attendance', 'Employee Checkin', 'Shift Request', 'Shift Assignment'] + 'items': ['Attendance', 'Employee Checkin'] + }, + { + 'items': ['Shift Request', 'Shift Assignment'] } ] } diff --git a/erpnext/payroll/doctype/gratuity/gratuity.py b/erpnext/payroll/doctype/gratuity/gratuity.py index f085ac90a35..d929fa86263 100644 --- a/erpnext/payroll/doctype/gratuity/gratuity.py +++ b/erpnext/payroll/doctype/gratuity/gratuity.py @@ -235,7 +235,7 @@ 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_structure_assignment = 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"], diff --git a/erpnext/payroll/doctype/overtime_slip/overtime_slip.json b/erpnext/payroll/doctype/overtime_slip/overtime_slip.json index 9f721d39e77..aef230c344d 100644 --- a/erpnext/payroll/doctype/overtime_slip/overtime_slip.json +++ b/erpnext/payroll/doctype/overtime_slip/overtime_slip.json @@ -18,6 +18,7 @@ "to_date", "column_break_10", "payroll_frequency", + "salary_slip", "section_break_12", "overtime_details", "section_break_13", @@ -134,12 +135,20 @@ "fieldtype": "Date", "label": "Posting Date", "reqd": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "salary_slip", + "fieldtype": "Link", + "label": "Salary Slip", + "options": "Salary Slip", + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-10 13:35:57.511257", + "modified": "2021-06-15 12:53:17.355755", "modified_by": "Administrator", "module": "Payroll", "name": "Overtime Slip", diff --git a/erpnext/payroll/doctype/overtime_slip/overtime_slip.py b/erpnext/payroll/doctype/overtime_slip/overtime_slip.py index 3f80db0fbbf..1ced834df9c 100644 --- a/erpnext/payroll/doctype/overtime_slip/overtime_slip.py +++ b/erpnext/payroll/doctype/overtime_slip/overtime_slip.py @@ -1,14 +1,43 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from erpnext.hr.doctype.attendance.attendance import get_overtime_type import frappe -from frappe import _ -from frappe.utils import get_datetime, getdate +from frappe import _, bold +from frappe.utils import get_datetime, getdate, get_link_to_form, formatdate 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 class OvertimeSlip(Document): + def validate(self): + self.validate_overlap() + if self.from_date >= self.to_date: + frappe.throw(_("From date can not be greater than To date")) + + if not (self.from_date or self.to_date or self.payroll_frequency): + date = self.from_date or self.posting_date + get_frequency_and_dates(self.employee, date) + + if not len(self.overtime_details): + self.get_emp_and_overtime_details() + + def validate_overlap(self): + if not self.name: + # hack! if name is null, it could cause problems with != + self.name = "new-overtime-slip-1" + + overtime_slips = frappe.db.get_all("Overtime Slip", filters = { + "docstatus": ("<", 2), + "employee": self.employee, + "to_date": (">=", self.from_date), + "from_date": ("<=", self.to_date) + }) + if len(overtime_slips): + form_link = get_link_to_form("Overtime Slip", overtime_slips[0].name) + msg = _("Overtime Slip:{0} has been created between {1} and {1}").format( + bold(form_link), + bold(formatdate(self.from_date)), bold(formatdate(self.to_date))) + frappe.throw(msg) + def on_submit(self): if self.status == "Pending": frappe.throw(_("Overtime Slip with Status 'Approved' or 'Rejected' are allowed for Submission")) diff --git a/erpnext/payroll/doctype/overtime_type/overtime_type.js b/erpnext/payroll/doctype/overtime_type/overtime_type.js index c1b86db27a9..da1f5bfef8e 100644 --- a/erpnext/payroll/doctype/overtime_type/overtime_type.js +++ b/erpnext/payroll/doctype/overtime_type/overtime_type.js @@ -3,11 +3,11 @@ frappe.ui.form.on('Overtime Type', { setup: function(frm) { - frm.set_query("party_type", () => { - let party_type = ["Employee", "Department", "Employee Grade"]; + frm.set_query("applicable_for", () => { + let doctype_list = ["Employee", "Department", "Employee Grade"]; return { filters: { - name: ["in", party_type] + name: ["in", doctype_list] } }; }); diff --git a/erpnext/payroll/doctype/overtime_type/overtime_type.json b/erpnext/payroll/doctype/overtime_type/overtime_type.json index b74c9f53f3f..a04c1a7f193 100644 --- a/erpnext/payroll/doctype/overtime_type/overtime_type.json +++ b/erpnext/payroll/doctype/overtime_type/overtime_type.json @@ -6,8 +6,10 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "party_type", - "party", + "applicable_for", + "employee", + "department", + "employee_grade", "column_break_3", "applicable_salary_component", "pay_rate_multipliers_section", @@ -19,20 +21,6 @@ "public_holiday_multiplier" ], "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" @@ -41,7 +29,8 @@ "fieldname": "applicable_salary_component", "fieldtype": "Table MultiSelect", "label": "Applicable Salary Component", - "options": "Overtime Salary Component" + "options": "Overtime Salary Component", + "reqd": 1 }, { "description": "Pay Rate Multipliers apply to the hourly wage for the position you\u2019re working during the overtime hours.", @@ -87,11 +76,43 @@ "fieldtype": "Float", "label": "Public Holiday Multiplier", "mandatory_depends_on": "eval: doc.applicable_for_public_holiday == 1" + }, + { + "fieldname": "applicable_for", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Applicable For", + "options": "DocType", + "reqd": 1 + }, + { + "depends_on": "eval: doc.applicable_for == \"Employee\"", + "fieldname": "employee", + "fieldtype": "Link", + "label": "Employee", + "mandatory_depends_on": "eval: doc.applicable_for == \"Employee\"", + "options": "Employee" + }, + { + "depends_on": "eval: doc.applicable_for == \"Department\"", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "mandatory_depends_on": "eval: doc.applicable_for == \"Department\"", + "options": "Department" + }, + { + "depends_on": "eval: doc.applicable_for == \"Employee Grade\"", + "fieldname": "employee_grade", + "fieldtype": "Link", + "label": "Employee Grade", + "mandatory_depends_on": "eval: doc.applicable_for == \"Employee Grade\"", + "options": "Employee Grade" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-06-09 15:43:43.891270", + "modified": "2021-06-15 13:49:20.612464", "modified_by": "Administrator", "module": "Payroll", "name": "Overtime Type", diff --git a/erpnext/payroll/doctype/overtime_type/overtime_type_dashboard.py b/erpnext/payroll/doctype/overtime_type/overtime_type_dashboard.py new file mode 100644 index 00000000000..411b387b2de --- /dev/null +++ b/erpnext/payroll/doctype/overtime_type/overtime_type_dashboard.py @@ -0,0 +1,15 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'overtime_type', + 'transactions': [ + { + 'items': ['Attendance', 'Timesheet'] + }, + { + 'items': ['Overtime Slip'] + } + ] + } \ No newline at end of file diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json index 110c6f74a7e..45b182325e6 100644 --- a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json +++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json @@ -20,10 +20,9 @@ "section_break_12", "overtime_based_on", "maximum_overtime_hours_allowed", - "overtime_salary_component", "column_break_14", - "fetch_standard_working_hours_from_shift_type", - "is_overtime_approval_required" + "overtime_salary_component", + "fetch_standard_working_hours_from_shift_type" ], "fields": [ { @@ -112,6 +111,7 @@ }, { "default": "0", + "depends_on": "eval: doc.overtime_based_on == \"Attendance\"", "description": "If unchecked, Standard Working Hours as defined in HR Settings will be taken into consideration.", "fieldname": "fetch_standard_working_hours_from_shift_type", "fieldtype": "Check", @@ -128,12 +128,6 @@ "label": "Calculate Overtime Hours Based On", "options": "Attendance\nTimesheet" }, - { - "default": "0", - "fieldname": "is_overtime_approval_required", - "fieldtype": "Check", - "label": "Is Overtime Approval Required" - }, { "description": "Overtime payment will not be given for more than the defined hours limit.", "fieldname": "maximum_overtime_hours_allowed", @@ -145,7 +139,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-05-26 15:56:25.313007", + "modified": "2021-06-15 13:55:44.563796", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll Settings", diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json index 42a0f290cb4..c82cf03ca08 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -423,7 +423,7 @@ { "fieldname": "net_pay_info", "fieldtype": "Section Break", - "label": "net pay info" + "label": "Net Pay Info" }, { "fieldname": "net_pay", @@ -631,7 +631,7 @@ "idx": 9, "is_submittable": 1, "links": [], - "modified": "2021-03-31 22:44:09.772331", + "modified": "2021-06-15 12:32:01.369615", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Slip", diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 66074507d4b..32aaff2a04d 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -82,8 +82,23 @@ class SalarySlip(TransactionBase): if (frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry: self.email_salary_slip() + self.update_overtime_slip() self.update_payment_status_for_gratuity() + def update_overtime_slip(self): + overtime_slips = [] + for data in self.earnings: + if data.overtime_slips: + overtime_slips.extend(data.overtime_slips.split(", ")) + + if self.docstatus == 1: + for slip in overtime_slips: + frappe.db.set_value("Overtime Slip", slip, "salary_slip", self.name) + + if self.docstatus == 2: + for slip in overtime_slips: + frappe.db.set_value("Overtime Slip", slip, "salary_slip", None) + def update_payment_status_for_gratuity(self): add_salary = frappe.db.get_all("Additional Salary", filters = { @@ -101,6 +116,7 @@ class SalarySlip(TransactionBase): def on_cancel(self): self.set_status() self.update_status() + self.update_overtime_slip self.update_payment_status_for_gratuity() self.cancel_loan_repayment_entry() @@ -513,7 +529,7 @@ class SalarySlip(TransactionBase): def process_overtime_slips(self): overtime_slips = self.get_overtime_slips() - amounts, processed_overtime_slips = self.get_overtime_amount(overtime_slips) + amounts, processed_overtime_slips = self.get_overtime_amount(overtime_slips) self.add_overtime_component(amounts, processed_overtime_slips) def get_overtime_slips(self): @@ -521,11 +537,14 @@ class SalarySlip(TransactionBase): 'employee': self.employee, 'posting_date': (">=", self.start_date), 'posting_date': ("<=", self.end_date), + 'salary_slip': '', 'docstatus': 1 }, fields = ["name", "from_date", 'to_date']) def get_overtime_amount(self, overtime_slips): - standard_duration_amount = 0; weekends_duration_amount= 0; public_holidays_duration_amount = 0 + standard_duration_amount = 0 + weekends_duration_amount= 0 + public_holidays_duration_amount = 0 calculated_amount = 0 processed_overtime_slips = [] overtime_types_details = {} @@ -541,7 +560,7 @@ class SalarySlip(TransactionBase): for detail in details: overtime_hours = detail.overtime_duration / 3600 - if not detail.overtime_type in overtime_types_details: + if detail.overtime_type not in overtime_types_details: details, applicable_components = self.get_overtime_type_detail(detail.overtime_type) overtime_types_details[detail.overtime_type] = details if len(applicable_components): @@ -551,24 +570,28 @@ class SalarySlip(TransactionBase): frappe.bold(detail.overtime_type))) if "applicable_amount" not in overtime_types_details[detail.overtime_type].keys(): - component_amount = sum([data.default_amount for data in self.earnings \ - if data.salary_component in overtime_types_details[detail.overtime_type]["components"] \ - and not data.get('additional_salary', None)]) + component_amount = sum([data.default_amount for data in self.earnings if data.salary_component in \ + overtime_types_details[detail.overtime_type]["components"] and not data.get('additional_salary', None)]) overtime_types_details[detail.overtime_type]["applicable_daily_amount"] = component_amount/self.total_working_days standard_working_hours = detail.standard_working_time/3600 applicable_hourly_wages = overtime_types_details[detail.overtime_type]["applicable_daily_amount"]/standard_working_hours + weekend_multiplier = overtime_types_details[detail.overtime_type]['standard_multiplier'] + public_holiday_multiplier = overtime_types_details[detail.overtime_type]['standard_multiplier'] + + if overtime_types_details[detail.overtime_type]['applicable_for_weekend']: + weekend_multiplier = overtime_types_details[detail.overtime_type]['weekend_multiplier'] + if overtime_types_details[detail.overtime_type]['applicable_for_public_holiday']: + public_holiday_multiplier = overtime_types_details[detail.overtime_type]['public_holiday_multiplier'] overtime_date = cstr(detail.date) if overtime_date in holiday_date_map.keys(): if holiday_date_map[overtime_date].weekly_off == 1: - calculated_amount = overtime_hours * applicable_hourly_wages *\ - overtime_types_details[detail.overtime_type]['weekend_multiplier'] + calculated_amount = overtime_hours * applicable_hourly_wages * weekend_multiplier weekends_duration_amount += calculated_amount elif holiday_date_map[overtime_date].weekly_off == 0: - calculated_amount = overtime_hours * applicable_hourly_wages *\ - overtime_types_details[detail.overtime_type]['public_holiday_multiplier'] + calculated_amount = overtime_hours * applicable_hourly_wages * public_holiday_multiplier public_holidays_duration_amount += calculated_amount else: calculated_amount = overtime_hours * applicable_hourly_wages *\ @@ -578,6 +601,7 @@ class SalarySlip(TransactionBase): processed_overtime_slips.append(slip.name) return [weekends_duration_amount, public_holidays_duration_amount, standard_duration_amount] , processed_overtime_slips + def add_overtime_component(self, amounts, processed_overtime_slips): if len(amounts): overtime_salary_component = frappe.db.get_single_value("Payroll Settings", "overtime_salary_component") @@ -602,7 +626,11 @@ class SalarySlip(TransactionBase): ) def get_overtime_type_detail(self, name): - detail = frappe.get_all("Overtime Type", filters = {"name": name}, fields = ["name", "standard_multiplier", "weekend_multiplier", "public_holiday_multiplier"])[0] + detail = frappe.get_all("Overtime Type", + filters = {"name": name}, + fields = ["name", "standard_multiplier", "weekend_multiplier", "public_holiday_multiplier", + "applicable_for_weekend", "applicable_for_public_holiday"] + )[0] components = frappe.get_all("Overtime Salary Component", filters = {"parent": name}, fields = ["salary_component"]) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip_dashboard.py b/erpnext/payroll/doctype/salary_slip/salary_slip_dashboard.py new file mode 100644 index 00000000000..7569a381458 --- /dev/null +++ b/erpnext/payroll/doctype/salary_slip/salary_slip_dashboard.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'salary_slip', + 'transactions': [ + { + 'items': ['Overtime Slip'] + } + ] + } \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/operations/defaults_setup.py b/erpnext/setup/setup_wizard/operations/defaults_setup.py index 76484753c3a..b4c0ba45469 100644 --- a/erpnext/setup/setup_wizard/operations/defaults_setup.py +++ b/erpnext/setup/setup_wizard/operations/defaults_setup.py @@ -67,7 +67,7 @@ def set_default_settings(args): payroll_settings = frappe.get_doc("Payroll Settings") payroll_settings.overtime_based_on = "Attendance" - Payroll_settings.overtime_salary_component = _("Overtime Allowance") + payroll_settings.overtime_salary_component = _("Overtime Allowance") payroll_settings.save() def set_no_copy_fields_in_variant_settings(): diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 5789081b91f..f9e4be1c15a 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -320,7 +320,7 @@ def update_hr_defaults(): def update_payroll_defaults(): payroll_settings = frappe.get_doc("Payroll Settings") payroll_settings.overtime_based_on = "Attendance" - Payroll_settings.overtime_salary_component = _("Overtime Allowance") + payroll_settings.overtime_salary_component = _("Overtime Allowance") payroll_settings.save()