mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-13 10:11:20 +00:00
feat: Validation, sider and form dashbord
This commit is contained in:
@@ -45,15 +45,21 @@ frappe.ui.form.on('Attendance', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
set_overtime_type: function(frm) {
|
set_overtime_type: function(frm) {
|
||||||
frappe.call({
|
frappe.db.get_single_value("Payroll Settings", "overtime_based_on").then((r)=>{
|
||||||
method: "erpnext.hr.doctype.attendance.attendance.get_overtime_type",
|
if (r == "Attendance") {
|
||||||
args: {
|
frappe.call({
|
||||||
employee: frm.doc.employee,
|
method: "erpnext.hr.doctype.attendance.attendance.get_overtime_type",
|
||||||
},
|
args: {
|
||||||
callback: function(r) {
|
employee: frm.doc.employee,
|
||||||
if (r.message) {
|
},
|
||||||
frm.set_value("overtime_type", r.message);
|
callback: function(r) {
|
||||||
}
|
if (r.message) {
|
||||||
|
frm.set_value("overtime_type", r.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frm.set_value("overtime_type", '');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,11 +4,10 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from frappe.utils import getdate, nowdate
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.hr.utils import validate_active_employee
|
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):
|
class Attendance(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@@ -58,7 +57,7 @@ class Attendance(Document):
|
|||||||
self.overtime_type = get_overtime_type(self.employee)
|
self.overtime_type = get_overtime_type(self.employee)
|
||||||
|
|
||||||
if self.overtime_type:
|
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'))
|
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")
|
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()
|
@frappe.whitelist()
|
||||||
def get_overtime_type(employee):
|
def get_overtime_type(employee):
|
||||||
emp_department = frappe.db.get_value("Employee", employee, "department")
|
emp_department = frappe.db.get_value("Employee", employee, "department")
|
||||||
if emp_department:
|
if emp_department:
|
||||||
overtime_type = frappe.get_list("Overtime Type", filters={"party_type": "Department", "party": emp_department}, fields=['name'])
|
overtime_type = frappe.get_list("Overtime Type", filters={
|
||||||
if len(overtime_type):
|
"applicable_for": "Department", "department": emp_department}, fields=['name'])
|
||||||
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'])
|
|
||||||
if len(overtime_type):
|
if len(overtime_type):
|
||||||
overtime_type = overtime_type[0].name
|
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()
|
@frappe.whitelist()
|
||||||
def get_events(start, end, filters=None):
|
def get_events(start, end, filters=None):
|
||||||
|
|||||||
@@ -42,11 +42,11 @@ class EmployeeCheckin(Document):
|
|||||||
self.shift_start = shift_actual_timings[2].start_datetime
|
self.shift_start = shift_actual_timings[2].start_datetime
|
||||||
self.shift_end = shift_actual_timings[2].end_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"):
|
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
|
#because after Actual time it takes check-in/out invalid
|
||||||
#if employee checkout late or check-in before before shift timing adding time buffer.
|
#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 = shift_actual_timings[2].shift_type.name
|
||||||
self.shift_start = shift_actual_timings[2].start_datetime
|
self.shift_start = shift_actual_timings[2].start_datetime
|
||||||
self.shift_end = shift_actual_timings[2].end_datetime
|
self.shift_end = shift_actual_timings[2].end_datetime
|
||||||
|
|
||||||
@frappe.whitelist()
|
@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'):
|
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'):
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
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.employee.employee import get_holiday_list_for_employee
|
||||||
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
|
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
|
||||||
from erpnext.hr.utils import validate_active_employee
|
from erpnext.hr.utils import validate_active_employee
|
||||||
@@ -61,12 +61,12 @@ class ShiftAssignment(Document):
|
|||||||
def throw_overlap_error(self, shift_details):
|
def throw_overlap_error(self, shift_details):
|
||||||
shift_details = frappe._dict(shift_details)
|
shift_details = frappe._dict(shift_details)
|
||||||
if shift_details.docstatus == 1 and shift_details.status == "Active":
|
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:
|
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"
|
title = "Ongoing Shift"
|
||||||
if shift_details.end_date:
|
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"
|
title = "Active Shift"
|
||||||
if msg:
|
if msg:
|
||||||
frappe.throw(msg, title=title)
|
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:
|
if timestamp and for_datetime <= timestamp:
|
||||||
timestamp_index = index
|
timestamp_index = index
|
||||||
break
|
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)]
|
shift_details = shift_timings_as_per_timestamp[int((timestamp_index-1)/2)]
|
||||||
actual_shift_start = shift_details.actual_start
|
actual_shift_start = shift_details.actual_start
|
||||||
actual_shift_end = shift_details.actual_end
|
actual_shift_end = shift_details.actual_end
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"column_break_19",
|
"column_break_19",
|
||||||
"process_attendance_after",
|
"process_attendance_after",
|
||||||
"last_sync_of_checkin",
|
"last_sync_of_checkin",
|
||||||
"grace_period_settings_auto_attendance_section",
|
"grace_period_settings_section",
|
||||||
"enable_entry_grace_period",
|
"enable_entry_grace_period",
|
||||||
"late_entry_grace_period",
|
"late_entry_grace_period",
|
||||||
"column_break_18",
|
"column_break_18",
|
||||||
@@ -141,12 +141,6 @@
|
|||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Auto Attendance Settings"
|
"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",
|
"default": "0",
|
||||||
"description": "Mark attendance based on 'Employee Checkin' for Employees assigned to this shift.",
|
"description": "Mark attendance based on 'Employee Checkin' for Employees assigned to this shift.",
|
||||||
@@ -188,10 +182,16 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_19",
|
"fieldname": "column_break_19",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "enable_auto_attendance",
|
||||||
|
"fieldname": "grace_period_settings_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Grace Period Settings"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-06-09 13:38:25.697100",
|
"modified": "2021-06-15 15:22:55.756078",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Shift Type",
|
"name": "Shift Type",
|
||||||
|
|||||||
@@ -4,11 +4,8 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import itertools
|
import itertools
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from math import modf
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import cint, getdate, get_datetime
|
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
|
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(":")
|
end_time = self.end_time.split(":")
|
||||||
start_time = self.start_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]))
|
shift_start = timedelta(hours =int(start_time[0]), minutes = int(start_time[1]), seconds = int(start_time[2]))
|
||||||
|
|
||||||
if shift_end > shift_start:
|
if shift_end > shift_start:
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ def get_data():
|
|||||||
},
|
},
|
||||||
'transactions': [
|
'transactions': [
|
||||||
{
|
{
|
||||||
'items': ['Attendance', 'Employee Checkin', 'Shift Request', 'Shift Assignment']
|
'items': ['Attendance', 'Employee Checkin']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'items': ['Shift Request', 'Shift Assignment']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
return frappe.get_all("Gratuity Rule Slab", filters= {'parent': gratuity_rule}, fields = ["*"], order_by="idx")
|
||||||
|
|
||||||
def get_salary_structure(employee):
|
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
|
"employee": employee, 'docstatus': 1
|
||||||
},
|
},
|
||||||
fields=["from_date", "salary_structure"],
|
fields=["from_date", "salary_structure"],
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"to_date",
|
"to_date",
|
||||||
"column_break_10",
|
"column_break_10",
|
||||||
"payroll_frequency",
|
"payroll_frequency",
|
||||||
|
"salary_slip",
|
||||||
"section_break_12",
|
"section_break_12",
|
||||||
"overtime_details",
|
"overtime_details",
|
||||||
"section_break_13",
|
"section_break_13",
|
||||||
@@ -134,12 +135,20 @@
|
|||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Posting Date",
|
"label": "Posting Date",
|
||||||
"reqd": 1
|
"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,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-06-10 13:35:57.511257",
|
"modified": "2021-06-15 12:53:17.355755",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Overtime Slip",
|
"name": "Overtime Slip",
|
||||||
|
|||||||
@@ -1,14 +1,43 @@
|
|||||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from erpnext.hr.doctype.attendance.attendance import get_overtime_type
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _, bold
|
||||||
from frappe.utils import get_datetime, getdate
|
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.payroll_entry.payroll_entry import get_start_end_dates
|
||||||
from erpnext.payroll.doctype.gratuity.gratuity import get_salary_structure
|
from erpnext.payroll.doctype.gratuity.gratuity import get_salary_structure
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
class OvertimeSlip(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):
|
def on_submit(self):
|
||||||
if self.status == "Pending":
|
if self.status == "Pending":
|
||||||
frappe.throw(_("Overtime Slip with Status 'Approved' or 'Rejected' are allowed for Submission"))
|
frappe.throw(_("Overtime Slip with Status 'Approved' or 'Rejected' are allowed for Submission"))
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Overtime Type', {
|
frappe.ui.form.on('Overtime Type', {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.set_query("party_type", () => {
|
frm.set_query("applicable_for", () => {
|
||||||
let party_type = ["Employee", "Department", "Employee Grade"];
|
let doctype_list = ["Employee", "Department", "Employee Grade"];
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
name: ["in", party_type]
|
name: ["in", doctype_list]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,8 +6,10 @@
|
|||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"party_type",
|
"applicable_for",
|
||||||
"party",
|
"employee",
|
||||||
|
"department",
|
||||||
|
"employee_grade",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
"applicable_salary_component",
|
"applicable_salary_component",
|
||||||
"pay_rate_multipliers_section",
|
"pay_rate_multipliers_section",
|
||||||
@@ -19,20 +21,6 @@
|
|||||||
"public_holiday_multiplier"
|
"public_holiday_multiplier"
|
||||||
],
|
],
|
||||||
"fields": [
|
"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",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
@@ -41,7 +29,8 @@
|
|||||||
"fieldname": "applicable_salary_component",
|
"fieldname": "applicable_salary_component",
|
||||||
"fieldtype": "Table MultiSelect",
|
"fieldtype": "Table MultiSelect",
|
||||||
"label": "Applicable Salary Component",
|
"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.",
|
"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",
|
"fieldtype": "Float",
|
||||||
"label": "Public Holiday Multiplier",
|
"label": "Public Holiday Multiplier",
|
||||||
"mandatory_depends_on": "eval: doc.applicable_for_public_holiday == 1"
|
"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,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-06-09 15:43:43.891270",
|
"modified": "2021-06-15 13:49:20.612464",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Overtime Type",
|
"name": "Overtime Type",
|
||||||
|
|||||||
@@ -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']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -20,10 +20,9 @@
|
|||||||
"section_break_12",
|
"section_break_12",
|
||||||
"overtime_based_on",
|
"overtime_based_on",
|
||||||
"maximum_overtime_hours_allowed",
|
"maximum_overtime_hours_allowed",
|
||||||
"overtime_salary_component",
|
|
||||||
"column_break_14",
|
"column_break_14",
|
||||||
"fetch_standard_working_hours_from_shift_type",
|
"overtime_salary_component",
|
||||||
"is_overtime_approval_required"
|
"fetch_standard_working_hours_from_shift_type"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -112,6 +111,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"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.",
|
"description": "If unchecked, Standard Working Hours as defined in HR Settings will be taken into consideration.",
|
||||||
"fieldname": "fetch_standard_working_hours_from_shift_type",
|
"fieldname": "fetch_standard_working_hours_from_shift_type",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
@@ -128,12 +128,6 @@
|
|||||||
"label": "Calculate Overtime Hours Based On",
|
"label": "Calculate Overtime Hours Based On",
|
||||||
"options": "Attendance\nTimesheet"
|
"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.",
|
"description": "Overtime payment will not be given for more than the defined hours limit.",
|
||||||
"fieldname": "maximum_overtime_hours_allowed",
|
"fieldname": "maximum_overtime_hours_allowed",
|
||||||
@@ -145,7 +139,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-05-26 15:56:25.313007",
|
"modified": "2021-06-15 13:55:44.563796",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Payroll Settings",
|
"name": "Payroll Settings",
|
||||||
|
|||||||
@@ -423,7 +423,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "net_pay_info",
|
"fieldname": "net_pay_info",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "net pay info"
|
"label": "Net Pay Info"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "net_pay",
|
"fieldname": "net_pay",
|
||||||
@@ -631,7 +631,7 @@
|
|||||||
"idx": 9,
|
"idx": 9,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 22:44:09.772331",
|
"modified": "2021-06-15 12:32:01.369615",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Slip",
|
"name": "Salary Slip",
|
||||||
|
|||||||
@@ -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:
|
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.email_salary_slip()
|
||||||
|
|
||||||
|
self.update_overtime_slip()
|
||||||
self.update_payment_status_for_gratuity()
|
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):
|
def update_payment_status_for_gratuity(self):
|
||||||
add_salary = frappe.db.get_all("Additional Salary",
|
add_salary = frappe.db.get_all("Additional Salary",
|
||||||
filters = {
|
filters = {
|
||||||
@@ -101,6 +116,7 @@ class SalarySlip(TransactionBase):
|
|||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.update_status()
|
self.update_status()
|
||||||
|
self.update_overtime_slip
|
||||||
self.update_payment_status_for_gratuity()
|
self.update_payment_status_for_gratuity()
|
||||||
self.cancel_loan_repayment_entry()
|
self.cancel_loan_repayment_entry()
|
||||||
|
|
||||||
@@ -513,7 +529,7 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
def process_overtime_slips(self):
|
def process_overtime_slips(self):
|
||||||
overtime_slips = self.get_overtime_slips()
|
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)
|
self.add_overtime_component(amounts, processed_overtime_slips)
|
||||||
|
|
||||||
def get_overtime_slips(self):
|
def get_overtime_slips(self):
|
||||||
@@ -521,11 +537,14 @@ class SalarySlip(TransactionBase):
|
|||||||
'employee': self.employee,
|
'employee': self.employee,
|
||||||
'posting_date': (">=", self.start_date),
|
'posting_date': (">=", self.start_date),
|
||||||
'posting_date': ("<=", self.end_date),
|
'posting_date': ("<=", self.end_date),
|
||||||
|
'salary_slip': '',
|
||||||
'docstatus': 1
|
'docstatus': 1
|
||||||
}, fields = ["name", "from_date", 'to_date'])
|
}, fields = ["name", "from_date", 'to_date'])
|
||||||
|
|
||||||
def get_overtime_amount(self, overtime_slips):
|
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
|
calculated_amount = 0
|
||||||
processed_overtime_slips = []
|
processed_overtime_slips = []
|
||||||
overtime_types_details = {}
|
overtime_types_details = {}
|
||||||
@@ -541,7 +560,7 @@ class SalarySlip(TransactionBase):
|
|||||||
for detail in details:
|
for detail in details:
|
||||||
overtime_hours = detail.overtime_duration / 3600
|
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)
|
details, applicable_components = self.get_overtime_type_detail(detail.overtime_type)
|
||||||
overtime_types_details[detail.overtime_type] = details
|
overtime_types_details[detail.overtime_type] = details
|
||||||
if len(applicable_components):
|
if len(applicable_components):
|
||||||
@@ -551,24 +570,28 @@ class SalarySlip(TransactionBase):
|
|||||||
frappe.bold(detail.overtime_type)))
|
frappe.bold(detail.overtime_type)))
|
||||||
|
|
||||||
if "applicable_amount" not in overtime_types_details[detail.overtime_type].keys():
|
if "applicable_amount" not in overtime_types_details[detail.overtime_type].keys():
|
||||||
component_amount = sum([data.default_amount for data in self.earnings \
|
component_amount = sum([data.default_amount for data in self.earnings if data.salary_component in \
|
||||||
if data.salary_component in overtime_types_details[detail.overtime_type]["components"] \
|
overtime_types_details[detail.overtime_type]["components"] and not data.get('additional_salary', None)])
|
||||||
and not data.get('additional_salary', None)])
|
|
||||||
|
|
||||||
overtime_types_details[detail.overtime_type]["applicable_daily_amount"] = component_amount/self.total_working_days
|
overtime_types_details[detail.overtime_type]["applicable_daily_amount"] = component_amount/self.total_working_days
|
||||||
|
|
||||||
standard_working_hours = detail.standard_working_time/3600
|
standard_working_hours = detail.standard_working_time/3600
|
||||||
applicable_hourly_wages = overtime_types_details[detail.overtime_type]["applicable_daily_amount"]/standard_working_hours
|
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)
|
overtime_date = cstr(detail.date)
|
||||||
if overtime_date in holiday_date_map.keys():
|
if overtime_date in holiday_date_map.keys():
|
||||||
if holiday_date_map[overtime_date].weekly_off == 1:
|
if holiday_date_map[overtime_date].weekly_off == 1:
|
||||||
calculated_amount = overtime_hours * applicable_hourly_wages *\
|
calculated_amount = overtime_hours * applicable_hourly_wages * weekend_multiplier
|
||||||
overtime_types_details[detail.overtime_type]['weekend_multiplier']
|
|
||||||
weekends_duration_amount += calculated_amount
|
weekends_duration_amount += calculated_amount
|
||||||
elif holiday_date_map[overtime_date].weekly_off == 0:
|
elif holiday_date_map[overtime_date].weekly_off == 0:
|
||||||
calculated_amount = overtime_hours * applicable_hourly_wages *\
|
calculated_amount = overtime_hours * applicable_hourly_wages * public_holiday_multiplier
|
||||||
overtime_types_details[detail.overtime_type]['public_holiday_multiplier']
|
|
||||||
public_holidays_duration_amount += calculated_amount
|
public_holidays_duration_amount += calculated_amount
|
||||||
else:
|
else:
|
||||||
calculated_amount = overtime_hours * applicable_hourly_wages *\
|
calculated_amount = overtime_hours * applicable_hourly_wages *\
|
||||||
@@ -578,6 +601,7 @@ class SalarySlip(TransactionBase):
|
|||||||
processed_overtime_slips.append(slip.name)
|
processed_overtime_slips.append(slip.name)
|
||||||
|
|
||||||
return [weekends_duration_amount, public_holidays_duration_amount, standard_duration_amount] , processed_overtime_slips
|
return [weekends_duration_amount, public_holidays_duration_amount, standard_duration_amount] , processed_overtime_slips
|
||||||
|
|
||||||
def add_overtime_component(self, amounts, processed_overtime_slips):
|
def add_overtime_component(self, amounts, processed_overtime_slips):
|
||||||
if len(amounts):
|
if len(amounts):
|
||||||
overtime_salary_component = frappe.db.get_single_value("Payroll Settings", "overtime_salary_component")
|
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):
|
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",
|
components = frappe.get_all("Overtime Salary Component",
|
||||||
filters = {"parent": name}, fields = ["salary_component"])
|
filters = {"parent": name}, fields = ["salary_component"])
|
||||||
|
|
||||||
|
|||||||
12
erpnext/payroll/doctype/salary_slip/salary_slip_dashboard.py
Normal file
12
erpnext/payroll/doctype/salary_slip/salary_slip_dashboard.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'salary_slip',
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'items': ['Overtime Slip']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -67,7 +67,7 @@ def set_default_settings(args):
|
|||||||
|
|
||||||
payroll_settings = frappe.get_doc("Payroll Settings")
|
payroll_settings = frappe.get_doc("Payroll Settings")
|
||||||
payroll_settings.overtime_based_on = "Attendance"
|
payroll_settings.overtime_based_on = "Attendance"
|
||||||
Payroll_settings.overtime_salary_component = _("Overtime Allowance")
|
payroll_settings.overtime_salary_component = _("Overtime Allowance")
|
||||||
payroll_settings.save()
|
payroll_settings.save()
|
||||||
|
|
||||||
def set_no_copy_fields_in_variant_settings():
|
def set_no_copy_fields_in_variant_settings():
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ def update_hr_defaults():
|
|||||||
def update_payroll_defaults():
|
def update_payroll_defaults():
|
||||||
payroll_settings = frappe.get_doc("Payroll Settings")
|
payroll_settings = frappe.get_doc("Payroll Settings")
|
||||||
payroll_settings.overtime_based_on = "Attendance"
|
payroll_settings.overtime_based_on = "Attendance"
|
||||||
Payroll_settings.overtime_salary_component = _("Overtime Allowance")
|
payroll_settings.overtime_salary_component = _("Overtime Allowance")
|
||||||
payroll_settings.save()
|
payroll_settings.save()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user