[Payroll Entry] Error while creating salary slips

I have around 6000 employees and when I am creating the payroll entry and creating the salary slips, it starts executing but after some time it gives this error:

rq.timeouts.JobTimeoutException: Task exceeded maximum timeout value (300 seconds)

Here is the full error message:

Traceback with variables (most recent call last): File "apps/hrms/hrms/payroll/doctype/payroll_entry/payroll_entry.py", line 1063, in create_salary_slips_for_employees frappe.get_doc(args).insert() employees = ['20860', '20859', '20858', '20851', '20855', '20825', '20789', '20849', '20848', '20845', '20826', '20846', '20844', '20828', '20838', '20856', '20827', '20814', '20787', '20786', '20839', '20745', '20830', '20785', '20747', '20843', '20842', '20748', '20797', '20815', '20852', '20723', '20722', '20721', '20720', '20784', '20746', '20841', '20813', '20783', '20729', '20824', '20725', '20823', '20740', '20782', '20726', '20724', '20822', '20821', '20727', '20820', '20743', '20792', '20853', '20742', '20796', '20708', '20741', '20781', '20719', '20732', '20780', '20819', '20817', '20707', '20794', '20818', '20778', '20777', '20733', '20776', '20737', '20775', '20710', '20774', '20779', '20812', '20767', '20713', '20811', '20734', '20772', '20718', '20816', '20766', '20771', '20717', '20749', '20798', '20770', '20769', '20768', '20714', '20735', '20773', '20810', '20765', '20809', '20716', '20764', '20808', '20791', '20763', '20807', '20806', '20762', '20751', '20800', '20756', '20805', ... args = {'salary_slip_based_on_timesheet': 0, 'payroll_frequency': 'Monthly', 'start_date': '2023-06-01', 'end_date': '2023-06-30', 'company': 'Jadeed Feeds Industries (Pvt) Ltd.', 'posting_date': '2023-06-30', 'deduct_tax_for_unclaimed_employee_benefits': 0, 'deduct_tax_for_unsubmitted_tax_exemption_proof': 0, 'payroll_entry': 'HR-PRUN-2023-00079', 'exchange_rate': 1, 'currency': 'PKR', 'doctype': 'Salary Slip', 'employee': '08149'} publish_progress = False payroll_entry = <PayrollEntry: HR-PRUN-2023-00079> salary_slips_exist_for = [] count = 3207 emp = '08149' e = JobTimeoutException('Task exceeded maximum timeout value (5000 seconds)') File "apps/frappe/frappe/model/document.py", line 259, in insert self.run_before_save_methods() self = <SalarySlip: Sal Slip/08149/00015> ignore_permissions = None ignore_links = None ignore_if_duplicate = False ignore_mandatory = None set_name = None set_child_names = True File "apps/frappe/frappe/model/document.py", line 1040, in run_before_save_methods self.run_method("validate") self = <SalarySlip: Sal Slip/08149/00015> File "apps/frappe/frappe/model/document.py", line 909, in run_method out = Document.hook(fn)(self, *args, **kwargs) self = <SalarySlip: Sal Slip/08149/00015> args = () kwargs = {} fn = <function Document.run_method.<locals>.fn at 0x7ff821353250> method = 'validate' File "apps/frappe/frappe/model/document.py", line 1259, in composer return composed(self, method, *args, **kwargs) self = <SalarySlip: Sal Slip/08149/00015> args = () kwargs = {} hooks = [<function apply at 0x7ff821bc7010>] method = 'validate' doc_events = {'*': {'after_insert': ['frappe.event_streaming.doctype.event_update_log.event_update_log.notify_consumers'], 'on_update': ['frappe.desk.notifications.clear_doctype_notifications', 'frappe.core.doctype.activity_log.feed.update_feed', 'frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions', 'frappe.automation.doctype.assignment_rule.assignment_rule.apply', 'frappe.core.doctype.file.utils.attach_files_to_document', 'frappe.event_streaming.doctype.event_update_log.event_update_log.notify_consumers', 'frappe.automation.doctype.assignment_rule.assignment_rule.update_due_date', 'frappe.core.doctype.user_type.user_type.apply_permissions_for_non_standard_user_type'], 'after_rename': ['frappe.desk.notifications.clear_doctype_notifications'], 'on_cancel': ['frappe.desk.notifications.clear_doctype_notifications', 'frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions', 'frappe.event_streaming.doctype.event_update_log.event_update_log.notify_co... handler = 'erpnext.support.doctype.service_level_agreement.service_level_agreement.apply' composed = <function Document.hook.<locals>.compose.<locals>.runner at 0x7ff81fba28c0> compose = <function Document.hook.<locals>.compose at 0x7ff81fba01f0> f = <function Document.run_method.<locals>.fn at 0x7ff821353250> File "apps/frappe/frappe/model/document.py", line 1241, in runner add_to_return_value(self, fn(self, *args, **kwargs)) self = <SalarySlip: Sal Slip/08149/00015> method = 'validate' args = () kwargs = {} add_to_return_value = <function Document.hook.<locals>.add_to_return_value at 0x7ff81fb4ad40> fn = <function Document.run_method.<locals>.fn at 0x7ff821353250> hooks = (<function apply at 0x7ff821bc7010>,) File "apps/frappe/frappe/model/document.py", line 906, in fn return method_object(*args, **kwargs) self = <SalarySlip: Sal Slip/08149/00015> args = () kwargs = {} method_object = <bound method SalarySlip.validate of <SalarySlip: Sal Slip/08149/00015>> method = 'validate' File "apps/hrms/hrms/payroll/doctype/salary_slip/salary_slip.py", line 92, in validate self.get_emp_and_working_day_details() self = <SalarySlip: Sal Slip/08149/00015> File "apps/hrms/hrms/payroll/doctype/salary_slip/salary_slip.py", line 258, in get_emp_and_working_day_details self.pull_sal_struct() self = <SalarySlip: Sal Slip/08149/00015> joining_date = datetime.date(2019, 4, 3) relieving_date = None struct = 'Jadeed Feeds Industries (Pvt) Ltd.' File "apps/hrms/hrms/payroll/doctype/salary_slip/salary_slip.py", line 338, in pull_sal_struct make_salary_slip(self._salary_structure_doc.name, self) self = <SalarySlip: Sal Slip/08149/00015> make_salary_slip = <function make_salary_slip at 0x7ff821bd1630> File "apps/hrms/hrms/payroll/doctype/salary_structure/salary_structure.py", line 325, in make_salary_slip doc = get_mapped_doc( source_name = 'Jadeed Feeds Industries (Pvt) Ltd.' target_doc = <SalarySlip: Sal Slip/08149/00015> as_print = False print_format = None ignore_permissions = False postprocess = <function make_salary_slip.<locals>.postprocess at 0x7ff81fba2950> employee = None for_preview = 0 posting_date = None File "apps/frappe/frappe/model/mapper.py", line 144, in get_mapped_doc postprocess(source_doc, target_doc) from_doctype = 'Salary Structure' from_docname = 'Jadeed Feeds Industries (Pvt) Ltd.' table_maps = {'Salary Structure': {'doctype': 'Salary Slip', 'field_map': {'total_earning': 'gross_pay', 'name': 'salary_structure', 'currency': 'currency'}}} target_doc = <SalarySlip: Sal Slip/08149/00015> postprocess = <function make_salary_slip.<locals>.postprocess at 0x7ff81fba2950> ignore_permissions = False ignore_child_tables = True apply_strict_user_permissions = 1 source_doc = <SalaryStructure: Jadeed Feeds Industries (Pvt) Ltd. docstatus=1> row_exists_for_parentfield = {} File "apps/hrms/hrms/payroll/doctype/salary_structure/salary_structure.py", line 323, in postprocess target.run_method("process_salary_structure", for_preview=for_preview) source = <SalaryStructure: Jadeed Feeds Industries (Pvt) Ltd. docstatus=1> target = <SalarySlip: Sal Slip/08149/00015> employee = None for_preview = 0 posting_date = None File "apps/frappe/frappe/model/document.py", line 909, in run_method out = Document.hook(fn)(self, *args, **kwargs) self = <SalarySlip: Sal Slip/08149/00015> args = () kwargs = {'for_preview': 0} fn = <function Document.run_method.<locals>.fn at 0x7ff820a275b0> method = 'process_salary_structure' File "apps/frappe/frappe/model/document.py", line 1259, in composer return composed(self, method, *args, **kwargs) self = <SalarySlip: Sal Slip/08149/00015> args = () kwargs = {'for_preview': 0} hooks = [] method = 'process_salary_structure' doc_events = {'*': {'after_insert': ['frappe.event_streaming.doctype.event_update_log.event_update_log.notify_consumers'], 'on_update': ['frappe.desk.notifications.clear_doctype_notifications', 'frappe.core.doctype.activity_log.feed.update_feed', 'frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions', 'frappe.automation.doctype.assignment_rule.assignment_rule.apply', 'frappe.core.doctype.file.utils.attach_files_to_document', 'frappe.event_streaming.doctype.event_update_log.event_update_log.notify_consumers', 'frappe.automation.doctype.assignment_rule.assignment_rule.update_due_date', 'frappe.core.doctype.user_type.user_type.apply_permissions_for_non_standard_user_type'], 'after_rename': ['frappe.desk.notifications.clear_doctype_notifications'], 'on_cancel': ['frappe.desk.notifications.clear_doctype_notifications', 'frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions', 'frappe.event_streaming.doctype.event_update_log.event_update_log.notify_co... composed = <function Document.hook.<locals>.compose.<locals>.runner at 0x7ff821353400> compose = <function Document.hook.<locals>.compose at 0x7ff8213516c0> f = <function Document.run_method.<locals>.fn at 0x7ff820a275b0> File "apps/frappe/frappe/model/document.py", line 1241, in runner add_to_return_value(self, fn(self, *args, **kwargs)) self = <SalarySlip: Sal Slip/08149/00015> method = 'process_salary_structure' args = () kwargs = {'for_preview': 0} add_to_return_value = <function Document.hook.<locals>.add_to_return_value at 0x7ff821350310> fn = <function Document.run_method.<locals>.fn at 0x7ff820a275b0> hooks = () File "apps/frappe/frappe/model/document.py", line 906, in fn return method_object(*args, **kwargs) self = <SalarySlip: Sal Slip/08149/00015> args = () kwargs = {'for_preview': 0} method_object = <bound method SalarySlip.process_salary_structure of <SalarySlip: Sal Slip/08149/00015>> method = 'process_salary_structure' File "apps/hrms/hrms/payroll/doctype/salary_slip/salary_slip.py", line 1880, in process_salary_structure self.calculate_net_pay() self = <SalarySlip: Sal Slip/08149/00015> for_preview = 0 File "apps/hrms/hrms/payroll/doctype/salary_slip/salary_slip.py", line 644, in calculate_net_pay self.calculate_component_amounts("deductions") self = <SalarySlip: Sal Slip/08149/00015> File "apps/hrms/hrms/payroll/doctype/salary_slip/salary_slip.py", line 936, in calculate_component_amounts self.add_structure_components(component_type) self = <SalarySlip: Sal Slip/08149/00015> component_type = 'deductions' File "apps/hrms/hrms/payroll/doctype/salary_slip/salary_slip.py", line 984, in add_structure_components self.update_component_row( self = <SalarySlip: Sal Slip/08149/00015> component_type = 'deductions' timesheet_component = None struct_row = <SalaryDetail: 799f57dc70 docstatus=1 parent=Jadeed Feeds Industries (Pvt) Ltd.> amount = 200.0 remove_if_zero_valued = 1 default_amount = 200.0 File "apps/hrms/hrms/payroll/doctype/salary_slip/salary_slip.py", line 1202, in update_component_row component_row = self.append(component_type) self = <SalarySlip: Sal Slip/08149/00015> amount = 200.0 component_type = 'deductions' is_recurring = 0 data = {'name': 'Sal Slip/08149/00015', 'creation': '2023-07-10 14:35:31.122118', 'modified': '2023-07-10 14:35:31.122118', 'modified_by': 'mazhar.dars@sowaan.com', 'owner': 'mazhar.dars@sowaan.com', 'docstatus': 0, 'parent': None, 'parentfield': None, 'parenttype': None, 'idx': 1, 'employee': '08149', 'employee_name': 'Khayzer Hayyat', 'department': 'Production - JFI', 'company': 'Jadeed Feeds Industries (Pvt) Ltd.', 'payroll_payable_account': 'Payroll Payable - JFI', 'designation': 'Farm Worker', 'salary_structure': 'Jadeed Feeds Industries (Pvt) Ltd.', 'from_date': datetime.date(2023, 2, 1), 'income_tax_slab': 'JFI(2022-2023)', 'currency': 'PKR', 'base': 33350.0, 'variable': 0.0, 'amended_from': None, '_user_tags': None, '_comments': None, '_assign': None, '_liked_by': None, 'employee_number': '08149', 'hill_allowance': 0, 'remote_allowance': 0, 'gun_allowance': 0, 'gp_allowance': 0, 'location': 'Jungle Maryala Farm', 'grade': 'W-2 (Unskilled)', 'taxable_earnings_till_date': 0.0, 'tax_dedu... default_amount = 200.0 remove_if_zero_valued = 1 d = <SalaryDetail: unsaved parent=Sal Slip/08149/00015> additional_salary = None component_data = <SalaryDetail: 799f57dc70 docstatus=1 parent=Jadeed Feeds Industries (Pvt) Ltd.> component_row = None File "apps/frappe/frappe/model/base_document.py", line 232, in append value = self._init_child(value, key) self = <SalarySlip: Sal Slip/08149/00015> key = ******** value = {'doctype': 'Salary Detail'} table = [<SalaryDetail: unsaved parent=Sal Slip/08149/00015>] File "apps/frappe/frappe/model/base_document.py", line 261, in _init_child value = get_controller(doctype)(value) self = <SalarySlip: Sal Slip/08149/00015> value = {'doctype': 'Salary Detail'} key = ******** doctype = 'Salary Detail' File "apps/frappe/frappe/model/base_document.py", line 76, in get_controller if frappe.local.dev_server or frappe.flags.in_migrate: _get_controller = <function get_controller.<locals>._get_controller at 0x7ff821350d30> doctype = 'Salary Detail' File "env/lib/python3.10/site-packages/werkzeug/local.py", line 82, in __getattr__ def __getattr__(self, name: str) -> t.Any: self = <werkzeug.local.Local object at 0x7ff82b210670> name = 'dev_server' File "env/lib/python3.10/site-packages/rq/timeouts.py", line 61, in handle_death_penalty raise self._exception('Task exceeded maximum timeout value ' self = <rq.timeouts.UnixSignalDeathPenalty object at 0x7ff8288e7010> signum = 14 frame = <frame at 0x7ff82b26cd60, file 'env/lib/python3.10/site-packages/werkzeug/local.py', line 82, code __getattr__> rq.timeouts.JobTimeoutException: Task exceeded maximum timeout value (300 seconds)

I am using this versions:

ERPNext: v14.10.0 (version-14)
Frappe Framework: v14.26.0 (version-14)
Frappe HR: v14.4.4 (version-14)

Hi,

I was also facing the same issue with 500 employee and done some changes to handle that.

I will post the code later today, when I have access to my PC.

Thanks,

Divyesh Mangroliya

Hi,

I override the below function in payroll_entry.py file to solve the issue, only change I done is timeout=3000 instead of timeout=600.

Also note that in Standard code in Develop branch frappe already fixed this issue with same solution.

@frappe.whitelist()
	def create_salary_slips(self):
		"""
		Creates salary slip for selected employees if already not created
		"""
		self.check_permission("write")
		employees = [emp.employee for emp in self.employees]

		if employees:
			args = frappe._dict(
				{
					"salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
					"payroll_frequency": self.payroll_frequency,
					"start_date": self.start_date,
					"end_date": self.end_date,
					"company": self.company,
					"posting_date": self.posting_date,
					"deduct_tax_for_unclaimed_employee_benefits": self.deduct_tax_for_unclaimed_employee_benefits,
					"deduct_tax_for_unsubmitted_tax_exemption_proof": self.deduct_tax_for_unsubmitted_tax_exemption_proof,
					"payroll_entry": self.name,
					"exchange_rate": self.exchange_rate,
					"currency": self.currency,
				}
			)
			if len(employees) > 30 or frappe.flags.enqueue_payroll_entry:
				self.db_set("status", "Queued")
				frappe.enqueue(
					create_salary_slips_for_employees,
					timeout=3000,
					employees=employees,
					args=args,
					publish_progress=False,
				)
				frappe.msgprint(
					_("Salary Slip creation is queued. It may take a few minutes"),
					alert=True,
					indicator="blue",
				)
			else:
				create_salary_slips_for_employees(employees, args, publish_progress=False)
				# since this method is called via frm.call this doc needs to be updated manually
				self. Reload()

Thanks,

Divyesh Mangroliya

2 Likes