diff --git a/erpnext/hr/README.md b/erpnext/hr/README.md deleted file mode 100644 index 5d1ae6e4ba..0000000000 --- a/erpnext/hr/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Key features: - -- Leave and Attendance -- Payroll -- Appraisal -- Expense Claim \ No newline at end of file diff --git a/erpnext/hr/__init__.py b/erpnext/hr/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/dashboard_chart/attendance_count/attendance_count.json b/erpnext/hr/dashboard_chart/attendance_count/attendance_count.json deleted file mode 100644 index 4666aec4c6..0000000000 --- a/erpnext/hr/dashboard_chart/attendance_count/attendance_count.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "chart_name": "Attendance Count", - "chart_type": "Report", - "creation": "2020-07-22 11:56:32.730068", - "custom_options": "{\n\t\t\"type\": \"line\",\n\t\t\"axisOptions\": {\n\t\t\t\"shortenYAxisNumbers\": 1\n\t\t},\n\t\t\"tooltipOptions\": {}\n\t}", - "docstatus": 0, - "doctype": "Dashboard Chart", - "dynamic_filters_json": "{\"month\":\"frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth() + 1\",\"year\":\"frappe.datetime.str_to_obj(frappe.datetime.get_today()).getFullYear();\",\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\"}", - "filters_json": "{}", - "group_by_type": "Count", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "modified": "2020-07-22 14:32:40.334424", - "modified_by": "Administrator", - "module": "HR", - "name": "Attendance Count", - "number_of_groups": 0, - "owner": "Administrator", - "report_name": "Monthly Attendance Sheet", - "time_interval": "Yearly", - "timeseries": 0, - "timespan": "Last Year", - "type": "Line", - "use_report_chart": 1, - "y_axis": [] -} \ No newline at end of file diff --git a/erpnext/hr/dashboard_chart/department_wise_employee_count/department_wise_employee_count.json b/erpnext/hr/dashboard_chart/department_wise_employee_count/department_wise_employee_count.json deleted file mode 100644 index c21bfb9f36..0000000000 --- a/erpnext/hr/dashboard_chart/department_wise_employee_count/department_wise_employee_count.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "chart_name": "Department Wise Employee Count", - "chart_type": "Group By", - "creation": "2020-07-22 11:56:32.760730", - "custom_options": "", - "docstatus": 0, - "doctype": "Dashboard Chart", - "document_type": "Employee", - "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", - "filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]", - "group_by_based_on": "department", - "group_by_type": "Count", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "last_synced_on": "2020-07-22 14:27:40.574194", - "modified": "2020-07-22 14:33:38.036794", - "modified_by": "Administrator", - "module": "HR", - "name": "Department Wise Employee Count", - "number_of_groups": 0, - "owner": "Administrator", - "time_interval": "Yearly", - "timeseries": 0, - "timespan": "Last Year", - "type": "Donut", - "use_report_chart": 0, - "y_axis": [] -} \ No newline at end of file diff --git a/erpnext/hr/dashboard_chart/department_wise_openings/department_wise_openings.json b/erpnext/hr/dashboard_chart/department_wise_openings/department_wise_openings.json deleted file mode 100644 index b1953d40ff..0000000000 --- a/erpnext/hr/dashboard_chart/department_wise_openings/department_wise_openings.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "aggregate_function_based_on": "planned_vacancies", - "chart_name": "Department Wise Openings", - "chart_type": "Group By", - "creation": "2020-07-22 11:56:32.849775", - "custom_options": "", - "docstatus": 0, - "doctype": "Dashboard Chart", - "document_type": "Job Opening", - "filters_json": "[]", - "group_by_based_on": "department", - "group_by_type": "Sum", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "last_synced_on": "2020-07-22 14:33:44.834801", - "modified": "2020-07-22 14:34:45.273591", - "modified_by": "Administrator", - "module": "HR", - "name": "Department Wise Openings", - "number_of_groups": 0, - "owner": "Administrator", - "time_interval": "Monthly", - "timeseries": 0, - "timespan": "Last Year", - "type": "Bar", - "use_report_chart": 0, - "y_axis": [] -} \ No newline at end of file diff --git a/erpnext/hr/dashboard_chart/designation_wise_employee_count/designation_wise_employee_count.json b/erpnext/hr/dashboard_chart/designation_wise_employee_count/designation_wise_employee_count.json deleted file mode 100644 index b10235cb8e..0000000000 --- a/erpnext/hr/dashboard_chart/designation_wise_employee_count/designation_wise_employee_count.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "chart_name": "Designation Wise Employee Count", - "chart_type": "Group By", - "creation": "2020-07-22 11:56:32.790337", - "custom_options": "", - "docstatus": 0, - "doctype": "Dashboard Chart", - "document_type": "Employee", - "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", - "filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]", - "group_by_based_on": "designation", - "group_by_type": "Count", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "last_synced_on": "2020-07-22 14:27:40.602783", - "modified": "2020-07-22 14:31:49.665555", - "modified_by": "Administrator", - "module": "HR", - "name": "Designation Wise Employee Count", - "number_of_groups": 0, - "owner": "Administrator", - "time_interval": "Yearly", - "timeseries": 0, - "timespan": "Last Year", - "type": "Donut", - "use_report_chart": 0, - "y_axis": [] -} \ No newline at end of file diff --git a/erpnext/hr/dashboard_chart/designation_wise_openings/designation_wise_openings.json b/erpnext/hr/dashboard_chart/designation_wise_openings/designation_wise_openings.json deleted file mode 100644 index 49ea98a4fc..0000000000 --- a/erpnext/hr/dashboard_chart/designation_wise_openings/designation_wise_openings.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "aggregate_function_based_on": "planned_vacancies", - "chart_name": "Designation Wise Openings", - "chart_type": "Group By", - "creation": "2020-07-22 11:56:32.820217", - "custom_options": "", - "docstatus": 0, - "doctype": "Dashboard Chart", - "document_type": "Job Opening", - "dynamic_filters_json": "", - "filters_json": "[]", - "group_by_based_on": "designation", - "group_by_type": "Sum", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "last_synced_on": "2020-07-22 14:33:44.806626", - "modified": "2020-07-22 14:34:32.711881", - "modified_by": "Administrator", - "module": "HR", - "name": "Designation Wise Openings", - "number_of_groups": 0, - "owner": "Administrator", - "time_interval": "Monthly", - "timeseries": 0, - "timespan": "Last Year", - "type": "Bar", - "use_report_chart": 0, - "y_axis": [] -} \ No newline at end of file diff --git a/erpnext/hr/dashboard_chart/gender_diversity_ratio/gender_diversity_ratio.json b/erpnext/hr/dashboard_chart/gender_diversity_ratio/gender_diversity_ratio.json deleted file mode 100644 index 48578c9728..0000000000 --- a/erpnext/hr/dashboard_chart/gender_diversity_ratio/gender_diversity_ratio.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "chart_name": "Gender Diversity Ratio", - "chart_type": "Group By", - "creation": "2020-07-22 11:56:32.667291", - "custom_options": "", - "docstatus": 0, - "doctype": "Dashboard Chart", - "document_type": "Employee", - "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", - "filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]", - "group_by_based_on": "gender", - "group_by_type": "Count", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "last_synced_on": "2020-07-22 14:27:40.143783", - "modified": "2020-07-22 14:32:50.962459", - "modified_by": "Administrator", - "module": "HR", - "name": "Gender Diversity Ratio", - "number_of_groups": 0, - "owner": "Administrator", - "time_interval": "Yearly", - "timeseries": 0, - "timespan": "Last Year", - "type": "Pie", - "use_report_chart": 0, - "y_axis": [] -} \ No newline at end of file diff --git a/erpnext/hr/dashboard_chart/job_application_status/job_application_status.json b/erpnext/hr/dashboard_chart/job_application_status/job_application_status.json deleted file mode 100644 index 42a830970e..0000000000 --- a/erpnext/hr/dashboard_chart/job_application_status/job_application_status.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "chart_name": "Job Application Status", - "chart_type": "Group By", - "creation": "2020-07-22 11:56:32.699696", - "custom_options": "", - "docstatus": 0, - "doctype": "Dashboard Chart", - "document_type": "Job Applicant", - "dynamic_filters_json": "", - "filters_json": "[[\"Job Applicant\",\"creation\",\"Timespan\",\"last month\",false]]", - "group_by_based_on": "status", - "group_by_type": "Count", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "last_synced_on": "2020-07-28 16:19:12.109979", - "modified": "2020-07-28 16:19:45.279490", - "modified_by": "Administrator", - "module": "HR", - "name": "Job Application Status", - "number_of_groups": 0, - "owner": "Administrator", - "time_interval": "Yearly", - "timeseries": 0, - "timespan": "Last Year", - "type": "Pie", - "use_report_chart": 0, - "y_axis": [] -} \ No newline at end of file diff --git a/erpnext/hr/doctype/__init__.py b/erpnext/hr/doctype/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/appointment_letter/__init__.py b/erpnext/hr/doctype/appointment_letter/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/appointment_letter/appointment_letter.js b/erpnext/hr/doctype/appointment_letter/appointment_letter.js deleted file mode 100644 index a338dc6513..0000000000 --- a/erpnext/hr/doctype/appointment_letter/appointment_letter.js +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Appointment Letter', { - appointment_letter_template: function(frm){ - if (frm.doc.appointment_letter_template){ - frappe.call({ - method: 'erpnext.hr.doctype.appointment_letter.appointment_letter.get_appointment_letter_details', - args : { - template : frm.doc.appointment_letter_template - }, - callback: function(r){ - if(r.message){ - let message_body = r.message; - frm.set_value("introduction", message_body[0].introduction); - frm.set_value("closing_notes", message_body[0].closing_notes); - frm.doc.terms = [] - for (var i in message_body[1].description){ - frm.add_child("terms"); - frm.fields_dict.terms.get_value()[i].title = message_body[1].description[i].title; - frm.fields_dict.terms.get_value()[i].description = message_body[1].description[i].description; - } - frm.refresh(); - } - } - - }); - } - }, -}); diff --git a/erpnext/hr/doctype/appointment_letter/appointment_letter.json b/erpnext/hr/doctype/appointment_letter/appointment_letter.json deleted file mode 100644 index 012f6b6b49..0000000000 --- a/erpnext/hr/doctype/appointment_letter/appointment_letter.json +++ /dev/null @@ -1,128 +0,0 @@ -{ - "actions": [], - "autoname": "HR-APP-LETTER-.#####", - "creation": "2019-12-26 12:35:49.574828", - "default_print_format": "Standard Appointment Letter", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "job_applicant", - "applicant_name", - "column_break_3", - "company", - "appointment_date", - "appointment_letter_template", - "body_section", - "introduction", - "terms", - "closing_notes" - ], - "fields": [ - { - "fetch_from": "job_applicant.applicant_name", - "fieldname": "applicant_name", - "fieldtype": "Data", - "in_global_search": 1, - "in_list_view": 1, - "label": "Applicant Name", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "appointment_date", - "fieldtype": "Date", - "label": "Appointment Date", - "reqd": 1 - }, - { - "fieldname": "appointment_letter_template", - "fieldtype": "Link", - "label": "Appointment Letter Template", - "options": "Appointment Letter Template", - "reqd": 1 - }, - { - "fetch_from": "appointment_letter_template.introduction", - "fieldname": "introduction", - "fieldtype": "Long Text", - "label": "Introduction", - "reqd": 1 - }, - { - "fieldname": "body_section", - "fieldtype": "Section Break", - "label": "Body" - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "job_applicant", - "fieldtype": "Link", - "label": "Job Applicant", - "options": "Job Applicant", - "reqd": 1 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "closing_notes", - "fieldtype": "Text", - "label": "Closing Notes" - }, - { - "fieldname": "terms", - "fieldtype": "Table", - "label": "Terms", - "options": "Appointment Letter content", - "reqd": 1 - } - ], - "links": [], - "modified": "2022-01-18 19:27:35.649424", - "modified_by": "Administrator", - "module": "HR", - "name": "Appointment Letter", - "name_case": "Title Case", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - } - ], - "search_fields": "applicant_name, company", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "applicant_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/appointment_letter/appointment_letter.py b/erpnext/hr/doctype/appointment_letter/appointment_letter.py deleted file mode 100644 index a58589af21..0000000000 --- a/erpnext/hr/doctype/appointment_letter/appointment_letter.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe.model.document import Document - - -class AppointmentLetter(Document): - pass - - -@frappe.whitelist() -def get_appointment_letter_details(template): - body = [] - intro = frappe.get_list( - "Appointment Letter Template", - fields=["introduction", "closing_notes"], - filters={"name": template}, - )[0] - content = frappe.get_all( - "Appointment Letter content", - fields=["title", "description"], - filters={"parent": template}, - order_by="idx", - ) - body.append(intro) - body.append({"description": content}) - return body diff --git a/erpnext/hr/doctype/appointment_letter/test_appointment_letter.py b/erpnext/hr/doctype/appointment_letter/test_appointment_letter.py deleted file mode 100644 index e0f65b45d4..0000000000 --- a/erpnext/hr/doctype/appointment_letter/test_appointment_letter.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -import unittest - - -class TestAppointmentLetter(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/appointment_letter_content/__init__.py b/erpnext/hr/doctype/appointment_letter_content/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.json b/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.json deleted file mode 100644 index 17a2b91cff..0000000000 --- a/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "actions": [], - "creation": "2019-12-26 12:22:16.575767", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "title", - "description" - ], - "fields": [ - { - "fieldname": "title", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Title", - "reqd": 1 - }, - { - "fieldname": "description", - "fieldtype": "Long Text", - "in_list_view": 1, - "label": "Description", - "reqd": 1 - } - ], - "istable": 1, - "links": [], - "modified": "2019-12-26 12:24:09.824084", - "modified_by": "Administrator", - "module": "HR", - "name": "Appointment Letter content", - "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/hr/doctype/appointment_letter_content/appointment_letter_content.py b/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.py deleted file mode 100644 index d158013d74..0000000000 --- a/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class AppointmentLettercontent(Document): - pass diff --git a/erpnext/hr/doctype/appointment_letter_template/__init__.py b/erpnext/hr/doctype/appointment_letter_template/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.js b/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.js deleted file mode 100644 index 8270f7aca2..0000000000 --- a/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Appointment Letter Template', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.json b/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.json deleted file mode 100644 index 5e50fe6d8f..0000000000 --- a/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "actions": [], - "autoname": "field:template_name", - "creation": "2019-12-26 12:20:14.219578", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "template_name", - "introduction", - "terms", - "closing_notes" - ], - "fields": [ - { - "fieldname": "introduction", - "fieldtype": "Long Text", - "in_list_view": 1, - "label": "Introduction", - "reqd": 1 - }, - { - "fieldname": "closing_notes", - "fieldtype": "Text", - "label": "Closing Notes" - }, - { - "fieldname": "terms", - "fieldtype": "Table", - "label": "Terms", - "options": "Appointment Letter content", - "reqd": 1 - }, - { - "fieldname": "template_name", - "fieldtype": "Data", - "label": "Template Name", - "reqd": 1, - "unique": 1 - } - ], - "links": [], - "modified": "2022-01-18 19:25:14.614616", - "modified_by": "Administrator", - "module": "HR", - "name": "Appointment Letter Template", - "naming_rule": "By fieldname", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - } - ], - "search_fields": "template_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "template_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.py b/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.py deleted file mode 100644 index 9ac726e06d..0000000000 --- a/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class AppointmentLetterTemplate(Document): - pass diff --git a/erpnext/hr/doctype/appointment_letter_template/test_appointment_letter_template.py b/erpnext/hr/doctype/appointment_letter_template/test_appointment_letter_template.py deleted file mode 100644 index aa87da323f..0000000000 --- a/erpnext/hr/doctype/appointment_letter_template/test_appointment_letter_template.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -import unittest - - -class TestAppointmentLetterTemplate(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/appraisal/README.md b/erpnext/hr/doctype/appraisal/README.md deleted file mode 100644 index 7798f188cc..0000000000 --- a/erpnext/hr/doctype/appraisal/README.md +++ /dev/null @@ -1 +0,0 @@ -Performance of an Employee in a Time Period against given goals. \ No newline at end of file diff --git a/erpnext/hr/doctype/appraisal/__init__.py b/erpnext/hr/doctype/appraisal/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/appraisal/appraisal.js b/erpnext/hr/doctype/appraisal/appraisal.js deleted file mode 100644 index 50612b923e..0000000000 --- a/erpnext/hr/doctype/appraisal/appraisal.js +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.ui.form.on('Appraisal', { - setup: function(frm) { - frm.add_fetch('employee', 'company', 'company'); - frm.add_fetch('employee', 'employee_name', 'employee_name'); - frm.fields_dict.employee.get_query = function(doc,cdt,cdn) { - return{ query: "erpnext.controllers.queries.employee_query" } - }; - }, - - onload: function(frm) { - if(!frm.doc.status) { - frm.set_value('status', 'Draft'); - } - }, - - kra_template: function(frm) { - frm.doc.goals = []; - erpnext.utils.map_current_doc({ - method: "erpnext.hr.doctype.appraisal.appraisal.fetch_appraisal_template", - source_name: frm.doc.kra_template, - frm: frm - }); - }, - - calculate_total: function(frm) { - let goals = frm.doc.goals || []; - let total = 0; - - if (goals == []) { - frm.set_value('total_score', 0); - return; - } - for (let i = 0; i 5) { - frappe.msgprint(__("Score must be less than or equal to 5")); - d.score = 0; - refresh_field('score', d.name, 'goals'); - } - else { - frm.trigger('set_score_earned'); - } - }, - per_weightage: function(frm) { - frm.trigger('set_score_earned'); - }, - goals_remove: function(frm) { - frm.trigger('set_score_earned'); - } -}); diff --git a/erpnext/hr/doctype/appraisal/appraisal.json b/erpnext/hr/doctype/appraisal/appraisal.json deleted file mode 100644 index 9ca7bcc354..0000000000 --- a/erpnext/hr/doctype/appraisal/appraisal.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "actions": [], - "autoname": "naming_series:", - "creation": "2013-01-10 16:34:12", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "employee_details", - "naming_series", - "kra_template", - "employee", - "employee_name", - "column_break0", - "status", - "start_date", - "end_date", - "department", - "section_break0", - "goals", - "total_score", - "section_break1", - "remarks", - "other_details", - "company", - "column_break_17", - "amended_from" - ], - "fields": [ - { - "fieldname": "employee_details", - "fieldtype": "Section Break", - "oldfieldtype": "Section Break" - }, - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "no_copy": 1, - "options": "HR-APR-.YY.-.MM.", - "print_hide": 1, - "reqd": 1, - "set_only_once": 1 - }, - { - "fieldname": "kra_template", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Appraisal Template", - "oldfieldname": "kra_template", - "oldfieldtype": "Link", - "options": "Appraisal Template", - "reqd": 1 - }, - { - "depends_on": "kra_template", - "fieldname": "employee", - "fieldtype": "Link", - "in_global_search": 1, - "in_standard_filter": 1, - "label": "For Employee", - "oldfieldname": "employee", - "oldfieldtype": "Link", - "options": "Employee", - "reqd": 1, - "search_index": 1 - }, - { - "depends_on": "kra_template", - "fieldname": "employee_name", - "fieldtype": "Data", - "in_global_search": 1, - "label": "For Employee Name", - "oldfieldname": "employee_name", - "oldfieldtype": "Data", - "read_only": 1 - }, - { - "depends_on": "kra_template", - "fieldname": "column_break0", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, - { - "default": "Draft", - "depends_on": "kra_template", - "fieldname": "status", - "fieldtype": "Select", - "in_standard_filter": 1, - "label": "Status", - "no_copy": 1, - "oldfieldname": "status", - "oldfieldtype": "Select", - "options": "\nDraft\nSubmitted\nCompleted\nCancelled", - "read_only": 1, - "reqd": 1, - "search_index": 1 - }, - { - "depends_on": "kra_template", - "fieldname": "start_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Start Date", - "oldfieldname": "start_date", - "oldfieldtype": "Date", - "reqd": 1 - }, - { - "depends_on": "kra_template", - "fieldname": "end_date", - "fieldtype": "Date", - "label": "End Date", - "oldfieldname": "end_date", - "oldfieldtype": "Date", - "reqd": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "depends_on": "kra_template", - "fieldname": "section_break0", - "fieldtype": "Section Break", - "label": "Goals", - "oldfieldtype": "Section Break", - "options": "Simple" - }, - { - "fieldname": "goals", - "fieldtype": "Table", - "label": "Goals", - "oldfieldname": "appraisal_details", - "oldfieldtype": "Table", - "options": "Appraisal Goal" - }, - { - "fieldname": "total_score", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Total Score (Out of 5)", - "no_copy": 1, - "oldfieldname": "total_score", - "oldfieldtype": "Currency", - "read_only": 1 - }, - { - "depends_on": "kra_template", - "fieldname": "section_break1", - "fieldtype": "Section Break" - }, - { - "description": "Any other remarks, noteworthy effort that should go in the records.", - "fieldname": "remarks", - "fieldtype": "Text", - "label": "Remarks" - }, - { - "depends_on": "kra_template", - "fieldname": "other_details", - "fieldtype": "Section Break" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "fieldname": "column_break_17", - "fieldtype": "Column Break" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 1, - "label": "Amended From", - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Appraisal", - "print_hide": 1, - "read_only": 1, - "report_hide": 1, - "width": "150px" - } - ], - "icon": "fa fa-thumbs-up", - "idx": 1, - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2020-10-03 21:48:33.297065", - "modified_by": "Administrator", - "module": "HR", - "name": "Appraisal", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "status, employee, employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "employee", - "title_field": "employee_name" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/appraisal/appraisal.py b/erpnext/hr/doctype/appraisal/appraisal.py deleted file mode 100644 index 382c643aba..0000000000 --- a/erpnext/hr/doctype/appraisal/appraisal.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.model.mapper import get_mapped_doc -from frappe.utils import flt, getdate - -from erpnext.hr.utils import set_employee_name, validate_active_employee - - -class Appraisal(Document): - def validate(self): - if not self.status: - self.status = "Draft" - - if not self.goals: - frappe.throw(_("Goals cannot be empty")) - - validate_active_employee(self.employee) - set_employee_name(self) - self.validate_dates() - self.validate_existing_appraisal() - self.calculate_total() - - def get_employee_name(self): - self.employee_name = frappe.db.get_value("Employee", self.employee, "employee_name") - return self.employee_name - - def validate_dates(self): - if getdate(self.start_date) > getdate(self.end_date): - frappe.throw(_("End Date can not be less than Start Date")) - - def validate_existing_appraisal(self): - chk = frappe.db.sql( - """select name from `tabAppraisal` where employee=%s - and (status='Submitted' or status='Completed') - and ((start_date>=%s and start_date<=%s) - or (end_date>=%s and end_date<=%s))""", - (self.employee, self.start_date, self.end_date, self.start_date, self.end_date), - ) - if chk: - frappe.throw( - _("Appraisal {0} created for Employee {1} in the given date range").format( - chk[0][0], self.employee_name - ) - ) - - def calculate_total(self): - total, total_w = 0, 0 - for d in self.get("goals"): - if d.score: - d.score_earned = flt(d.score) * flt(d.per_weightage) / 100 - total = total + d.score_earned - total_w += flt(d.per_weightage) - - if int(total_w) != 100: - frappe.throw( - _("Total weightage assigned should be 100%.
It is {0}").format(str(total_w) + "%") - ) - - if ( - frappe.db.get_value("Employee", self.employee, "user_id") != frappe.session.user and total == 0 - ): - frappe.throw(_("Total cannot be zero")) - - self.total_score = total - - def on_submit(self): - frappe.db.set(self, "status", "Submitted") - - def on_cancel(self): - frappe.db.set(self, "status", "Cancelled") - - -@frappe.whitelist() -def fetch_appraisal_template(source_name, target_doc=None): - target_doc = get_mapped_doc( - "Appraisal Template", - source_name, - { - "Appraisal Template": { - "doctype": "Appraisal", - }, - "Appraisal Template Goal": { - "doctype": "Appraisal Goal", - }, - }, - target_doc, - ) - - return target_doc diff --git a/erpnext/hr/doctype/appraisal/test_appraisal.py b/erpnext/hr/doctype/appraisal/test_appraisal.py deleted file mode 100644 index 13a39f3820..0000000000 --- a/erpnext/hr/doctype/appraisal/test_appraisal.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors -# See license.txt - -import unittest - -# test_records = frappe.get_test_records('Appraisal') - - -class TestAppraisal(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/appraisal_goal/README.md b/erpnext/hr/doctype/appraisal_goal/README.md deleted file mode 100644 index 851a403bde..0000000000 --- a/erpnext/hr/doctype/appraisal_goal/README.md +++ /dev/null @@ -1 +0,0 @@ -Goal for the parent Appraisal. \ No newline at end of file diff --git a/erpnext/hr/doctype/appraisal_goal/__init__.py b/erpnext/hr/doctype/appraisal_goal/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.json b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.json deleted file mode 100644 index b8ec5a8afb..0000000000 --- a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.json +++ /dev/null @@ -1,220 +0,0 @@ -{ - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "hash", - "beta": 0, - "creation": "2013-02-22 01:27:44", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "description": "Key Responsibility Area", - "fieldname": "kra", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Goal", - "length": 0, - "no_copy": 0, - "oldfieldname": "kra", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "240px", - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "240px" - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "per_weightage", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Weightage (%)", - "length": 0, - "no_copy": 0, - "oldfieldname": "per_weightage", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "70px", - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "70px" - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "score", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Score (0-5)", - "length": 0, - "no_copy": 1, - "oldfieldname": "score", - "oldfieldtype": "Select", - "options": "", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "70px", - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "70px" - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "score_earned", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Score Earned", - "length": 0, - "no_copy": 1, - "oldfieldname": "score_earned", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "70px", - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "70px" - } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "module": "HR", - "name": "Appraisal Goal", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py deleted file mode 100644 index 3cbc9188ee..0000000000 --- a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -from frappe.model.document import Document - - -class AppraisalGoal(Document): - pass diff --git a/erpnext/hr/doctype/appraisal_template/README.md b/erpnext/hr/doctype/appraisal_template/README.md deleted file mode 100644 index d58a744867..0000000000 --- a/erpnext/hr/doctype/appraisal_template/README.md +++ /dev/null @@ -1 +0,0 @@ -Standard set of goals for an Employee / Designation / Job Profile. New Appraisal transactions can be created from the Template. \ No newline at end of file diff --git a/erpnext/hr/doctype/appraisal_template/__init__.py b/erpnext/hr/doctype/appraisal_template/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.js b/erpnext/hr/doctype/appraisal_template/appraisal_template.js deleted file mode 100644 index cc772927f8..0000000000 --- a/erpnext/hr/doctype/appraisal_template/appraisal_template.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Appraisal Template', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.json b/erpnext/hr/doctype/appraisal_template/appraisal_template.json deleted file mode 100644 index 7329c35dee..0000000000 --- a/erpnext/hr/doctype/appraisal_template/appraisal_template.json +++ /dev/null @@ -1,170 +0,0 @@ -{ - "allow_copy": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:kra_title", - "beta": 0, - "creation": "2012-07-03 13:30:39", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "kra_title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Appraisal Template Title", - "length": 0, - "no_copy": 0, - "oldfieldname": "kra_title", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "300px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "300px" - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "goals", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Goals", - "length": 0, - "no_copy": 0, - "oldfieldname": "kra_sheet", - "oldfieldtype": "Table", - "options": "Appraisal Template Goal", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-file-text", - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "module": "HR", - "name": "Appraisal Template", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Employee", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "sort_order": "DESC", - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.py b/erpnext/hr/doctype/appraisal_template/appraisal_template.py deleted file mode 100644 index 6b5921e6a6..0000000000 --- a/erpnext/hr/doctype/appraisal_template/appraisal_template.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import cint, flt - - -class AppraisalTemplate(Document): - def validate(self): - self.check_total_points() - - def check_total_points(self): - total_points = 0 - for d in self.get("goals"): - total_points += flt(d.per_weightage) - - if cint(total_points) != 100: - frappe.throw(_("Sum of points for all goals should be 100. It is {0}").format(total_points)) diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py b/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py deleted file mode 100644 index 476de4f51b..0000000000 --- a/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py +++ /dev/null @@ -1,7 +0,0 @@ -def get_data(): - return { - "fieldname": "kra_template", - "transactions": [ - {"items": ["Appraisal"]}, - ], - } diff --git a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py deleted file mode 100644 index 560e992e8a..0000000000 --- a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors -# See license.txt - -import unittest - -# test_records = frappe.get_test_records('Appraisal Template') - - -class TestAppraisalTemplate(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/appraisal_template_goal/README.md b/erpnext/hr/doctype/appraisal_template_goal/README.md deleted file mode 100644 index 1b81afbfce..0000000000 --- a/erpnext/hr/doctype/appraisal_template_goal/README.md +++ /dev/null @@ -1 +0,0 @@ -Goal details for the parent Appraisal Template. \ No newline at end of file diff --git a/erpnext/hr/doctype/appraisal_template_goal/__init__.py b/erpnext/hr/doctype/appraisal_template_goal/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.json b/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.json deleted file mode 100644 index 2858419f33..0000000000 --- a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "hash", - "beta": 0, - "creation": "2013-02-22 01:27:44", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "description": "Key Performance Area", - "fieldname": "kra", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "KRA", - "length": 0, - "no_copy": 0, - "oldfieldname": "kra", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "200px", - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "200px" - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "per_weightage", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Weightage (%)", - "length": 0, - "no_copy": 0, - "oldfieldname": "per_weightage", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "100px" - } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "module": "HR", - "name": "Appraisal Template Goal", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py b/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py deleted file mode 100644 index e6c5f64e08..0000000000 --- a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -from frappe.model.document import Document - - -class AppraisalTemplateGoal(Document): - pass diff --git a/erpnext/hr/doctype/attendance/README.md b/erpnext/hr/doctype/attendance/README.md deleted file mode 100644 index f420b0709c..0000000000 --- a/erpnext/hr/doctype/attendance/README.md +++ /dev/null @@ -1 +0,0 @@ -Attendance record of an Employee on a particular date. \ No newline at end of file diff --git a/erpnext/hr/doctype/attendance/__init__.py b/erpnext/hr/doctype/attendance/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/attendance/attendance.js b/erpnext/hr/doctype/attendance/attendance.js deleted file mode 100644 index 7964078c7f..0000000000 --- a/erpnext/hr/doctype/attendance/attendance.js +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -cur_frm.add_fetch('employee', 'company', 'company'); -cur_frm.add_fetch('employee', 'employee_name', 'employee_name'); - -cur_frm.cscript.onload = function(doc, cdt, cdn) { - if(doc.__islocal) cur_frm.set_value("attendance_date", frappe.datetime.get_today()); -} - -cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) { - return{ - query: "erpnext.controllers.queries.employee_query" - } -} diff --git a/erpnext/hr/doctype/attendance/attendance.json b/erpnext/hr/doctype/attendance/attendance.json deleted file mode 100644 index 134098f252..0000000000 --- a/erpnext/hr/doctype/attendance/attendance.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "naming_series:", - "creation": "2013-01-10 16:34:13", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "attendance_details", - "naming_series", - "employee", - "employee_name", - "working_hours", - "status", - "leave_type", - "leave_application", - "column_break0", - "attendance_date", - "company", - "department", - "attendance_request", - "details_section", - "shift", - "in_time", - "out_time", - "column_break_18", - "late_entry", - "early_exit", - "amended_from" - ], - "fields": [ - { - "fieldname": "attendance_details", - "fieldtype": "Section Break", - "oldfieldtype": "Section Break", - "options": "Simple" - }, - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "no_copy": 1, - "oldfieldname": "naming_series", - "oldfieldtype": "Select", - "options": "HR-ATT-.YYYY.-", - "reqd": 1, - "set_only_once": 1 - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "in_global_search": 1, - "in_standard_filter": 1, - "label": "Employee", - "oldfieldname": "employee", - "oldfieldtype": "Link", - "options": "Employee", - "reqd": 1, - "search_index": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "in_global_search": 1, - "label": "Employee Name", - "oldfieldname": "employee_name", - "oldfieldtype": "Data", - "read_only": 1 - }, - { - "depends_on": "working_hours", - "fieldname": "working_hours", - "fieldtype": "Float", - "label": "Working Hours", - "precision": "1", - "read_only": 1 - }, - { - "default": "Present", - "fieldname": "status", - "fieldtype": "Select", - "in_standard_filter": 1, - "label": "Status", - "no_copy": 1, - "oldfieldname": "status", - "oldfieldtype": "Select", - "options": "\nPresent\nAbsent\nOn Leave\nHalf Day\nWork From Home", - "reqd": 1, - "search_index": 1 - }, - { - "depends_on": "eval:in_list([\"On Leave\", \"Half Day\"], doc.status)", - "fieldname": "leave_type", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Leave Type", - "mandatory_depends_on": "eval:in_list([\"On Leave\", \"Half Day\"], doc.status)", - "oldfieldname": "leave_type", - "oldfieldtype": "Link", - "options": "Leave Type" - }, - { - "fieldname": "leave_application", - "fieldtype": "Link", - "label": "Leave Application", - "no_copy": 1, - "options": "Leave Application", - "read_only": 1 - }, - { - "fieldname": "column_break0", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, - { - "fieldname": "attendance_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Attendance Date", - "oldfieldname": "attendance_date", - "oldfieldtype": "Date", - "reqd": 1 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "shift", - "fieldtype": "Link", - "label": "Shift", - "options": "Shift Type" - }, - { - "fieldname": "attendance_request", - "fieldtype": "Link", - "label": "Attendance Request", - "options": "Attendance Request", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Amended From", - "no_copy": 1, - "options": "Attendance", - "print_hide": 1, - "read_only": 1 - }, - { - "default": "0", - "fieldname": "late_entry", - "fieldtype": "Check", - "label": "Late Entry" - }, - { - "default": "0", - "fieldname": "early_exit", - "fieldtype": "Check", - "label": "Early Exit" - }, - { - "fieldname": "details_section", - "fieldtype": "Section Break", - "label": "Details" - }, - { - "depends_on": "shift", - "fieldname": "in_time", - "fieldtype": "Datetime", - "label": "In Time", - "read_only": 1 - }, - { - "depends_on": "shift", - "fieldname": "out_time", - "fieldtype": "Datetime", - "label": "Out Time", - "read_only": 1 - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" - } - ], - "icon": "fa fa-ok", - "idx": 1, - "is_submittable": 1, - "links": [], - "modified": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "module": "HR", - "name": "Attendance", - "owner": "Administrator", - "permissions": [ - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "import": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "employee,employee_name,attendance_date,status", - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py deleted file mode 100644 index f3cae8089c..0000000000 --- a/erpnext/hr/doctype/attendance/attendance.py +++ /dev/null @@ -1,390 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.query_builder import Criterion -from frappe.utils import cint, cstr, formatdate, get_datetime, get_link_to_form, getdate, nowdate - -from erpnext.hr.doctype.shift_assignment.shift_assignment import has_overlapping_timings -from erpnext.hr.utils import get_holiday_dates_for_employee, validate_active_employee - - -class DuplicateAttendanceError(frappe.ValidationError): - pass - - -class OverlappingShiftAttendanceError(frappe.ValidationError): - pass - - -class Attendance(Document): - def validate(self): - from erpnext.controllers.status_updater import validate_status - - validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"]) - validate_active_employee(self.employee) - self.validate_attendance_date() - self.validate_duplicate_record() - self.validate_overlapping_shift_attendance() - self.validate_employee_status() - self.check_leave_record() - - def on_cancel(self): - self.unlink_attendance_from_checkins() - - def validate_attendance_date(self): - date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") - - # leaves can be marked for future dates - if ( - self.status != "On Leave" - and not self.leave_application - and getdate(self.attendance_date) > getdate(nowdate()) - ): - frappe.throw(_("Attendance can not be marked for future dates")) - elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining): - frappe.throw(_("Attendance date can not be less than employee's joining date")) - - def validate_duplicate_record(self): - duplicate = get_duplicate_attendance_record( - self.employee, self.attendance_date, self.shift, self.name - ) - - if duplicate: - frappe.throw( - _("Attendance for employee {0} is already marked for the date {1}: {2}").format( - frappe.bold(self.employee), - frappe.bold(self.attendance_date), - get_link_to_form("Attendance", duplicate[0].name), - ), - title=_("Duplicate Attendance"), - exc=DuplicateAttendanceError, - ) - - def validate_overlapping_shift_attendance(self): - attendance = get_overlapping_shift_attendance( - self.employee, self.attendance_date, self.shift, self.name - ) - - if attendance: - frappe.throw( - _("Attendance for employee {0} is already marked for an overlapping shift {1}: {2}").format( - frappe.bold(self.employee), - frappe.bold(attendance.shift), - get_link_to_form("Attendance", attendance.name), - ), - title=_("Overlapping Shift Attendance"), - exc=OverlappingShiftAttendanceError, - ) - - def validate_employee_status(self): - if frappe.db.get_value("Employee", self.employee, "status") == "Inactive": - frappe.throw(_("Cannot mark attendance for an Inactive employee {0}").format(self.employee)) - - def check_leave_record(self): - leave_record = frappe.db.sql( - """ - select leave_type, half_day, half_day_date - from `tabLeave Application` - where employee = %s - and %s between from_date and to_date - and status = 'Approved' - and docstatus = 1 - """, - (self.employee, self.attendance_date), - as_dict=True, - ) - if leave_record: - for d in leave_record: - self.leave_type = d.leave_type - if d.half_day_date == getdate(self.attendance_date): - self.status = "Half Day" - frappe.msgprint( - _("Employee {0} on Half day on {1}").format(self.employee, formatdate(self.attendance_date)) - ) - else: - self.status = "On Leave" - frappe.msgprint( - _("Employee {0} is on Leave on {1}").format(self.employee, formatdate(self.attendance_date)) - ) - - if self.status in ("On Leave", "Half Day"): - if not leave_record: - frappe.msgprint( - _("No leave record found for employee {0} on {1}").format( - self.employee, formatdate(self.attendance_date) - ), - alert=1, - ) - elif self.leave_type: - self.leave_type = None - self.leave_application = None - - def validate_employee(self): - emp = frappe.db.sql( - "select name from `tabEmployee` where name = %s and status = 'Active'", self.employee - ) - if not emp: - frappe.throw(_("Employee {0} is not active or does not exist").format(self.employee)) - - def unlink_attendance_from_checkins(self): - EmployeeCheckin = frappe.qb.DocType("Employee Checkin") - linked_logs = ( - frappe.qb.from_(EmployeeCheckin) - .select(EmployeeCheckin.name) - .where(EmployeeCheckin.attendance == self.name) - .for_update() - .run(as_dict=True) - ) - - if linked_logs: - ( - frappe.qb.update(EmployeeCheckin) - .set("attendance", "") - .where(EmployeeCheckin.attendance == self.name) - ).run() - - frappe.msgprint( - msg=_("Unlinked Attendance record from Employee Checkins: {}").format( - ", ".join(get_link_to_form("Employee Checkin", log.name) for log in linked_logs) - ), - title=_("Unlinked logs"), - indicator="blue", - is_minimizable=True, - wide=True, - ) - - -def get_duplicate_attendance_record(employee, attendance_date, shift, name=None): - attendance = frappe.qb.DocType("Attendance") - query = ( - frappe.qb.from_(attendance) - .select(attendance.name) - .where((attendance.employee == employee) & (attendance.docstatus < 2)) - ) - - if shift: - query = query.where( - Criterion.any( - [ - Criterion.all( - [ - ((attendance.shift.isnull()) | (attendance.shift == "")), - (attendance.attendance_date == attendance_date), - ] - ), - Criterion.all( - [ - ((attendance.shift.isnotnull()) | (attendance.shift != "")), - (attendance.attendance_date == attendance_date), - (attendance.shift == shift), - ] - ), - ] - ) - ) - else: - query = query.where((attendance.attendance_date == attendance_date)) - - if name: - query = query.where(attendance.name != name) - - return query.run(as_dict=True) - - -def get_overlapping_shift_attendance(employee, attendance_date, shift, name=None): - if not shift: - return {} - - attendance = frappe.qb.DocType("Attendance") - query = ( - frappe.qb.from_(attendance) - .select(attendance.name, attendance.shift) - .where( - (attendance.employee == employee) - & (attendance.docstatus < 2) - & (attendance.attendance_date == attendance_date) - & (attendance.shift != shift) - ) - ) - - if name: - query = query.where(attendance.name != name) - - overlapping_attendance = query.run(as_dict=True) - - if overlapping_attendance and has_overlapping_timings(shift, overlapping_attendance[0].shift): - return overlapping_attendance[0] - return {} - - -@frappe.whitelist() -def get_events(start, end, filters=None): - events = [] - - employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user}) - - if not employee: - return events - - from frappe.desk.reportview import get_filters_cond - - conditions = get_filters_cond("Attendance", filters, []) - add_attendance(events, start, end, conditions=conditions) - return events - - -def add_attendance(events, start, end, conditions=None): - query = """select name, attendance_date, status - from `tabAttendance` where - attendance_date between %(from_date)s and %(to_date)s - and docstatus < 2""" - if conditions: - query += conditions - - for d in frappe.db.sql(query, {"from_date": start, "to_date": end}, as_dict=True): - e = { - "name": d.name, - "doctype": "Attendance", - "start": d.attendance_date, - "end": d.attendance_date, - "title": cstr(d.status), - "docstatus": d.docstatus, - } - if e not in events: - events.append(e) - - -def mark_attendance( - employee, - attendance_date, - status, - shift=None, - leave_type=None, - ignore_validate=False, - late_entry=False, - early_exit=False, -): - if get_duplicate_attendance_record(employee, attendance_date, shift): - return - - if get_overlapping_shift_attendance(employee, attendance_date, shift): - return - - company = frappe.db.get_value("Employee", employee, "company") - attendance = frappe.get_doc( - { - "doctype": "Attendance", - "employee": employee, - "attendance_date": attendance_date, - "status": status, - "company": company, - "shift": shift, - "leave_type": leave_type, - "late_entry": late_entry, - "early_exit": early_exit, - } - ) - attendance.flags.ignore_validate = ignore_validate - attendance.insert() - attendance.submit() - return attendance.name - - -@frappe.whitelist() -def mark_bulk_attendance(data): - import json - - if isinstance(data, str): - data = json.loads(data) - data = frappe._dict(data) - company = frappe.get_value("Employee", data.employee, "company") - if not data.unmarked_days: - frappe.throw(_("Please select a date.")) - return - - for date in data.unmarked_days: - doc_dict = { - "doctype": "Attendance", - "employee": data.employee, - "attendance_date": get_datetime(date), - "status": data.status, - "company": company, - } - attendance = frappe.get_doc(doc_dict).insert() - attendance.submit() - - -def get_month_map(): - return frappe._dict( - { - "January": 1, - "February": 2, - "March": 3, - "April": 4, - "May": 5, - "June": 6, - "July": 7, - "August": 8, - "September": 9, - "October": 10, - "November": 11, - "December": 12, - } - ) - - -@frappe.whitelist() -def get_unmarked_days(employee, month, exclude_holidays=0): - import calendar - - month_map = get_month_map() - today = get_datetime() - - joining_date, relieving_date = frappe.get_cached_value( - "Employee", employee, ["date_of_joining", "relieving_date"] - ) - start_day = 1 - end_day = calendar.monthrange(today.year, month_map[month])[1] + 1 - - if joining_date and joining_date.month == month_map[month]: - start_day = joining_date.day - - if relieving_date and relieving_date.month == month_map[month]: - end_day = relieving_date.day + 1 - - dates_of_month = [ - "{}-{}-{}".format(today.year, month_map[month], r) for r in range(start_day, end_day) - ] - month_start, month_end = dates_of_month[0], dates_of_month[-1] - - records = frappe.get_all( - "Attendance", - fields=["attendance_date", "employee"], - filters=[ - ["attendance_date", ">=", month_start], - ["attendance_date", "<=", month_end], - ["employee", "=", employee], - ["docstatus", "!=", 2], - ], - ) - - marked_days = [get_datetime(record.attendance_date) for record in records] - if cint(exclude_holidays): - holiday_dates = get_holiday_dates_for_employee(employee, month_start, month_end) - holidays = [get_datetime(record) for record in holiday_dates] - marked_days.extend(holidays) - - unmarked_days = [] - - for date in dates_of_month: - date_time = get_datetime(date) - if today.day <= date_time.day and today.month <= date_time.month: - break - if date_time not in marked_days: - unmarked_days.append(date) - - return unmarked_days diff --git a/erpnext/hr/doctype/attendance/attendance_calendar.js b/erpnext/hr/doctype/attendance/attendance_calendar.js deleted file mode 100644 index d9f6d2eb3e..0000000000 --- a/erpnext/hr/doctype/attendance/attendance_calendar.js +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -frappe.views.calendar["Attendance"] = { - options: { - header: { - left: 'prev,next today', - center: 'title', - right: 'month' - } - }, - get_events_method: "erpnext.hr.doctype.attendance.attendance.get_events" -}; diff --git a/erpnext/hr/doctype/attendance/attendance_dashboard.py b/erpnext/hr/doctype/attendance/attendance_dashboard.py deleted file mode 100644 index abed78fbbb..0000000000 --- a/erpnext/hr/doctype/attendance/attendance_dashboard.py +++ /dev/null @@ -1,2 +0,0 @@ -def get_data(): - return {"fieldname": "attendance", "transactions": [{"label": "", "items": ["Employee Checkin"]}]} diff --git a/erpnext/hr/doctype/attendance/attendance_list.js b/erpnext/hr/doctype/attendance/attendance_list.js deleted file mode 100644 index 3a5c591539..0000000000 --- a/erpnext/hr/doctype/attendance/attendance_list.js +++ /dev/null @@ -1,164 +0,0 @@ -frappe.listview_settings['Attendance'] = { - add_fields: ["status", "attendance_date"], - get_indicator: function (doc) { - if (["Present", "Work From Home"].includes(doc.status)) { - return [__(doc.status), "green", "status,=," + doc.status]; - } else if (["Absent", "On Leave"].includes(doc.status)) { - return [__(doc.status), "red", "status,=," + doc.status]; - } else if (doc.status == "Half Day") { - return [__(doc.status), "orange", "status,=," + doc.status]; - } - }, - - onload: function(list_view) { - let me = this; - const months = moment.months(); - list_view.page.add_inner_button(__("Mark Attendance"), function() { - let dialog = new frappe.ui.Dialog({ - title: __("Mark Attendance"), - fields: [{ - fieldname: 'employee', - label: __('For Employee'), - fieldtype: 'Link', - options: 'Employee', - get_query: () => { - return {query: "erpnext.controllers.queries.employee_query"}; - }, - reqd: 1, - onchange: function() { - dialog.set_df_property("unmarked_days", "hidden", 1); - dialog.set_df_property("status", "hidden", 1); - dialog.set_df_property("exclude_holidays", "hidden", 1); - dialog.set_df_property("month", "value", ''); - dialog.set_df_property("unmarked_days", "options", []); - dialog.no_unmarked_days_left = false; - } - }, - { - label: __("For Month"), - fieldtype: "Select", - fieldname: "month", - options: months, - reqd: 1, - onchange: function() { - if (dialog.fields_dict.employee.value && dialog.fields_dict.month.value) { - dialog.set_df_property("status", "hidden", 0); - dialog.set_df_property("exclude_holidays", "hidden", 0); - dialog.set_df_property("unmarked_days", "options", []); - dialog.no_unmarked_days_left = false; - me.get_multi_select_options( - dialog.fields_dict.employee.value, - dialog.fields_dict.month.value, - dialog.fields_dict.exclude_holidays.get_value() - ).then(options => { - if (options.length > 0) { - dialog.set_df_property("unmarked_days", "hidden", 0); - dialog.set_df_property("unmarked_days", "options", options); - } else { - dialog.no_unmarked_days_left = true; - } - }); - } - } - }, - { - label: __("Status"), - fieldtype: "Select", - fieldname: "status", - options: ["Present", "Absent", "Half Day", "Work From Home"], - hidden: 1, - reqd: 1, - - }, - { - label: __("Exclude Holidays"), - fieldtype: "Check", - fieldname: "exclude_holidays", - hidden: 1, - onchange: function() { - if (dialog.fields_dict.employee.value && dialog.fields_dict.month.value) { - dialog.set_df_property("status", "hidden", 0); - dialog.set_df_property("unmarked_days", "options", []); - dialog.no_unmarked_days_left = false; - me.get_multi_select_options( - dialog.fields_dict.employee.value, - dialog.fields_dict.month.value, - dialog.fields_dict.exclude_holidays.get_value() - ).then(options => { - if (options.length > 0) { - dialog.set_df_property("unmarked_days", "hidden", 0); - dialog.set_df_property("unmarked_days", "options", options); - } else { - dialog.no_unmarked_days_left = true; - } - }); - } - } - }, - { - label: __("Unmarked Attendance for days"), - fieldname: "unmarked_days", - fieldtype: "MultiCheck", - options: [], - columns: 2, - hidden: 1 - }], - primary_action(data) { - if (cur_dialog.no_unmarked_days_left) { - frappe.msgprint(__("Attendance for the month of {0} , has already been marked for the Employee {1}", - [dialog.fields_dict.month.value, dialog.fields_dict.employee.value])); - } else { - frappe.confirm(__('Mark attendance as {0} for {1} on selected dates?', [data.status, data.month]), () => { - frappe.call({ - method: "erpnext.hr.doctype.attendance.attendance.mark_bulk_attendance", - args: { - data: data - }, - callback: function (r) { - if (r.message === 1) { - frappe.show_alert({ - message: __("Attendance Marked"), - indicator: 'blue' - }); - cur_dialog.hide(); - } - } - }); - }); - } - dialog.hide(); - list_view.refresh(); - }, - primary_action_label: __('Mark Attendance') - - }); - dialog.show(); - }); - }, - - get_multi_select_options: function(employee, month, exclude_holidays) { - return new Promise(resolve => { - frappe.call({ - method: 'erpnext.hr.doctype.attendance.attendance.get_unmarked_days', - async: false, - args: { - employee: employee, - month: month, - exclude_holidays: exclude_holidays - } - }).then(r => { - var options = []; - for (var d in r.message) { - var momentObj = moment(r.message[d], 'YYYY-MM-DD'); - var date = momentObj.format('DD-MM-YYYY'); - options.push({ - "label": date, - "value": r.message[d], - "checked": 1 - }); - } - resolve(options); - }); - }); - } -}; diff --git a/erpnext/hr/doctype/attendance/test_attendance.py b/erpnext/hr/doctype/attendance/test_attendance.py deleted file mode 100644 index c85ec6551a..0000000000 --- a/erpnext/hr/doctype/attendance/test_attendance.py +++ /dev/null @@ -1,232 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors -# See license.txt - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import ( - add_days, - add_months, - get_last_day, - get_year_ending, - get_year_start, - getdate, - nowdate, -) - -from erpnext.hr.doctype.attendance.attendance import ( - DuplicateAttendanceError, - OverlappingShiftAttendanceError, - get_month_map, - get_unmarked_days, - mark_attendance, -) -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.leave_application.test_leave_application import get_first_sunday - -test_records = frappe.get_test_records("Attendance") - - -class TestAttendance(FrappeTestCase): - def setUp(self): - from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list - - from_date = get_year_start(getdate()) - to_date = get_year_ending(getdate()) - self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date) - frappe.db.delete("Attendance") - - def test_duplicate_attendance(self): - employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company") - date = nowdate() - - mark_attendance(employee, date, "Present") - attendance = frappe.get_doc( - { - "doctype": "Attendance", - "employee": employee, - "attendance_date": date, - "status": "Absent", - "company": "_Test Company", - } - ) - - self.assertRaises(DuplicateAttendanceError, attendance.insert) - - def test_duplicate_attendance_with_shift(self): - from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type - - employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company") - date = nowdate() - - shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00") - mark_attendance(employee, date, "Present", shift=shift_1.name) - - # attendance record with shift - attendance = frappe.get_doc( - { - "doctype": "Attendance", - "employee": employee, - "attendance_date": date, - "status": "Absent", - "company": "_Test Company", - "shift": shift_1.name, - } - ) - - self.assertRaises(DuplicateAttendanceError, attendance.insert) - - # attendance record without any shift - attendance = frappe.get_doc( - { - "doctype": "Attendance", - "employee": employee, - "attendance_date": date, - "status": "Absent", - "company": "_Test Company", - } - ) - - self.assertRaises(DuplicateAttendanceError, attendance.insert) - - def test_overlapping_shift_attendance_validation(self): - from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type - - employee = make_employee("test_overlap_attendance@example.com", company="_Test Company") - date = nowdate() - - shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00") - shift_2 = setup_shift_type(shift_type="Shift 2", start_time="09:30:00", end_time="11:00:00") - - mark_attendance(employee, date, "Present", shift=shift_1.name) - - # attendance record with overlapping shift - attendance = frappe.get_doc( - { - "doctype": "Attendance", - "employee": employee, - "attendance_date": date, - "status": "Absent", - "company": "_Test Company", - "shift": shift_2.name, - } - ) - - self.assertRaises(OverlappingShiftAttendanceError, attendance.insert) - - def test_allow_attendance_with_different_shifts(self): - # allows attendance with 2 different non-overlapping shifts - from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type - - employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company") - date = nowdate() - - shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00") - shift_2 = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="12:00:00") - - mark_attendance(employee, date, "Present", shift_1.name) - frappe.get_doc( - { - "doctype": "Attendance", - "employee": employee, - "attendance_date": date, - "status": "Absent", - "company": "_Test Company", - "shift": shift_2.name, - } - ).insert() - - def test_mark_absent(self): - employee = make_employee("test_mark_absent@example.com") - date = nowdate() - - attendance = mark_attendance(employee, date, "Absent") - fetch_attendance = frappe.get_value( - "Attendance", {"employee": employee, "attendance_date": date, "status": "Absent"} - ) - self.assertEqual(attendance, fetch_attendance) - - def test_unmarked_days(self): - first_sunday = get_first_sunday( - self.holiday_list, for_date=get_last_day(add_months(getdate(), -1)) - ) - attendance_date = add_days(first_sunday, 1) - - employee = make_employee( - "test_unmarked_days@example.com", date_of_joining=add_days(attendance_date, -1) - ) - frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list) - - mark_attendance(employee, attendance_date, "Present") - month_name = get_month_name(attendance_date) - - unmarked_days = get_unmarked_days(employee, month_name) - unmarked_days = [getdate(date) for date in unmarked_days] - - # attendance already marked for the day - self.assertNotIn(attendance_date, unmarked_days) - # attendance unmarked - self.assertIn(getdate(add_days(attendance_date, 1)), unmarked_days) - # holiday considered in unmarked days - self.assertIn(first_sunday, unmarked_days) - - def test_unmarked_days_excluding_holidays(self): - first_sunday = get_first_sunday( - self.holiday_list, for_date=get_last_day(add_months(getdate(), -1)) - ) - attendance_date = add_days(first_sunday, 1) - - employee = make_employee( - "test_unmarked_days@example.com", date_of_joining=add_days(attendance_date, -1) - ) - frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list) - - mark_attendance(employee, attendance_date, "Present") - month_name = get_month_name(attendance_date) - - unmarked_days = get_unmarked_days(employee, month_name, exclude_holidays=True) - unmarked_days = [getdate(date) for date in unmarked_days] - - # attendance already marked for the day - self.assertNotIn(attendance_date, unmarked_days) - # attendance unmarked - self.assertIn(getdate(add_days(attendance_date, 1)), unmarked_days) - # holidays not considered in unmarked days - self.assertNotIn(first_sunday, unmarked_days) - - def test_unmarked_days_as_per_joining_and_relieving_dates(self): - first_sunday = get_first_sunday( - self.holiday_list, for_date=get_last_day(add_months(getdate(), -1)) - ) - date = add_days(first_sunday, 1) - - doj = add_days(date, 1) - relieving_date = add_days(date, 5) - employee = make_employee( - "test_unmarked_days_as_per_doj@example.com", date_of_joining=doj, relieving_date=relieving_date - ) - - frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list) - - attendance_date = add_days(date, 2) - mark_attendance(employee, attendance_date, "Present") - month_name = get_month_name(attendance_date) - - unmarked_days = get_unmarked_days(employee, month_name) - unmarked_days = [getdate(date) for date in unmarked_days] - - # attendance already marked for the day - self.assertNotIn(attendance_date, unmarked_days) - # date before doj not in unmarked days - self.assertNotIn(add_days(doj, -1), unmarked_days) - # date after relieving not in unmarked days - self.assertNotIn(add_days(relieving_date, 1), unmarked_days) - - def tearDown(self): - frappe.db.rollback() - - -def get_month_name(date): - month_number = date.month - for month, number in get_month_map().items(): - if number == month_number: - return month diff --git a/erpnext/hr/doctype/attendance/test_records.json b/erpnext/hr/doctype/attendance/test_records.json deleted file mode 100644 index 096f95c6f7..0000000000 --- a/erpnext/hr/doctype/attendance/test_records.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "doctype": "Attendance", - "name": "_Test Attendance 1", - "employee": "_T-Employee-00001", - "status": "Present", - "attendance_date": "2014-02-01", - "company": "_Test Company" - } -] diff --git a/erpnext/hr/doctype/attendance_request/__init__.py b/erpnext/hr/doctype/attendance_request/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/attendance_request/attendance_request.js b/erpnext/hr/doctype/attendance_request/attendance_request.js deleted file mode 100644 index 2d25d14181..0000000000 --- a/erpnext/hr/doctype/attendance_request/attendance_request.js +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -cur_frm.add_fetch('employee', 'company', 'company'); - -frappe.ui.form.on('Attendance Request', { - half_day: function(frm) { - if(frm.doc.half_day == 1){ - frm.set_df_property('half_day_date', 'reqd', true); - } - else{ - frm.set_df_property('half_day_date', 'reqd', false); - } - } -}); diff --git a/erpnext/hr/doctype/attendance_request/attendance_request.json b/erpnext/hr/doctype/attendance_request/attendance_request.json deleted file mode 100644 index 837a87824f..0000000000 --- a/erpnext/hr/doctype/attendance_request/attendance_request.json +++ /dev/null @@ -1,190 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-ARQ-.YY.-.MM.-.#####", - "creation": "2018-04-13 15:37:40.918990", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "department", - "column_break_5", - "company", - "from_date", - "to_date", - "half_day", - "half_day_date", - "reason_section", - "reason", - "column_break_4", - "explanation", - "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 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "fieldname": "column_break_5", - "fieldtype": "Column Break" - }, - { - "fieldname": "from_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "From Date", - "reqd": 1 - }, - { - "fieldname": "to_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "To Date", - "reqd": 1 - }, - { - "default": "0", - "fieldname": "half_day", - "fieldtype": "Check", - "label": "Half Day" - }, - { - "depends_on": "half_day", - "fieldname": "half_day_date", - "fieldtype": "Date", - "label": "Half Day Date" - }, - { - "fieldname": "reason_section", - "fieldtype": "Section Break", - "label": "Reason" - }, - { - "fieldname": "reason", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Reason", - "options": "Work From Home\nOn Duty", - "reqd": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "fieldname": "explanation", - "fieldtype": "Small Text", - "label": "Explanation" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Attendance Request", - "print_hide": 1, - "read_only": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2019-12-16 11:49:26.943173", - "modified_by": "Administrator", - "module": "HR", - "name": "Attendance Request", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/attendance_request/attendance_request.py b/erpnext/hr/doctype/attendance_request/attendance_request.py deleted file mode 100644 index 78652f669d..0000000000 --- a/erpnext/hr/doctype/attendance_request/attendance_request.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import add_days, date_diff, getdate - -from erpnext.hr.doctype.employee.employee import is_holiday -from erpnext.hr.utils import validate_active_employee, validate_dates - - -class AttendanceRequest(Document): - def validate(self): - validate_active_employee(self.employee) - validate_dates(self, self.from_date, self.to_date) - if self.half_day: - if not getdate(self.from_date) <= getdate(self.half_day_date) <= getdate(self.to_date): - frappe.throw(_("Half day date should be in between from date and to date")) - - def on_submit(self): - self.create_attendance() - - def on_cancel(self): - attendance_list = frappe.get_list( - "Attendance", {"employee": self.employee, "attendance_request": self.name} - ) - if attendance_list: - for attendance in attendance_list: - attendance_obj = frappe.get_doc("Attendance", attendance["name"]) - attendance_obj.cancel() - - def create_attendance(self): - request_days = date_diff(self.to_date, self.from_date) + 1 - for number in range(request_days): - attendance_date = add_days(self.from_date, number) - skip_attendance = self.validate_if_attendance_not_applicable(attendance_date) - if not skip_attendance: - attendance = frappe.new_doc("Attendance") - attendance.employee = self.employee - attendance.employee_name = self.employee_name - if self.half_day and date_diff(getdate(self.half_day_date), getdate(attendance_date)) == 0: - attendance.status = "Half Day" - elif self.reason == "Work From Home": - attendance.status = "Work From Home" - else: - attendance.status = "Present" - attendance.attendance_date = attendance_date - attendance.company = self.company - attendance.attendance_request = self.name - attendance.save(ignore_permissions=True) - attendance.submit() - - def validate_if_attendance_not_applicable(self, attendance_date): - # Check if attendance_date is a Holiday - if is_holiday(self.employee, attendance_date): - frappe.msgprint( - _("Attendance not submitted for {0} as it is a Holiday.").format(attendance_date), alert=1 - ) - return True - - # Check if employee on Leave - leave_record = frappe.db.sql( - """select half_day from `tabLeave Application` - where employee = %s and %s between from_date and to_date - and docstatus = 1""", - (self.employee, attendance_date), - as_dict=True, - ) - if leave_record: - frappe.msgprint( - _("Attendance not submitted for {0} as {1} on leave.").format(attendance_date, self.employee), - alert=1, - ) - return True - - return False diff --git a/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py b/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py deleted file mode 100644 index 059725cb44..0000000000 --- a/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py +++ /dev/null @@ -1,2 +0,0 @@ -def get_data(): - return {"fieldname": "attendance_request", "transactions": [{"items": ["Attendance"]}]} diff --git a/erpnext/hr/doctype/attendance_request/test_attendance_request.py b/erpnext/hr/doctype/attendance_request/test_attendance_request.py deleted file mode 100644 index ee436f5068..0000000000 --- a/erpnext/hr/doctype/attendance_request/test_attendance_request.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest -from datetime import date - -import frappe -from frappe.utils import nowdate - -test_dependencies = ["Employee"] - - -class TestAttendanceRequest(unittest.TestCase): - def setUp(self): - for doctype in ["Attendance Request", "Attendance"]: - frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype)) - - def tearDown(self): - frappe.db.rollback() - - def test_on_duty_attendance_request(self): - "Test creation/updation of Attendace from Attendance Request, on duty." - today = nowdate() - employee = get_employee() - attendance_request = frappe.new_doc("Attendance Request") - attendance_request.employee = employee.name - attendance_request.from_date = date(date.today().year, 1, 1) - attendance_request.to_date = date(date.today().year, 1, 2) - attendance_request.reason = "On Duty" - attendance_request.company = "_Test Company" - attendance_request.insert() - attendance_request.submit() - - attendance = frappe.db.get_value( - "Attendance", - filters={ - "attendance_request": attendance_request.name, - "attendance_date": date(date.today().year, 1, 1), - }, - fieldname=["status", "docstatus"], - as_dict=True, - ) - self.assertEqual(attendance.status, "Present") - self.assertEqual(attendance.docstatus, 1) - - # cancelling attendance request cancels linked attendances - attendance_request.cancel() - - # cancellation alters docname - # fetch attendance value again to avoid stale docname - attendance_docstatus = frappe.db.get_value( - "Attendance", - filters={ - "attendance_request": attendance_request.name, - "attendance_date": date(date.today().year, 1, 1), - }, - fieldname="docstatus", - ) - self.assertEqual(attendance_docstatus, 2) - - def test_work_from_home_attendance_request(self): - "Test creation/updation of Attendace from Attendance Request, work from home." - today = nowdate() - employee = get_employee() - attendance_request = frappe.new_doc("Attendance Request") - attendance_request.employee = employee.name - attendance_request.from_date = date(date.today().year, 1, 1) - attendance_request.to_date = date(date.today().year, 1, 2) - attendance_request.reason = "Work From Home" - attendance_request.company = "_Test Company" - attendance_request.insert() - attendance_request.submit() - - attendance_status = frappe.db.get_value( - "Attendance", - filters={ - "attendance_request": attendance_request.name, - "attendance_date": date(date.today().year, 1, 1), - }, - fieldname="status", - ) - self.assertEqual(attendance_status, "Work From Home") - - attendance_request.cancel() - - # cancellation alters docname - # fetch attendance value again to avoid stale docname - attendance_docstatus = frappe.db.get_value( - "Attendance", - filters={ - "attendance_request": attendance_request.name, - "attendance_date": date(date.today().year, 1, 1), - }, - fieldname="docstatus", - ) - self.assertEqual(attendance_docstatus, 2) - - -def get_employee(): - return frappe.get_doc("Employee", "_T-Employee-00001") diff --git a/erpnext/hr/doctype/compensatory_leave_request/__init__.py b/erpnext/hr/doctype/compensatory_leave_request/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.js b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.js deleted file mode 100644 index 1baa1e04e3..0000000000 --- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Compensatory Leave Request', { - refresh: function(frm) { - frm.set_query("leave_type", function() { - return { - filters: { - "is_compensatory": true - } - }; - }); - }, - half_day: function(frm) { - if(frm.doc.half_day == 1){ - frm.set_df_property('half_day_date', 'reqd', true); - } - else{ - frm.set_df_property('half_day_date', 'reqd', false); - } - } -}); diff --git a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.json b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.json deleted file mode 100644 index 3c6374fcde..0000000000 --- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.json +++ /dev/null @@ -1,575 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "HR-CMP-.YY.-.MM.-.#####", - "beta": 0, - "creation": "2018-04-13 14:51:39.326768", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "leave_type", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Leave Type", - "length": 0, - "no_copy": 0, - "options": "Leave Type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "leave_allocation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Leave Allocation", - "length": 0, - "no_copy": 0, - "options": "Leave Allocation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "worked_on", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Worked On Holiday", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "work_from_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Work From Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "work_end_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Work End Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "half_day", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Half Day", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "half_day", - "fieldname": "half_day_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Half Day Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reason", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reason", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Compensatory Leave Request", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 16:15:36.266924", - "modified_by": "Administrator", - "module": "HR", - "name": "Compensatory Leave Request", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py deleted file mode 100644 index d233226e66..0000000000 --- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import add_days, cint, date_diff, format_date, getdate - -from erpnext.hr.utils import ( - create_additional_leave_ledger_entry, - get_holiday_dates_for_employee, - get_leave_period, - validate_active_employee, - validate_dates, - validate_overlap, -) - - -class CompensatoryLeaveRequest(Document): - def validate(self): - validate_active_employee(self.employee) - validate_dates(self, self.work_from_date, self.work_end_date) - if self.half_day: - if not self.half_day_date: - frappe.throw(_("Half Day Date is mandatory")) - if ( - not getdate(self.work_from_date) <= getdate(self.half_day_date) <= getdate(self.work_end_date) - ): - frappe.throw(_("Half Day Date should be in between Work From Date and Work End Date")) - validate_overlap(self, self.work_from_date, self.work_end_date) - self.validate_holidays() - self.validate_attendance() - if not self.leave_type: - frappe.throw(_("Leave Type is madatory")) - - def validate_attendance(self): - attendance = frappe.get_all( - "Attendance", - filters={ - "attendance_date": ["between", (self.work_from_date, self.work_end_date)], - "status": "Present", - "docstatus": 1, - "employee": self.employee, - }, - fields=["attendance_date", "status"], - ) - - if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1: - frappe.throw(_("You are not present all day(s) between compensatory leave request days")) - - def validate_holidays(self): - holidays = get_holiday_dates_for_employee(self.employee, self.work_from_date, self.work_end_date) - if len(holidays) < date_diff(self.work_end_date, self.work_from_date) + 1: - if date_diff(self.work_end_date, self.work_from_date): - msg = _("The days between {0} to {1} are not valid holidays.").format( - frappe.bold(format_date(self.work_from_date)), frappe.bold(format_date(self.work_end_date)) - ) - else: - msg = _("{0} is not a holiday.").format(frappe.bold(format_date(self.work_from_date))) - - frappe.throw(msg) - - def on_submit(self): - company = frappe.db.get_value("Employee", self.employee, "company") - date_difference = date_diff(self.work_end_date, self.work_from_date) + 1 - if self.half_day: - date_difference -= 0.5 - leave_period = get_leave_period(self.work_from_date, self.work_end_date, company) - if leave_period: - leave_allocation = self.get_existing_allocation_for_period(leave_period) - if leave_allocation: - leave_allocation.new_leaves_allocated += date_difference - leave_allocation.validate() - leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated) - leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated) - - # generate additional ledger entry for the new compensatory leaves off - create_additional_leave_ledger_entry( - leave_allocation, date_difference, add_days(self.work_end_date, 1) - ) - - else: - leave_allocation = self.create_leave_allocation(leave_period, date_difference) - self.db_set("leave_allocation", leave_allocation.name) - else: - frappe.throw( - _("There is no leave period in between {0} and {1}").format( - format_date(self.work_from_date), format_date(self.work_end_date) - ) - ) - - def on_cancel(self): - if self.leave_allocation: - date_difference = date_diff(self.work_end_date, self.work_from_date) + 1 - if self.half_day: - date_difference -= 0.5 - leave_allocation = frappe.get_doc("Leave Allocation", self.leave_allocation) - if leave_allocation: - leave_allocation.new_leaves_allocated -= date_difference - if leave_allocation.new_leaves_allocated - date_difference <= 0: - leave_allocation.new_leaves_allocated = 0 - leave_allocation.validate() - leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated) - leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated) - - # create reverse entry on cancelation - create_additional_leave_ledger_entry( - leave_allocation, date_difference * -1, add_days(self.work_end_date, 1) - ) - - def get_existing_allocation_for_period(self, leave_period): - leave_allocation = frappe.db.sql( - """ - select name - from `tabLeave Allocation` - where employee=%(employee)s and leave_type=%(leave_type)s - and docstatus=1 - and (from_date between %(from_date)s and %(to_date)s - or to_date between %(from_date)s and %(to_date)s - or (from_date < %(from_date)s and to_date > %(to_date)s)) - """, - { - "from_date": leave_period[0].from_date, - "to_date": leave_period[0].to_date, - "employee": self.employee, - "leave_type": self.leave_type, - }, - as_dict=1, - ) - - if leave_allocation: - return frappe.get_doc("Leave Allocation", leave_allocation[0].name) - else: - return False - - def create_leave_allocation(self, leave_period, date_difference): - is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward") - allocation = frappe.get_doc( - dict( - doctype="Leave Allocation", - employee=self.employee, - employee_name=self.employee_name, - leave_type=self.leave_type, - from_date=add_days(self.work_end_date, 1), - to_date=leave_period[0].to_date, - carry_forward=cint(is_carry_forward), - new_leaves_allocated=date_difference, - total_leaves_allocated=date_difference, - description=self.reason, - ) - ) - allocation.insert(ignore_permissions=True) - allocation.submit() - return allocation diff --git a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py deleted file mode 100644 index 7bbec293f4..0000000000 --- a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import add_days, add_months, today - -from erpnext.hr.doctype.attendance_request.test_attendance_request import get_employee -from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on -from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period - -test_dependencies = ["Employee"] - - -class TestCompensatoryLeaveRequest(unittest.TestCase): - def setUp(self): - frappe.db.sql(""" delete from `tabCompensatory Leave Request`""") - frappe.db.sql(""" delete from `tabLeave Ledger Entry`""") - frappe.db.sql(""" delete from `tabLeave Allocation`""") - frappe.db.sql( - """ delete from `tabAttendance` where attendance_date in {0} """.format( - (today(), add_days(today(), -1)) - ) - ) # nosec - create_leave_period(add_months(today(), -3), add_months(today(), 3), "_Test Company") - create_holiday_list() - - employee = get_employee() - employee.holiday_list = "_Test Compensatory Leave" - employee.save() - - def test_leave_balance_on_submit(self): - """check creation of leave allocation on submission of compensatory leave request""" - employee = get_employee() - mark_attendance(employee) - compensatory_leave_request = get_compensatory_leave_request(employee.name) - - before = get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, today()) - compensatory_leave_request.submit() - - self.assertEqual( - get_leave_balance_on( - employee.name, compensatory_leave_request.leave_type, add_days(today(), 1) - ), - before + 1, - ) - - def test_leave_allocation_update_on_submit(self): - employee = get_employee() - mark_attendance(employee, date=add_days(today(), -1)) - compensatory_leave_request = get_compensatory_leave_request( - employee.name, leave_date=add_days(today(), -1) - ) - compensatory_leave_request.submit() - - # leave allocation creation on submit - leaves_allocated = frappe.db.get_value( - "Leave Allocation", - {"name": compensatory_leave_request.leave_allocation}, - ["total_leaves_allocated"], - ) - self.assertEqual(leaves_allocated, 1) - - mark_attendance(employee) - compensatory_leave_request = get_compensatory_leave_request(employee.name) - compensatory_leave_request.submit() - - # leave allocation updates on submission of second compensatory leave request - leaves_allocated = frappe.db.get_value( - "Leave Allocation", - {"name": compensatory_leave_request.leave_allocation}, - ["total_leaves_allocated"], - ) - self.assertEqual(leaves_allocated, 2) - - def test_creation_of_leave_ledger_entry_on_submit(self): - """check creation of leave ledger entry on submission of leave request""" - employee = get_employee() - mark_attendance(employee) - compensatory_leave_request = get_compensatory_leave_request(employee.name) - compensatory_leave_request.submit() - - filters = dict(transaction_name=compensatory_leave_request.leave_allocation) - leave_ledger_entry = frappe.get_all("Leave Ledger Entry", fields="*", filters=filters) - - self.assertEqual(len(leave_ledger_entry), 1) - self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee) - self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type) - self.assertEqual(leave_ledger_entry[0].leaves, 1) - - # check reverse leave ledger entry on cancellation - compensatory_leave_request.cancel() - leave_ledger_entry = frappe.get_all( - "Leave Ledger Entry", fields="*", filters=filters, order_by="creation desc" - ) - - self.assertEqual(len(leave_ledger_entry), 2) - self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee) - self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type) - self.assertEqual(leave_ledger_entry[0].leaves, -1) - - -def get_compensatory_leave_request(employee, leave_date=today()): - prev_comp_leave_req = frappe.db.get_value( - "Compensatory Leave Request", - dict( - leave_type="Compensatory Off", - work_from_date=leave_date, - work_end_date=leave_date, - employee=employee, - ), - "name", - ) - if prev_comp_leave_req: - return frappe.get_doc("Compensatory Leave Request", prev_comp_leave_req) - - return frappe.get_doc( - dict( - doctype="Compensatory Leave Request", - employee=employee, - leave_type="Compensatory Off", - work_from_date=leave_date, - work_end_date=leave_date, - reason="test", - ) - ).insert() - - -def mark_attendance(employee, date=today(), status="Present"): - if not frappe.db.exists( - dict(doctype="Attendance", employee=employee.name, attendance_date=date, status="Present") - ): - attendance = frappe.get_doc( - {"doctype": "Attendance", "employee": employee.name, "attendance_date": date, "status": status} - ) - attendance.save() - attendance.submit() - - -def create_holiday_list(): - if frappe.db.exists("Holiday List", "_Test Compensatory Leave"): - return - - holiday_list = frappe.get_doc( - { - "doctype": "Holiday List", - "from_date": add_months(today(), -3), - "to_date": add_months(today(), 3), - "holidays": [ - {"description": "Test Holiday", "holiday_date": today()}, - {"description": "Test Holiday 1", "holiday_date": add_days(today(), -1)}, - ], - "holiday_list_name": "_Test Compensatory Leave", - } - ) - holiday_list.save() diff --git a/erpnext/hr/doctype/daily_work_summary/__init__.py b/erpnext/hr/doctype/daily_work_summary/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.js b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.js deleted file mode 100644 index 82364801ce..0000000000 --- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Daily Work Summary', { - refresh: function (frm) { - - } -}); diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json deleted file mode 100644 index 259416edd4..0000000000 --- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json +++ /dev/null @@ -1,175 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-11-08 04:58:20.001780", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "daily_work_summary_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Daily Work Summary Group", - "length": 0, - "no_copy": 0, - "options": "Daily Work Summary Group", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Open", - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Open\nSent", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "email_sent_to", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Email Sent To", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 1, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-02-16 11:02:06.727749", - "modified_by": "Administrator", - "module": "HR", - "name": "Daily Work Summary", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Employee", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 - } - ], - "quick_entry": 1, - "read_only": 1, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py deleted file mode 100644 index bcb0161841..0000000000 --- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from email_reply_parser import EmailReplyParser -from frappe import _ -from frappe.model.document import Document -from frappe.utils import global_date_format - - -class DailyWorkSummary(Document): - def send_mails(self, dws_group, emails): - """Send emails to get daily work summary to all users \ - in selected daily work summary group""" - incoming_email_account = frappe.db.get_value( - "Email Account", dict(enable_incoming=1, default_incoming=1), "email_id" - ) - - self.db_set("email_sent_to", "\n".join(emails)) - frappe.sendmail( - recipients=emails, - message=dws_group.message, - subject=dws_group.subject, - reference_doctype=self.doctype, - reference_name=self.name, - reply_to=incoming_email_account, - ) - - def send_summary(self): - """Send summary of all replies. Called at midnight""" - args = self.get_message_details() - emails = get_user_emails_from_group(self.daily_work_summary_group) - frappe.sendmail( - recipients=emails, - template="daily_work_summary", - args=args, - subject=_(self.daily_work_summary_group), - reference_doctype=self.doctype, - reference_name=self.name, - ) - - self.db_set("status", "Sent") - - def get_message_details(self): - """Return args for template""" - dws_group = frappe.get_doc("Daily Work Summary Group", self.daily_work_summary_group) - - replies = frappe.get_all( - "Communication", - fields=["content", "text_content", "sender"], - filters=dict( - reference_doctype=self.doctype, - reference_name=self.name, - communication_type="Communication", - sent_or_received="Received", - ), - order_by="creation asc", - ) - - did_not_reply = self.email_sent_to.split() - - for d in replies: - user = frappe.db.get_values( - "User", {"email": d.sender}, ["full_name", "user_image"], as_dict=True - ) - - d.sender_name = user[0].full_name if user else d.sender - d.image = user[0].image if user and user[0].image else None - - original_image = d.image - # make thumbnail image - try: - if original_image: - file_name = frappe.get_list("File", {"file_url": original_image}) - - if file_name: - file_name = file_name[0].name - file_doc = frappe.get_doc("File", file_name) - thumbnail_image = file_doc.make_thumbnail( - set_as_thumbnail=False, width=100, height=100, crop=True - ) - d.image = thumbnail_image - except Exception: - d.image = original_image - - if d.sender in did_not_reply: - did_not_reply.remove(d.sender) - if d.text_content: - d.content = frappe.utils.md_to_html(EmailReplyParser.parse_reply(d.text_content)) - - did_not_reply = [ - (frappe.db.get_value("User", {"email": email}, "full_name") or email) for email in did_not_reply - ] - - return dict( - replies=replies, - original_message=dws_group.message, - title=_("Work Summary for {0}").format(global_date_format(self.creation)), - did_not_reply=", ".join(did_not_reply) or "", - did_not_reply_title=_("No replies from"), - ) - - -def get_user_emails_from_group(group): - """Returns list of email of enabled users from the given group - - :param group: Daily Work Summary Group `name`""" - group_doc = group - if isinstance(group_doc, str): - group_doc = frappe.get_doc("Daily Work Summary Group", group) - - emails = get_users_email(group_doc) - - return emails - - -def get_users_email(doc): - return [d.email for d in doc.users if frappe.db.get_value("User", d.user, "enabled")] diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py deleted file mode 100644 index 703436529d..0000000000 --- a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import os -import unittest - -import frappe -import frappe.utils - -# test_records = frappe.get_test_records('Daily Work Summary') - - -class TestDailyWorkSummary(unittest.TestCase): - def test_email_trigger(self): - self.setup_and_prepare_test() - for d in self.users: - # check that email is sent to users - if d.message: - self.assertTrue( - d.email in [d.recipient for d in self.emails if self.groups.subject in d.message] - ) - - def test_email_trigger_failed(self): - hour = "00:00" - if frappe.utils.nowtime().split(":")[0] == "00": - hour = "01:00" - - self.setup_and_prepare_test(hour) - - for d in self.users: - # check that email is not sent to users - self.assertFalse( - d.email in [d.recipient for d in self.emails if self.groups.subject in d.message] - ) - - def test_incoming(self): - # get test mail with message-id as in-reply-to - self.setup_and_prepare_test() - with open(os.path.join(os.path.dirname(__file__), "test_data", "test-reply.raw"), "r") as f: - if not self.emails: - return - test_mails = [ - f.read() - .replace("{{ sender }}", self.users[-1].email) - .replace("{{ message_id }}", self.emails[-1].message_id) - ] - - # pull the mail - email_account = frappe.get_doc("Email Account", "_Test Email Account 1") - email_account.db_set("enable_incoming", 1) - email_account.receive(test_mails=test_mails) - - daily_work_summary = frappe.get_doc( - "Daily Work Summary", frappe.get_all("Daily Work Summary")[0].name - ) - - args = daily_work_summary.get_message_details() - - self.assertTrue("I built Daily Work Summary!" in args.get("replies")[0].content) - - def setup_and_prepare_test(self, hour=None): - frappe.db.sql("delete from `tabDaily Work Summary`") - frappe.db.sql("delete from `tabEmail Queue`") - frappe.db.sql("delete from `tabEmail Queue Recipient`") - frappe.db.sql("delete from `tabCommunication`") - frappe.db.sql("delete from `tabDaily Work Summary Group`") - - self.users = frappe.get_all( - "User", fields=["email"], filters=dict(email=("!=", "test@example.com")) - ) - self.setup_groups(hour) - - from erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group import trigger_emails - - trigger_emails() - - # check if emails are created - - self.emails = frappe.db.sql( - """select r.recipient, q.message, q.message_id \ - from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r \ - where q.name = r.parent""", - as_dict=1, - ) - - def setup_groups(self, hour=None): - # setup email to trigger at this hour - if not hour: - hour = frappe.utils.nowtime().split(":")[0] - hour = hour + ":00" - - groups = frappe.get_doc( - dict( - doctype="Daily Work Summary Group", - name="Daily Work Summary", - users=self.users, - send_emails_at=hour, - subject="this is a subject for testing summary emails", - message="this is a message for testing summary emails", - ) - ) - groups.insert() - - self.groups = groups - self.groups.save() diff --git a/erpnext/hr/doctype/daily_work_summary/test_data/test-reply.raw b/erpnext/hr/doctype/daily_work_summary/test_data/test-reply.raw deleted file mode 100644 index ba01bc2827..0000000000 --- a/erpnext/hr/doctype/daily_work_summary/test_data/test-reply.raw +++ /dev/null @@ -1,75 +0,0 @@ -From: {{ sender }} -Content-Type: multipart/alternative; - boundary="Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361" -Message-Id: <07D687F6-10AA-4B9F-82DE-27753096164E@gmail.com> -Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\)) -X-Smtp-Server: 73CC8281-7E8F-4B47-8324-D5DA86EEDD4F -Subject: Re: What did you work on today? -Date: Thu, 10 Nov 2016 16:04:43 +0530 -X-Universally-Unique-Identifier: A4D9669F-179C-42D8-A3D3-AA6A8C49A6F2 -References: <{{ message_id }}> -To: test_in@iwebnotes.com -In-Reply-To: <{{ message_id }}> - - ---Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361 -Content-Transfer-Encoding: quoted-printable -Content-Type: text/plain; - charset=us-ascii - -I built Daily Work Summary! - -> On 10-Nov-2016, at 3:20 PM, Frappe wrote: ->=20 -> Please share what did you do today. If you reply by midnight, your = -response will be recorded! ->=20 -> This email was sent to rmehta@gmail.com -> Unsubscribe from this list = - -> Sent via ERPNext - - ---Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361 -Content-Transfer-Encoding: 7bit -Content-Type: text/html; - charset=us-ascii - -I built Daily Work Summary!

On 10-Nov-2016, at 3:20 PM, Frappe <test@erpnext.com> wrote:

- - - - -What did you work on today? - -
- -

Please share what did you do today. If you reply by midnight, your response will be recorded!

- -
- - - - - - -
-

---Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361-- diff --git a/erpnext/hr/doctype/daily_work_summary_group/__init__.py b/erpnext/hr/doctype/daily_work_summary_group/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.js b/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.js deleted file mode 100644 index 43206d5dcf..0000000000 --- a/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.js +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Daily Work Summary Group', { - refresh: function (frm) { - if (!frm.is_new()) { - frm.add_custom_button(__('Daily Work Summary'), function () { - frappe.set_route('List', 'Daily Work Summary'); - }); - } - } -}); diff --git a/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.json b/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.json deleted file mode 100644 index 562183f354..0000000000 --- a/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.json +++ /dev/null @@ -1,309 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "Prompt", - "beta": 0, - "creation": "2018-02-12 15:06:18.767239", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fieldname": "enabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Enabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_users", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Select Users", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "users", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Users", - "length": 0, - "no_copy": 0, - "options": "Daily Work Summary Group User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "send_emails_at", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Send Emails At", - "length": 0, - "no_copy": 0, - "options": "00:00\n01:00\n02:00\n03:00\n04:00\n05:00\n06:00\n07:00\n08:00\n09:00\n10:00\n11:00\n12:00\n13:00\n14:00\n15:00\n16:00\n17:00\n18:00\n19:00\n20:00\n21:00\n22:00\n23:00", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "holiday_list", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Holiday List", - "length": 0, - "no_copy": 0, - "options": "Holiday List", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mail_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reminder", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "What did you work on today?", - "fieldname": "subject", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subject", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "

Please share what did you do today. If you reply by midnight, your response will be recorded!

", - "fieldname": "message", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Message", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-02-16 10:56:03.998495", - "modified_by": "Administrator", - "module": "HR", - "name": "Daily Work Summary Group", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py b/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py deleted file mode 100644 index 4342f1c450..0000000000 --- a/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py +++ /dev/null @@ -1,55 +0,0 @@ -# # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# # For license information, please see license.txt - - -import frappe -import frappe.utils -from frappe import _ -from frappe.model.document import Document - -from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_user_emails_from_group -from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday - - -class DailyWorkSummaryGroup(Document): - def validate(self): - if self.users: - if not frappe.flags.in_test and not is_incoming_account_enabled(): - frappe.throw( - _("Please enable default incoming account before creating Daily Work Summary Group") - ) - - -def trigger_emails(): - """Send emails to Employees at the given hour asking - them what did they work on today""" - groups = frappe.get_all("Daily Work Summary Group") - for d in groups: - group_doc = frappe.get_doc("Daily Work Summary Group", d) - if ( - is_current_hour(group_doc.send_emails_at) - and not is_holiday(group_doc.holiday_list) - and group_doc.enabled - ): - emails = get_user_emails_from_group(group_doc) - # find emails relating to a company - if emails: - daily_work_summary = frappe.get_doc( - dict(doctype="Daily Work Summary", daily_work_summary_group=group_doc.name) - ).insert() - daily_work_summary.send_mails(group_doc, emails) - - -def is_current_hour(hour): - return frappe.utils.nowtime().split(":")[0] == hour.split(":")[0] - - -def send_summary(): - """Send summary to everyone""" - for d in frappe.get_all("Daily Work Summary", dict(status="Open")): - daily_work_summary = frappe.get_doc("Daily Work Summary", d.name) - daily_work_summary.send_summary() - - -def is_incoming_account_enabled(): - return frappe.db.get_value("Email Account", dict(enable_incoming=1, default_incoming=1)) diff --git a/erpnext/hr/doctype/daily_work_summary_group_user/__init__.py b/erpnext/hr/doctype/daily_work_summary_group_user/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.json b/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.json deleted file mode 100644 index b22c051dfc..0000000000 --- a/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-02-12 14:57:38.332692", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "user", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "user.email", - "fieldname": "email", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "email", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-05-16 22:43:00.481019", - "modified_by": "Administrator", - "module": "HR", - "name": "Daily Work Summary Group User", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py b/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py deleted file mode 100644 index 6e0809af33..0000000000 --- a/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class DailyWorkSummaryGroupUser(Document): - pass diff --git a/erpnext/hr/doctype/department_approver/__init__.py b/erpnext/hr/doctype/department_approver/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/department_approver/department_approver.json b/erpnext/hr/doctype/department_approver/department_approver.json deleted file mode 100644 index 317ecb3031..0000000000 --- a/erpnext/hr/doctype/department_approver/department_approver.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "hash", - "beta": 0, - "creation": "2018-04-08 16:31:02.433252", - "custom": 0, - "description": "", - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "approver", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Approver", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, - "width": "200" - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-04-17 11:05:46.294340", - "modified_by": "Administrator", - "module": "HR", - "name": "Department Approver", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py deleted file mode 100644 index 87bdddd6ff..0000000000 --- a/erpnext/hr/doctype/department_approver/department_approver.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document - - -class DepartmentApprover(Document): - pass - - -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def get_approvers(doctype, txt, searchfield, start, page_len, filters): - - if not filters.get("employee"): - frappe.throw(_("Please select Employee first.")) - - approvers = [] - department_details = {} - department_list = [] - employee = frappe.get_value( - "Employee", - filters.get("employee"), - ["employee_name", "department", "leave_approver", "expense_approver", "shift_request_approver"], - as_dict=True, - ) - - employee_department = filters.get("department") or employee.department - if employee_department: - department_details = frappe.db.get_value( - "Department", {"name": employee_department}, ["lft", "rgt"], as_dict=True - ) - if department_details: - department_list = frappe.db.sql( - """select name from `tabDepartment` where lft <= %s - and rgt >= %s - and disabled=0 - order by lft desc""", - (department_details.lft, department_details.rgt), - as_list=True, - ) - - if filters.get("doctype") == "Leave Application" and employee.leave_approver: - approvers.append( - frappe.db.get_value("User", employee.leave_approver, ["name", "first_name", "last_name"]) - ) - - if filters.get("doctype") == "Expense Claim" and employee.expense_approver: - approvers.append( - frappe.db.get_value("User", employee.expense_approver, ["name", "first_name", "last_name"]) - ) - - if filters.get("doctype") == "Shift Request" and employee.shift_request_approver: - approvers.append( - frappe.db.get_value( - "User", employee.shift_request_approver, ["name", "first_name", "last_name"] - ) - ) - - if filters.get("doctype") == "Leave Application": - parentfield = "leave_approvers" - field_name = "Leave Approver" - elif filters.get("doctype") == "Expense Claim": - parentfield = "expense_approvers" - field_name = "Expense Approver" - elif filters.get("doctype") == "Shift Request": - parentfield = "shift_request_approver" - field_name = "Shift Request Approver" - if department_list: - for d in department_list: - approvers += frappe.db.sql( - """select user.name, user.first_name, user.last_name from - tabUser user, `tabDepartment Approver` approver where - approver.parent = %s - and user.name like %s - and approver.parentfield = %s - and approver.approver=user.name""", - (d, "%" + txt + "%", parentfield), - as_list=True, - ) - - if len(approvers) == 0: - error_msg = _("Please set {0} for the Employee: {1}").format( - field_name, frappe.bold(employee.employee_name) - ) - if department_list: - error_msg += " " + _("or for Department: {0}").format(frappe.bold(employee_department)) - frappe.throw(error_msg, title=_(field_name + " Missing")) - - return set(tuple(approver) for approver in approvers) diff --git a/erpnext/hr/doctype/driver/__init__.py b/erpnext/hr/doctype/driver/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/driver/driver.js b/erpnext/hr/doctype/driver/driver.js deleted file mode 100644 index b3c24c1a0e..0000000000 --- a/erpnext/hr/doctype/driver/driver.js +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on("Driver", { - setup: function(frm) { - frm.set_query("transporter", function() { - return { - filters: { - is_transporter: 1 - } - }; - }); - }, - - refresh: function(frm) { - frm.set_query("address", function() { - return { - filters: { - is_your_company_address: !frm.doc.transporter ? 1 : 0 - } - }; - }); - }, - - transporter: function(frm, cdt, cdn) { - // this assumes that supplier's address has same title as supplier's name - frappe.db - .get_doc("Address", null, { address_title: frm.doc.transporter }) - .then(r => { - frappe.model.set_value(cdt, cdn, "address", r.name); - }) - .catch(err => { - console.log(err); - }); - } -}); diff --git a/erpnext/hr/doctype/driver/driver.json b/erpnext/hr/doctype/driver/driver.json deleted file mode 100644 index 0a670c0601..0000000000 --- a/erpnext/hr/doctype/driver/driver.json +++ /dev/null @@ -1,169 +0,0 @@ -{ - "allow_import": 1, - "allow_rename": 1, - "autoname": "naming_series:", - "creation": "2017-10-17 08:21:50.489773", - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "naming_series", - "full_name", - "status", - "transporter", - "column_break_2", - "employee", - "cell_number", - "address", - "license_details", - "license_number", - "column_break_8", - "issuing_date", - "column_break_10", - "expiry_date", - "driving_license_categories", - "driving_license_category" - ], - "fields": [ - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "options": "HR-DRI-.YYYY.-" - }, - { - "fieldname": "full_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Full Name", - "reqd": 1 - }, - { - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Status", - "options": "Active\nSuspended\nLeft", - "reqd": 1 - }, - { - "allow_in_quick_entry": 1, - "description": "Applicable for external driver", - "fieldname": "transporter", - "fieldtype": "Link", - "label": "Transporter", - "options": "Supplier" - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Employee", - "options": "Employee" - }, - { - "fieldname": "cell_number", - "fieldtype": "Data", - "label": "Cellphone Number" - }, - { - "fieldname": "license_details", - "fieldtype": "Section Break", - "label": "License Details" - }, - { - "fieldname": "license_number", - "fieldtype": "Data", - "label": "License Number" - }, - { - "fieldname": "column_break_8", - "fieldtype": "Column Break" - }, - { - "fieldname": "issuing_date", - "fieldtype": "Date", - "label": "Issuing Date" - }, - { - "fieldname": "column_break_10", - "fieldtype": "Column Break" - }, - { - "fieldname": "expiry_date", - "fieldtype": "Date", - "label": "Expiry Date" - }, - { - "fieldname": "driving_license_categories", - "fieldtype": "Section Break", - "label": "Driving License Categories" - }, - { - "fieldname": "driving_license_category", - "fieldtype": "Table", - "label": "Driving License Category", - "options": "Driving License Category" - }, - { - "fieldname": "address", - "fieldtype": "Link", - "label": "Address", - "options": "Address" - } - ], - "icon": "fa fa-user", - "modified": "2019-07-18 16:29:14.151380", - "modified_by": "Administrator", - "module": "HR", - "name": "Driver", - "name_case": "Title Case", - "owner": "Administrator", - "permissions": [ - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Fleet Manager", - "share": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "search_fields": "full_name", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "full_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/driver/driver.py b/erpnext/hr/doctype/driver/driver.py deleted file mode 100644 index 26981890ed..0000000000 --- a/erpnext/hr/doctype/driver/driver.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class Driver(Document): - pass diff --git a/erpnext/hr/doctype/driver/test_driver.py b/erpnext/hr/doctype/driver/test_driver.py deleted file mode 100644 index 22707293a0..0000000000 --- a/erpnext/hr/doctype/driver/test_driver.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestDriver(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/driving_license_category/__init__.py b/erpnext/hr/doctype/driving_license_category/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/driving_license_category/driving_license_category.json b/erpnext/hr/doctype/driving_license_category/driving_license_category.json deleted file mode 100644 index 8bc61a808a..0000000000 --- a/erpnext/hr/doctype/driving_license_category/driving_license_category.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "actions": [], - "creation": "2017-10-17 08:19:43.142329", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "class", - "description", - "issuing_date", - "expiry_date" - ], - "fields": [ - { - "fieldname": "class", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Driver licence class" - }, - { - "fieldname": "description", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Description" - }, - { - "fieldname": "issuing_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Issuing Date" - }, - { - "fieldname": "expiry_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Expiry Date" - } - ], - "istable": 1, - "links": [], - "modified": "2019-12-12 14:40:55.209022", - "modified_by": "Administrator", - "module": "HR", - "name": "Driving License Category", - "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/hr/doctype/driving_license_category/driving_license_category.py b/erpnext/hr/doctype/driving_license_category/driving_license_category.py deleted file mode 100644 index a1a6d55cb4..0000000000 --- a/erpnext/hr/doctype/driving_license_category/driving_license_category.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class DrivingLicenseCategory(Document): - pass diff --git a/erpnext/hr/doctype/employee_advance/__init__.py b/erpnext/hr/doctype/employee_advance/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js deleted file mode 100644 index 37ae75aeb6..0000000000 --- a/erpnext/hr/doctype/employee_advance/employee_advance.js +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Advance', { - setup: function(frm) { - frm.add_fetch("employee", "company", "company"); - frm.add_fetch("company", "default_employee_advance_account", "advance_account"); - - frm.set_query("employee", function() { - return { - filters: { - "status": "Active" - } - }; - }); - - frm.set_query("advance_account", function() { - if (!frm.doc.employee) { - frappe.msgprint(__("Please select employee first")); - } - let company_currency = erpnext.get_currency(frm.doc.company); - let currencies = [company_currency]; - if (frm.doc.currency && (frm.doc.currency != company_currency)) { - currencies.push(frm.doc.currency); - } - - return { - filters: { - "root_type": "Asset", - "is_group": 0, - "company": frm.doc.company, - "account_currency": ["in", currencies], - } - }; - }); - - frm.set_query('salary_component', function() { - return { - filters: { - "type": "Deduction" - } - }; - }); - }, - - refresh: function(frm) { - if (frm.doc.docstatus === 1 && - (flt(frm.doc.paid_amount) < flt(frm.doc.advance_amount)) && - frappe.model.can_create("Payment Entry")) { - frm.add_custom_button(__('Payment'), - function () { - frm.events.make_payment_entry(frm); - }, __('Create')); - } else if ( - frm.doc.docstatus === 1 && - flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount) && - frappe.model.can_create("Expense Claim") - ) { - frm.add_custom_button( - __("Expense Claim"), - function () { - frm.events.make_expense_claim(frm); - }, - __('Create') - ); - } - - if ( - frm.doc.docstatus === 1 - && (flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount)) - ) { - if (frm.doc.repay_unclaimed_amount_from_salary == 0 && frappe.model.can_create("Journal Entry")) { - frm.add_custom_button(__("Return"), function() { - frm.trigger('make_return_entry'); - }, __('Create')); - } else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")) { - frm.add_custom_button(__("Deduction from Salary"), function() { - frm.events.make_deduction_via_additional_salary(frm); - }, __('Create')); - } - } - }, - - make_deduction_via_additional_salary: function(frm) { - frappe.call({ - method: "erpnext.hr.doctype.employee_advance.employee_advance.create_return_through_additional_salary", - args: { - doc: frm.doc - }, - callback: function(r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }); - }, - - make_payment_entry: function(frm) { - var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry"; - if (frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) { - method = "erpnext.hr.doctype.employee_advance.employee_advance.make_bank_entry"; - } - return frappe.call({ - method: method, - args: { - "dt": frm.doc.doctype, - "dn": frm.doc.name - }, - callback: function(r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }); - }, - - make_expense_claim: function(frm) { - return frappe.call({ - method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim", - args: { - "employee_name": frm.doc.employee, - "company": frm.doc.company, - "employee_advance_name": frm.doc.name, - "posting_date": frm.doc.posting_date, - "paid_amount": frm.doc.paid_amount, - "claimed_amount": frm.doc.claimed_amount - }, - callback: function(r) { - const doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }); - }, - - make_return_entry: function(frm) { - frappe.call({ - method: 'erpnext.hr.doctype.employee_advance.employee_advance.make_return_entry', - args: { - 'employee': frm.doc.employee, - 'company': frm.doc.company, - 'employee_advance_name': frm.doc.name, - 'return_amount': flt(frm.doc.paid_amount - frm.doc.claimed_amount), - 'advance_account': frm.doc.advance_account, - 'mode_of_payment': frm.doc.mode_of_payment, - 'currency': frm.doc.currency, - 'exchange_rate': frm.doc.exchange_rate - }, - callback: function(r) { - const doclist = frappe.model.sync(r.message); - frappe.set_route('Form', doclist[0].doctype, doclist[0].name); - } - }); - }, - - employee: function(frm) { - if (frm.doc.employee) { - frappe.run_serially([ - () => frm.trigger('get_employee_currency'), - () => frm.trigger('get_pending_amount') - ]); - } - }, - - get_pending_amount: function(frm) { - frappe.call({ - method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount", - args: { - "employee": frm.doc.employee, - "posting_date": frm.doc.posting_date - }, - callback: function(r) { - frm.set_value("pending_amount", r.message); - } - }); - }, - - get_employee_currency: function(frm) { - frappe.call({ - method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", - args: { - employee: frm.doc.employee, - }, - callback: function(r) { - if (r.message) { - frm.set_value('currency', r.message); - frm.refresh_fields(); - } - } - }); - }, - - currency: function(frm) { - if (frm.doc.currency) { - var from_currency = frm.doc.currency; - var company_currency; - if (!frm.doc.company) { - company_currency = erpnext.get_currency(frappe.defaults.get_default("Company")); - } else { - company_currency = erpnext.get_currency(frm.doc.company); - } - if (from_currency != company_currency) { - frm.events.set_exchange_rate(frm, from_currency, company_currency); - } else { - frm.set_value("exchange_rate", 1.0); - frm.set_df_property('exchange_rate', 'hidden', 1); - frm.set_df_property("exchange_rate", "description", ""); - } - frm.refresh_fields(); - } - }, - - set_exchange_rate: function(frm, from_currency, company_currency) { - frappe.call({ - method: "erpnext.setup.utils.get_exchange_rate", - args: { - from_currency: from_currency, - to_currency: company_currency, - }, - callback: function(r) { - frm.set_value("exchange_rate", flt(r.message)); - frm.set_df_property('exchange_rate', 'hidden', 0); - frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + - " = [?] " + company_currency); - } - }); - } -}); diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json deleted file mode 100644 index b0501830cc..0000000000 --- a/erpnext/hr/doctype/employee_advance/employee_advance.json +++ /dev/null @@ -1,277 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "naming_series:", - "creation": "2022-01-17 18:36:51.450395", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "naming_series", - "employee", - "employee_name", - "department", - "column_break_4", - "posting_date", - "currency", - "exchange_rate", - "repay_unclaimed_amount_from_salary", - "section_break_8", - "purpose", - "column_break_11", - "advance_amount", - "paid_amount", - "pending_amount", - "claimed_amount", - "return_amount", - "section_break_7", - "status", - "company", - "amended_from", - "column_break_18", - "advance_account", - "mode_of_payment" - ], - "fields": [ - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "options": "HR-EAD-.YYYY.-" - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Read Only", - "label": "Employee Name" - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Posting Date", - "reqd": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "section_break_8", - "fieldtype": "Section Break" - }, - { - "fieldname": "purpose", - "fieldtype": "Small Text", - "in_list_view": 1, - "label": "Purpose", - "reqd": 1 - }, - { - "fieldname": "column_break_11", - "fieldtype": "Column Break" - }, - { - "fieldname": "advance_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Advance Amount", - "options": "currency", - "reqd": 1 - }, - { - "fieldname": "paid_amount", - "fieldtype": "Currency", - "label": "Paid Amount", - "no_copy": 1, - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "claimed_amount", - "fieldtype": "Currency", - "label": "Claimed Amount", - "no_copy": 1, - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "section_break_7", - "fieldtype": "Section Break" - }, - { - "fieldname": "status", - "fieldtype": "Select", - "label": "Status", - "no_copy": 1, - "options": "Draft\nPaid\nUnpaid\nClaimed\nReturned\nPartly Claimed and Returned\nCancelled", - "read_only": 1 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Employee Advance", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" - }, - { - "fieldname": "advance_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Advance Account", - "options": "Account", - "reqd": 1 - }, - { - "fieldname": "mode_of_payment", - "fieldtype": "Link", - "label": "Mode of Payment", - "options": "Mode of Payment" - }, - { - "allow_on_submit": 1, - "fieldname": "return_amount", - "fieldtype": "Currency", - "label": "Returned Amount", - "options": "currency", - "read_only": 1 - }, - { - "default": "0", - "fieldname": "repay_unclaimed_amount_from_salary", - "fieldtype": "Check", - "label": "Repay Unclaimed Amount from Salary" - }, - { - "depends_on": "eval:cur_frm.doc.employee", - "fieldname": "pending_amount", - "fieldtype": "Currency", - "label": "Pending Amount", - "options": "currency", - "read_only": 1 - }, - { - "depends_on": "eval:(doc.docstatus==1 || doc.employee)", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "reqd": 1 - }, - { - "depends_on": "currency", - "fieldname": "exchange_rate", - "fieldtype": "Float", - "label": "Exchange Rate", - "precision": "9", - "print_hide": 1, - "reqd": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-17 19:33:52.345823", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Advance", - "naming_rule": "By \"Naming Series\" field", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Expense Approver", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "employee,employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [ - { - "color": "Red", - "custom": 1, - "title": "Draft" - }, - { - "color": "Green", - "custom": 1, - "title": "Paid" - }, - { - "color": "Orange", - "custom": 1, - "title": "Unpaid" - }, - { - "color": "Blue", - "custom": 1, - "title": "Claimed" - }, - { - "color": "Gray", - "title": "Returned" - }, - { - "color": "Yellow", - "title": "Partly Claimed and Returned" - }, - { - "color": "Red", - "custom": 1, - "title": "Cancelled" - } - ], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py deleted file mode 100644 index c1876b1175..0000000000 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ /dev/null @@ -1,323 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.query_builder.functions import Sum -from frappe.utils import flt, nowdate - -import erpnext -from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account -from erpnext.hr.utils import validate_active_employee - - -class EmployeeAdvanceOverPayment(frappe.ValidationError): - pass - - -class EmployeeAdvance(Document): - def onload(self): - self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value( - "Accounts Settings", "make_payment_via_journal_entry" - ) - - def validate(self): - validate_active_employee(self.employee) - self.set_status() - - def on_cancel(self): - self.ignore_linked_doctypes = "GL Entry" - self.set_status(update=True) - - def set_status(self, update=False): - precision = self.precision("paid_amount") - total_amount = flt(flt(self.claimed_amount) + flt(self.return_amount), precision) - status = None - - if self.docstatus == 0: - status = "Draft" - elif self.docstatus == 1: - if flt(self.claimed_amount) > 0 and flt(self.claimed_amount, precision) == flt( - self.paid_amount, precision - ): - status = "Claimed" - elif flt(self.return_amount) > 0 and flt(self.return_amount, precision) == flt( - self.paid_amount, precision - ): - status = "Returned" - elif ( - flt(self.claimed_amount) > 0 - and (flt(self.return_amount) > 0) - and total_amount == flt(self.paid_amount, precision) - ): - status = "Partly Claimed and Returned" - elif flt(self.paid_amount) > 0 and flt(self.advance_amount, precision) == flt( - self.paid_amount, precision - ): - status = "Paid" - else: - status = "Unpaid" - elif self.docstatus == 2: - status = "Cancelled" - - if update: - self.db_set("status", status) - else: - self.status = status - - def set_total_advance_paid(self): - gle = frappe.qb.DocType("GL Entry") - - paid_amount = ( - frappe.qb.from_(gle) - .select(Sum(gle.debit).as_("paid_amount")) - .where( - (gle.against_voucher_type == "Employee Advance") - & (gle.against_voucher == self.name) - & (gle.party_type == "Employee") - & (gle.party == self.employee) - & (gle.docstatus == 1) - & (gle.is_cancelled == 0) - ) - ).run(as_dict=True)[0].paid_amount or 0 - - return_amount = ( - frappe.qb.from_(gle) - .select(Sum(gle.credit).as_("return_amount")) - .where( - (gle.against_voucher_type == "Employee Advance") - & (gle.voucher_type != "Expense Claim") - & (gle.against_voucher == self.name) - & (gle.party_type == "Employee") - & (gle.party == self.employee) - & (gle.docstatus == 1) - & (gle.is_cancelled == 0) - ) - ).run(as_dict=True)[0].return_amount or 0 - - if paid_amount != 0: - paid_amount = flt(paid_amount) / flt(self.exchange_rate) - if return_amount != 0: - return_amount = flt(return_amount) / flt(self.exchange_rate) - - if flt(paid_amount) > self.advance_amount: - frappe.throw( - _("Row {0}# Paid Amount cannot be greater than requested advance amount"), - EmployeeAdvanceOverPayment, - ) - - if flt(return_amount) > self.paid_amount - self.claimed_amount: - frappe.throw(_("Return amount cannot be greater unclaimed amount")) - - self.db_set("paid_amount", paid_amount) - self.db_set("return_amount", return_amount) - self.set_status(update=True) - - def update_claimed_amount(self): - claimed_amount = ( - frappe.db.sql( - """ - SELECT sum(ifnull(allocated_amount, 0)) - FROM `tabExpense Claim Advance` eca, `tabExpense Claim` ec - WHERE - eca.employee_advance = %s - AND ec.approval_status="Approved" - AND ec.name = eca.parent - AND ec.docstatus=1 - AND eca.allocated_amount > 0 - """, - self.name, - )[0][0] - or 0 - ) - - frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount)) - self.reload() - self.set_status(update=True) - - -@frappe.whitelist() -def get_pending_amount(employee, posting_date): - employee_due_amount = frappe.get_all( - "Employee Advance", - filters={"employee": employee, "docstatus": 1, "posting_date": ("<=", posting_date)}, - fields=["advance_amount", "paid_amount"], - ) - return sum([(emp.advance_amount - emp.paid_amount) for emp in employee_due_amount]) - - -@frappe.whitelist() -def make_bank_entry(dt, dn): - doc = frappe.get_doc(dt, dn) - payment_account = get_default_bank_cash_account( - doc.company, account_type="Cash", mode_of_payment=doc.mode_of_payment - ) - if not payment_account: - frappe.throw(_("Please set a Default Cash Account in Company defaults")) - - advance_account_currency = frappe.db.get_value("Account", doc.advance_account, "account_currency") - - advance_amount, advance_exchange_rate = get_advance_amount_advance_exchange_rate( - advance_account_currency, doc - ) - - paying_amount, paying_exchange_rate = get_paying_amount_paying_exchange_rate(payment_account, doc) - - je = frappe.new_doc("Journal Entry") - je.posting_date = nowdate() - je.voucher_type = "Bank Entry" - je.company = doc.company - je.remark = "Payment against Employee Advance: " + dn + "\n" + doc.purpose - je.multi_currency = 1 if advance_account_currency != payment_account.account_currency else 0 - - je.append( - "accounts", - { - "account": doc.advance_account, - "account_currency": advance_account_currency, - "exchange_rate": flt(advance_exchange_rate), - "debit_in_account_currency": flt(advance_amount), - "reference_type": "Employee Advance", - "reference_name": doc.name, - "party_type": "Employee", - "cost_center": erpnext.get_default_cost_center(doc.company), - "party": doc.employee, - "is_advance": "Yes", - }, - ) - - je.append( - "accounts", - { - "account": payment_account.account, - "cost_center": erpnext.get_default_cost_center(doc.company), - "credit_in_account_currency": flt(paying_amount), - "account_currency": payment_account.account_currency, - "account_type": payment_account.account_type, - "exchange_rate": flt(paying_exchange_rate), - }, - ) - - return je.as_dict() - - -def get_advance_amount_advance_exchange_rate(advance_account_currency, doc): - if advance_account_currency != doc.currency: - advance_amount = flt(doc.advance_amount) * flt(doc.exchange_rate) - advance_exchange_rate = 1 - else: - advance_amount = doc.advance_amount - advance_exchange_rate = doc.exchange_rate - - return advance_amount, advance_exchange_rate - - -def get_paying_amount_paying_exchange_rate(payment_account, doc): - if payment_account.account_currency != doc.currency: - paying_amount = flt(doc.advance_amount) * flt(doc.exchange_rate) - paying_exchange_rate = 1 - else: - paying_amount = doc.advance_amount - paying_exchange_rate = doc.exchange_rate - - return paying_amount, paying_exchange_rate - - -@frappe.whitelist() -def create_return_through_additional_salary(doc): - import json - - if isinstance(doc, str): - doc = frappe._dict(json.loads(doc)) - - additional_salary = frappe.new_doc("Additional Salary") - additional_salary.employee = doc.employee - additional_salary.currency = doc.currency - additional_salary.amount = doc.paid_amount - doc.claimed_amount - additional_salary.company = doc.company - additional_salary.ref_doctype = doc.doctype - additional_salary.ref_docname = doc.name - - return additional_salary - - -@frappe.whitelist() -def make_return_entry( - employee, - company, - employee_advance_name, - return_amount, - advance_account, - currency, - exchange_rate, - mode_of_payment=None, -): - bank_cash_account = get_default_bank_cash_account( - company, account_type="Cash", mode_of_payment=mode_of_payment - ) - if not bank_cash_account: - frappe.throw(_("Please set a Default Cash Account in Company defaults")) - - advance_account_currency = frappe.db.get_value("Account", advance_account, "account_currency") - - je = frappe.new_doc("Journal Entry") - je.posting_date = nowdate() - je.voucher_type = get_voucher_type(mode_of_payment) - je.company = company - je.remark = "Return against Employee Advance: " + employee_advance_name - je.multi_currency = 1 if advance_account_currency != bank_cash_account.account_currency else 0 - - advance_account_amount = ( - flt(return_amount) - if advance_account_currency == currency - else flt(return_amount) * flt(exchange_rate) - ) - - je.append( - "accounts", - { - "account": advance_account, - "credit_in_account_currency": advance_account_amount, - "account_currency": advance_account_currency, - "exchange_rate": flt(exchange_rate) if advance_account_currency == currency else 1, - "reference_type": "Employee Advance", - "reference_name": employee_advance_name, - "party_type": "Employee", - "party": employee, - "is_advance": "Yes", - "cost_center": erpnext.get_default_cost_center(company), - }, - ) - - bank_amount = ( - flt(return_amount) - if bank_cash_account.account_currency == currency - else flt(return_amount) * flt(exchange_rate) - ) - - je.append( - "accounts", - { - "account": bank_cash_account.account, - "debit_in_account_currency": bank_amount, - "account_currency": bank_cash_account.account_currency, - "account_type": bank_cash_account.account_type, - "exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1, - "cost_center": erpnext.get_default_cost_center(company), - }, - ) - - return je.as_dict() - - -def get_voucher_type(mode_of_payment=None): - voucher_type = "Cash Entry" - - if mode_of_payment: - mode_of_payment_type = frappe.get_cached_value("Mode of Payment", mode_of_payment, "type") - if mode_of_payment_type == "Bank": - voucher_type = "Bank Entry" - - return voucher_type diff --git a/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py b/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py deleted file mode 100644 index 73fac5131e..0000000000 --- a/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py +++ /dev/null @@ -1,9 +0,0 @@ -def get_data(): - return { - "fieldname": "employee_advance", - "non_standard_fieldnames": { - "Payment Entry": "reference_name", - "Journal Entry": "reference_name", - }, - "transactions": [{"items": ["Expense Claim"]}, {"items": ["Payment Entry", "Journal Entry"]}], - } diff --git a/erpnext/hr/doctype/employee_advance/test_employee_advance.py b/erpnext/hr/doctype/employee_advance/test_employee_advance.py deleted file mode 100644 index 44d68c9483..0000000000 --- a/erpnext/hr/doctype/employee_advance/test_employee_advance.py +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import flt, nowdate - -import erpnext -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.employee_advance.employee_advance import ( - EmployeeAdvanceOverPayment, - create_return_through_additional_salary, - make_bank_entry, - make_return_entry, -) -from erpnext.hr.doctype.expense_claim.expense_claim import get_advances -from erpnext.hr.doctype.expense_claim.test_expense_claim import ( - get_payable_account, - make_expense_claim, -) -from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component -from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - - -class TestEmployeeAdvance(unittest.TestCase): - def setUp(self): - frappe.db.delete("Employee Advance") - - def test_paid_amount_and_status(self): - employee_name = make_employee("_T@employe.advance") - advance = make_employee_advance(employee_name) - - journal_entry = make_payment_entry(advance) - journal_entry.submit() - - advance.reload() - - self.assertEqual(advance.paid_amount, 1000) - self.assertEqual(advance.status, "Paid") - - # try making over payment - journal_entry1 = make_payment_entry(advance) - self.assertRaises(EmployeeAdvanceOverPayment, journal_entry1.submit) - - def test_paid_amount_on_pe_cancellation(self): - employee_name = make_employee("_T@employe.advance") - advance = make_employee_advance(employee_name) - - pe = make_payment_entry(advance) - pe.submit() - - advance.reload() - - self.assertEqual(advance.paid_amount, 1000) - self.assertEqual(advance.status, "Paid") - - pe.cancel() - advance.reload() - - self.assertEqual(advance.paid_amount, 0) - self.assertEqual(advance.status, "Unpaid") - - advance.cancel() - advance.reload() - self.assertEqual(advance.status, "Cancelled") - - def test_claimed_status(self): - # CLAIMED Status check, full amount claimed - payable_account = get_payable_account("_Test Company") - claim = make_expense_claim( - payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True - ) - - advance = make_employee_advance(claim.employee) - pe = make_payment_entry(advance) - pe.submit() - - claim = get_advances_for_claim(claim, advance.name) - claim.save() - claim.submit() - - advance.reload() - self.assertEqual(advance.claimed_amount, 1000) - self.assertEqual(advance.status, "Claimed") - - # advance should not be shown in claims - advances = get_advances(claim.employee) - advances = [entry.name for entry in advances] - self.assertTrue(advance.name not in advances) - - # cancel claim; status should be Paid - claim.cancel() - advance.reload() - self.assertEqual(advance.claimed_amount, 0) - self.assertEqual(advance.status, "Paid") - - def test_partly_claimed_and_returned_status(self): - payable_account = get_payable_account("_Test Company") - claim = make_expense_claim( - payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True - ) - - advance = make_employee_advance(claim.employee) - pe = make_payment_entry(advance) - pe.submit() - - # PARTLY CLAIMED AND RETURNED status check - # 500 Claimed, 500 Returned - claim = make_expense_claim( - payable_account, 500, 500, "_Test Company", "Travel Expenses - _TC", do_not_submit=True - ) - - advance = make_employee_advance(claim.employee) - pe = make_payment_entry(advance) - pe.submit() - - claim = get_advances_for_claim(claim, advance.name, amount=500) - claim.save() - claim.submit() - - advance.reload() - self.assertEqual(advance.claimed_amount, 500) - self.assertEqual(advance.status, "Paid") - - entry = make_return_entry( - employee=advance.employee, - company=advance.company, - employee_advance_name=advance.name, - return_amount=flt(advance.paid_amount - advance.claimed_amount), - advance_account=advance.advance_account, - mode_of_payment=advance.mode_of_payment, - currency=advance.currency, - exchange_rate=advance.exchange_rate, - ) - - entry = frappe.get_doc(entry) - entry.insert() - entry.submit() - - advance.reload() - self.assertEqual(advance.return_amount, 500) - self.assertEqual(advance.status, "Partly Claimed and Returned") - - # advance should not be shown in claims - advances = get_advances(claim.employee) - advances = [entry.name for entry in advances] - self.assertTrue(advance.name not in advances) - - # Cancel return entry; status should change to PAID - entry.cancel() - advance.reload() - self.assertEqual(advance.return_amount, 0) - self.assertEqual(advance.status, "Paid") - - # advance should be shown in claims - advances = get_advances(claim.employee) - advances = [entry.name for entry in advances] - self.assertTrue(advance.name in advances) - - def test_repay_unclaimed_amount_from_salary(self): - employee_name = make_employee("_T@employe.advance") - advance = make_employee_advance(employee_name, {"repay_unclaimed_amount_from_salary": 1}) - pe = make_payment_entry(advance) - pe.submit() - - args = {"type": "Deduction"} - create_salary_component("Advance Salary - Deduction", **args) - make_salary_structure( - "Test Additional Salary for Advance Return", "Monthly", employee=employee_name - ) - - # additional salary for 700 first - advance.reload() - additional_salary = create_return_through_additional_salary(advance) - additional_salary.salary_component = "Advance Salary - Deduction" - additional_salary.payroll_date = nowdate() - additional_salary.amount = 700 - additional_salary.insert() - additional_salary.submit() - - advance.reload() - self.assertEqual(advance.return_amount, 700) - - # additional salary for remaining 300 - additional_salary = create_return_through_additional_salary(advance) - additional_salary.salary_component = "Advance Salary - Deduction" - additional_salary.payroll_date = nowdate() - additional_salary.amount = 300 - additional_salary.insert() - additional_salary.submit() - - advance.reload() - self.assertEqual(advance.return_amount, 1000) - self.assertEqual(advance.status, "Returned") - - # update advance return amount on additional salary cancellation - additional_salary.cancel() - advance.reload() - self.assertEqual(advance.return_amount, 700) - self.assertEqual(advance.status, "Paid") - - def tearDown(self): - frappe.db.rollback() - - -def make_payment_entry(advance): - journal_entry = frappe.get_doc(make_bank_entry("Employee Advance", advance.name)) - journal_entry.cheque_no = "123123" - journal_entry.cheque_date = nowdate() - journal_entry.save() - - return journal_entry - - -def make_employee_advance(employee_name, args=None): - doc = frappe.new_doc("Employee Advance") - doc.employee = employee_name - doc.company = "_Test company" - doc.purpose = "For site visit" - doc.currency = erpnext.get_company_currency("_Test company") - doc.exchange_rate = 1 - doc.advance_amount = 1000 - doc.posting_date = nowdate() - doc.advance_account = "_Test Employee Advance - _TC" - - if args: - doc.update(args) - - doc.insert() - doc.submit() - - return doc - - -def get_advances_for_claim(claim, advance_name, amount=None): - advances = get_advances(claim.employee, advance_name) - - for entry in advances: - if amount: - allocated_amount = amount - else: - allocated_amount = flt(entry.paid_amount) - flt(entry.claimed_amount) - - claim.append( - "advances", - { - "employee_advance": entry.name, - "posting_date": entry.posting_date, - "advance_account": entry.advance_account, - "advance_paid": entry.paid_amount, - "unclaimed_amount": allocated_amount, - "allocated_amount": allocated_amount, - }, - ) - - return claim diff --git a/erpnext/hr/doctype/employee_attendance_tool/__init__.py b/erpnext/hr/doctype/employee_attendance_tool/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css deleted file mode 100644 index c8d6644b2f..0000000000 --- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css +++ /dev/null @@ -1,21 +0,0 @@ -.top-toolbar{ - padding-bottom: 30px; - margin-left: -17px; -} - -.bottom-toolbar{ - margin-left: -17px; - margin-top: 20px; -} - -.btn{ - margin-right: 5px; -} - -.marked-employee-label{ - font-weight: normal; -} - -.checkbox{ - margin-top: -3px; -} diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js deleted file mode 100644 index 5ae8c6bd03..0000000000 --- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js +++ /dev/null @@ -1,269 +0,0 @@ -frappe.ui.form.on("Employee Attendance Tool", { - refresh: function(frm) { - frm.disable_save(); - }, - - onload: function(frm) { - frm.set_value("date", frappe.datetime.get_today()); - erpnext.employee_attendance_tool.load_employees(frm); - }, - - date: function(frm) { - erpnext.employee_attendance_tool.load_employees(frm); - }, - - department: function(frm) { - erpnext.employee_attendance_tool.load_employees(frm); - }, - - branch: function(frm) { - erpnext.employee_attendance_tool.load_employees(frm); - }, - - company: function(frm) { - erpnext.employee_attendance_tool.load_employees(frm); - } - -}); - - -erpnext.employee_attendance_tool = { - load_employees: function(frm) { - if(frm.doc.date) { - frappe.call({ - method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.get_employees", - args: { - date: frm.doc.date, - department: frm.doc.department, - branch: frm.doc.branch, - company: frm.doc.company - }, - callback: function(r) { - if(r.message['unmarked'].length > 0) { - unhide_field('unmarked_attendance_section') - if(!frm.employee_area) { - frm.employee_area = $('
') - .appendTo(frm.fields_dict.employees_html.wrapper); - } - frm.EmployeeSelector = new erpnext.EmployeeSelector(frm, frm.employee_area, r.message['unmarked']) - } - else{ - hide_field('unmarked_attendance_section') - } - - if(r.message['marked'].length > 0) { - unhide_field('marked_attendance_section') - if(!frm.marked_employee_area) { - frm.marked_employee_area = $('
') - .appendTo(frm.fields_dict.marked_attendance_html.wrapper); - } - frm.marked_employee = new erpnext.MarkedEmployee(frm, frm.marked_employee_area, r.message['marked']) - } - else{ - hide_field('marked_attendance_section') - } - } - }); - } - } -} - -erpnext.MarkedEmployee = class MarkedEmployee { - constructor(frm, wrapper, employee) { - this.wrapper = wrapper; - this.frm = frm; - this.make(frm, employee); - } - make(frm, employee) { - var me = this; - $(this.wrapper).empty(); - - var row; - $.each(employee, function(i, m) { - var attendance_icon = "fa fa-check"; - var color_class = ""; - if(m.status == "Absent") { - attendance_icon = "fa fa-check-empty" - color_class = "text-muted"; - } - else if(m.status == "Half Day") { - attendance_icon = "fa fa-check-minus" - } - - if (i===0 || i % 4===0) { - row = $('
').appendTo(me.wrapper); - } - - $(repl('
\ - \ -
', { - employee: m.employee_name, - icon: attendance_icon, - color_class: color_class - })).appendTo(row); - }); - } -}; - - -erpnext.EmployeeSelector = class EmployeeSelector { - constructor(frm, wrapper, employee) { - this.wrapper = wrapper; - this.frm = frm; - this.make(frm, employee); - } - make(frm, employee) { - var me = this; - - $(this.wrapper).empty(); - var employee_toolbar = $('
\ - \ - \ -
').appendTo($(this.wrapper)); - - var mark_employee_toolbar = $('
\ - \ - \ - \ - \ -
'); - - employee_toolbar.find(".btn-add") - .html(__('Check all')) - .on("click", function() { - $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { - if(!$(check).is(":checked")) { - check.checked = true; - } - }); - }); - - employee_toolbar.find(".btn-remove") - .html(__('Uncheck all')) - .on("click", function() { - $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { - if($(check).is(":checked")) { - check.checked = false; - } - }); - }); - - mark_employee_toolbar.find(".btn-mark-present") - .html(__('Mark Present')) - .on("click", function() { - var employee_present = []; - $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { - if($(check).is(":checked")) { - employee_present.push(employee[i]); - } - }); - frappe.call({ - method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance", - args:{ - "employee_list":employee_present, - "status":"Present", - "date":frm.doc.date, - "company":frm.doc.company - }, - - callback: function(r) { - erpnext.employee_attendance_tool.load_employees(frm); - - } - }); - }); - - mark_employee_toolbar.find(".btn-mark-absent") - .html(__('Mark Absent')) - .on("click", function() { - var employee_absent = []; - $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { - if($(check).is(":checked")) { - employee_absent.push(employee[i]); - } - }); - frappe.call({ - method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance", - args:{ - "employee_list":employee_absent, - "status":"Absent", - "date":frm.doc.date, - "company":frm.doc.company - }, - - callback: function(r) { - erpnext.employee_attendance_tool.load_employees(frm); - - } - }); - }); - - - mark_employee_toolbar.find(".btn-mark-half-day") - .html(__('Mark Half Day')) - .on("click", function() { - var employee_half_day = []; - $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { - if($(check).is(":checked")) { - employee_half_day.push(employee[i]); - } - }); - frappe.call({ - method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance", - args:{ - "employee_list":employee_half_day, - "status":"Half Day", - "date":frm.doc.date, - "company":frm.doc.company - }, - - callback: function(r) { - erpnext.employee_attendance_tool.load_employees(frm); - - } - }); - }); - - - mark_employee_toolbar.find(".btn-mark-work-from-home") - .html(__('Mark Work From Home')) - .on("click", function() { - var employee_work_from_home = []; - $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { - if($(check).is(":checked")) { - employee_work_from_home.push(employee[i]); - } - }); - frappe.call({ - method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance", - args:{ - "employee_list":employee_work_from_home, - "status":"Work From Home", - "date":frm.doc.date, - "company":frm.doc.company - }, - - callback: function(r) { - erpnext.employee_attendance_tool.load_employees(frm); - - } - }); - }); - - var row; - $.each(employee, function(i, m) { - if (i===0 || (i % 4) === 0) { - row = $('
').appendTo(me.wrapper); - } - - $(repl('
\ -
\ - \ -
', {employee: m.employee_name})).appendTo(row); - }); - - mark_employee_toolbar.appendTo($(this.wrapper)); - } -}; diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.json b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.json deleted file mode 100644 index 256e056ec1..0000000000 --- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.json +++ /dev/null @@ -1,275 +0,0 @@ -{ - "allow_copy": 1, - "allow_import": 0, - "allow_rename": 0, - "creation": "2016-01-27 14:59:47.849379", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "default": "Today", - "fieldname": "date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "branch", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Branch", - "length": 0, - "no_copy": 0, - "options": "Branch", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "depends_on": "date", - "fieldname": "unmarked_attendance_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Unmarked Attendance", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "employees_html", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Employees HTML", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "depends_on": "date", - "fieldname": "marked_attendance_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Marked Attendance", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "marked_attendance_html", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Marked Attendance HTML", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "hide_heading": 1, - "hide_toolbar": 1, - "idx": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2016-01-29 02:14:36.034952", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Attendance Tool", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 1 - } - ], - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py deleted file mode 100644 index 43665cc8b2..0000000000 --- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import json - -import frappe -from frappe.model.document import Document -from frappe.utils import getdate - - -class EmployeeAttendanceTool(Document): - pass - - -@frappe.whitelist() -def get_employees(date, department=None, branch=None, company=None): - attendance_not_marked = [] - attendance_marked = [] - filters = {"status": "Active", "date_of_joining": ["<=", date]} - - for field, value in {"department": department, "branch": branch, "company": company}.items(): - if value: - filters[field] = value - - employee_list = frappe.get_list( - "Employee", fields=["employee", "employee_name"], filters=filters, order_by="employee_name" - ) - marked_employee = {} - for emp in frappe.get_list( - "Attendance", fields=["employee", "status"], filters={"attendance_date": date} - ): - marked_employee[emp["employee"]] = emp["status"] - - for employee in employee_list: - employee["status"] = marked_employee.get(employee["employee"]) - if employee["employee"] not in marked_employee: - attendance_not_marked.append(employee) - else: - attendance_marked.append(employee) - return {"marked": attendance_marked, "unmarked": attendance_not_marked} - - -@frappe.whitelist() -def mark_employee_attendance(employee_list, status, date, leave_type=None, company=None): - - employee_list = json.loads(employee_list) - for employee in employee_list: - - if status == "On Leave" and leave_type: - leave_type = leave_type - else: - leave_type = None - - company = frappe.db.get_value("Employee", employee["employee"], "Company", cache=True) - - attendance = frappe.get_doc( - dict( - doctype="Attendance", - employee=employee.get("employee"), - employee_name=employee.get("employee_name"), - attendance_date=getdate(date), - status=status, - leave_type=leave_type, - company=company, - ) - ) - attendance.insert() - attendance.submit() diff --git a/erpnext/hr/doctype/employee_boarding_activity/__init__.py b/erpnext/hr/doctype/employee_boarding_activity/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.json b/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.json deleted file mode 100644 index 8474bd09d5..0000000000 --- a/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "actions": [], - "creation": "2018-05-09 05:37:18.439763", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "activity_name", - "user", - "role", - "begin_on", - "duration", - "column_break_3", - "task", - "task_weight", - "required_for_employee_creation", - "section_break_6", - "description" - ], - "fields": [ - { - "columns": 3, - "fieldname": "activity_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Activity Name", - "reqd": 1 - }, - { - "columns": 2, - "depends_on": "eval:!doc.role", - "fieldname": "user", - "fieldtype": "Link", - "in_list_view": 1, - "label": "User", - "options": "User" - }, - { - "columns": 1, - "depends_on": "eval:!doc.user", - "fieldname": "role", - "fieldtype": "Link", - "label": "Role", - "options": "Role" - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "task", - "fieldtype": "Link", - "label": "Task", - "no_copy": 1, - "options": "Task", - "read_only": 1 - }, - { - "fieldname": "task_weight", - "fieldtype": "Float", - "label": "Task Weight" - }, - { - "default": "0", - "depends_on": "eval:['Employee Onboarding', 'Employee Onboarding Template'].includes(doc.parenttype)", - "description": "Applicable in the case of Employee Onboarding", - "fieldname": "required_for_employee_creation", - "fieldtype": "Check", - "label": "Required for Employee Creation" - }, - { - "fieldname": "section_break_6", - "fieldtype": "Section Break" - }, - { - "fieldname": "description", - "fieldtype": "Text Editor", - "label": "Description" - }, - { - "columns": 2, - "fieldname": "duration", - "fieldtype": "Int", - "in_list_view": 1, - "label": "Duration (Days)" - }, - { - "columns": 2, - "fieldname": "begin_on", - "fieldtype": "Int", - "in_list_view": 1, - "label": "Begin On (Days)" - } - ], - "istable": 1, - "links": [], - "modified": "2022-01-29 14:05:00.543122", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Boarding Activity", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.py b/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.py deleted file mode 100644 index e824081327..0000000000 --- a/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class EmployeeBoardingActivity(Document): - pass diff --git a/erpnext/hr/doctype/employee_checkin/__init__.py b/erpnext/hr/doctype/employee_checkin/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.js b/erpnext/hr/doctype/employee_checkin/employee_checkin.js deleted file mode 100644 index c2403ca2bd..0000000000 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.js +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Checkin', { - setup: (frm) => { - if(!frm.doc.time) { - frm.set_value("time", frappe.datetime.now_datetime()); - } - } -}); diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.json b/erpnext/hr/doctype/employee_checkin/employee_checkin.json deleted file mode 100644 index d34316dc0f..0000000000 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.json +++ /dev/null @@ -1,208 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "EMP-CKIN-.MM.-.YYYY.-.######", - "creation": "2019-06-10 11:56:34.536413", - "doctype": "DocType", - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "log_type", - "shift", - "column_break_4", - "time", - "device_id", - "skip_auto_attendance", - "attendance", - "shift_start", - "shift_end", - "shift_actual_start", - "shift_actual_end" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "label": "Employee", - "options": "Employee", - "reqd": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Employee Name", - "read_only": 1 - }, - { - "fieldname": "log_type", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Log Type", - "options": "\nIN\nOUT" - }, - { - "fieldname": "shift", - "fieldtype": "Link", - "label": "Shift", - "options": "Shift Type", - "read_only": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "default": "Now", - "fieldname": "time", - "fieldtype": "Datetime", - "in_list_view": 1, - "label": "Time", - "permlevel": 1, - "reqd": 1 - }, - { - "fieldname": "device_id", - "fieldtype": "Data", - "label": "Location / Device ID" - }, - { - "default": "0", - "fieldname": "skip_auto_attendance", - "fieldtype": "Check", - "label": "Skip Auto Attendance" - }, - { - "fieldname": "attendance", - "fieldtype": "Link", - "label": "Attendance Marked", - "options": "Attendance", - "read_only": 1 - }, - { - "fieldname": "shift_start", - "fieldtype": "Datetime", - "hidden": 1, - "label": "Shift Start" - }, - { - "fieldname": "shift_end", - "fieldtype": "Datetime", - "hidden": 1, - "label": "Shift End" - }, - { - "fieldname": "shift_actual_start", - "fieldtype": "Datetime", - "hidden": 1, - "label": "Shift Actual Start" - }, - { - "fieldname": "shift_actual_end", - "fieldtype": "Datetime", - "hidden": 1, - "label": "Shift Actual End" - } - ], - "links": [], - "modified": "2020-07-08 11:02:32.660986", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Checkin", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "import": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "import": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "import": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "read": 1, - "role": "Employee", - "write": 1 - }, - { - "delete": 1, - "email": 1, - "export": 1, - "permlevel": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "delete": 1, - "email": 1, - "export": 1, - "permlevel": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "delete": 1, - "email": 1, - "export": 1, - "permlevel": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - }, - { - "permlevel": 1, - "read": 1, - "role": "Employee" - } - ], - "sort_field": "modified", - "sort_order": "ASC", - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py deleted file mode 100644 index ecee541ad6..0000000000 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py +++ /dev/null @@ -1,278 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import cint, get_datetime, get_link_to_form - -from erpnext.hr.doctype.attendance.attendance import ( - get_duplicate_attendance_record, - get_overlapping_shift_attendance, -) -from erpnext.hr.doctype.shift_assignment.shift_assignment import ( - get_actual_start_end_datetime_of_shift, -) -from erpnext.hr.utils import validate_active_employee - - -class EmployeeCheckin(Document): - def validate(self): - validate_active_employee(self.employee) - self.validate_duplicate_log() - self.fetch_shift() - - def validate_duplicate_log(self): - doc = frappe.db.exists( - "Employee Checkin", {"employee": self.employee, "time": self.time, "name": ["!=", self.name]} - ) - if doc: - doc_link = frappe.get_desk_link("Employee Checkin", doc) - frappe.throw( - _("This employee already has a log with the same timestamp.{0}").format("
" + doc_link) - ) - - 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: - if ( - shift_actual_timings.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.shift_type.name - ) - ) - if not self.attendance: - self.shift = shift_actual_timings.shift_type.name - self.shift_actual_start = shift_actual_timings.actual_start - self.shift_actual_end = shift_actual_timings.actual_end - self.shift_start = shift_actual_timings.start_datetime - self.shift_end = shift_actual_timings.end_datetime - else: - self.shift = None - - -@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", -): - """Finds the relevant Employee using the employee field value and creates a Employee Checkin. - - :param employee_field_value: The value to look for in employee field. - :param timestamp: The timestamp of the Log. Currently expected in the following format as string: '2019-05-08 10:48:08.000000' - :param device_id: (optional)Location / Device ID. A short string is expected. - :param log_type: (optional)Direction of the Punch if available (IN/OUT). - :param skip_auto_attendance: (optional)Skip auto attendance field will be set for this log(0/1). - :param employee_fieldname: (Default: attendance_device_id)Name of the field in Employee DocType based on which employee lookup will happen. - """ - - if not employee_field_value or not timestamp: - frappe.throw(_("'employee_field_value' and 'timestamp' are required.")) - - employee = frappe.db.get_values( - "Employee", - {employee_fieldname: employee_field_value}, - ["name", "employee_name", employee_fieldname], - as_dict=True, - ) - if employee: - employee = employee[0] - else: - frappe.throw( - _("No Employee found for the given employee field value. '{}': {}").format( - employee_fieldname, employee_field_value - ) - ) - - doc = frappe.new_doc("Employee Checkin") - doc.employee = employee.name - doc.employee_name = employee.employee_name - doc.time = timestamp - doc.device_id = device_id - doc.log_type = log_type - if cint(skip_auto_attendance) == 1: - doc.skip_auto_attendance = "1" - doc.insert() - - return doc - - -def mark_attendance_and_link_log( - logs, - attendance_status, - attendance_date, - working_hours=None, - late_entry=False, - early_exit=False, - in_time=None, - out_time=None, - shift=None, -): - """Creates an attendance and links the attendance to the Employee Checkin. - Note: If attendance is already present for the given date, the logs are marked as skipped and no exception is thrown. - - :param logs: The List of 'Employee Checkin'. - :param attendance_status: Attendance status to be marked. One of: (Present, Absent, Half Day, Skip). Note: 'On Leave' is not supported by this function. - :param attendance_date: Date of the attendance to be created. - :param working_hours: (optional)Number of working hours for the given date. - """ - log_names = [x.name for x in logs] - employee = logs[0].employee - - if attendance_status == "Skip": - skip_attendance_in_checkins(log_names) - return None - - elif attendance_status in ("Present", "Absent", "Half Day"): - employee_doc = frappe.get_doc("Employee", employee) - duplicate = get_duplicate_attendance_record(employee, attendance_date, shift) - overlapping = get_overlapping_shift_attendance(employee, attendance_date, shift) - - if not duplicate and not overlapping: - doc_dict = { - "doctype": "Attendance", - "employee": employee, - "attendance_date": attendance_date, - "status": attendance_status, - "working_hours": working_hours, - "company": employee_doc.company, - "shift": shift, - "late_entry": late_entry, - "early_exit": early_exit, - "in_time": in_time, - "out_time": out_time, - } - attendance = frappe.get_doc(doc_dict).insert() - attendance.submit() - - if attendance_status == "Absent": - attendance.add_comment( - text=_("Employee was marked Absent for not meeting the working hours threshold.") - ) - - frappe.db.sql( - """update `tabEmployee Checkin` - set attendance = %s - where name in %s""", - (attendance.name, log_names), - ) - return attendance - else: - skip_attendance_in_checkins(log_names) - add_comment_in_checkins(log_names, duplicate, overlapping) - return None - - else: - frappe.throw(_("{} is an invalid Attendance Status.").format(attendance_status)) - - -def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type): - """Given a set of logs in chronological order calculates the total working hours based on the parameters. - Zero is returned for all invalid cases. - - :param logs: The List of 'Employee Checkin'. - :param check_in_out_type: One of: 'Alternating entries as IN and OUT during the same shift', 'Strictly based on Log Type in Employee Checkin' - :param working_hours_calc_type: One of: 'First Check-in and Last Check-out', 'Every Valid Check-in and Check-out' - """ - total_hours = 0 - in_time = out_time = None - if check_in_out_type == "Alternating entries as IN and OUT during the same shift": - in_time = logs[0].time - if len(logs) >= 2: - out_time = logs[-1].time - if working_hours_calc_type == "First Check-in and Last Check-out": - # assumption in this case: First log always taken as IN, Last log always taken as OUT - total_hours = time_diff_in_hours(in_time, logs[-1].time) - elif working_hours_calc_type == "Every Valid Check-in and Check-out": - logs = logs[:] - while len(logs) >= 2: - total_hours += time_diff_in_hours(logs[0].time, logs[1].time) - del logs[:2] - - elif check_in_out_type == "Strictly based on Log Type in Employee Checkin": - if working_hours_calc_type == "First Check-in and Last Check-out": - first_in_log_index = find_index_in_dict(logs, "log_type", "IN") - first_in_log = ( - logs[first_in_log_index] if first_in_log_index or first_in_log_index == 0 else None - ) - last_out_log_index = find_index_in_dict(reversed(logs), "log_type", "OUT") - last_out_log = ( - logs[len(logs) - 1 - last_out_log_index] - if last_out_log_index or last_out_log_index == 0 - else None - ) - if first_in_log and last_out_log: - in_time, out_time = first_in_log.time, last_out_log.time - total_hours = time_diff_in_hours(in_time, out_time) - elif working_hours_calc_type == "Every Valid Check-in and Check-out": - in_log = out_log = None - for log in logs: - if in_log and out_log: - if not in_time: - in_time = in_log.time - out_time = out_log.time - total_hours += time_diff_in_hours(in_log.time, out_log.time) - in_log = out_log = None - if not in_log: - in_log = log if log.log_type == "IN" else None - if in_log and not in_time: - in_time = in_log.time - elif not out_log: - out_log = log if log.log_type == "OUT" else None - - if in_log and out_log: - out_time = out_log.time - total_hours += time_diff_in_hours(in_log.time, out_log.time) - - return total_hours, in_time, out_time - - -def time_diff_in_hours(start, end): - return round(float((end - start).total_seconds()) / 3600, 2) - - -def find_index_in_dict(dict_list, key, value): - return next((index for (index, d) in enumerate(dict_list) if d[key] == value), None) - - -def add_comment_in_checkins(log_names, duplicate, overlapping): - if duplicate: - text = _("Auto Attendance skipped due to duplicate attendance record: {}").format( - get_link_to_form("Attendance", duplicate[0].name) - ) - else: - text = _("Auto Attendance skipped due to overlapping attendance record: {}").format( - get_link_to_form("Attendance", overlapping.name) - ) - - for name in log_names: - frappe.get_doc( - { - "doctype": "Comment", - "comment_type": "Comment", - "reference_doctype": "Employee Checkin", - "reference_name": name, - "content": text, - } - ).insert(ignore_permissions=True) - - -def skip_attendance_in_checkins(log_names): - EmployeeCheckin = frappe.qb.DocType("Employee Checkin") - ( - frappe.qb.update(EmployeeCheckin) - .set("skip_auto_attendance", 1) - .where(EmployeeCheckin.name.isin(log_names)) - ).run() diff --git a/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py b/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py deleted file mode 100644 index eb81f7d67c..0000000000 --- a/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py +++ /dev/null @@ -1,346 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest -from datetime import datetime, timedelta - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import ( - add_days, - get_time, - get_year_ending, - get_year_start, - getdate, - now_datetime, - nowdate, -) - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.employee_checkin.employee_checkin import ( - add_log_based_on_employee_field, - calculate_working_hours, - mark_attendance_and_link_log, -) -from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list -from erpnext.hr.doctype.leave_application.test_leave_application import get_first_sunday -from erpnext.hr.doctype.shift_type.test_shift_type import make_shift_assignment, setup_shift_type -from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list - - -class TestEmployeeCheckin(FrappeTestCase): - def setUp(self): - frappe.db.delete("Shift Type") - frappe.db.delete("Shift Assignment") - frappe.db.delete("Employee Checkin") - - from_date = get_year_start(getdate()) - to_date = get_year_ending(getdate()) - self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date) - - def test_add_log_based_on_employee_field(self): - employee = make_employee("test_add_log_based_on_employee_field@example.com") - employee = frappe.get_doc("Employee", employee) - employee.attendance_device_id = "3344" - employee.save() - - time_now = now_datetime().__str__()[:-7] - employee_checkin = add_log_based_on_employee_field("3344", time_now, "mumbai_first_floor", "IN") - self.assertEqual(employee_checkin.employee, employee.name) - self.assertEqual(employee_checkin.time, time_now) - self.assertEqual(employee_checkin.device_id, "mumbai_first_floor") - self.assertEqual(employee_checkin.log_type, "IN") - - def test_mark_attendance_and_link_log(self): - employee = make_employee("test_mark_attendance_and_link_log@example.com") - logs = make_n_checkins(employee, 3) - mark_attendance_and_link_log(logs, "Skip", nowdate()) - log_names = [log.name for log in logs] - logs_count = frappe.db.count( - "Employee Checkin", {"name": ["in", log_names], "skip_auto_attendance": 1} - ) - self.assertEqual(logs_count, 3) - - logs = make_n_checkins(employee, 4, 2) - now_date = nowdate() - frappe.db.delete("Attendance", {"employee": employee}) - attendance = mark_attendance_and_link_log(logs, "Present", now_date, 8.2) - log_names = [log.name for log in logs] - logs_count = frappe.db.count( - "Employee Checkin", {"name": ["in", log_names], "attendance": attendance.name} - ) - self.assertEqual(logs_count, 4) - attendance_count = frappe.db.count( - "Attendance", - {"status": "Present", "working_hours": 8.2, "employee": employee, "attendance_date": now_date}, - ) - self.assertEqual(attendance_count, 1) - - def test_unlink_attendance_on_cancellation(self): - employee = make_employee("test_mark_attendance_and_link_log@example.com") - logs = make_n_checkins(employee, 3) - - frappe.db.delete("Attendance", {"employee": employee}) - attendance = mark_attendance_and_link_log(logs, "Present", nowdate(), 8.2) - attendance.cancel() - - linked_logs = frappe.db.get_all("Employee Checkin", {"attendance": attendance.name}) - self.assertEquals(len(linked_logs), 0) - - def test_calculate_working_hours(self): - check_in_out_type = [ - "Alternating entries as IN and OUT during the same shift", - "Strictly based on Log Type in Employee Checkin", - ] - working_hours_calc_type = [ - "First Check-in and Last Check-out", - "Every Valid Check-in and Check-out", - ] - logs_type_1 = [ - {"time": now_datetime() - timedelta(minutes=390)}, - {"time": now_datetime() - timedelta(minutes=300)}, - {"time": now_datetime() - timedelta(minutes=270)}, - {"time": now_datetime() - timedelta(minutes=90)}, - {"time": now_datetime() - timedelta(minutes=0)}, - ] - logs_type_2 = [ - {"time": now_datetime() - timedelta(minutes=390), "log_type": "OUT"}, - {"time": now_datetime() - timedelta(minutes=360), "log_type": "IN"}, - {"time": now_datetime() - timedelta(minutes=300), "log_type": "OUT"}, - {"time": now_datetime() - timedelta(minutes=290), "log_type": "IN"}, - {"time": now_datetime() - timedelta(minutes=260), "log_type": "OUT"}, - {"time": now_datetime() - timedelta(minutes=240), "log_type": "IN"}, - {"time": now_datetime() - timedelta(minutes=150), "log_type": "IN"}, - {"time": now_datetime() - timedelta(minutes=60), "log_type": "OUT"}, - ] - logs_type_1 = [frappe._dict(x) for x in logs_type_1] - logs_type_2 = [frappe._dict(x) for x in logs_type_2] - - working_hours = calculate_working_hours( - logs_type_1, check_in_out_type[0], working_hours_calc_type[0] - ) - self.assertEqual(working_hours, (6.5, logs_type_1[0].time, logs_type_1[-1].time)) - - working_hours = calculate_working_hours( - logs_type_1, check_in_out_type[0], working_hours_calc_type[1] - ) - self.assertEqual(working_hours, (4.5, logs_type_1[0].time, logs_type_1[-1].time)) - - working_hours = calculate_working_hours( - logs_type_2, check_in_out_type[1], working_hours_calc_type[0] - ) - self.assertEqual(working_hours, (5, logs_type_2[1].time, logs_type_2[-1].time)) - - working_hours = calculate_working_hours( - logs_type_2, check_in_out_type[1], working_hours_calc_type[1] - ) - self.assertEqual(working_hours, (4.5, logs_type_2[1].time, logs_type_2[-1].time)) - - working_hours = calculate_working_hours( - [logs_type_2[1], logs_type_2[-1]], check_in_out_type[1], working_hours_calc_type[1] - ) - self.assertEqual(working_hours, (5.0, logs_type_2[1].time, logs_type_2[-1].time)) - - def test_fetch_shift(self): - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - - # shift setup for 8-12 - shift_type = setup_shift_type() - date = getdate() - make_shift_assignment(shift_type.name, employee, date) - - # within shift time - timestamp = datetime.combine(date, get_time("08:45:00")) - log = make_checkin(employee, timestamp) - self.assertEqual(log.shift, shift_type.name) - - # "begin checkin before shift time" = 60 mins, so should work for 7:00:00 - timestamp = datetime.combine(date, get_time("07:00:00")) - log = make_checkin(employee, timestamp) - self.assertEqual(log.shift, shift_type.name) - - # "allow checkout after shift end time" = 60 mins, so should work for 13:00:00 - timestamp = datetime.combine(date, get_time("13:00:00")) - log = make_checkin(employee, timestamp) - self.assertEqual(log.shift, shift_type.name) - - # should not fetch this shift beyond allowed time - timestamp = datetime.combine(date, get_time("13:01:00")) - log = make_checkin(employee, timestamp) - self.assertIsNone(log.shift) - - def test_fetch_shift_for_assignment_with_end_date(self): - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - - # shift setup for 8-12 - shift1 = setup_shift_type() - # 12:30 - 16:30 - shift2 = setup_shift_type(shift_type="Shift 2", start_time="12:30:00", end_time="16:30:00") - - date = getdate() - make_shift_assignment(shift1.name, employee, date, add_days(date, 15)) - make_shift_assignment(shift2.name, employee, date, add_days(date, 15)) - - timestamp = datetime.combine(date, get_time("08:45:00")) - log = make_checkin(employee, timestamp) - self.assertEqual(log.shift, shift1.name) - - timestamp = datetime.combine(date, get_time("12:45:00")) - log = make_checkin(employee, timestamp) - self.assertEqual(log.shift, shift2.name) - - # log after end date - timestamp = datetime.combine(add_days(date, 16), get_time("12:45:00")) - log = make_checkin(employee, timestamp) - self.assertIsNone(log.shift) - - def test_shift_start_and_end_timings(self): - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - - # shift setup for 8-12 - shift_type = setup_shift_type() - date = getdate() - make_shift_assignment(shift_type.name, employee, date) - - timestamp = datetime.combine(date, get_time("08:45:00")) - log = make_checkin(employee, timestamp) - - self.assertEqual(log.shift, shift_type.name) - self.assertEqual(log.shift_start, datetime.combine(date, get_time("08:00:00"))) - self.assertEqual(log.shift_end, datetime.combine(date, get_time("12:00:00"))) - self.assertEqual(log.shift_actual_start, datetime.combine(date, get_time("07:00:00"))) - self.assertEqual(log.shift_actual_end, datetime.combine(date, get_time("13:00:00"))) - - def test_fetch_shift_based_on_default_shift(self): - employee = make_employee("test_default_shift@example.com", company="_Test Company") - default_shift = setup_shift_type( - shift_type="Default Shift", start_time="14:00:00", end_time="16:00:00" - ) - - date = getdate() - frappe.db.set_value("Employee", employee, "default_shift", default_shift.name) - - timestamp = datetime.combine(date, get_time("14:45:00")) - log = make_checkin(employee, timestamp) - - # should consider default shift - self.assertEqual(log.shift, default_shift.name) - - def test_fetch_shift_spanning_over_two_days(self): - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - shift_type = setup_shift_type( - shift_type="Midnight Shift", start_time="23:00:00", end_time="01:00:00" - ) - date = getdate() - next_day = add_days(date, 1) - make_shift_assignment(shift_type.name, employee, date) - - # log falls in the first day - timestamp = datetime.combine(date, get_time("23:00:00")) - log = make_checkin(employee, timestamp) - - self.assertEqual(log.shift, shift_type.name) - self.assertEqual(log.shift_start, datetime.combine(date, get_time("23:00:00"))) - self.assertEqual(log.shift_end, datetime.combine(next_day, get_time("01:00:00"))) - self.assertEqual(log.shift_actual_start, datetime.combine(date, get_time("22:00:00"))) - self.assertEqual(log.shift_actual_end, datetime.combine(next_day, get_time("02:00:00"))) - - log.delete() - - # log falls in the second day - prev_day = add_days(date, -1) - timestamp = datetime.combine(date, get_time("01:30:00")) - log = make_checkin(employee, timestamp) - self.assertEqual(log.shift, shift_type.name) - self.assertEqual(log.shift_start, datetime.combine(prev_day, get_time("23:00:00"))) - self.assertEqual(log.shift_end, datetime.combine(date, get_time("01:00:00"))) - self.assertEqual(log.shift_actual_start, datetime.combine(prev_day, get_time("22:00:00"))) - self.assertEqual(log.shift_actual_end, datetime.combine(date, get_time("02:00:00"))) - - def test_no_shift_fetched_on_holiday_as_per_shift_holiday_list(self): - date = getdate() - from_date = get_year_start(date) - to_date = get_year_ending(date) - holiday_list = make_holiday_list(from_date=from_date, to_date=to_date) - - employee = make_employee("test_shift_with_holiday@example.com", company="_Test Company") - setup_shift_type(shift_type="Test Holiday Shift", holiday_list=holiday_list) - - first_sunday = get_first_sunday(holiday_list, for_date=date) - timestamp = datetime.combine(first_sunday, get_time("08:00:00")) - log = make_checkin(employee, timestamp) - - self.assertIsNone(log.shift) - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_no_shift_fetched_on_holiday_as_per_employee_holiday_list(self): - employee = make_employee("test_shift_with_holiday@example.com", company="_Test Company") - shift_type = setup_shift_type(shift_type="Test Holiday Shift") - shift_type.holiday_list = None - shift_type.save() - - date = getdate() - - first_sunday = get_first_sunday(self.holiday_list, for_date=date) - timestamp = datetime.combine(first_sunday, get_time("08:00:00")) - log = make_checkin(employee, timestamp) - - self.assertIsNone(log.shift) - - def test_consecutive_shift_assignments_overlapping_within_grace_period(self): - # test adjustment for start and end times if they are overlapping - # within "begin_check_in_before_shift_start_time" and "allow_check_out_after_shift_end_time" periods - employee = make_employee("test_shift@example.com", company="_Test Company") - - # 8 - 12 - shift1 = setup_shift_type() - # 12:30 - 16:30 - shift2 = setup_shift_type( - shift_type="Consecutive Shift", start_time="12:30:00", end_time="16:30:00" - ) - - # the actual start and end times (with grace) for these shifts are 7 - 13 and 11:30 - 17:30 - date = getdate() - make_shift_assignment(shift1.name, employee, date) - make_shift_assignment(shift2.name, employee, date) - - # log at 12:30 should set shift2 and actual start as 12 and not 11:30 - timestamp = datetime.combine(date, get_time("12:30:00")) - log = make_checkin(employee, timestamp) - self.assertEqual(log.shift, shift2.name) - self.assertEqual(log.shift_start, datetime.combine(date, get_time("12:30:00"))) - self.assertEqual(log.shift_actual_start, datetime.combine(date, get_time("12:00:00"))) - - # log at 12:00 should set shift1 and actual end as 12 and not 1 since the next shift's grace starts - timestamp = datetime.combine(date, get_time("12:00:00")) - log = make_checkin(employee, timestamp) - self.assertEqual(log.shift, shift1.name) - self.assertEqual(log.shift_end, datetime.combine(date, get_time("12:00:00"))) - self.assertEqual(log.shift_actual_end, datetime.combine(date, get_time("12:00:00"))) - - # log at 12:01 should set shift2 - timestamp = datetime.combine(date, get_time("12:01:00")) - log = make_checkin(employee, timestamp) - self.assertEqual(log.shift, shift2.name) - - -def make_n_checkins(employee, n, hours_to_reverse=1): - logs = [make_checkin(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n + 1))] - for i in range(n - 1): - logs.append( - make_checkin(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n - i)) - ) - return logs - - -def make_checkin(employee, time=now_datetime()): - log = frappe.get_doc( - { - "doctype": "Employee Checkin", - "employee": employee, - "time": time, - "device_id": "device1", - "log_type": "IN", - } - ).insert() - return log diff --git a/erpnext/hr/doctype/employee_grade/__init__.py b/erpnext/hr/doctype/employee_grade/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_grade/employee_grade.js b/erpnext/hr/doctype/employee_grade/employee_grade.js deleted file mode 100644 index 6c67f54160..0000000000 --- a/erpnext/hr/doctype/employee_grade/employee_grade.js +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Grade', { - refresh: function (frm) { - - }, - setup: function (frm) { - frm.set_query("default_salary_structure", function () { - return { - "filters": { - "docstatus": 1, - "is_active": "Yes" - } - }; - }); - - frm.set_query("default_leave_policy", function () { - return { - "filters": { - "docstatus": 1 - } - }; - }); - - - } - -}); diff --git a/erpnext/hr/doctype/employee_grade/employee_grade.json b/erpnext/hr/doctype/employee_grade/employee_grade.json deleted file mode 100644 index 4967137840..0000000000 --- a/erpnext/hr/doctype/employee_grade/employee_grade.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "Prompt", - "creation": "2018-04-13 16:14:24.174138", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "default_salary_structure", - "currency", - "default_base_pay" - ], - "fields": [ - { - "fieldname": "default_salary_structure", - "fieldtype": "Link", - "label": "Default Salary Structure", - "options": "Salary Structure" - }, - { - "depends_on": "default_salary_structure", - "fieldname": "default_base_pay", - "fieldtype": "Currency", - "label": "Default Base Pay", - "options": "currency" - }, - { - "fetch_from": "default_salary_structure.currency", - "fieldname": "currency", - "fieldtype": "Link", - "hidden": 1, - "label": "Currency", - "options": "Currency", - "read_only": 1 - } - ], - "index_web_pages_for_search": 1, - "links": [], - "modified": "2022-05-06 15:42:10.395508", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Grade", - "naming_rule": "Set by user", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_grade/employee_grade.py b/erpnext/hr/doctype/employee_grade/employee_grade.py deleted file mode 100644 index 41b7915c95..0000000000 --- a/erpnext/hr/doctype/employee_grade/employee_grade.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class EmployeeGrade(Document): - pass diff --git a/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py b/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py deleted file mode 100644 index efc68ce87a..0000000000 --- a/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py +++ /dev/null @@ -1,9 +0,0 @@ -def get_data(): - return { - "transactions": [ - { - "items": ["Employee", "Leave Period"], - }, - {"items": ["Employee Onboarding Template", "Employee Separation Template"]}, - ] - } diff --git a/erpnext/hr/doctype/employee_grade/test_employee_grade.py b/erpnext/hr/doctype/employee_grade/test_employee_grade.py deleted file mode 100644 index a70d685348..0000000000 --- a/erpnext/hr/doctype/employee_grade/test_employee_grade.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestEmployeeGrade(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/employee_grievance/__init__.py b/erpnext/hr/doctype/employee_grievance/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance.js b/erpnext/hr/doctype/employee_grievance/employee_grievance.js deleted file mode 100644 index 25c5badbc7..0000000000 --- a/erpnext/hr/doctype/employee_grievance/employee_grievance.js +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Grievance', { - setup: function(frm) { - frm.set_query('grievance_against_party', function() { - return { - filters: { - name: ['in', [ - 'Company', 'Department', 'Employee Group', 'Employee Grade', 'Employee'] - ] - } - }; - }); - frm.set_query('associated_document_type', function() { - let ignore_modules = ["Setup", "Core", "Integrations", "Automation", "Website", - "Utilities", "Event Streaming", "Social", "Chat", "Data Migration", "Printing", "Desk", "Custom"]; - return { - filters: { - istable: 0, - issingle: 0, - module: ["Not In", ignore_modules] - } - }; - }); - }, - - grievance_against_party: function(frm) { - let filters = {}; - if (frm.doc.grievance_against_party == 'Employee' && frm.doc.raised_by) { - filters.name = ["!=", frm.doc.raised_by]; - } - frm.set_query('grievance_against', function() { - return { - filters: filters - }; - }); - }, -}); diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance.json b/erpnext/hr/doctype/employee_grievance/employee_grievance.json deleted file mode 100644 index 5a918562af..0000000000 --- a/erpnext/hr/doctype/employee_grievance/employee_grievance.json +++ /dev/null @@ -1,261 +0,0 @@ -{ - "actions": [], - "autoname": "HR-GRIEV-.YYYY.-.#####", - "creation": "2021-05-11 13:41:51.485295", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "subject", - "raised_by", - "employee_name", - "designation", - "column_break_3", - "date", - "status", - "reports_to", - "grievance_details_section", - "grievance_against_party", - "grievance_against", - "grievance_type", - "column_break_11", - "associated_document_type", - "associated_document", - "section_break_14", - "description", - "investigation_details_section", - "cause_of_grievance", - "resolution_details_section", - "resolved_by", - "resolution_date", - "employee_responsible", - "column_break_16", - "resolution_detail", - "amended_from" - ], - "fields": [ - { - "fieldname": "grievance_type", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Grievance Type", - "options": "Grievance Type", - "reqd": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Date ", - "reqd": 1 - }, - { - "default": "Open", - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Status", - "options": "Open\nInvestigated\nResolved\nInvalid", - "reqd": 1 - }, - { - "fieldname": "description", - "fieldtype": "Text", - "label": "Description", - "reqd": 1 - }, - { - "fieldname": "cause_of_grievance", - "fieldtype": "Text", - "label": "Cause of Grievance", - "mandatory_depends_on": "eval: doc.status == \"Investigated\" || doc.status == \"Resolved\"" - }, - { - "fieldname": "resolution_details_section", - "fieldtype": "Section Break", - "label": "Resolution Details" - }, - { - "fieldname": "resolved_by", - "fieldtype": "Link", - "label": "Resolved By", - "mandatory_depends_on": "eval: doc.status == \"Resolved\"", - "options": "User" - }, - { - "fieldname": "employee_responsible", - "fieldtype": "Link", - "label": "Employee Responsible ", - "options": "Employee" - }, - { - "fieldname": "resolution_detail", - "fieldtype": "Small Text", - "label": "Resolution Details", - "mandatory_depends_on": "eval: doc.status == \"Resolved\"" - }, - { - "fieldname": "column_break_16", - "fieldtype": "Column Break" - }, - { - "fieldname": "resolution_date", - "fieldtype": "Date", - "label": "Resolution Date", - "mandatory_depends_on": "eval: doc.status == \"Resolved\"" - }, - { - "fieldname": "grievance_against", - "fieldtype": "Dynamic Link", - "label": "Grievance Against", - "options": "grievance_against_party", - "reqd": 1 - }, - { - "fieldname": "raised_by", - "fieldtype": "Link", - "label": "Raised By", - "options": "Employee", - "reqd": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Employee Grievance", - "print_hide": 1, - "read_only": 1 - }, - { - "fetch_from": "raised_by.designation", - "fieldname": "designation", - "fieldtype": "Link", - "label": "Designation", - "options": "Designation", - "read_only": 1 - }, - { - "fetch_from": "raised_by.reports_to", - "fieldname": "reports_to", - "fieldtype": "Link", - "label": "Reports To", - "options": "Employee", - "read_only": 1 - }, - { - "fieldname": "grievance_details_section", - "fieldtype": "Section Break", - "label": "Grievance Details" - }, - { - "fieldname": "column_break_11", - "fieldtype": "Column Break" - }, - { - "fieldname": "section_break_14", - "fieldtype": "Section Break" - }, - { - "fieldname": "grievance_against_party", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Grievance Against Party", - "options": "DocType", - "reqd": 1 - }, - { - "fieldname": "associated_document_type", - "fieldtype": "Link", - "label": "Associated Document Type", - "options": "DocType" - }, - { - "fieldname": "associated_document", - "fieldtype": "Dynamic Link", - "label": "Associated Document", - "options": "associated_document_type" - }, - { - "fieldname": "investigation_details_section", - "fieldtype": "Section Break", - "label": "Investigation Details" - }, - { - "fetch_from": "raised_by.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "label": "Employee Name", - "read_only": 1 - }, - { - "fieldname": "subject", - "fieldtype": "Data", - "label": "Subject", - "reqd": 1 - } - ], - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2021-06-21 12:51:01.499486", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Grievance", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "select": 1, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "select": 1, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - } - ], - "search_fields": "subject,raised_by,grievance_against_party", - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "subject", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance.py b/erpnext/hr/doctype/employee_grievance/employee_grievance.py deleted file mode 100644 index 45de79f4f5..0000000000 --- a/erpnext/hr/doctype/employee_grievance/employee_grievance.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -import frappe -from frappe import _, bold -from frappe.model.document import Document - - -class EmployeeGrievance(Document): - def on_submit(self): - if self.status not in ["Invalid", "Resolved"]: - frappe.throw( - _("Only Employee Grievance with status {0} or {1} can be submitted").format( - bold("Invalid"), bold("Resolved") - ) - ) diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js b/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js deleted file mode 100644 index 11672ca4e0..0000000000 --- a/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js +++ /dev/null @@ -1,12 +0,0 @@ -frappe.listview_settings["Employee Grievance"] = { - has_indicator_for_draft: 1, - get_indicator: function(doc) { - var colors = { - "Open": "red", - "Investigated": "orange", - "Resolved": "green", - "Invalid": "grey" - }; - return [__(doc.status), colors[doc.status], "status,=," + doc.status]; - } -}; diff --git a/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py b/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py deleted file mode 100644 index 910d882860..0000000000 --- a/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import today - -from erpnext.hr.doctype.employee.test_employee import make_employee - - -class TestEmployeeGrievance(unittest.TestCase): - def test_create_employee_grievance(self): - create_employee_grievance() - - -def create_employee_grievance(): - grievance_type = create_grievance_type() - emp_1 = make_employee("test_emp_grievance_@example.com", company="_Test Company") - emp_2 = make_employee("testculprit@example.com", company="_Test Company") - - grievance = frappe.new_doc("Employee Grievance") - grievance.subject = "Test Employee Grievance" - grievance.raised_by = emp_1 - grievance.date = today() - grievance.grievance_type = grievance_type - grievance.grievance_against_party = "Employee" - grievance.grievance_against = emp_2 - grievance.description = "test descrip" - - # set cause - grievance.cause_of_grievance = "test cause" - - # resolution details - grievance.resolution_date = today() - grievance.resolution_detail = "test resolution detail" - grievance.resolved_by = "test_emp_grievance_@example.com" - grievance.employee_responsible = emp_2 - grievance.status = "Resolved" - - grievance.save() - grievance.submit() - - return grievance - - -def create_grievance_type(): - if frappe.db.exists("Grievance Type", "Employee Abuse"): - return frappe.get_doc("Grievance Type", "Employee Abuse") - grievance_type = frappe.new_doc("Grievance Type") - grievance_type.name = "Employee Abuse" - grievance_type.description = "Test" - grievance_type.save() - - return grievance_type.name diff --git a/erpnext/hr/doctype/employee_health_insurance/__init__.py b/erpnext/hr/doctype/employee_health_insurance/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.js b/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.js deleted file mode 100644 index 69d46e20d0..0000000000 --- a/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Health Insurance', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.json b/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.json deleted file mode 100644 index e63da3974c..0000000000 --- a/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "autoname": "field:health_insurance_name", - "beta": 0, - "creation": "2017-03-27 14:32:51.628588", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "health_insurance_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Health Insurance Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-04-15 14:56:46.924890", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Health Insurance", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "health_insurance_name", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.py b/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.py deleted file mode 100644 index 4a8c437d64..0000000000 --- a/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class EmployeeHealthInsurance(Document): - pass diff --git a/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.py b/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.py deleted file mode 100644 index 4f042b7079..0000000000 --- a/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestEmployeeHealthInsurance(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/employee_loan/employee_loan.js b/erpnext/hr/doctype/employee_loan/employee_loan.js deleted file mode 100644 index c948be1b0f..0000000000 --- a/erpnext/hr/doctype/employee_loan/employee_loan.js +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Loan', { - onload: function (frm) { - frm.set_query("employee_loan_application", function () { - return { - "filters": { - "employee": frm.doc.employee, - "docstatus": 1, - "status": "Approved" - } - }; - }); - - frm.set_query("interest_income_account", function () { - return { - "filters": { - "company": frm.doc.company, - "root_type": "Income", - "is_group": 0 - } - }; - }); - - frm.set_query("employee", function() { - return { - "filters": { - "company": frm.doc.company, - } - }; - }); - - $.each(["payment_account", "employee_loan_account"], function (i, field) { - frm.set_query(field, function () { - return { - "filters": { - "company": frm.doc.company, - "root_type": "Asset", - "is_group": 0 - } - }; - }); - }) - }, - - refresh: function (frm) { - if (frm.doc.docstatus == 1 && (frm.doc.status == "Sanctioned" || frm.doc.status == "Partially Disbursed")) { - frm.add_custom_button(__('Create Disbursement Entry'), function () { - frm.trigger("make_jv"); - }) - } - frm.trigger("toggle_fields"); - }, - - make_jv: function (frm) { - frappe.call({ - args: { - "employee_loan": frm.doc.name, - "company": frm.doc.company, - "employee_loan_account": frm.doc.employee_loan_account, - "employee": frm.doc.employee, - "loan_amount": frm.doc.loan_amount, - "payment_account": frm.doc.payment_account - }, - method: "erpnext.hr.doctype.employee_loan.employee_loan.make_jv_entry", - callback: function (r) { - if (r.message) - var doc = frappe.model.sync(r.message)[0]; - frappe.set_route("Form", doc.doctype, doc.name); - } - }) - }, - - mode_of_payment: function (frm) { - if (frm.doc.mode_of_payment && frm.doc.company) { - frappe.call({ - method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account", - args: { - "mode_of_payment": frm.doc.mode_of_payment, - "company": frm.doc.company - }, - callback: function (r, rt) { - if (r.message) { - frm.set_value("payment_account", r.message.account); - } - } - }); - } - }, - - employee_loan_application: function (frm) { - if(frm.doc.employee_loan_application){ - return frappe.call({ - method: "erpnext.hr.doctype.employee_loan.employee_loan.get_employee_loan_application", - args: { - "employee_loan_application": frm.doc.employee_loan_application - }, - callback: function (r) { - if (!r.exc && r.message) { - frm.set_value("loan_type", r.message.loan_type); - frm.set_value("loan_amount", r.message.loan_amount); - frm.set_value("repayment_method", r.message.repayment_method); - frm.set_value("monthly_repayment_amount", r.message.repayment_amount); - frm.set_value("repayment_periods", r.message.repayment_periods); - frm.set_value("rate_of_interest", r.message.rate_of_interest); - } - } - }); - } - }, - - repayment_method: function (frm) { - frm.trigger("toggle_fields") - }, - - toggle_fields: function (frm) { - frm.toggle_enable("monthly_repayment_amount", frm.doc.repayment_method == "Repay Fixed Amount per Period") - frm.toggle_enable("repayment_periods", frm.doc.repayment_method == "Repay Over Number of Periods") - } -}); diff --git a/erpnext/hr/doctype/employee_onboarding/__init__.py b/erpnext/hr/doctype/employee_onboarding/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js deleted file mode 100644 index 6fbb54d002..0000000000 --- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Onboarding', { - setup: function(frm) { - frm.set_query("job_applicant", function () { - return { - filters:{ - "status": "Accepted", - } - }; - }); - - frm.set_query('job_offer', function () { - return { - filters: { - 'job_applicant': frm.doc.job_applicant, - 'docstatus': 1 - } - }; - }); - }, - - refresh: function(frm) { - if (frm.doc.employee) { - frm.add_custom_button(__('Employee'), function() { - frappe.set_route("Form", "Employee", frm.doc.employee); - },__("View")); - } - if (frm.doc.project) { - frm.add_custom_button(__('Project'), function() { - frappe.set_route("Form", "Project", frm.doc.project); - },__("View")); - frm.add_custom_button(__('Task'), function() { - frappe.set_route('List', 'Task', {project: frm.doc.project}); - },__("View")); - } - if ((!frm.doc.employee) && (frm.doc.docstatus === 1)) { - frm.add_custom_button(__('Employee'), function () { - frappe.model.open_mapped_doc({ - method: "erpnext.hr.doctype.employee_onboarding.employee_onboarding.make_employee", - frm: frm - }); - }, __('Create')); - frm.page.set_inner_btn_group_as_primary(__('Create')); - } - }, - - employee_onboarding_template: function(frm) { - frm.set_value("activities" ,""); - if (frm.doc.employee_onboarding_template) { - frappe.call({ - method: "erpnext.controllers.employee_boarding_controller.get_onboarding_details", - args: { - "parent": frm.doc.employee_onboarding_template, - "parenttype": "Employee Onboarding Template" - }, - callback: function(r) { - if (r.message) { - r.message.forEach((d) => { - frm.add_child("activities", d); - }); - refresh_field("activities"); - } - } - }); - } - }, - - job_applicant: function(frm) { - if (frm.doc.job_applicant) { - frappe.db.get_value('Employee', {'job_applicant': frm.doc.job_applicant}, 'name', (r) => { - if (r.name) { - frm.set_value('employee', r.name); - } else { - frm.set_value('employee', ''); - } - }); - } else { - frm.set_value('employee', ''); - } - } -}); diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json deleted file mode 100644 index 1d2ea0c669..0000000000 --- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json +++ /dev/null @@ -1,205 +0,0 @@ -{ - "actions": [], - "autoname": "HR-EMP-ONB-.YYYY.-.#####", - "creation": "2018-05-09 04:57:20.016220", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "job_applicant", - "job_offer", - "employee_onboarding_template", - "column_break_7", - "company", - "boarding_status", - "project", - "details_section", - "employee", - "employee_name", - "department", - "designation", - "employee_grade", - "holiday_list", - "column_break_13", - "date_of_joining", - "boarding_begins_on", - "table_for_activity", - "activities", - "notify_users_by_email", - "amended_from" - ], - "fields": [ - { - "fieldname": "job_applicant", - "fieldtype": "Link", - "label": "Job Applicant", - "options": "Job Applicant", - "reqd": 1 - }, - { - "fieldname": "job_offer", - "fieldtype": "Link", - "label": "Job Offer", - "options": "Job Offer", - "reqd": 1 - }, - { - "fetch_from": "job_applicant.applicant_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Employee Name", - "reqd": 1 - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "label": "Employee", - "options": "Employee", - "read_only": 1 - }, - { - "fieldname": "date_of_joining", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Date of Joining", - "reqd": 1 - }, - { - "allow_on_submit": 1, - "default": "Pending", - "fieldname": "boarding_status", - "fieldtype": "Select", - "label": "Status", - "options": "Pending\nIn Process\nCompleted", - "read_only": 1 - }, - { - "allow_on_submit": 1, - "default": "0", - "fieldname": "notify_users_by_email", - "fieldtype": "Check", - "label": "Notify users by email" - }, - { - "fieldname": "column_break_7", - "fieldtype": "Column Break" - }, - { - "fieldname": "employee_onboarding_template", - "fieldtype": "Link", - "label": "Employee Onboarding Template", - "options": "Employee Onboarding Template" - }, - { - "fetch_from": "employee_onboarding_template.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fetch_from": "employee_onboarding_template.department", - "fieldname": "department", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Department", - "options": "Department" - }, - { - "fetch_from": "employee_onboarding_template.designation", - "fieldname": "designation", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Designation", - "options": "Designation" - }, - { - "fetch_from": "employee_onboarding_template.employee_grade", - "fieldname": "employee_grade", - "fieldtype": "Link", - "label": "Employee Grade", - "options": "Employee Grade" - }, - { - "fieldname": "project", - "fieldtype": "Link", - "label": "Project", - "options": "Project", - "read_only": 1 - }, - { - "fieldname": "table_for_activity", - "fieldtype": "Section Break", - "label": "Onboarding Activities" - }, - { - "allow_on_submit": 1, - "fieldname": "activities", - "fieldtype": "Table", - "label": "Activities", - "options": "Employee Boarding Activity" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Employee Onboarding", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "details_section", - "fieldtype": "Section Break", - "label": "Employee Details" - }, - { - "fieldname": "column_break_13", - "fieldtype": "Column Break" - }, - { - "fieldname": "boarding_begins_on", - "fieldtype": "Date", - "label": "Onboarding Begins On", - "reqd": 1 - }, - { - "fieldname": "holiday_list", - "fieldtype": "Link", - "label": "Holiday List", - "options": "Holiday List" - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-29 12:33:57.120384", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Onboarding", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py deleted file mode 100644 index 059f83a5a2..0000000000 --- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.mapper import get_mapped_doc - -from erpnext.controllers.employee_boarding_controller import EmployeeBoardingController - - -class IncompleteTaskError(frappe.ValidationError): - pass - - -class EmployeeOnboarding(EmployeeBoardingController): - def validate(self): - super(EmployeeOnboarding, self).validate() - self.set_employee() - self.validate_duplicate_employee_onboarding() - - def set_employee(self): - if not self.employee: - self.employee = frappe.db.get_value("Employee", {"job_applicant": self.job_applicant}, "name") - - def validate_duplicate_employee_onboarding(self): - emp_onboarding = frappe.db.exists( - "Employee Onboarding", {"job_applicant": self.job_applicant, "docstatus": ("!=", 2)} - ) - if emp_onboarding and emp_onboarding != self.name: - frappe.throw( - _("Employee Onboarding: {0} already exists for Job Applicant: {1}").format( - frappe.bold(emp_onboarding), frappe.bold(self.job_applicant) - ) - ) - - def validate_employee_creation(self): - if self.docstatus != 1: - frappe.throw(_("Submit this to create the Employee record")) - else: - for activity in self.activities: - if not activity.required_for_employee_creation: - continue - else: - task_status = frappe.db.get_value("Task", activity.task, "status") - if task_status not in ["Completed", "Cancelled"]: - frappe.throw( - _("All the mandatory tasks for employee creation are not completed yet."), - IncompleteTaskError, - ) - - def on_submit(self): - super(EmployeeOnboarding, self).on_submit() - - def on_update_after_submit(self): - self.create_task_and_notify_user() - - def on_cancel(self): - super(EmployeeOnboarding, self).on_cancel() - - -@frappe.whitelist() -def make_employee(source_name, target_doc=None): - doc = frappe.get_doc("Employee Onboarding", source_name) - doc.validate_employee_creation() - - def set_missing_values(source, target): - target.personal_email = frappe.db.get_value("Job Applicant", source.job_applicant, "email_id") - target.status = "Active" - - doc = get_mapped_doc( - "Employee Onboarding", - source_name, - { - "Employee Onboarding": { - "doctype": "Employee", - "field_map": { - "first_name": "employee_name", - "employee_grade": "grade", - }, - } - }, - target_doc, - set_missing_values, - ) - return doc diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding_list.js b/erpnext/hr/doctype/employee_onboarding/employee_onboarding_list.js deleted file mode 100644 index a33619bed4..0000000000 --- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding_list.js +++ /dev/null @@ -1,7 +0,0 @@ -frappe.listview_settings['Employee Onboarding'] = { - add_fields: ["boarding_status", "employee_name", "date_of_joining", "department"], - filters:[["boarding_status","=", "Pending"]], - get_indicator: function(doc) { - return [__(doc.boarding_status), frappe.utils.guess_colour(doc.boarding_status), "status,=," + doc.boarding_status]; - } -}; diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py deleted file mode 100644 index 9d91e4bd62..0000000000 --- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import add_days, getdate - -from erpnext.hr.doctype.employee_onboarding.employee_onboarding import ( - IncompleteTaskError, - make_employee, -) -from erpnext.hr.doctype.job_offer.test_job_offer import create_job_offer -from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list - - -class TestEmployeeOnboarding(unittest.TestCase): - def setUp(self): - if frappe.db.exists("Employee Onboarding", {"employee_name": "Test Researcher"}): - frappe.delete_doc("Employee Onboarding", {"employee_name": "Test Researcher"}) - - project = "Employee Onboarding : test@researcher.com" - frappe.db.sql("delete from tabProject where name=%s", project) - frappe.db.sql("delete from tabTask where project=%s", project) - - def test_employee_onboarding_incomplete_task(self): - onboarding = create_employee_onboarding() - - project_name = frappe.db.get_value("Project", onboarding.project, "project_name") - self.assertEqual(project_name, "Employee Onboarding : test@researcher.com") - - # don't allow making employee if onboarding is not complete - self.assertRaises(IncompleteTaskError, make_employee, onboarding.name) - - # boarding status - self.assertEqual(onboarding.boarding_status, "Pending") - - # start and end dates - start_date, end_date = frappe.db.get_value( - "Task", onboarding.activities[0].task, ["exp_start_date", "exp_end_date"] - ) - self.assertEqual(getdate(start_date), getdate(onboarding.boarding_begins_on)) - self.assertEqual(getdate(end_date), add_days(start_date, onboarding.activities[0].duration)) - - start_date, end_date = frappe.db.get_value( - "Task", onboarding.activities[1].task, ["exp_start_date", "exp_end_date"] - ) - self.assertEqual( - getdate(start_date), add_days(onboarding.boarding_begins_on, onboarding.activities[0].duration) - ) - self.assertEqual(getdate(end_date), add_days(start_date, onboarding.activities[1].duration)) - - # complete the task - project = frappe.get_doc("Project", onboarding.project) - for task in frappe.get_all("Task", dict(project=project.name)): - task = frappe.get_doc("Task", task.name) - task.status = "Completed" - task.save() - - # boarding status - onboarding.reload() - self.assertEqual(onboarding.boarding_status, "Completed") - - # make employee - onboarding.reload() - employee = make_employee(onboarding.name) - employee.first_name = employee.employee_name - employee.date_of_joining = getdate() - employee.date_of_birth = "1990-05-08" - employee.gender = "Female" - employee.insert() - self.assertEqual(employee.employee_name, "Test Researcher") - - def tearDown(self): - frappe.db.rollback() - - -def get_job_applicant(): - if frappe.db.exists("Job Applicant", "test@researcher.com"): - return frappe.get_doc("Job Applicant", "test@researcher.com") - applicant = frappe.new_doc("Job Applicant") - applicant.applicant_name = "Test Researcher" - applicant.email_id = "test@researcher.com" - applicant.designation = "Researcher" - applicant.status = "Open" - applicant.cover_letter = "I am a great Researcher." - applicant.insert() - return applicant - - -def get_job_offer(applicant_name): - job_offer = frappe.db.exists("Job Offer", {"job_applicant": applicant_name}) - if job_offer: - return frappe.get_doc("Job Offer", job_offer) - - job_offer = create_job_offer(job_applicant=applicant_name) - job_offer.submit() - return job_offer - - -def create_employee_onboarding(): - applicant = get_job_applicant() - job_offer = get_job_offer(applicant.name) - - holiday_list = make_holiday_list("_Test Employee Boarding") - holiday_list = frappe.get_doc("Holiday List", holiday_list) - holiday_list.holidays = [] - holiday_list.save() - - onboarding = frappe.new_doc("Employee Onboarding") - onboarding.job_applicant = applicant.name - onboarding.job_offer = job_offer.name - onboarding.date_of_joining = onboarding.boarding_begins_on = getdate() - onboarding.company = "_Test Company" - onboarding.holiday_list = holiday_list.name - onboarding.designation = "Researcher" - onboarding.append( - "activities", - { - "activity_name": "Assign ID Card", - "role": "HR User", - "required_for_employee_creation": 1, - "begin_on": 0, - "duration": 1, - }, - ) - onboarding.append( - "activities", - {"activity_name": "Assign a laptop", "role": "HR User", "begin_on": 1, "duration": 1}, - ) - onboarding.status = "Pending" - onboarding.insert() - onboarding.submit() - - return onboarding diff --git a/erpnext/hr/doctype/employee_onboarding_template/__init__.py b/erpnext/hr/doctype/employee_onboarding_template/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.js b/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.js deleted file mode 100644 index 5e1b6e9946..0000000000 --- a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.js +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Onboarding Template', { - setup: function(frm) { - frm.set_query("department", function() { - return { - filters: { - company: frm.doc.company - } - }; - }); - } -}); diff --git a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.json b/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.json deleted file mode 100644 index 04de08e161..0000000000 --- a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.json +++ /dev/null @@ -1,292 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "HR-EMP-ONT-.#####", - "beta": 0, - "creation": "2018-05-09 05:27:02.393377", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "designation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Designation", - "length": 0, - "no_copy": 0, - "options": "Designation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee_grade", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee Grade", - "length": 0, - "no_copy": 0, - "options": "Employee Grade", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Activities", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "activities", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Activities", - "length": 0, - "no_copy": 0, - "options": "Employee Boarding Activity", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 16:15:55.720946", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Onboarding Template", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "designation", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.py b/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.py deleted file mode 100644 index 199013a5a1..0000000000 --- a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class EmployeeOnboardingTemplate(Document): - pass diff --git a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py b/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py deleted file mode 100644 index 93237ee379..0000000000 --- a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py +++ /dev/null @@ -1,7 +0,0 @@ -def get_data(): - return { - "fieldname": "employee_onboarding_template", - "transactions": [ - {"items": ["Employee Onboarding"]}, - ], - } diff --git a/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.py b/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.py deleted file mode 100644 index db09011c87..0000000000 --- a/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestEmployeeOnboardingTemplate(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/employee_promotion/__init__.py b/erpnext/hr/doctype/employee_promotion/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.js b/erpnext/hr/doctype/employee_promotion/employee_promotion.js deleted file mode 100644 index 54e06f4581..0000000000 --- a/erpnext/hr/doctype/employee_promotion/employee_promotion.js +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -{% include 'erpnext/hr/employee_property_update.js' %} - -frappe.ui.form.on('Employee Promotion', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.json b/erpnext/hr/doctype/employee_promotion/employee_promotion.json deleted file mode 100644 index 173573e203..0000000000 --- a/erpnext/hr/doctype/employee_promotion/employee_promotion.json +++ /dev/null @@ -1,172 +0,0 @@ -{ - "actions": [], - "autoname": "HR-EMP-PRO-.YYYY.-.#####", - "creation": "2018-04-13 18:33:59.476562", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "department", - "salary_currency", - "column_break_3", - "promotion_date", - "company", - "details_section", - "promotion_details", - "salary_details_section", - "current_ctc", - "column_break_12", - "revised_ctc", - "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 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "promotion_date", - "fieldtype": "Date", - "label": "Promotion Date", - "reqd": 1 - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company" - }, - { - "description": "Set the properties that should be updated in the Employee master on promotion submission", - "fieldname": "details_section", - "fieldtype": "Section Break", - "label": "Employee Promotion Details" - }, - { - "fieldname": "promotion_details", - "fieldtype": "Table", - "options": "Employee Property History", - "reqd": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Employee Promotion", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "salary_details_section", - "fieldtype": "Section Break", - "label": "Salary Details" - }, - { - "fieldname": "column_break_12", - "fieldtype": "Column Break" - }, - { - "fetch_from": "employee.salary_currency", - "fieldname": "salary_currency", - "fieldtype": "Link", - "label": "Salary Currency", - "options": "Currency", - "read_only": 1 - }, - { - "fetch_from": "employee.ctc", - "fetch_if_empty": 1, - "fieldname": "current_ctc", - "fieldtype": "Currency", - "label": "Current CTC", - "mandatory_depends_on": "revised_ctc", - "options": "salary_currency" - }, - { - "depends_on": "current_ctc", - "fieldname": "revised_ctc", - "fieldtype": "Currency", - "label": "Revised CTC", - "options": "salary_currency" - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-04-22 18:47:10.168744", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Promotion", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1 - }, - { - "create": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.py b/erpnext/hr/doctype/employee_promotion/employee_promotion.py deleted file mode 100644 index 8c802e9991..0000000000 --- a/erpnext/hr/doctype/employee_promotion/employee_promotion.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import getdate - -from erpnext.hr.utils import update_employee_work_history, validate_active_employee - - -class EmployeePromotion(Document): - def validate(self): - validate_active_employee(self.employee) - - def before_submit(self): - if getdate(self.promotion_date) > getdate(): - frappe.throw( - _("Employee Promotion cannot be submitted before Promotion Date"), - frappe.DocstatusTransitionError, - ) - - def on_submit(self): - employee = frappe.get_doc("Employee", self.employee) - employee = update_employee_work_history( - employee, self.promotion_details, date=self.promotion_date - ) - - if self.revised_ctc: - employee.ctc = self.revised_ctc - - employee.save() - - def on_cancel(self): - employee = frappe.get_doc("Employee", self.employee) - employee = update_employee_work_history(employee, self.promotion_details, cancel=True) - - if self.revised_ctc: - employee.ctc = self.current_ctc - - employee.save() diff --git a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py deleted file mode 100644 index 71bb1a62c1..0000000000 --- a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days, getdate - -from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee - - -class TestEmployeePromotion(FrappeTestCase): - def setUp(self): - frappe.db.delete("Employee Promotion") - - def test_submit_before_promotion_date(self): - employee = make_employee("employee@promotions.com") - promotion = frappe.get_doc( - { - "doctype": "Employee Promotion", - "employee": employee, - "promotion_details": [ - { - "property": "Designation", - "current": "Software Developer", - "new": "Project Manager", - "fieldname": "designation", - } - ], - } - ) - promotion.promotion_date = add_days(getdate(), 1) - self.assertRaises(frappe.DocstatusTransitionError, promotion.submit) - - promotion.promotion_date = getdate() - promotion.submit() - self.assertEqual(promotion.docstatus, 1) - - def test_employee_history(self): - for grade in ["L1", "L2"]: - frappe.get_doc({"doctype": "Employee Grade", "__newname": grade}).insert() - - employee = make_employee( - "test_employee_promotion@example.com", - company="_Test Company", - date_of_birth=getdate("30-09-1980"), - date_of_joining=getdate("01-10-2021"), - designation="Software Developer", - grade="L1", - salary_currency="INR", - ctc="500000", - ) - - promotion = frappe.get_doc( - { - "doctype": "Employee Promotion", - "employee": employee, - "promotion_date": getdate(), - "revised_ctc": "1000000", - "promotion_details": [ - { - "property": "Designation", - "current": "Software Developer", - "new": "Project Manager", - "fieldname": "designation", - }, - {"property": "Grade", "current": "L1", "new": "L2", "fieldname": "grade"}, - ], - } - ).submit() - - # employee fields updated - employee = frappe.get_doc("Employee", employee) - self.assertEqual(employee.grade, "L2") - self.assertEqual(employee.designation, "Project Manager") - self.assertEqual(employee.ctc, 1000000) - - # internal work history updated - self.assertEqual(employee.internal_work_history[0].designation, "Software Developer") - self.assertEqual(employee.internal_work_history[0].from_date, getdate("01-10-2021")) - - self.assertEqual(employee.internal_work_history[1].designation, "Project Manager") - self.assertEqual(employee.internal_work_history[1].from_date, getdate()) - - promotion.cancel() - employee.reload() - - # fields restored - self.assertEqual(employee.grade, "L1") - self.assertEqual(employee.designation, "Software Developer") - self.assertEqual(employee.ctc, 500000) - - # internal work history updated on cancellation - self.assertEqual(len(employee.internal_work_history), 1) - self.assertEqual(employee.internal_work_history[0].designation, "Software Developer") - self.assertEqual(employee.internal_work_history[0].from_date, getdate("01-10-2021")) diff --git a/erpnext/hr/doctype/employee_property_history/__init__.py b/erpnext/hr/doctype/employee_property_history/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_property_history/employee_property_history.json b/erpnext/hr/doctype/employee_property_history/employee_property_history.json deleted file mode 100644 index 0a51579549..0000000000 --- a/erpnext/hr/doctype/employee_property_history/employee_property_history.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 18:24:30.579965", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 4, - "fieldname": "property", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Property", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "fieldname": "current", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Current", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "fieldname": "new", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "New", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "fieldname", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Field Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-05-02 18:19:54.436391", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Property History", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_property_history/employee_property_history.py b/erpnext/hr/doctype/employee_property_history/employee_property_history.py deleted file mode 100644 index 345899e43c..0000000000 --- a/erpnext/hr/doctype/employee_property_history/employee_property_history.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class EmployeePropertyHistory(Document): - pass diff --git a/erpnext/hr/doctype/employee_referral/__init__.py b/erpnext/hr/doctype/employee_referral/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.js b/erpnext/hr/doctype/employee_referral/employee_referral.js deleted file mode 100644 index 8722019fb1..0000000000 --- a/erpnext/hr/doctype/employee_referral/employee_referral.js +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on("Employee Referral", { - refresh: function(frm) { - if (frm.doc.docstatus === 1 && frm.doc.status === "Pending") { - frm.add_custom_button(__("Reject Employee Referral"), function() { - frappe.confirm( - __("Are you sure you want to reject the Employee Referral?"), - function() { - frm.doc.status = "Rejected"; - frm.dirty(); - frm.save_or_update(); - }, - function() { - window.close(); - } - ); - }); - - frm.add_custom_button(__("Create Job Applicant"), function() { - frm.events.create_job_applicant(frm); - }).addClass("btn-primary"); - } - - // To check whether Payment is done or not - if (frm.doc.docstatus === 1 && frm.doc.status === "Accepted") { - frappe.db.get_list("Additional Salary", { - filters: { - ref_docname: cur_frm.doc.name, - docstatus: 1 - }, - fields: ["count(name) as additional_salary_count"] - }).then((data) => { - - let additional_salary_count = data[0].additional_salary_count; - - if (frm.doc.is_applicable_for_referral_bonus && !additional_salary_count) { - frm.add_custom_button(__("Create Additional Salary"), function() { - frm.events.create_additional_salary(frm); - }).addClass("btn-primary"); - } - }); - } - - }, - create_job_applicant: function(frm) { - frappe.model.open_mapped_doc({ - method: "erpnext.hr.doctype.employee_referral.employee_referral.create_job_applicant", - frm: frm - }); - }, - - create_additional_salary: function(frm) { - frappe.call({ - method: "erpnext.hr.doctype.employee_referral.employee_referral.create_additional_salary", - args: { - doc: frm.doc - }, - callback: function (r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }); - }, -}); diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.json b/erpnext/hr/doctype/employee_referral/employee_referral.json deleted file mode 100644 index 3ae73a9e4d..0000000000 --- a/erpnext/hr/doctype/employee_referral/employee_referral.json +++ /dev/null @@ -1,305 +0,0 @@ -{ - "actions": [], - "autoname": "format:HR-REF-{####}", - "creation": "2021-03-23 14:54:45.047051", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "first_name", - "last_name", - "full_name", - "column_break_6", - "date", - "status", - "for_designation", - "referral_details_section", - "email", - "contact_no", - "resume_link", - "column_break_12", - "current_employer", - "current_job_title", - "resume", - "referrer_details_section", - "referrer", - "referrer_name", - "column_break_14", - "is_applicable_for_referral_bonus", - "referral_payment_status", - "department", - "additional_information_section", - "qualification_reason", - "work_references", - "amended_from" - ], - "fields": [ - { - "fieldname": "first_name", - "fieldtype": "Data", - "label": "First Name ", - "reqd": 1 - }, - { - "fieldname": "last_name", - "fieldtype": "Data", - "label": "Last Name", - "reqd": 1 - }, - { - "fieldname": "full_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Full Name", - "read_only": 1 - }, - { - "fieldname": "contact_no", - "fieldtype": "Data", - "in_standard_filter": 1, - "label": "Contact No.", - "options": "Phone" - }, - { - "fieldname": "current_employer", - "fieldtype": "Data", - "label": "Current Employer " - }, - { - "fieldname": "column_break_6", - "fieldtype": "Column Break" - }, - { - "fieldname": "date", - "fieldtype": "Date", - "in_standard_filter": 1, - "label": "Date", - "reqd": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "status", - "fieldtype": "Select", - "in_standard_filter": 1, - "label": "Status", - "no_copy": 1, - "options": "Pending\nIn Process\nAccepted\nRejected", - "permlevel": 1, - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "current_job_title", - "fieldtype": "Data", - "label": "Current Job Title" - }, - { - "fieldname": "resume", - "fieldtype": "Attach", - "label": "Resume" - }, - { - "fieldname": "referrer_details_section", - "fieldtype": "Section Break", - "label": "Referrer Details" - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "additional_information_section", - "fieldtype": "Section Break", - "label": "Additional Information " - }, - { - "fieldname": "work_references", - "fieldtype": "Text Editor", - "label": "Work References" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Employee Referral", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "column_break_14", - "fieldtype": "Column Break" - }, - { - "fieldname": "for_designation", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "For Designation ", - "options": "Designation", - "reqd": 1 - }, - { - "fieldname": "email", - "fieldtype": "Data", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Email", - "options": "Email", - "reqd": 1, - "unique": 1 - }, - { - "default": "1", - "fieldname": "is_applicable_for_referral_bonus", - "fieldtype": "Check", - "label": "Is Applicable for Referral Bonus" - }, - { - "fieldname": "qualification_reason", - "fieldtype": "Text Editor", - "label": "Why is this Candidate Qualified for this Position?" - }, - { - "fieldname": "referrer", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Referrer", - "options": "Employee", - "reqd": 1 - }, - { - "fetch_from": "referrer.employee_name", - "fieldname": "referrer_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Referrer Name", - "read_only": 1 - }, - { - "fieldname": "resume_link", - "fieldtype": "Data", - "label": "Resume Link" - }, - { - "fieldname": "referral_payment_status", - "fieldtype": "Select", - "label": "Referral Bonus Payment Status", - "options": "\nUnpaid\nPaid", - "read_only": 1 - }, - { - "fieldname": "referral_details_section", - "fieldtype": "Section Break", - "label": "Referral Details" - }, - { - "fieldname": "column_break_12", - "fieldtype": "Column Break" - } - ], - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2021-05-04 17:03:26.134560", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Referral", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "amend": 1, - "create": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "delete": 1, - "email": 1, - "export": 1, - "permlevel": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "delete": 1, - "email": 1, - "export": 1, - "permlevel": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - }, - { - "email": 1, - "export": 1, - "permlevel": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "full_name" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.py b/erpnext/hr/doctype/employee_referral/employee_referral.py deleted file mode 100644 index 47cbfbcd9d..0000000000 --- a/erpnext/hr/doctype/employee_referral/employee_referral.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import get_link_to_form - -from erpnext.hr.utils import validate_active_employee - - -class EmployeeReferral(Document): - def validate(self): - validate_active_employee(self.referrer) - self.set_full_name() - self.set_referral_bonus_payment_status() - - def set_full_name(self): - self.full_name = " ".join(filter(None, [self.first_name, self.last_name])) - - def set_referral_bonus_payment_status(self): - if not self.is_applicable_for_referral_bonus: - self.referral_payment_status = "" - else: - if not self.referral_payment_status: - self.referral_payment_status = "Unpaid" - - -@frappe.whitelist() -def create_job_applicant(source_name, target_doc=None): - emp_ref = frappe.get_doc("Employee Referral", source_name) - # just for Api call if some set status apart from default Status - status = emp_ref.status - if emp_ref.status in ["Pending", "In process"]: - status = "Open" - - job_applicant = frappe.new_doc("Job Applicant") - job_applicant.source = "Employee Referral" - job_applicant.employee_referral = emp_ref.name - job_applicant.status = status - job_applicant.designation = emp_ref.for_designation - job_applicant.applicant_name = emp_ref.full_name - job_applicant.email_id = emp_ref.email - job_applicant.phone_number = emp_ref.contact_no - job_applicant.resume_attachment = emp_ref.resume - job_applicant.resume_link = emp_ref.resume_link - job_applicant.save() - - frappe.msgprint( - _("Job Applicant {0} created successfully.").format( - get_link_to_form("Job Applicant", job_applicant.name) - ), - title=_("Success"), - indicator="green", - ) - - emp_ref.db_set("status", "In Process") - - return job_applicant - - -@frappe.whitelist() -def create_additional_salary(doc): - import json - - if isinstance(doc, str): - doc = frappe._dict(json.loads(doc)) - - if not frappe.db.exists("Additional Salary", {"ref_docname": doc.name}): - additional_salary = frappe.new_doc("Additional Salary") - additional_salary.employee = doc.referrer - additional_salary.company = frappe.db.get_value("Employee", doc.referrer, "company") - additional_salary.overwrite_salary_structure_amount = 0 - additional_salary.ref_doctype = doc.doctype - additional_salary.ref_docname = doc.name - - return additional_salary diff --git a/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py deleted file mode 100644 index 4d683fbfcf..0000000000 --- a/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py +++ /dev/null @@ -1,8 +0,0 @@ -def get_data(): - return { - "fieldname": "employee_referral", - "non_standard_fieldnames": {"Additional Salary": "ref_docname"}, - "transactions": [ - {"items": ["Job Applicant", "Additional Salary"]}, - ], - } diff --git a/erpnext/hr/doctype/employee_referral/employee_referral_list.js b/erpnext/hr/doctype/employee_referral/employee_referral_list.js deleted file mode 100644 index 38dfc4d4c8..0000000000 --- a/erpnext/hr/doctype/employee_referral/employee_referral_list.js +++ /dev/null @@ -1,14 +0,0 @@ -frappe.listview_settings['Employee Referral'] = { - add_fields: ["status"], - get_indicator: function (doc) { - if (doc.status == "Pending") { - return [__(doc.status), "grey", "status,=," + doc.status]; - } else if (doc.status == "In Process") { - return [__(doc.status), "orange", "status,=," + doc.status]; - } else if (doc.status == "Accepted") { - return [__(doc.status), "green", "status,=," + doc.status]; - } else if (doc.status == "Rejected") { - return [__(doc.status), "red", "status,=," + doc.status]; - } - }, -}; diff --git a/erpnext/hr/doctype/employee_referral/test_employee_referral.py b/erpnext/hr/doctype/employee_referral/test_employee_referral.py deleted file mode 100644 index 475a935e86..0000000000 --- a/erpnext/hr/doctype/employee_referral/test_employee_referral.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import today - -from erpnext.hr.doctype.designation.test_designation import create_designation -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.employee_referral.employee_referral import ( - create_additional_salary, - create_job_applicant, -) - - -class TestEmployeeReferral(unittest.TestCase): - def setUp(self): - frappe.db.sql("DELETE FROM `tabJob Applicant`") - frappe.db.sql("DELETE FROM `tabEmployee Referral`") - - def test_workflow_and_status_sync(self): - emp_ref = create_employee_referral() - - # Check Initial status - self.assertTrue(emp_ref.status, "Pending") - - job_applicant = create_job_applicant(emp_ref.name) - - # Check status sync - emp_ref.reload() - self.assertTrue(emp_ref.status, "In Process") - - job_applicant.reload() - job_applicant.status = "Rejected" - job_applicant.save() - - emp_ref.reload() - self.assertTrue(emp_ref.status, "Rejected") - - job_applicant.reload() - job_applicant.status = "Accepted" - job_applicant.save() - - emp_ref.reload() - self.assertTrue(emp_ref.status, "Accepted") - - # Check for Referral reference in additional salary - - add_sal = create_additional_salary(emp_ref) - self.assertTrue(add_sal.ref_docname, emp_ref.name) - - def tearDown(self): - frappe.db.sql("DELETE FROM `tabJob Applicant`") - frappe.db.sql("DELETE FROM `tabEmployee Referral`") - - -def create_employee_referral(): - emp_ref = frappe.new_doc("Employee Referral") - emp_ref.first_name = "Mahesh" - emp_ref.last_name = "Singh" - emp_ref.email = "a@b.c" - emp_ref.date = today() - emp_ref.for_designation = create_designation().name - emp_ref.referrer = make_employee("testassetmovemp@example.com", company="_Test Company") - emp_ref.is_applicable_for_employee_referral_compensation = 1 - emp_ref.save() - emp_ref.submit() - - return emp_ref diff --git a/erpnext/hr/doctype/employee_separation/__init__.py b/erpnext/hr/doctype/employee_separation/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.js b/erpnext/hr/doctype/employee_separation/employee_separation.js deleted file mode 100644 index d9011b2001..0000000000 --- a/erpnext/hr/doctype/employee_separation/employee_separation.js +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Separation', { - setup: function(frm) { - frm.add_fetch("employee_separation_template", "company", "company"); - frm.add_fetch("employee_separation_template", "department", "department"); - frm.add_fetch("employee_separation_template", "designation", "designation"); - frm.add_fetch("employee_separation_template", "employee_grade", "employee_grade"); - }, - - refresh: function(frm) { - if (frm.doc.employee) { - frm.add_custom_button(__('Employee'), function() { - frappe.set_route("Form", "Employee", frm.doc.employee); - },__("View")); - } - if (frm.doc.project) { - frm.add_custom_button(__('Project'), function() { - frappe.set_route("Form", "Project", frm.doc.project); - },__("View")); - frm.add_custom_button(__('Task'), function() { - frappe.set_route('List', 'Task', {project: frm.doc.project}); - },__("View")); - } - }, - - employee_separation_template: function(frm) { - frm.set_value("activities" ,""); - if (frm.doc.employee_separation_template) { - frappe.call({ - method: "erpnext.controllers.employee_boarding_controller.get_onboarding_details", - args: { - "parent": frm.doc.employee_separation_template, - "parenttype": "Employee Separation Template" - }, - callback: function(r) { - if (r.message) { - $.each(r.message, function(i, d) { - var row = frappe.model.add_child(frm.doc, "Employee Boarding Activity", "activities"); - $.extend(row, d); - }); - } - refresh_field("activities"); - } - }); - } - } -}); diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.json b/erpnext/hr/doctype/employee_separation/employee_separation.json deleted file mode 100644 index c240493e82..0000000000 --- a/erpnext/hr/doctype/employee_separation/employee_separation.json +++ /dev/null @@ -1,185 +0,0 @@ -{ - "actions": [], - "autoname": "HR-EMP-SEP-.YYYY.-.#####", - "creation": "2018-05-10 02:29:16.740490", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "department", - "designation", - "employee_grade", - "column_break_7", - "company", - "boarding_status", - "resignation_letter_date", - "boarding_begins_on", - "project", - "table_for_activity", - "employee_separation_template", - "activities", - "notify_users_by_email", - "section_break_14", - "exit_interview", - "amended_from" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "label": "Employee", - "options": "Employee", - "reqd": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Employee Name", - "read_only": 1 - }, - { - "fetch_from": "employee.resignation_letter_date", - "fieldname": "resignation_letter_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Resignation Letter Date", - "read_only": 1 - }, - { - "allow_on_submit": 1, - "default": "Pending", - "fieldname": "boarding_status", - "fieldtype": "Select", - "label": "Status", - "options": "Pending\nIn Process\nCompleted", - "read_only": 1 - }, - { - "allow_on_submit": 1, - "default": "0", - "fieldname": "notify_users_by_email", - "fieldtype": "Check", - "label": "Notify users by email" - }, - { - "fieldname": "column_break_7", - "fieldtype": "Column Break" - }, - { - "fieldname": "employee_separation_template", - "fieldtype": "Link", - "label": "Employee Separation Template", - "options": "Employee Separation Template" - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "project", - "fieldtype": "Link", - "label": "Project", - "options": "Project", - "read_only": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fetch_from": "employee.designation", - "fieldname": "designation", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Designation", - "options": "Designation", - "read_only": 1 - }, - { - "fetch_from": "employee.grade", - "fieldname": "employee_grade", - "fieldtype": "Link", - "label": "Employee Grade", - "options": "Employee Grade", - "read_only": 1 - }, - { - "fieldname": "table_for_activity", - "fieldtype": "Section Break", - "label": "Separation Activities" - }, - { - "allow_on_submit": 1, - "fieldname": "activities", - "fieldtype": "Table", - "label": "Activities", - "options": "Employee Boarding Activity" - }, - { - "fieldname": "section_break_14", - "fieldtype": "Section Break" - }, - { - "fieldname": "exit_interview", - "fieldtype": "Text Editor", - "label": "Exit Interview Summary" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Employee Separation", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "boarding_begins_on", - "fieldtype": "Date", - "label": "Separation Begins On", - "reqd": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2021-07-30 14:03:51.218791", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Separation", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.py b/erpnext/hr/doctype/employee_separation/employee_separation.py deleted file mode 100644 index 915e9a876e..0000000000 --- a/erpnext/hr/doctype/employee_separation/employee_separation.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from erpnext.controllers.employee_boarding_controller import EmployeeBoardingController - - -class EmployeeSeparation(EmployeeBoardingController): - def validate(self): - super(EmployeeSeparation, self).validate() - - def on_submit(self): - super(EmployeeSeparation, self).on_submit() - - def on_update_after_submit(self): - self.create_task_and_notify_user() - - def on_cancel(self): - super(EmployeeSeparation, self).on_cancel() diff --git a/erpnext/hr/doctype/employee_separation/employee_separation_list.js b/erpnext/hr/doctype/employee_separation/employee_separation_list.js deleted file mode 100644 index 76c58f5632..0000000000 --- a/erpnext/hr/doctype/employee_separation/employee_separation_list.js +++ /dev/null @@ -1,7 +0,0 @@ -frappe.listview_settings['Employee Separation'] = { - add_fields: ["boarding_status", "employee_name", "department"], - filters:[["boarding_status","=", "Pending"]], - get_indicator: function(doc) { - return [__(doc.boarding_status), frappe.utils.guess_colour(doc.boarding_status), "status,=," + doc.boarding_status]; - } -}; diff --git a/erpnext/hr/doctype/employee_separation/test_employee_separation.py b/erpnext/hr/doctype/employee_separation/test_employee_separation.py deleted file mode 100644 index df31d09179..0000000000 --- a/erpnext/hr/doctype/employee_separation/test_employee_separation.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import getdate - -test_dependencies = ["Employee Onboarding"] - - -class TestEmployeeSeparation(unittest.TestCase): - def test_employee_separation(self): - separation = create_employee_separation() - - self.assertEqual(separation.docstatus, 1) - self.assertEqual(separation.boarding_status, "Pending") - - project = frappe.get_doc("Project", separation.project) - project.percent_complete_method = "Manual" - project.status = "Completed" - project.save() - - separation.reload() - self.assertEqual(separation.boarding_status, "Completed") - - separation.cancel() - self.assertEqual(separation.project, "") - - def tearDown(self): - for entry in frappe.get_all("Employee Separation"): - doc = frappe.get_doc("Employee Separation", entry.name) - if doc.docstatus == 1: - doc.cancel() - doc.delete() - - -def create_employee_separation(): - employee = frappe.db.get_value("Employee", {"status": "Active", "company": "_Test Company"}) - separation = frappe.new_doc("Employee Separation") - separation.employee = employee - separation.boarding_begins_on = getdate() - separation.company = "_Test Company" - separation.append("activities", {"activity_name": "Deactivate Employee", "role": "HR User"}) - separation.boarding_status = "Pending" - separation.insert() - separation.submit() - return separation diff --git a/erpnext/hr/doctype/employee_separation_template/__init__.py b/erpnext/hr/doctype/employee_separation_template/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_separation_template/employee_separation_template.js b/erpnext/hr/doctype/employee_separation_template/employee_separation_template.js deleted file mode 100644 index 172ff9fc5b..0000000000 --- a/erpnext/hr/doctype/employee_separation_template/employee_separation_template.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Separation Template', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/employee_separation_template/employee_separation_template.json b/erpnext/hr/doctype/employee_separation_template/employee_separation_template.json deleted file mode 100644 index e58323159b..0000000000 --- a/erpnext/hr/doctype/employee_separation_template/employee_separation_template.json +++ /dev/null @@ -1,292 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "HR-EMP-STP-.#####", - "beta": 0, - "creation": "2018-05-09 06:31:44.498557", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "designation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Designation", - "length": 0, - "no_copy": 0, - "options": "Designation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee_grade", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee Grade", - "length": 0, - "no_copy": 0, - "options": "Employee Grade", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Activities", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "activities", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Activities", - "length": 0, - "no_copy": 0, - "options": "Employee Boarding Activity", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 16:15:57.469756", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Separation Template", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "designation", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_separation_template/employee_separation_template.py b/erpnext/hr/doctype/employee_separation_template/employee_separation_template.py deleted file mode 100644 index 70b84b1755..0000000000 --- a/erpnext/hr/doctype/employee_separation_template/employee_separation_template.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class EmployeeSeparationTemplate(Document): - pass diff --git a/erpnext/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py b/erpnext/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py deleted file mode 100644 index 3ffd8dd6e2..0000000000 --- a/erpnext/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py +++ /dev/null @@ -1,7 +0,0 @@ -def get_data(): - return { - "fieldname": "employee_separation_template", - "transactions": [ - {"items": ["Employee Separation"]}, - ], - } diff --git a/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.py b/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.py deleted file mode 100644 index 6a0c9479db..0000000000 --- a/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestEmployeeSeparationTemplate(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/employee_skill/__init__.py b/erpnext/hr/doctype/employee_skill/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_skill/employee_skill.json b/erpnext/hr/doctype/employee_skill/employee_skill.json deleted file mode 100644 index 4b1419e1ec..0000000000 --- a/erpnext/hr/doctype/employee_skill/employee_skill.json +++ /dev/null @@ -1,141 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2019-04-16 09:57:52.751635", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "skill", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Skill", - "length": 0, - "no_copy": 0, - "options": "Skill", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "proficiency", - "fieldtype": "Rating", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Proficiency", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fetch_if_empty": 0, - "fieldname": "evaluation_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Evaluation Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_toolbar": 0, - "idx": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-04-16 14:13:17.111035", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Skill", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_skill/employee_skill.py b/erpnext/hr/doctype/employee_skill/employee_skill.py deleted file mode 100644 index 13bee34253..0000000000 --- a/erpnext/hr/doctype/employee_skill/employee_skill.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class EmployeeSkill(Document): - pass diff --git a/erpnext/hr/doctype/employee_skill_map/__init__.py b/erpnext/hr/doctype/employee_skill_map/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.js b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.js deleted file mode 100644 index b82b18d43b..0000000000 --- a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.js +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Skill Map', { - // refresh: function(frm) { - - // } - designation: (frm) => { - frm.set_value('employee_skills', null); - if (frm.doc.designation) { - frappe.db.get_doc('Designation', frm.doc.designation).then((designation) => { - designation.skills.forEach(designation_skill => { - let row = frappe.model.add_child(frm.doc, 'Employee Skill', 'employee_skills'); - row.skill = designation_skill.skill; - row.proficiency = 1; - }); - refresh_field('employee_skills'); - }); - } - } -}); diff --git a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.json b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.json deleted file mode 100644 index 21e25132a1..0000000000 --- a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "actions": [], - "autoname": "field:employee", - "creation": "2019-04-16 10:07:48.303426", - "doctype": "DocType", - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "column_break_3", - "designation", - "skills_section", - "employee_skills", - "trainings_section", - "trainings" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "label": "Employee", - "options": "Employee", - "unique": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Read Only", - "label": "Employee Name" - }, - { - "fetch_from": "employee.designation", - "fieldname": "designation", - "fieldtype": "Read Only", - "label": "Designation" - }, - { - "fieldname": "skills_section", - "fieldtype": "Section Break", - "label": "Skills" - }, - { - "fieldname": "employee_skills", - "fieldtype": "Table", - "label": "Employee Skills", - "options": "Employee Skill" - }, - { - "fieldname": "trainings_section", - "fieldtype": "Section Break", - "label": "Trainings" - }, - { - "fieldname": "trainings", - "fieldtype": "Table", - "label": "Trainings", - "options": "Employee Training" - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - } - ], - "links": [], - "modified": "2019-12-16 11:31:09.916893", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Skill Map", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "ASC", - "title_field": "employee_name" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.py b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.py deleted file mode 100644 index ea7da9edf9..0000000000 --- a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class EmployeeSkillMap(Document): - pass diff --git a/erpnext/hr/doctype/employee_training/__init__.py b/erpnext/hr/doctype/employee_training/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_training/employee_training.json b/erpnext/hr/doctype/employee_training/employee_training.json deleted file mode 100644 index 0e0dc15195..0000000000 --- a/erpnext/hr/doctype/employee_training/employee_training.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2019-04-16 16:15:50.931545", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "training", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Training", - "length": 0, - "no_copy": 0, - "options": "Training Event", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "training.end_time", - "fetch_if_empty": 0, - "fieldname": "training_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Training Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_toolbar": 0, - "idx": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-04-22 12:48:56.925419", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Training", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_training/employee_training.py b/erpnext/hr/doctype/employee_training/employee_training.py deleted file mode 100644 index cd92dd609f..0000000000 --- a/erpnext/hr/doctype/employee_training/employee_training.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class EmployeeTraining(Document): - pass diff --git a/erpnext/hr/doctype/employee_transfer/__init__.py b/erpnext/hr/doctype/employee_transfer/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.js b/erpnext/hr/doctype/employee_transfer/employee_transfer.js deleted file mode 100644 index af751a7c05..0000000000 --- a/erpnext/hr/doctype/employee_transfer/employee_transfer.js +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -{% include 'erpnext/hr/employee_property_update.js' %} - -frappe.ui.form.on('Employee Transfer', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.json b/erpnext/hr/doctype/employee_transfer/employee_transfer.json deleted file mode 100644 index d66ef64305..0000000000 --- a/erpnext/hr/doctype/employee_transfer/employee_transfer.json +++ /dev/null @@ -1,528 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "HR-EMP-TRN-.YYYY.-.#####", - "beta": 0, - "creation": "2018-04-13 18:20:01.603830", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transfer_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transfer Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "new_company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "New Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "details_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Transfer Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transfer_details", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Transfer Detail", - "length": 0, - "no_copy": 0, - "options": "Employee Property History", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reallocate_leaves", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Re-allocate Leaves", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "create_new_employee_id", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Create New Employee Id", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "new_employee_id", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "New Employee ID", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Employee Transfer", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 16:15:36.876312", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Transfer", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py deleted file mode 100644 index 6dbefe59da..0000000000 --- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import getdate - -from erpnext.hr.utils import update_employee_work_history - - -class EmployeeTransfer(Document): - def before_submit(self): - if getdate(self.transfer_date) > getdate(): - frappe.throw( - _("Employee Transfer cannot be submitted before Transfer Date"), - frappe.DocstatusTransitionError, - ) - - def on_submit(self): - employee = frappe.get_doc("Employee", self.employee) - if self.create_new_employee_id: - new_employee = frappe.copy_doc(employee) - new_employee.name = None - new_employee.employee_number = None - new_employee = update_employee_work_history( - new_employee, self.transfer_details, date=self.transfer_date - ) - if self.new_company and self.company != self.new_company: - new_employee.internal_work_history = [] - new_employee.date_of_joining = self.transfer_date - new_employee.company = self.new_company - # move user_id to new employee before insert - if employee.user_id and not self.validate_user_in_details(): - new_employee.user_id = employee.user_id - employee.db_set("user_id", "") - new_employee.insert() - self.db_set("new_employee_id", new_employee.name) - # relieve the old employee - employee.db_set("relieving_date", self.transfer_date) - employee.db_set("status", "Left") - else: - employee = update_employee_work_history( - employee, self.transfer_details, date=self.transfer_date - ) - if self.new_company and self.company != self.new_company: - employee.company = self.new_company - employee.date_of_joining = self.transfer_date - employee.save() - - def on_cancel(self): - employee = frappe.get_doc("Employee", self.employee) - if self.create_new_employee_id: - if self.new_employee_id: - frappe.throw( - _("Please delete the Employee {0} to cancel this document").format( - "{0}".format(self.new_employee_id) - ) - ) - # mark the employee as active - employee.status = "Active" - employee.relieving_date = "" - else: - employee = update_employee_work_history( - employee, self.transfer_details, date=self.transfer_date, cancel=True - ) - if self.new_company != self.company: - employee.company = self.company - employee.save() - - def validate_user_in_details(self): - for item in self.transfer_details: - if item.fieldname == "user_id" and item.new != item.current: - return True - return False diff --git a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py deleted file mode 100644 index 37a190a162..0000000000 --- a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import add_days, getdate - -from erpnext.hr.doctype.employee.test_employee import make_employee - - -class TestEmployeeTransfer(unittest.TestCase): - def setUp(self): - create_company() - - def tearDown(self): - frappe.db.rollback() - - def test_submit_before_transfer_date(self): - make_employee("employee2@transfers.com") - - transfer_obj = frappe.get_doc( - { - "doctype": "Employee Transfer", - "employee": frappe.get_value("Employee", {"user_id": "employee2@transfers.com"}, "name"), - "transfer_details": [ - { - "property": "Designation", - "current": "Software Developer", - "new": "Project Manager", - "fieldname": "designation", - } - ], - } - ) - transfer_obj.transfer_date = add_days(getdate(), 1) - transfer_obj.save() - self.assertRaises(frappe.DocstatusTransitionError, transfer_obj.submit) - transfer = frappe.get_doc("Employee Transfer", transfer_obj.name) - transfer.transfer_date = getdate() - transfer.submit() - self.assertEqual(transfer.docstatus, 1) - - def test_new_employee_creation(self): - make_employee("employee3@transfers.com") - - transfer = frappe.get_doc( - { - "doctype": "Employee Transfer", - "employee": frappe.get_value("Employee", {"user_id": "employee3@transfers.com"}, "name"), - "create_new_employee_id": 1, - "transfer_date": getdate(), - "transfer_details": [ - { - "property": "Designation", - "current": "Software Developer", - "new": "Project Manager", - "fieldname": "designation", - } - ], - } - ).insert() - transfer.submit() - self.assertTrue(transfer.new_employee_id) - self.assertEqual(frappe.get_value("Employee", transfer.new_employee_id, "status"), "Active") - self.assertEqual(frappe.get_value("Employee", transfer.employee, "status"), "Left") - - def test_employee_history(self): - employee = make_employee( - "employee4@transfers.com", - company="Test Company", - date_of_birth=getdate("30-09-1980"), - date_of_joining=getdate("01-10-2021"), - department="Accounts - TC", - designation="Accountant", - ) - transfer = create_employee_transfer(employee) - - count = 0 - department = ["Accounts - TC", "Management - TC"] - designation = ["Accountant", "Manager"] - dt = [getdate("01-10-2021"), getdate()] - - employee = frappe.get_doc("Employee", employee) - for data in employee.internal_work_history: - self.assertEqual(data.department, department[count]) - self.assertEqual(data.designation, designation[count]) - self.assertEqual(data.from_date, dt[count]) - count = count + 1 - - transfer.cancel() - employee.reload() - - for data in employee.internal_work_history: - self.assertEqual(data.designation, designation[0]) - self.assertEqual(data.department, department[0]) - self.assertEqual(data.from_date, dt[0]) - - -def create_company(): - if not frappe.db.exists("Company", "Test Company"): - frappe.get_doc( - { - "doctype": "Company", - "company_name": "Test Company", - "default_currency": "INR", - "country": "India", - } - ).insert() - - -def create_employee_transfer(employee): - doc = frappe.get_doc( - { - "doctype": "Employee Transfer", - "employee": employee, - "transfer_date": getdate(), - "transfer_details": [ - { - "property": "Designation", - "current": "Accountant", - "new": "Manager", - "fieldname": "designation", - }, - { - "property": "Department", - "current": "Accounts - TC", - "new": "Management - TC", - "fieldname": "department", - }, - ], - } - ) - - doc.save() - doc.submit() - - return doc diff --git a/erpnext/hr/doctype/employment_type/README.md b/erpnext/hr/doctype/employment_type/README.md deleted file mode 100644 index 19de703b24..0000000000 --- a/erpnext/hr/doctype/employment_type/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Type of employment. - -e.g. Permanent, Probation, Intern etc. \ No newline at end of file diff --git a/erpnext/hr/doctype/employment_type/__init__.py b/erpnext/hr/doctype/employment_type/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/employment_type/employment_type.json b/erpnext/hr/doctype/employment_type/employment_type.json deleted file mode 100644 index 6b0b505c83..0000000000 --- a/erpnext/hr/doctype/employment_type/employment_type.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:employee_type_name", - "beta": 0, - "creation": "2013-01-10 16:34:14", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee_type_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employment Type", - "length": 0, - "no_copy": 0, - "oldfieldname": "employee_type_name", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 1 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-flag", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-29 06:37:02.041245", - "modified_by": "Administrator", - "module": "HR", - "name": "Employment Type", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 1, - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employment_type/employment_type.py b/erpnext/hr/doctype/employment_type/employment_type.py deleted file mode 100644 index b2262c0c58..0000000000 --- a/erpnext/hr/doctype/employment_type/employment_type.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -from frappe.model.document import Document - - -class EmploymentType(Document): - pass diff --git a/erpnext/hr/doctype/employment_type/test_employment_type.py b/erpnext/hr/doctype/employment_type/test_employment_type.py deleted file mode 100644 index fdf6965e48..0000000000 --- a/erpnext/hr/doctype/employment_type/test_employment_type.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -test_records = frappe.get_test_records("Employment Type") diff --git a/erpnext/hr/doctype/employment_type/test_records.json b/erpnext/hr/doctype/employment_type/test_records.json deleted file mode 100644 index fa571798a0..0000000000 --- a/erpnext/hr/doctype/employment_type/test_records.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "doctype": "Employment Type", - "employee_type_name": "_Test Employment Type" - } -] \ No newline at end of file diff --git a/erpnext/hr/doctype/exit_interview/__init__.py b/erpnext/hr/doctype/exit_interview/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/exit_interview/exit_interview.js b/erpnext/hr/doctype/exit_interview/exit_interview.js deleted file mode 100644 index 502af423a2..0000000000 --- a/erpnext/hr/doctype/exit_interview/exit_interview.js +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Exit Interview', { - refresh: function(frm) { - if (!frm.doc.__islocal && !frm.doc.questionnaire_email_sent && frappe.boot.user.can_write.includes('Exit Interview')) { - frm.add_custom_button(__('Send Exit Questionnaire'), function () { - frm.trigger('send_exit_questionnaire'); - }); - } - }, - - employee: function(frm) { - frappe.db.get_value('Employee', frm.doc.employee, 'relieving_date', (message) => { - if (!message.relieving_date) { - frappe.throw({ - message: __('Please set the relieving date for employee {0}', - ['' + frm.doc.employee + '']), - title: __('Relieving Date Missing') - }); - } - }); - }, - - send_exit_questionnaire: function(frm) { - frappe.call({ - method: 'erpnext.hr.doctype.exit_interview.exit_interview.send_exit_questionnaire', - args: { - 'interviews': [frm.doc] - }, - callback: function(r) { - if (!r.exc) { - frm.refresh_field('questionnaire_email_sent'); - } - } - }); - } -}); diff --git a/erpnext/hr/doctype/exit_interview/exit_interview.json b/erpnext/hr/doctype/exit_interview/exit_interview.json deleted file mode 100644 index 989a1b8118..0000000000 --- a/erpnext/hr/doctype/exit_interview/exit_interview.json +++ /dev/null @@ -1,246 +0,0 @@ -{ - "actions": [], - "allow_rename": 1, - "autoname": "naming_series:", - "creation": "2021-12-05 13:56:36.241690", - "doctype": "DocType", - "editable_grid": 1, - "email_append_to": 1, - "engine": "InnoDB", - "field_order": [ - "naming_series", - "employee", - "employee_name", - "email", - "column_break_5", - "company", - "status", - "date", - "employee_details_section", - "department", - "designation", - "reports_to", - "column_break_9", - "date_of_joining", - "relieving_date", - "exit_questionnaire_section", - "ref_doctype", - "questionnaire_email_sent", - "column_break_10", - "reference_document_name", - "interview_summary_section", - "interviewers", - "interview_summary", - "employee_status_section", - "employee_status", - "amended_from" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 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.relieving_date", - "fieldname": "relieving_date", - "fieldtype": "Date", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Relieving Date", - "read_only": 1 - }, - { - "fieldname": "column_break_5", - "fieldtype": "Column Break" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "date", - "fieldtype": "Date", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Date", - "mandatory_depends_on": "eval:doc.status==='Scheduled';" - }, - { - "fieldname": "exit_questionnaire_section", - "fieldtype": "Section Break", - "label": "Exit Questionnaire" - }, - { - "fieldname": "ref_doctype", - "fieldtype": "Link", - "label": "Reference Document Type", - "options": "DocType" - }, - { - "fieldname": "reference_document_name", - "fieldtype": "Dynamic Link", - "in_list_view": 1, - "label": "Reference Document Name", - "options": "ref_doctype" - }, - { - "fieldname": "interview_summary_section", - "fieldtype": "Section Break", - "label": "Interview Details" - }, - { - "fieldname": "column_break_10", - "fieldtype": "Column Break" - }, - { - "fieldname": "interviewers", - "fieldtype": "Table MultiSelect", - "label": "Interviewers", - "mandatory_depends_on": "eval:doc.status==='Scheduled';", - "options": "Interviewer" - }, - { - "fetch_from": "employee.date_of_joining", - "fieldname": "date_of_joining", - "fieldtype": "Date", - "label": "Date of Joining", - "read_only": 1 - }, - { - "fetch_from": "employee.reports_to", - "fieldname": "reports_to", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Reports To", - "options": "Employee", - "read_only": 1 - }, - { - "fieldname": "employee_details_section", - "fieldtype": "Section Break", - "label": "Employee Details" - }, - { - "fetch_from": "employee.designation", - "fieldname": "designation", - "fieldtype": "Link", - "label": "Designation", - "options": "Designation", - "read_only": 1 - }, - { - "fieldname": "column_break_9", - "fieldtype": "Column Break" - }, - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Naming Series", - "options": "HR-EXIT-INT-" - }, - { - "default": "0", - "fieldname": "questionnaire_email_sent", - "fieldtype": "Check", - "in_standard_filter": 1, - "label": "Questionnaire Email Sent", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "email", - "fieldtype": "Data", - "label": "Email ID", - "options": "Email", - "read_only": 1 - }, - { - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Status", - "options": "Pending\nScheduled\nCompleted\nCancelled", - "reqd": 1 - }, - { - "fieldname": "employee_status_section", - "fieldtype": "Section Break" - }, - { - "fieldname": "employee_status", - "fieldtype": "Select", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Final Decision", - "mandatory_depends_on": "eval:doc.status==='Completed';", - "options": "\nEmployee Retained\nExit Confirmed" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Exit Interview", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "interview_summary", - "fieldtype": "Text Editor", - "label": "Interview Summary" - } - ], - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2021-12-07 23:39:22.645401", - "modified_by": "Administrator", - "module": "HR", - "name": "Exit Interview", - "naming_rule": "By \"Naming Series\" field", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "sender_field": "email", - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/exit_interview/exit_interview.py b/erpnext/hr/doctype/exit_interview/exit_interview.py deleted file mode 100644 index 8317310202..0000000000 --- a/erpnext/hr/doctype/exit_interview/exit_interview.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import get_link_to_form - -from erpnext.hr.doctype.employee.employee import get_employee_email - - -class ExitInterview(Document): - def validate(self): - self.validate_relieving_date() - self.validate_duplicate_interview() - self.set_employee_email() - - def validate_relieving_date(self): - if not frappe.db.get_value("Employee", self.employee, "relieving_date"): - frappe.throw( - _("Please set the relieving date for employee {0}").format( - get_link_to_form("Employee", self.employee) - ), - title=_("Relieving Date Missing"), - ) - - def validate_duplicate_interview(self): - doc = frappe.db.exists( - "Exit Interview", {"employee": self.employee, "name": ("!=", self.name), "docstatus": ("!=", 2)} - ) - if doc: - frappe.throw( - _("Exit Interview {0} already exists for Employee: {1}").format( - get_link_to_form("Exit Interview", doc), frappe.bold(self.employee) - ), - frappe.DuplicateEntryError, - ) - - def set_employee_email(self): - employee = frappe.get_doc("Employee", self.employee) - self.email = get_employee_email(employee) - - def on_submit(self): - if self.status != "Completed": - frappe.throw(_("Only Completed documents can be submitted")) - - self.update_interview_date_in_employee() - - def on_cancel(self): - self.update_interview_date_in_employee() - self.db_set("status", "Cancelled") - - def update_interview_date_in_employee(self): - if self.docstatus == 1: - frappe.db.set_value("Employee", self.employee, "held_on", self.date) - elif self.docstatus == 2: - frappe.db.set_value("Employee", self.employee, "held_on", None) - - -@frappe.whitelist() -def send_exit_questionnaire(interviews): - interviews = get_interviews(interviews) - validate_questionnaire_settings() - - email_success = [] - email_failure = [] - - for exit_interview in interviews: - interview = frappe.get_doc("Exit Interview", exit_interview.get("name")) - if interview.get("questionnaire_email_sent"): - continue - - employee = frappe.get_doc("Employee", interview.employee) - email = get_employee_email(employee) - - context = interview.as_dict() - context.update(employee.as_dict()) - template_name = frappe.db.get_single_value( - "HR Settings", "exit_questionnaire_notification_template" - ) - template = frappe.get_doc("Email Template", template_name) - - if email: - frappe.sendmail( - recipients=email, - subject=template.subject, - message=frappe.render_template(template.response, context), - reference_doctype=interview.doctype, - reference_name=interview.name, - ) - interview.db_set("questionnaire_email_sent", True) - interview.notify_update() - email_success.append(email) - else: - email_failure.append(get_link_to_form("Employee", employee.name)) - - show_email_summary(email_success, email_failure) - - -def get_interviews(interviews): - import json - - if isinstance(interviews, str): - interviews = json.loads(interviews) - - if not len(interviews): - frappe.throw(_("Atleast one interview has to be selected.")) - - return interviews - - -def validate_questionnaire_settings(): - settings = frappe.db.get_value( - "HR Settings", - "HR Settings", - ["exit_questionnaire_web_form", "exit_questionnaire_notification_template"], - as_dict=True, - ) - - if ( - not settings.exit_questionnaire_web_form or not settings.exit_questionnaire_notification_template - ): - frappe.throw( - _("Please set {0} and {1} in {2}.").format( - frappe.bold("Exit Questionnaire Web Form"), - frappe.bold("Notification Template"), - get_link_to_form("HR Settings", "HR Settings"), - ), - title=_("Settings Missing"), - ) - - -def show_email_summary(email_success, email_failure): - message = "" - if email_success: - message += _("{0}: {1}").format(frappe.bold("Sent Successfully"), ", ".join(email_success)) - if message and email_failure: - message += "

" - if email_failure: - message += _("{0} due to missing email information for employee(s): {1}").format( - frappe.bold("Sending Failed"), ", ".join(email_failure) - ) - - frappe.msgprint( - message, title=_("Exit Questionnaire"), indicator="blue", is_minimizable=True, wide=True - ) diff --git a/erpnext/hr/doctype/exit_interview/exit_interview_list.js b/erpnext/hr/doctype/exit_interview/exit_interview_list.js deleted file mode 100644 index 93d7b213f2..0000000000 --- a/erpnext/hr/doctype/exit_interview/exit_interview_list.js +++ /dev/null @@ -1,27 +0,0 @@ -frappe.listview_settings['Exit Interview'] = { - has_indicator_for_draft: 1, - get_indicator: function(doc) { - let status_color = { - 'Pending': 'orange', - 'Scheduled': 'yellow', - 'Completed': 'green', - 'Cancelled': 'red', - }; - return [__(doc.status), status_color[doc.status], 'status,=,'+doc.status]; - }, - - onload: function(listview) { - if (frappe.boot.user.can_write.includes('Exit Interview')) { - listview.page.add_action_item(__('Send Exit Questionnaires'), function() { - const interviews = listview.get_checked_items(); - frappe.call({ - method: 'erpnext.hr.doctype.exit_interview.exit_interview.send_exit_questionnaire', - freeze: true, - args: { - 'interviews': interviews - } - }); - }); - } - } -}; diff --git a/erpnext/hr/doctype/exit_interview/exit_questionnaire_notification_template.html b/erpnext/hr/doctype/exit_interview/exit_questionnaire_notification_template.html deleted file mode 100644 index 0317b1a102..0000000000 --- a/erpnext/hr/doctype/exit_interview/exit_questionnaire_notification_template.html +++ /dev/null @@ -1,16 +0,0 @@ -

Exit Questionnaire

-
- -

- Dear {{ employee_name }}, -

- - Thank you for the contribution you have made during your time at {{ company }}. We value your opinion and welcome the feedback on your experience working with us. - Request you to take out a few minutes to fill up this Exit Questionnaire. - - {% set web_form = frappe.db.get_value('HR Settings', 'HR Settings', 'exit_questionnaire_web_form') %} - {% set web_form_link = frappe.utils.get_url(uri=frappe.db.get_value('Web Form', web_form, 'route')) %} - -

- {{ _('Submit Now') }} -

diff --git a/erpnext/hr/doctype/exit_interview/test_exit_interview.py b/erpnext/hr/doctype/exit_interview/test_exit_interview.py deleted file mode 100644 index 9c2c6442f8..0000000000 --- a/erpnext/hr/doctype/exit_interview/test_exit_interview.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import os -import unittest - -import frappe -from frappe import _ -from frappe.core.doctype.user_permission.test_user_permission import create_user -from frappe.tests.test_webform import create_custom_doctype, create_webform -from frappe.utils import getdate - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.exit_interview.exit_interview import send_exit_questionnaire - - -class TestExitInterview(unittest.TestCase): - def setUp(self): - frappe.db.sql("delete from `tabExit Interview`") - - def test_duplicate_interview(self): - employee = make_employee("employeeexitint1@example.com") - frappe.db.set_value("Employee", employee, "relieving_date", getdate()) - interview = create_exit_interview(employee) - - doc = frappe.copy_doc(interview) - self.assertRaises(frappe.DuplicateEntryError, doc.save) - - def test_relieving_date_validation(self): - employee = make_employee("employeeexitint2@example.com") - # unset relieving date - frappe.db.set_value("Employee", employee, "relieving_date", None) - - interview = create_exit_interview(employee, save=False) - self.assertRaises(frappe.ValidationError, interview.save) - - # set relieving date - frappe.db.set_value("Employee", employee, "relieving_date", getdate()) - interview = create_exit_interview(employee) - self.assertTrue(interview.name) - - def test_interview_date_updated_in_employee_master(self): - employee = make_employee("employeeexit3@example.com") - frappe.db.set_value("Employee", employee, "relieving_date", getdate()) - - interview = create_exit_interview(employee) - interview.status = "Completed" - interview.employee_status = "Exit Confirmed" - - # exit interview date updated on submit - interview.submit() - self.assertEqual(frappe.db.get_value("Employee", employee, "held_on"), interview.date) - - # exit interview reset on cancel - interview.reload() - interview.cancel() - self.assertEqual(frappe.db.get_value("Employee", employee, "held_on"), None) - - def test_send_exit_questionnaire(self): - create_custom_doctype() - create_webform() - template = create_notification_template() - - webform = frappe.db.get_all("Web Form", limit=1) - frappe.db.set_value( - "HR Settings", - "HR Settings", - { - "exit_questionnaire_web_form": webform[0].name, - "exit_questionnaire_notification_template": template, - }, - ) - - employee = make_employee("employeeexit3@example.com") - frappe.db.set_value("Employee", employee, "relieving_date", getdate()) - - interview = create_exit_interview(employee) - send_exit_questionnaire([interview]) - - email_queue = frappe.db.get_all("Email Queue", ["name", "message"], limit=1) - self.assertTrue("Subject: Exit Questionnaire Notification" in email_queue[0].message) - - def tearDown(self): - frappe.db.rollback() - - -def create_exit_interview(employee, save=True): - interviewer = create_user("test_exit_interviewer@example.com") - - doc = frappe.get_doc( - { - "doctype": "Exit Interview", - "employee": employee, - "company": "_Test Company", - "status": "Pending", - "date": getdate(), - "interviewers": [{"interviewer": interviewer.name}], - "interview_summary": "Test", - } - ) - - if save: - return doc.insert() - return doc - - -def create_notification_template(): - template = frappe.db.exists("Email Template", _("Exit Questionnaire Notification")) - if not template: - base_path = frappe.get_app_path("erpnext", "hr", "doctype") - response = frappe.read_file( - os.path.join(base_path, "exit_interview/exit_questionnaire_notification_template.html") - ) - - template = frappe.get_doc( - { - "doctype": "Email Template", - "name": _("Exit Questionnaire Notification"), - "response": response, - "subject": _("Exit Questionnaire Notification"), - "owner": frappe.session.user, - } - ).insert(ignore_permissions=True) - template = template.name - - return template diff --git a/erpnext/hr/doctype/expected_skill_set/__init__.py b/erpnext/hr/doctype/expected_skill_set/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/expected_skill_set/expected_skill_set.json b/erpnext/hr/doctype/expected_skill_set/expected_skill_set.json deleted file mode 100644 index 899f5bd0ff..0000000000 --- a/erpnext/hr/doctype/expected_skill_set/expected_skill_set.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "actions": [], - "creation": "2021-04-12 13:05:06.741330", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "skill", - "description" - ], - "fields": [ - { - "fieldname": "skill", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Skill", - "options": "Skill", - "reqd": 1 - }, - { - "fetch_from": "skill.description", - "fieldname": "description", - "fieldtype": "Small Text", - "in_list_view": 1, - "label": "Description" - } - ], - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2021-04-12 14:26:33.062549", - "modified_by": "Administrator", - "module": "HR", - "name": "Expected Skill Set", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/expected_skill_set/expected_skill_set.py b/erpnext/hr/doctype/expected_skill_set/expected_skill_set.py deleted file mode 100644 index 0062ba91a1..0000000000 --- a/erpnext/hr/doctype/expected_skill_set/expected_skill_set.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 ExpectedSkillSet(Document): - pass diff --git a/erpnext/hr/doctype/expense_claim/README.md b/erpnext/hr/doctype/expense_claim/README.md deleted file mode 100644 index 489029ce39..0000000000 --- a/erpnext/hr/doctype/expense_claim/README.md +++ /dev/null @@ -1 +0,0 @@ -Amount claimed by Employee for expense made by the Employee on organization's behalf. \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim/__init__.py b/erpnext/hr/doctype/expense_claim/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js deleted file mode 100644 index af80b63845..0000000000 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ /dev/null @@ -1,467 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.provide("erpnext.hr"); -frappe.provide("erpnext.accounts.dimensions"); - -frappe.ui.form.on('Expense Claim', { - onload: function(frm) { - erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); - }, - company: function(frm) { - erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); - var expenses = frm.doc.expenses; - for (var i = 0; i < expenses.length; i++) { - var expense = expenses[i]; - if (!expense.expense_type) { - continue; - } - frappe.call({ - method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim_account_and_cost_center", - args: { - "expense_claim_type": expense.expense_type, - "company": frm.doc.company - }, - callback: function(r) { - if (r.message) { - expense.default_account = r.message.account; - expense.cost_center = r.message.cost_center; - } - } - }); - } - }, -}); - -frappe.ui.form.on('Expense Claim Detail', { - expense_type: function(frm, cdt, cdn) { - var d = locals[cdt][cdn]; - if (!frm.doc.company) { - d.expense_type = ""; - frappe.msgprint(__("Please set the Company")); - this.frm.refresh_fields(); - return; - } - - if(!d.expense_type) { - return; - } - return frappe.call({ - method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim_account_and_cost_center", - args: { - "expense_claim_type": d.expense_type, - "company": frm.doc.company - }, - callback: function(r) { - if (r.message) { - d.default_account = r.message.account; - d.cost_center = r.message.cost_center; - } - } - }); - } -}); - -cur_frm.add_fetch('employee', 'company', 'company'); -cur_frm.add_fetch('employee','employee_name','employee_name'); -cur_frm.add_fetch('expense_type','description','description'); - -cur_frm.cscript.onload = function(doc) { - if (doc.__islocal) { - cur_frm.set_value("posting_date", frappe.datetime.get_today()); - cur_frm.cscript.clear_sanctioned(doc); - } -}; - -cur_frm.cscript.clear_sanctioned = function(doc) { - var val = doc.expenses || []; - for(var i = 0; i 0 && frappe.model.can_read(entry_doctype)) { - cur_frm.add_custom_button(__('Bank Entries'), function() { - frappe.route_options = { - party_type: "Employee", - party: doc.employee, - company: doc.company - }; - frappe.set_route("List", entry_doctype); - }, __("View")); - } - /* eslint-enable */ - } - } -}; - -cur_frm.cscript.set_help = function(doc) { - cur_frm.set_intro(""); - if(doc.__islocal && !in_list(frappe.user_roles, "HR User")) { - cur_frm.set_intro(__("Fill the form and save it")); - } -}; - -cur_frm.cscript.validate = function(doc) { - cur_frm.cscript.calculate_total(doc); -}; - -cur_frm.cscript.calculate_total = function(doc){ - doc.total_claimed_amount = 0; - doc.total_sanctioned_amount = 0; - $.each((doc.expenses || []), function(i, d) { - doc.total_claimed_amount += d.amount; - doc.total_sanctioned_amount += d.sanctioned_amount; - }); -}; - -cur_frm.cscript.calculate_total_amount = function(doc,cdt,cdn){ - cur_frm.cscript.calculate_total(doc,cdt,cdn); -}; - -cur_frm.fields_dict['cost_center'].get_query = function(doc) { - return { - filters: { - "company": doc.company - } - } -}; - -erpnext.expense_claim = { - set_title: function(frm) { - if (!frm.doc.task) { - frm.set_value("title", frm.doc.employee_name); - } - else { - frm.set_value("title", frm.doc.employee_name + " for "+ frm.doc.task); - } - } -}; - -frappe.ui.form.on("Expense Claim", { - setup: function(frm) { - frm.add_fetch("company", "cost_center", "cost_center"); - frm.add_fetch("company", "default_expense_claim_payable_account", "payable_account"); - - frm.set_query("employee_advance", "advances", function() { - return { - filters: [ - ['docstatus', '=', 1], - ['employee', '=', frm.doc.employee], - ['paid_amount', '>', 0], - ['status', 'not in', ['Claimed', 'Returned', 'Partly Claimed and Returned']] - ] - }; - }); - - frm.set_query("expense_approver", function() { - return { - query: "erpnext.hr.doctype.department_approver.department_approver.get_approvers", - filters: { - employee: frm.doc.employee, - doctype: frm.doc.doctype - } - }; - }); - - frm.set_query("account_head", "taxes", function() { - return { - filters: [ - ['company', '=', frm.doc.company], - ['account_type', 'in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation"]] - ] - }; - }); - - frm.set_query("payable_account", function() { - return { - filters: { - "report_type": "Balance Sheet", - "account_type": "Payable", - "company": frm.doc.company, - "is_group": 0 - } - }; - }); - - frm.set_query("task", function() { - return { - filters: { - 'project': frm.doc.project - } - }; - }); - - frm.set_query("employee", function() { - return { - query: "erpnext.controllers.queries.employee_query" - }; - }); - }, - - onload: function(frm) { - if (frm.doc.docstatus == 0) { - return frappe.call({ - method: "erpnext.hr.doctype.leave_application.leave_application.get_mandatory_approval", - args: { - doctype: frm.doc.doctype, - }, - callback: function(r) { - if (!r.exc && r.message) { - frm.toggle_reqd("expense_approver", true); - } - } - }); - } - }, - - refresh: function(frm) { - frm.trigger("toggle_fields"); - - if(frm.doc.docstatus > 0 && frm.doc.approval_status !== "Rejected") { - frm.add_custom_button(__('Accounting Ledger'), function() { - frappe.route_options = { - voucher_no: frm.doc.name, - company: frm.doc.company, - from_date: frm.doc.posting_date, - to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), - group_by: '', - show_cancelled_entries: frm.doc.docstatus === 2 - }; - frappe.set_route("query-report", "General Ledger"); - }, __("View")); - } - - if (frm.doc.docstatus===1 && !cint(frm.doc.is_paid) && cint(frm.doc.grand_total) > 0 - && (cint(frm.doc.total_amount_reimbursed) < cint(frm.doc.total_sanctioned_amount)) - && frappe.model.can_create("Payment Entry")) { - frm.add_custom_button(__('Payment'), - function() { frm.events.make_payment_entry(frm); }, __('Create')); - } - }, - - calculate_grand_total: function(frm) { - var grand_total = flt(frm.doc.total_sanctioned_amount) + flt(frm.doc.total_taxes_and_charges) - flt(frm.doc.total_advance_amount); - frm.set_value("grand_total", grand_total); - frm.refresh_fields(); - }, - - grand_total: function(frm) { - frm.trigger("update_employee_advance_claimed_amount"); - }, - - update_employee_advance_claimed_amount: function(frm) { - let amount_to_be_allocated = frm.doc.grand_total; - $.each(frm.doc.advances || [], function(i, advance){ - if (amount_to_be_allocated >= advance.unclaimed_amount){ - advance.allocated_amount = frm.doc.advances[i].unclaimed_amount; - amount_to_be_allocated -= advance.allocated_amount; - } else { - advance.allocated_amount = amount_to_be_allocated; - amount_to_be_allocated = 0; - } - frm.refresh_field("advances"); - }); - }, - - make_payment_entry: function(frm) { - var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry"; - if(frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) { - method = "erpnext.hr.doctype.expense_claim.expense_claim.make_bank_entry"; - } - return frappe.call({ - method: method, - args: { - "dt": frm.doc.doctype, - "dn": frm.doc.name - }, - callback: function(r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }); - }, - - is_paid: function(frm) { - frm.trigger("toggle_fields"); - }, - - toggle_fields: function(frm) { - frm.toggle_reqd("mode_of_payment", frm.doc.is_paid); - }, - - employee_name: function(frm) { - erpnext.expense_claim.set_title(frm); - }, - - task: function(frm) { - erpnext.expense_claim.set_title(frm); - }, - - employee: function(frm) { - frm.events.get_advances(frm); - }, - - cost_center: function(frm) { - frm.events.set_child_cost_center(frm); - }, - - validate: function(frm) { - frm.events.set_child_cost_center(frm); - }, - - set_child_cost_center: function(frm){ - (frm.doc.expenses || []).forEach(function(d) { - if (!d.cost_center){ - d.cost_center = frm.doc.cost_center; - } - }); - }, - get_taxes: function(frm) { - if(frm.doc.taxes) { - frappe.call({ - method: "calculate_taxes", - doc: frm.doc, - callback: () => { - refresh_field("taxes"); - frm.trigger("update_employee_advance_claimed_amount"); - } - }); - } - }, - - get_advances: function(frm) { - frappe.model.clear_table(frm.doc, "advances"); - if (frm.doc.employee) { - return frappe.call({ - method: "erpnext.hr.doctype.expense_claim.expense_claim.get_advances", - args: { - employee: frm.doc.employee - }, - callback: function(r, rt) { - - if(r.message) { - $.each(r.message, function(i, d) { - var row = frappe.model.add_child(frm.doc, "Expense Claim Advance", "advances"); - row.employee_advance = d.name; - row.posting_date = d.posting_date; - row.advance_account = d.advance_account; - row.advance_paid = d.paid_amount; - row.unclaimed_amount = flt(d.paid_amount) - flt(d.claimed_amount); - row.allocated_amount = 0; - }); - refresh_field("advances"); - } - } - }); - } - } -}); - -frappe.ui.form.on("Expense Claim Detail", { - amount: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.amount); - }, - - sanctioned_amount: function(frm, cdt, cdn) { - cur_frm.cscript.calculate_total(frm.doc, cdt, cdn); - frm.trigger("get_taxes"); - frm.trigger("calculate_grand_total"); - }, - - cost_center: function(frm, cdt, cdn) { - erpnext.utils.copy_value_in_all_rows(frm.doc, cdt, cdn, "expenses", "cost_center"); - } -}); - -frappe.ui.form.on("Expense Claim Advance", { - employee_advance: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if(!frm.doc.employee){ - frappe.msgprint(__('Select an employee to get the employee advance.')); - frm.doc.advances = []; - refresh_field("advances"); - } - else { - return frappe.call({ - method: "erpnext.hr.doctype.expense_claim.expense_claim.get_advances", - args: { - employee: frm.doc.employee, - advance_id: child.employee_advance - }, - callback: function(r, rt) { - if(r.message) { - child.employee_advance = r.message[0].name; - child.posting_date = r.message[0].posting_date; - child.advance_account = r.message[0].advance_account; - child.advance_paid = r.message[0].paid_amount; - child.unclaimed_amount = flt(r.message[0].paid_amount) - flt(r.message[0].claimed_amount); - child.allocated_amount = flt(r.message[0].paid_amount) - flt(r.message[0].claimed_amount); - frm.trigger('calculate_grand_total'); - refresh_field("advances"); - } - } - }); - } - } -}); - -frappe.ui.form.on("Expense Taxes and Charges", { - account_head: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if(child.account_head && !child.description) { - // set description from account head - child.description = child.account_head.split(' - ').slice(0, -1).join(' - '); - refresh_field("taxes"); - } - }, - - calculate_total_tax: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - child.total = flt(frm.doc.total_sanctioned_amount) + flt(child.tax_amount); - frm.trigger("calculate_tax_amount", cdt, cdn); - }, - - calculate_tax_amount: function(frm) { - frm.doc.total_taxes_and_charges = 0; - (frm.doc.taxes || []).forEach(function(d) { - frm.doc.total_taxes_and_charges += d.tax_amount; - }); - frm.trigger("calculate_grand_total"); - }, - - rate: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if(!child.amount) { - child.tax_amount = flt(frm.doc.total_sanctioned_amount) * (flt(child.rate)/100); - } - frm.trigger("calculate_total_tax", cdt, cdn); - }, - - tax_amount: function(frm, cdt, cdn) { - frm.trigger("calculate_total_tax", cdt, cdn); - } -}); diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json deleted file mode 100644 index 45b78bfb54..0000000000 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ /dev/null @@ -1,450 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "naming_series:", - "creation": "2013-01-10 16:34:14", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "naming_series", - "employee", - "employee_name", - "department", - "column_break_5", - "expense_approver", - "approval_status", - "delivery_trip", - "is_paid", - "expense_details", - "expenses", - "sb1", - "taxes", - "transactions_section", - "total_sanctioned_amount", - "total_taxes_and_charges", - "total_advance_amount", - "column_break_17", - "grand_total", - "total_claimed_amount", - "total_amount_reimbursed", - "section_break_16", - "posting_date", - "vehicle_log", - "task", - "cb1", - "remark", - "title", - "email_id", - "accounting_details", - "company", - "mode_of_payment", - "clearance_date", - "column_break_24", - "payable_account", - "accounting_dimensions_section", - "project", - "dimension_col_break", - "cost_center", - "more_details", - "status", - "amended_from", - "advance_payments", - "advances" - ], - "fields": [ - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "no_copy": 1, - "options": "HR-EXP-.YYYY.-", - "print_hide": 1, - "reqd": 1, - "set_only_once": 1 - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "in_global_search": 1, - "in_standard_filter": 1, - "label": "From Employee", - "oldfieldname": "employee", - "oldfieldtype": "Link", - "options": "Employee", - "reqd": 1, - "search_index": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "in_global_search": 1, - "label": "Employee Name", - "oldfieldname": "employee_name", - "oldfieldtype": "Data", - "read_only": 1, - "width": "150px" - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "column_break_5", - "fieldtype": "Column Break" - }, - { - "fieldname": "expense_approver", - "fieldtype": "Link", - "label": "Expense Approver", - "options": "User" - }, - { - "default": "Draft", - "fieldname": "approval_status", - "fieldtype": "Select", - "label": "Approval Status", - "no_copy": 1, - "options": "Draft\nApproved\nRejected", - "search_index": 1 - }, - { - "fieldname": "total_claimed_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Total Claimed Amount", - "no_copy": 1, - "oldfieldname": "total_claimed_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "read_only": 1, - "width": "160px" - }, - { - "fieldname": "total_sanctioned_amount", - "fieldtype": "Currency", - "label": "Total Sanctioned Amount", - "no_copy": 1, - "oldfieldname": "total_sanctioned_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "read_only": 1, - "width": "160px" - }, - { - "default": "0", - "depends_on": "eval:(doc.docstatus==0 || doc.is_paid)", - "fieldname": "is_paid", - "fieldtype": "Check", - "label": "Is Paid" - }, - { - "fieldname": "expense_details", - "fieldtype": "Section Break", - "oldfieldtype": "Section Break" - }, - { - "fieldname": "expenses", - "fieldtype": "Table", - "label": "Expenses", - "oldfieldname": "expense_voucher_details", - "oldfieldtype": "Table", - "options": "Expense Claim Detail", - "reqd": 1 - }, - { - "fieldname": "sb1", - "fieldtype": "Section Break", - "options": "Simple" - }, - { - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "in_standard_filter": 1, - "label": "Posting Date", - "oldfieldname": "posting_date", - "oldfieldtype": "Date", - "reqd": 1 - }, - { - "fieldname": "vehicle_log", - "fieldtype": "Link", - "label": "Vehicle Log", - "options": "Vehicle Log", - "read_only": 1 - }, - { - "fieldname": "project", - "fieldtype": "Link", - "label": "Project", - "options": "Project" - }, - { - "fieldname": "task", - "fieldtype": "Link", - "label": "Task", - "options": "Task", - "remember_last_selected_value": 1 - }, - { - "fieldname": "cb1", - "fieldtype": "Column Break" - }, - { - "fieldname": "total_amount_reimbursed", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Total Amount Reimbursed", - "no_copy": 1, - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "remark", - "fieldtype": "Small Text", - "label": "Remark", - "no_copy": 1, - "oldfieldname": "remark", - "oldfieldtype": "Small Text" - }, - { - "allow_on_submit": 1, - "default": "{employee_name}", - "fieldname": "title", - "fieldtype": "Data", - "hidden": 1, - "label": "Title", - "no_copy": 1 - }, - { - "fieldname": "email_id", - "fieldtype": "Data", - "hidden": 1, - "label": "Employees Email Id", - "oldfieldname": "email_id", - "oldfieldtype": "Data", - "print_hide": 1 - }, - { - "fieldname": "accounting_details", - "fieldtype": "Section Break", - "label": "Accounting Details" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Company", - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "depends_on": "is_paid", - "fieldname": "mode_of_payment", - "fieldtype": "Link", - "label": "Mode of Payment", - "options": "Mode of Payment" - }, - { - "fieldname": "clearance_date", - "fieldtype": "Date", - "label": "Clearance Date" - }, - { - "fieldname": "column_break_24", - "fieldtype": "Column Break" - }, - { - "fieldname": "payable_account", - "fieldtype": "Link", - "label": "Payable Account", - "options": "Account", - "reqd": 1 - }, - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center" - }, - { - "collapsible": 1, - "fieldname": "more_details", - "fieldtype": "Section Break", - "label": "More Details" - }, - { - "default": "Draft", - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Status", - "no_copy": 1, - "options": "Draft\nPaid\nUnpaid\nRejected\nSubmitted\nCancelled", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Amended From", - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Expense Claim", - "print_hide": 1, - "read_only": 1, - "report_hide": 1, - "width": "160px" - }, - { - "fieldname": "advance_payments", - "fieldtype": "Section Break", - "label": "Advance Payments" - }, - { - "fieldname": "advances", - "fieldtype": "Table", - "label": "Advances", - "options": "Expense Claim Advance" - }, - { - "fieldname": "total_advance_amount", - "fieldtype": "Currency", - "label": "Total Advance Amount", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "accounting_dimensions_section", - "fieldtype": "Section Break", - "label": "Accounting Dimensions" - }, - { - "fieldname": "dimension_col_break", - "fieldtype": "Column Break" - }, - { - "fieldname": "taxes", - "fieldtype": "Table", - "label": "Expense Taxes and Charges", - "options": "Expense Taxes and Charges" - }, - { - "fieldname": "section_break_16", - "fieldtype": "Section Break" - }, - { - "fieldname": "transactions_section", - "fieldtype": "Section Break" - }, - { - "fieldname": "grand_total", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Grand Total", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "column_break_17", - "fieldtype": "Column Break" - }, - { - "fieldname": "total_taxes_and_charges", - "fieldtype": "Currency", - "label": "Total Taxes and Charges", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "depends_on": "eval: doc.delivery_trip", - "fieldname": "delivery_trip", - "fieldtype": "Link", - "label": "Delivery Trip", - "options": "Delivery Trip" - } - ], - "icon": "fa fa-money", - "idx": 1, - "is_submittable": 1, - "links": [], - "modified": "2021-11-22 16:26:57.787838", - "modified_by": "Administrator", - "module": "HR", - "name": "Expense Claim", - "name_case": "Title Case", - "naming_rule": "By \"Naming Series\" field", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Expense Approver", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "employee,employee_name", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "employee", - "title_field": "title" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py deleted file mode 100644 index 589763c0a9..0000000000 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ /dev/null @@ -1,464 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe import _ -from frappe.utils import cstr, flt, get_link_to_form - -import erpnext -from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account -from erpnext.accounts.general_ledger import make_gl_entries -from erpnext.controllers.accounts_controller import AccountsController -from erpnext.hr.utils import set_employee_name, share_doc_with_approver, validate_active_employee - - -class InvalidExpenseApproverError(frappe.ValidationError): - pass - - -class ExpenseApproverIdentityError(frappe.ValidationError): - pass - - -class ExpenseClaim(AccountsController): - def onload(self): - self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value( - "Accounts Settings", "make_payment_via_journal_entry" - ) - - def validate(self): - validate_active_employee(self.employee) - set_employee_name(self) - self.validate_sanctioned_amount() - self.calculate_total_amount() - self.validate_advances() - self.set_expense_account(validate=True) - self.set_payable_account() - self.set_cost_center() - self.calculate_taxes() - self.set_status() - if self.task and not self.project: - self.project = frappe.db.get_value("Task", self.task, "project") - - def set_status(self, update=False): - status = {"0": "Draft", "1": "Submitted", "2": "Cancelled"}[cstr(self.docstatus or 0)] - - precision = self.precision("grand_total") - - if ( - # set as paid - self.is_paid - or ( - flt(self.total_sanctioned_amount > 0) - and ( - # grand total is reimbursed - ( - self.docstatus == 1 - and flt(self.grand_total, precision) == flt(self.total_amount_reimbursed, precision) - ) - # grand total (to be paid) is 0 since linked advances already cover the claimed amount - or (flt(self.grand_total, precision) == 0) - ) - ) - ) and self.approval_status == "Approved": - status = "Paid" - elif ( - flt(self.total_sanctioned_amount) > 0 - and self.docstatus == 1 - and self.approval_status == "Approved" - ): - status = "Unpaid" - elif self.docstatus == 1 and self.approval_status == "Rejected": - status = "Rejected" - - if update: - self.db_set("status", status) - else: - self.status = status - - def on_update(self): - share_doc_with_approver(self, self.expense_approver) - - def set_payable_account(self): - if not self.payable_account and not self.is_paid: - self.payable_account = frappe.get_cached_value( - "Company", self.company, "default_expense_claim_payable_account" - ) - - def set_cost_center(self): - if not self.cost_center: - self.cost_center = frappe.get_cached_value("Company", self.company, "cost_center") - - def on_submit(self): - if self.approval_status == "Draft": - frappe.throw(_("""Approval Status must be 'Approved' or 'Rejected'""")) - - self.update_task_and_project() - self.make_gl_entries() - - if self.is_paid: - update_reimbursed_amount(self, self.grand_total) - - self.set_status(update=True) - self.update_claimed_amount_in_employee_advance() - - def on_cancel(self): - self.update_task_and_project() - self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry") - if self.payable_account: - self.make_gl_entries(cancel=True) - - if self.is_paid: - update_reimbursed_amount(self, -1 * self.grand_total) - - self.update_claimed_amount_in_employee_advance() - - def update_claimed_amount_in_employee_advance(self): - for d in self.get("advances"): - frappe.get_doc("Employee Advance", d.employee_advance).update_claimed_amount() - - def update_task_and_project(self): - if self.task: - self.update_task() - elif self.project: - frappe.get_doc("Project", self.project).update_project() - - def make_gl_entries(self, cancel=False): - if flt(self.total_sanctioned_amount) > 0: - gl_entries = self.get_gl_entries() - make_gl_entries(gl_entries, cancel) - - def get_gl_entries(self): - gl_entry = [] - self.validate_account_details() - - # payable entry - if self.grand_total: - gl_entry.append( - self.get_gl_dict( - { - "account": self.payable_account, - "credit": self.grand_total, - "credit_in_account_currency": self.grand_total, - "against": ",".join([d.default_account for d in self.expenses]), - "party_type": "Employee", - "party": self.employee, - "against_voucher_type": self.doctype, - "against_voucher": self.name, - "cost_center": self.cost_center, - }, - item=self, - ) - ) - - # expense entries - for data in self.expenses: - gl_entry.append( - self.get_gl_dict( - { - "account": data.default_account, - "debit": data.sanctioned_amount, - "debit_in_account_currency": data.sanctioned_amount, - "against": self.employee, - "cost_center": data.cost_center or self.cost_center, - }, - item=data, - ) - ) - - for data in self.advances: - gl_entry.append( - self.get_gl_dict( - { - "account": data.advance_account, - "credit": data.allocated_amount, - "credit_in_account_currency": data.allocated_amount, - "against": ",".join([d.default_account for d in self.expenses]), - "party_type": "Employee", - "party": self.employee, - "against_voucher_type": "Employee Advance", - "against_voucher": data.employee_advance, - } - ) - ) - - self.add_tax_gl_entries(gl_entry) - - if self.is_paid and self.grand_total: - # payment entry - payment_account = get_bank_cash_account(self.mode_of_payment, self.company).get("account") - gl_entry.append( - self.get_gl_dict( - { - "account": payment_account, - "credit": self.grand_total, - "credit_in_account_currency": self.grand_total, - "against": self.employee, - }, - item=self, - ) - ) - - gl_entry.append( - self.get_gl_dict( - { - "account": self.payable_account, - "party_type": "Employee", - "party": self.employee, - "against": payment_account, - "debit": self.grand_total, - "debit_in_account_currency": self.grand_total, - "against_voucher": self.name, - "against_voucher_type": self.doctype, - }, - item=self, - ) - ) - - return gl_entry - - def add_tax_gl_entries(self, gl_entries): - # tax table gl entries - for tax in self.get("taxes"): - gl_entries.append( - self.get_gl_dict( - { - "account": tax.account_head, - "debit": tax.tax_amount, - "debit_in_account_currency": tax.tax_amount, - "against": self.employee, - "cost_center": self.cost_center, - "against_voucher_type": self.doctype, - "against_voucher": self.name, - }, - item=tax, - ) - ) - - def validate_account_details(self): - for data in self.expenses: - if not data.cost_center: - frappe.throw( - _("Row {0}: {1} is required in the expenses table to book an expense claim.").format( - data.idx, frappe.bold("Cost Center") - ) - ) - - if self.is_paid: - if not self.mode_of_payment: - frappe.throw(_("Mode of payment is required to make a payment").format(self.employee)) - - def calculate_total_amount(self): - self.total_claimed_amount = 0 - self.total_sanctioned_amount = 0 - for d in self.get("expenses"): - if self.approval_status == "Rejected": - d.sanctioned_amount = 0.0 - - self.total_claimed_amount += flt(d.amount) - self.total_sanctioned_amount += flt(d.sanctioned_amount) - - @frappe.whitelist() - def calculate_taxes(self): - self.total_taxes_and_charges = 0 - for tax in self.taxes: - if tax.rate: - tax.tax_amount = flt(self.total_sanctioned_amount) * flt(tax.rate / 100) - - tax.total = flt(tax.tax_amount) + flt(self.total_sanctioned_amount) - self.total_taxes_and_charges += flt(tax.tax_amount) - - self.grand_total = ( - flt(self.total_sanctioned_amount) - + flt(self.total_taxes_and_charges) - - flt(self.total_advance_amount) - ) - - def update_task(self): - task = frappe.get_doc("Task", self.task) - task.update_total_expense_claim() - task.save() - - def validate_advances(self): - self.total_advance_amount = 0 - for d in self.get("advances"): - ref_doc = frappe.db.get_value( - "Employee Advance", - d.employee_advance, - ["posting_date", "paid_amount", "claimed_amount", "advance_account"], - as_dict=1, - ) - d.posting_date = ref_doc.posting_date - d.advance_account = ref_doc.advance_account - d.advance_paid = ref_doc.paid_amount - d.unclaimed_amount = flt(ref_doc.paid_amount) - flt(ref_doc.claimed_amount) - - if d.allocated_amount and flt(d.allocated_amount) > flt(d.unclaimed_amount): - frappe.throw( - _("Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2}").format( - d.idx, d.allocated_amount, d.unclaimed_amount - ) - ) - - self.total_advance_amount += flt(d.allocated_amount) - - if self.total_advance_amount: - precision = self.precision("total_advance_amount") - if flt(self.total_advance_amount, precision) > flt(self.total_claimed_amount, precision): - frappe.throw(_("Total advance amount cannot be greater than total claimed amount")) - - if self.total_sanctioned_amount and flt(self.total_advance_amount, precision) > flt( - self.total_sanctioned_amount, precision - ): - frappe.throw(_("Total advance amount cannot be greater than total sanctioned amount")) - - def validate_sanctioned_amount(self): - for d in self.get("expenses"): - if flt(d.sanctioned_amount) > flt(d.amount): - frappe.throw( - _("Sanctioned Amount cannot be greater than Claim Amount in Row {0}.").format(d.idx) - ) - - def set_expense_account(self, validate=False): - for expense in self.expenses: - if not expense.default_account or not validate: - expense.default_account = get_expense_claim_account(expense.expense_type, self.company)[ - "account" - ] - - -def update_reimbursed_amount(doc, amount): - - doc.total_amount_reimbursed += amount - frappe.db.set_value( - "Expense Claim", doc.name, "total_amount_reimbursed", doc.total_amount_reimbursed - ) - - doc.set_status() - frappe.db.set_value("Expense Claim", doc.name, "status", doc.status) - - -@frappe.whitelist() -def make_bank_entry(dt, dn): - from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account - - expense_claim = frappe.get_doc(dt, dn) - default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Bank") - if not default_bank_cash_account: - default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Cash") - - payable_amount = ( - flt(expense_claim.total_sanctioned_amount) - - flt(expense_claim.total_amount_reimbursed) - - flt(expense_claim.total_advance_amount) - ) - - je = frappe.new_doc("Journal Entry") - je.voucher_type = "Bank Entry" - je.company = expense_claim.company - je.remark = "Payment against Expense Claim: " + dn - - je.append( - "accounts", - { - "account": expense_claim.payable_account, - "debit_in_account_currency": payable_amount, - "reference_type": "Expense Claim", - "party_type": "Employee", - "party": expense_claim.employee, - "cost_center": erpnext.get_default_cost_center(expense_claim.company), - "reference_name": expense_claim.name, - }, - ) - - je.append( - "accounts", - { - "account": default_bank_cash_account.account, - "credit_in_account_currency": payable_amount, - "reference_type": "Expense Claim", - "reference_name": expense_claim.name, - "balance": default_bank_cash_account.balance, - "account_currency": default_bank_cash_account.account_currency, - "cost_center": erpnext.get_default_cost_center(expense_claim.company), - "account_type": default_bank_cash_account.account_type, - }, - ) - - return je.as_dict() - - -@frappe.whitelist() -def get_expense_claim_account_and_cost_center(expense_claim_type, company): - data = get_expense_claim_account(expense_claim_type, company) - cost_center = erpnext.get_default_cost_center(company) - - return {"account": data.get("account"), "cost_center": cost_center} - - -@frappe.whitelist() -def get_expense_claim_account(expense_claim_type, company): - account = frappe.db.get_value( - "Expense Claim Account", {"parent": expense_claim_type, "company": company}, "default_account" - ) - if not account: - frappe.throw( - _("Set the default account for the {0} {1}").format( - frappe.bold("Expense Claim Type"), get_link_to_form("Expense Claim Type", expense_claim_type) - ) - ) - - return {"account": account} - - -@frappe.whitelist() -def get_advances(employee, advance_id=None): - advance = frappe.qb.DocType("Employee Advance") - - query = frappe.qb.from_(advance).select( - advance.name, - advance.posting_date, - advance.paid_amount, - advance.claimed_amount, - advance.advance_account, - ) - - if not advance_id: - query = query.where( - (advance.docstatus == 1) - & (advance.employee == employee) - & (advance.paid_amount > 0) - & (advance.status.notin(["Claimed", "Returned", "Partly Claimed and Returned"])) - ) - else: - query = query.where(advance.name == advance_id) - - return query.run(as_dict=True) - - -@frappe.whitelist() -def get_expense_claim( - employee_name, company, employee_advance_name, posting_date, paid_amount, claimed_amount -): - default_payable_account = frappe.get_cached_value("Company", company, "default_payable_account") - default_cost_center = frappe.get_cached_value("Company", company, "cost_center") - - expense_claim = frappe.new_doc("Expense Claim") - expense_claim.company = company - expense_claim.employee = employee_name - expense_claim.payable_account = default_payable_account - expense_claim.cost_center = default_cost_center - expense_claim.is_paid = 1 if flt(paid_amount) else 0 - expense_claim.append( - "advances", - { - "employee_advance": employee_advance_name, - "posting_date": posting_date, - "advance_paid": flt(paid_amount), - "unclaimed_amount": flt(paid_amount) - flt(claimed_amount), - "allocated_amount": flt(paid_amount) - flt(claimed_amount), - }, - ) - - return expense_claim diff --git a/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py b/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py deleted file mode 100644 index 8b1acc619e..0000000000 --- a/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py +++ /dev/null @@ -1,12 +0,0 @@ -from frappe import _ - - -def get_data(): - return { - "fieldname": "reference_name", - "internal_links": {"Employee Advance": ["advances", "employee_advance"]}, - "transactions": [ - {"label": _("Payment"), "items": ["Payment Entry", "Journal Entry"]}, - {"label": _("Reference"), "items": ["Employee Advance"]}, - ], - } diff --git a/erpnext/hr/doctype/expense_claim/expense_claim_list.js b/erpnext/hr/doctype/expense_claim/expense_claim_list.js deleted file mode 100644 index 9bafc18562..0000000000 --- a/erpnext/hr/doctype/expense_claim/expense_claim_list.js +++ /dev/null @@ -1,12 +0,0 @@ -frappe.listview_settings['Expense Claim'] = { - add_fields: ["total_claimed_amount", "docstatus", "company"], - get_indicator: function(doc) { - if(doc.status == "Paid") { - return [__("Paid"), "green", "status,=,Paid"]; - }else if(doc.status == "Unpaid") { - return [__("Unpaid"), "orange", "status,=,Unpaid"]; - } else if(doc.status == "Rejected") { - return [__("Rejected"), "grey", "status,=,Rejected"]; - } - } -}; diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py deleted file mode 100644 index 9b3d53a210..0000000000 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ /dev/null @@ -1,372 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import flt, nowdate, random_string - -from erpnext.accounts.doctype.account.test_account import create_account -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry - -test_dependencies = ["Employee"] -company_name = "_Test Company 3" - - -class TestExpenseClaim(unittest.TestCase): - def tearDown(self): - frappe.db.rollback() - - def test_total_expense_claim_for_project(self): - frappe.db.sql("""delete from `tabTask`""") - frappe.db.sql("""delete from `tabProject`""") - frappe.db.sql("update `tabExpense Claim` set project = '', task = ''") - - project = frappe.get_doc({"project_name": "_Test Project 1", "doctype": "Project"}) - project.save() - - task = frappe.get_doc( - dict(doctype="Task", subject="_Test Project Task 1", status="Open", project=project.name) - ).insert() - - task_name = task.name - payable_account = get_payable_account(company_name) - - make_expense_claim( - payable_account, 300, 200, company_name, "Travel Expenses - _TC3", project.name, task_name - ) - - self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) - self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 200) - - expense_claim2 = make_expense_claim( - payable_account, 600, 500, company_name, "Travel Expenses - _TC3", project.name, task_name - ) - - self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700) - self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 700) - - expense_claim2.cancel() - - self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) - self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 200) - - def test_expense_claim_status(self): - payable_account = get_payable_account(company_name) - expense_claim = make_expense_claim( - payable_account, 300, 200, company_name, "Travel Expenses - _TC3" - ) - - je_dict = make_bank_entry("Expense Claim", expense_claim.name) - je = frappe.get_doc(je_dict) - je.posting_date = nowdate() - je.cheque_no = random_string(5) - je.cheque_date = nowdate() - je.submit() - - expense_claim = frappe.get_doc("Expense Claim", expense_claim.name) - self.assertEqual(expense_claim.status, "Paid") - - je.cancel() - expense_claim = frappe.get_doc("Expense Claim", expense_claim.name) - self.assertEqual(expense_claim.status, "Unpaid") - - # expense claim without any sanctioned amount should not have status as Paid - claim = make_expense_claim(payable_account, 1000, 0, "_Test Company", "Travel Expenses - _TC") - self.assertEqual(claim.total_sanctioned_amount, 0) - self.assertEqual(claim.status, "Submitted") - - # no gl entries created - gl_entry = frappe.get_all( - "GL Entry", {"voucher_type": "Expense Claim", "voucher_no": claim.name} - ) - self.assertEqual(len(gl_entry), 0) - - def test_expense_claim_against_fully_paid_advances(self): - from erpnext.hr.doctype.employee_advance.test_employee_advance import ( - get_advances_for_claim, - make_employee_advance, - make_payment_entry, - ) - - frappe.db.delete("Employee Advance") - - payable_account = get_payable_account("_Test Company") - claim = make_expense_claim( - payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True - ) - - advance = make_employee_advance(claim.employee) - pe = make_payment_entry(advance) - pe.submit() - - # claim for already paid out advances - claim = get_advances_for_claim(claim, advance.name) - claim.save() - claim.submit() - - self.assertEqual(claim.grand_total, 0) - self.assertEqual(claim.status, "Paid") - - def test_expense_claim_partially_paid_via_advance(self): - from erpnext.hr.doctype.employee_advance.test_employee_advance import ( - get_advances_for_claim, - make_employee_advance, - ) - from erpnext.hr.doctype.employee_advance.test_employee_advance import ( - make_payment_entry as make_advance_payment, - ) - - frappe.db.delete("Employee Advance") - - payable_account = get_payable_account("_Test Company") - claim = make_expense_claim( - payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True - ) - - # link advance for partial amount - advance = make_employee_advance(claim.employee, {"advance_amount": 500}) - pe = make_advance_payment(advance) - pe.submit() - - claim = get_advances_for_claim(claim, advance.name) - claim.save() - claim.submit() - - self.assertEqual(claim.grand_total, 500) - self.assertEqual(claim.status, "Unpaid") - - # reimburse remaning amount - make_payment_entry(claim, payable_account, 500) - claim.reload() - - self.assertEqual(claim.total_amount_reimbursed, 500) - self.assertEqual(claim.status, "Paid") - - def test_expense_claim_gl_entry(self): - payable_account = get_payable_account(company_name) - taxes = generate_taxes() - expense_claim = make_expense_claim( - payable_account, - 300, - 200, - company_name, - "Travel Expenses - _TC3", - do_not_submit=True, - taxes=taxes, - ) - expense_claim.submit() - - gl_entries = frappe.db.sql( - """select account, debit, credit - from `tabGL Entry` where voucher_type='Expense Claim' and voucher_no=%s - order by account asc""", - expense_claim.name, - as_dict=1, - ) - - self.assertTrue(gl_entries) - - expected_values = dict( - (d[0], d) - for d in [ - ["Output Tax CGST - _TC3", 18.0, 0.0], - [payable_account, 0.0, 218.0], - ["Travel Expenses - _TC3", 200.0, 0.0], - ] - ) - - for gle in gl_entries: - self.assertEqual(expected_values[gle.account][0], gle.account) - self.assertEqual(expected_values[gle.account][1], gle.debit) - self.assertEqual(expected_values[gle.account][2], gle.credit) - - def test_rejected_expense_claim(self): - payable_account = get_payable_account(company_name) - expense_claim = frappe.get_doc( - { - "doctype": "Expense Claim", - "employee": "_T-Employee-00001", - "payable_account": payable_account, - "approval_status": "Rejected", - "expenses": [ - { - "expense_type": "Travel", - "default_account": "Travel Expenses - _TC3", - "amount": 300, - "sanctioned_amount": 200, - } - ], - } - ) - expense_claim.submit() - - self.assertEqual(expense_claim.status, "Rejected") - self.assertEqual(expense_claim.total_sanctioned_amount, 0.0) - - gl_entry = frappe.get_all( - "GL Entry", {"voucher_type": "Expense Claim", "voucher_no": expense_claim.name} - ) - self.assertEqual(len(gl_entry), 0) - - def test_expense_approver_perms(self): - user = "test_approver_perm_emp@example.com" - make_employee(user, "_Test Company") - - # check doc shared - payable_account = get_payable_account("_Test Company") - expense_claim = make_expense_claim( - payable_account, 300, 200, "_Test Company", "Travel Expenses - _TC", do_not_submit=True - ) - expense_claim.expense_approver = user - expense_claim.save() - self.assertTrue(expense_claim.name in frappe.share.get_shared("Expense Claim", user)) - - # check shared doc revoked - expense_claim.reload() - expense_claim.expense_approver = "test@example.com" - expense_claim.save() - self.assertTrue(expense_claim.name not in frappe.share.get_shared("Expense Claim", user)) - - expense_claim.reload() - expense_claim.expense_approver = user - expense_claim.save() - - frappe.set_user(user) - expense_claim.reload() - expense_claim.status = "Approved" - expense_claim.submit() - frappe.set_user("Administrator") - - def test_multiple_payment_entries_against_expense(self): - # Creating expense claim - payable_account = get_payable_account("_Test Company") - expense_claim = make_expense_claim( - payable_account, 5500, 5500, "_Test Company", "Travel Expenses - _TC" - ) - expense_claim.save() - expense_claim.submit() - - # Payment entry 1: paying 500 - make_payment_entry(expense_claim, payable_account, 500) - outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts( - expense_claim - ) - self.assertEqual(outstanding_amount, 5000) - self.assertEqual(total_amount_reimbursed, 500) - - # Payment entry 1: paying 2000 - make_payment_entry(expense_claim, payable_account, 2000) - outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts( - expense_claim - ) - self.assertEqual(outstanding_amount, 3000) - self.assertEqual(total_amount_reimbursed, 2500) - - # Payment entry 1: paying 3000 - make_payment_entry(expense_claim, payable_account, 3000) - outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts( - expense_claim - ) - self.assertEqual(outstanding_amount, 0) - self.assertEqual(total_amount_reimbursed, 5500) - - -def get_payable_account(company): - return frappe.get_cached_value("Company", company, "default_payable_account") - - -def generate_taxes(): - parent_account = frappe.db.get_value( - "Account", {"company": company_name, "is_group": 1, "account_type": "Tax"}, "name" - ) - account = create_account( - company=company_name, - account_name="Output Tax CGST", - account_type="Tax", - parent_account=parent_account, - ) - return { - "taxes": [ - {"account_head": account, "rate": 9, "description": "CGST", "tax_amount": 10, "total": 210} - ] - } - - -def make_expense_claim( - payable_account, - amount, - sanctioned_amount, - company, - account, - project=None, - task_name=None, - do_not_submit=False, - taxes=None, -): - employee = frappe.db.get_value("Employee", {"status": "Active"}) - if not employee: - employee = make_employee("test_employee@expense_claim.com", company=company) - - currency, cost_center = frappe.db.get_value( - "Company", company, ["default_currency", "cost_center"] - ) - expense_claim = { - "doctype": "Expense Claim", - "employee": employee, - "payable_account": payable_account, - "approval_status": "Approved", - "company": company, - "currency": currency, - "expenses": [ - { - "expense_type": "Travel", - "default_account": account, - "currency": currency, - "amount": amount, - "sanctioned_amount": sanctioned_amount, - "cost_center": cost_center, - } - ], - } - if taxes: - expense_claim.update(taxes) - - expense_claim = frappe.get_doc(expense_claim) - - if project: - expense_claim.project = project - if task_name: - expense_claim.task = task_name - - if do_not_submit: - return expense_claim - expense_claim.submit() - return expense_claim - - -def get_outstanding_and_total_reimbursed_amounts(expense_claim): - outstanding_amount = flt( - frappe.db.get_value("Expense Claim", expense_claim.name, "total_sanctioned_amount") - ) - flt(frappe.db.get_value("Expense Claim", expense_claim.name, "total_amount_reimbursed")) - total_amount_reimbursed = flt( - frappe.db.get_value("Expense Claim", expense_claim.name, "total_amount_reimbursed") - ) - - return outstanding_amount, total_amount_reimbursed - - -def make_payment_entry(expense_claim, payable_account, amt): - from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry - - pe = get_payment_entry( - "Expense Claim", expense_claim.name, bank_account="_Test Bank USD - _TC", bank_amount=amt - ) - pe.reference_no = "1" - pe.reference_date = nowdate() - pe.source_exchange_rate = 1 - pe.paid_to = payable_account - pe.references[0].allocated_amount = amt - pe.insert() - pe.submit() diff --git a/erpnext/hr/doctype/expense_claim_account/__init__.py b/erpnext/hr/doctype/expense_claim_account/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/expense_claim_account/expense_claim_account.json b/erpnext/hr/doctype/expense_claim_account/expense_claim_account.json deleted file mode 100644 index c7d71d31c1..0000000000 --- a/erpnext/hr/doctype/expense_claim_account/expense_claim_account.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-07-18 12:24:16.507860", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "default_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Default Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2016-07-18 12:39:29.709848", - "modified_by": "Administrator", - "module": "HR", - "name": "Expense Claim Account", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim_account/expense_claim_account.py b/erpnext/hr/doctype/expense_claim_account/expense_claim_account.py deleted file mode 100644 index 0d46a22608..0000000000 --- a/erpnext/hr/doctype/expense_claim_account/expense_claim_account.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class ExpenseClaimAccount(Document): - pass diff --git a/erpnext/hr/doctype/expense_claim_advance/__init__.py b/erpnext/hr/doctype/expense_claim_advance/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json deleted file mode 100644 index aa479c8308..0000000000 --- a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "actions": [], - "creation": "2017-10-09 16:53:26.410762", - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee_advance", - "posting_date", - "advance_paid", - "column_break_4", - "unclaimed_amount", - "allocated_amount", - "advance_account" - ], - "fields": [ - { - "columns": 2, - "fieldname": "employee_advance", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Employee Advance", - "no_copy": 1, - "oldfieldname": "journal_voucher", - "oldfieldtype": "Link", - "options": "Employee Advance", - "print_width": "250px", - "reqd": 1, - "width": "250px" - }, - { - "columns": 2, - "fieldname": "posting_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Posting Date", - "read_only": 1 - }, - { - "columns": 2, - "fieldname": "advance_paid", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Advance Paid", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "columns": 2, - "fieldname": "unclaimed_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Unclaimed Amount", - "no_copy": 1, - "oldfieldname": "advance_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_width": "120px", - "read_only": 1, - "reqd": 1, - "width": "120px" - }, - { - "columns": 2, - "fieldname": "allocated_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Allocated Amount", - "no_copy": 1, - "oldfieldname": "allocated_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_width": "120px", - "width": "120px" - }, - { - "fieldname": "advance_account", - "fieldtype": "Link", - "hidden": 1, - "label": "Advance Account", - "options": "Account" - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - } - ], - "istable": 1, - "links": [], - "modified": "2021-11-22 16:33:58.515819", - "modified_by": "Administrator", - "module": "HR", - "name": "Expense Claim Advance", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.py b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.py deleted file mode 100644 index 68b2963f2b..0000000000 --- a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class ExpenseClaimAdvance(Document): - pass diff --git a/erpnext/hr/doctype/expense_claim_detail/README.md b/erpnext/hr/doctype/expense_claim_detail/README.md deleted file mode 100644 index 4a747e2ea5..0000000000 --- a/erpnext/hr/doctype/expense_claim_detail/README.md +++ /dev/null @@ -1 +0,0 @@ -Detail of expense in parent Expense Claim. \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim_detail/__init__.py b/erpnext/hr/doctype/expense_claim_detail/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json deleted file mode 100644 index 6edbcb5c39..0000000000 --- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "actions": [], - "creation": "2013-02-22 01:27:46", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "expense_date", - "column_break_2", - "expense_type", - "default_account", - "section_break_4", - "description", - "section_break_6", - "amount", - "column_break_8", - "sanctioned_amount", - "accounting_dimensions_section", - "cost_center", - "dimension_col_break" - ], - "fields": [ - { - "fieldname": "expense_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Expense Date", - "oldfieldname": "expense_date", - "oldfieldtype": "Date", - "print_width": "150px", - "width": "150px" - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fieldname": "expense_type", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Expense Claim Type", - "oldfieldname": "expense_type", - "oldfieldtype": "Link", - "options": "Expense Claim Type", - "print_width": "150px", - "reqd": 1, - "width": "150px" - }, - { - "depends_on": "expense_type", - "fieldname": "default_account", - "fieldtype": "Link", - "hidden": 1, - "label": "Default Account", - "options": "Account", - "read_only": 1 - }, - { - "fieldname": "section_break_4", - "fieldtype": "Section Break" - }, - { - "fieldname": "description", - "fieldtype": "Text Editor", - "in_list_view": 1, - "label": "Description", - "oldfieldname": "description", - "oldfieldtype": "Small Text", - "print_width": "300px", - "width": "300px" - }, - { - "fieldname": "section_break_6", - "fieldtype": "Section Break" - }, - { - "fieldname": "amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Amount", - "oldfieldname": "claim_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_width": "150px", - "reqd": 1, - "width": "150px" - }, - { - "fieldname": "column_break_8", - "fieldtype": "Column Break" - }, - { - "fieldname": "sanctioned_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Sanctioned Amount", - "oldfieldname": "sanctioned_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_width": "150px", - "width": "150px" - }, - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center" - }, - { - "fieldname": "accounting_dimensions_section", - "fieldtype": "Section Break", - "label": "Accounting Dimensions" - }, - { - "fieldname": "dimension_col_break", - "fieldtype": "Column Break" - } - ], - "idx": 1, - "istable": 1, - "links": [], - "modified": "2021-11-26 14:23:45.539922", - "modified_by": "Administrator", - "module": "HR", - "name": "Expense Claim Detail", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py deleted file mode 100644 index f58f1287cb..0000000000 --- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -from frappe.model.document import Document - - -class ExpenseClaimDetail(Document): - pass diff --git a/erpnext/hr/doctype/expense_claim_type/README.md b/erpnext/hr/doctype/expense_claim_type/README.md deleted file mode 100644 index 13a654eab2..0000000000 --- a/erpnext/hr/doctype/expense_claim_type/README.md +++ /dev/null @@ -1 +0,0 @@ -Type of expense for Expense Claim. \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim_type/__init__.py b/erpnext/hr/doctype/expense_claim_type/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.js b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.js deleted file mode 100644 index d007e1a6c2..0000000000 --- a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.js +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.ui.form.on("Expense Claim Type", { - refresh: function(frm) { - frm.fields_dict["accounts"].grid.get_field("default_account").get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "is_group": 0, - "root_type": frm.doc.deferred_expense_account ? "Asset" : "Expense", - 'company': d.company - } - } - } - } -}) diff --git a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json deleted file mode 100644 index 02ab4cb6ac..0000000000 --- a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:expense_type", - "creation": "2012-03-27 14:35:55", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "deferred_expense_account", - "expense_type", - "accounts", - "description" - ], - "fields": [ - { - "fieldname": "expense_type", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Expense Claim Type", - "oldfieldname": "expense_type", - "oldfieldtype": "Data", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "label": "Description", - "oldfieldname": "description", - "oldfieldtype": "Small Text", - "width": "300px" - }, - { - "fieldname": "accounts", - "fieldtype": "Table", - "label": "Accounts", - "options": "Expense Claim Account" - }, - { - "default": "0", - "fieldname": "deferred_expense_account", - "fieldtype": "Check", - "label": "Deferred Expense Account" - } - ], - "icon": "fa fa-flag", - "idx": 1, - "links": [], - "modified": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "module": "HR", - "name": "Expense Claim Type", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "read": 1, - "role": "Employee" - } - ], - "sort_field": "modified", - "sort_order": "ASC" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py deleted file mode 100644 index 6d29f7d8e7..0000000000 --- a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document - - -class ExpenseClaimType(Document): - def validate(self): - self.validate_accounts() - self.validate_repeating_companies() - - def validate_repeating_companies(self): - """Error when Same Company is entered multiple times in accounts""" - accounts_list = [] - for entry in self.accounts: - accounts_list.append(entry.company) - - if len(accounts_list) != len(set(accounts_list)): - frappe.throw(_("Same Company is entered more than once")) - - def validate_accounts(self): - for entry in self.accounts: - """Error when Company of Ledger account doesn't match with Company Selected""" - if frappe.db.get_value("Account", entry.default_account, "company") != entry.company: - frappe.throw( - _("Account {0} does not match with Company {1}").format(entry.default_account, entry.company) - ) diff --git a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.py b/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.py deleted file mode 100644 index 62348e2825..0000000000 --- a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors -# See license.txt - -import unittest - -# test_records = frappe.get_test_records('Expense Claim Type') - - -class TestExpenseClaimType(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/__init__.py b/erpnext/hr/doctype/expense_taxes_and_charges/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json deleted file mode 100644 index 2f7b8fcf67..0000000000 --- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "actions": [], - "autoname": "hash", - "creation": "2019-06-03 11:42:33.123976", - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "account_head", - "rate", - "col_break1", - "description", - "section_break_6", - "tax_amount", - "column_break_8", - "total", - "accounting_dimensions_section", - "cost_center", - "dimension_col_break" - ], - "fields": [ - { - "fieldname": "col_break1", - "fieldtype": "Column Break" - }, - { - "columns": 2, - "fieldname": "account_head", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Account Head", - "oldfieldname": "account_head", - "oldfieldtype": "Link", - "options": "Account", - "reqd": 1 - }, - { - "default": ":Company", - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "oldfieldname": "cost_center", - "oldfieldtype": "Link", - "options": "Cost Center" - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "label": "Description", - "oldfieldname": "description", - "oldfieldtype": "Small Text", - "print_width": "300px", - "reqd": 1, - "width": "300px" - }, - { - "columns": 2, - "fieldname": "rate", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Rate", - "oldfieldname": "rate", - "oldfieldtype": "Currency" - }, - { - "columns": 2, - "fieldname": "tax_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Amount", - "options": "currency" - }, - { - "columns": 2, - "fieldname": "total", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Total", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "section_break_6", - "fieldtype": "Section Break" - }, - { - "fieldname": "column_break_8", - "fieldtype": "Column Break" - }, - { - "fieldname": "accounting_dimensions_section", - "fieldtype": "Section Break", - "label": "Accounting Dimensions" - }, - { - "fieldname": "dimension_col_break", - "fieldtype": "Column Break" - } - ], - "istable": 1, - "links": [], - "modified": "2021-10-26 20:27:36.027728", - "modified_by": "Administrator", - "module": "HR", - "name": "Expense Taxes and Charges", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1 -} diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py deleted file mode 100644 index a28ef57b3f..0000000000 --- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class ExpenseTaxesandCharges(Document): - pass diff --git a/erpnext/hr/doctype/full_and_final_asset/__init__.py b/erpnext/hr/doctype/full_and_final_asset/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.js b/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.js deleted file mode 100644 index 1965b46651..0000000000 --- a/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Full and Final Asset', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.json b/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.json deleted file mode 100644 index 3ad8335049..0000000000 --- a/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "actions": [], - "creation": "2021-06-28 13:36:58.658985", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "reference", - "asset_name", - "date", - "status", - "description" - ], - "fields": [ - { - "fieldname": "reference", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Reference", - "options": "Asset Movement", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Status", - "options": "Owned\nReturned", - "reqd": 1 - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "label": "Description" - }, - { - "fieldname": "asset_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Asset Name", - "read_only": 1 - }, - { - "fieldname": "date", - "fieldtype": "Datetime", - "in_list_view": 1, - "label": "Date", - "read_only": 1 - } - ], - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2021-07-15 15:17:31.309834", - "modified_by": "Administrator", - "module": "HR", - "name": "Full and Final Asset", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.py b/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.py deleted file mode 100644 index 661af7da88..0000000000 --- a/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.py +++ /dev/null @@ -1,9 +0,0 @@ -# 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 FullandFinalAsset(Document): - pass diff --git a/erpnext/hr/doctype/full_and_final_asset/test_full_and_final_asset.py b/erpnext/hr/doctype/full_and_final_asset/test_full_and_final_asset.py deleted file mode 100644 index 9afe0f2c16..0000000000 --- a/erpnext/hr/doctype/full_and_final_asset/test_full_and_final_asset.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -import unittest - - -class TestFullandFinalAsset(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/full_and_final_outstanding_statement/__init__.py b/erpnext/hr/doctype/full_and_final_outstanding_statement/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.json b/erpnext/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.json deleted file mode 100644 index be242e2c44..0000000000 --- a/erpnext/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "actions": [], - "creation": "2021-06-28 13:32:02.167317", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "component", - "reference_document_type", - "reference_document", - "account", - "paid_via_salary_slip", - "column_break_4", - "amount", - "status", - "remark" - ], - "fields": [ - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "columns": 2, - "default": "Unsettled", - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Status", - "options": "Settled\nUnsettled" - }, - { - "fieldname": "remark", - "fieldtype": "Small Text", - "label": "Remark" - }, - { - "columns": 2, - "depends_on": "reference_document_type", - "fieldname": "reference_document", - "fieldtype": "Dynamic Link", - "in_list_view": 1, - "label": "Reference Document", - "mandatory_depends_on": "reference_document_type", - "options": "reference_document_type", - "search_index": 1 - }, - { - "columns": 2, - "fieldname": "component", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Component", - "reqd": 1 - }, - { - "fieldname": "account", - "fieldtype": "Link", - "label": "Account", - "options": "Account" - }, - { - "columns": 2, - "fieldname": "amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Amount" - }, - { - "columns": 2, - "fieldname": "reference_document_type", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Reference Document Type", - "options": "DocType" - }, - { - "default": "0", - "fieldname": "paid_via_salary_slip", - "fieldtype": "Check", - "label": "Paid via Salary Slip" - } - ], - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2021-07-20 16:59:34.447934", - "modified_by": "Administrator", - "module": "HR", - "name": "Full and Final Outstanding Statement", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.py b/erpnext/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.py deleted file mode 100644 index 4b239abec3..0000000000 --- a/erpnext/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.py +++ /dev/null @@ -1,9 +0,0 @@ -# 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 FullandFinalOutstandingStatement(Document): - pass diff --git a/erpnext/hr/doctype/full_and_final_statement/__init__.py b/erpnext/hr/doctype/full_and_final_statement/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.js b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.js deleted file mode 100644 index 074d85b709..0000000000 --- a/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.js +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Full and Final Statement', { - refresh: function(frm) { - frm.events.set_queries(frm, "payables"); - frm.events.set_queries(frm, "receivables"); - - if (frm.doc.docstatus == 1 && frm.doc.status == "Unpaid") { - frm.add_custom_button(__("Create Journal Entry"), function () { - frm.events.create_journal_entry(frm); - }); - } - }, - - set_queries: function(frm, type) { - frm.set_query("reference_document_type", type, function () { - let modules = ["HR", "Payroll", "Loan Management"]; - return { - filters: { - istable: 0, - issingle: 0, - module: ["In", modules] - } - }; - }); - - let filters = {}; - - frm.set_query('reference_document', type, function(doc, cdt, cdn) { - let fnf_doc = frappe.get_doc(cdt, cdn); - - frappe.model.with_doctype(fnf_doc.reference_document_type, function() { - if (frappe.model.is_tree(fnf_doc.reference_document_type)) { - filters['is_group'] = 0; - } - - if (frappe.meta.has_field(fnf_doc.reference_document_type, 'company')) { - filters['company'] = frm.doc.company; - } - - if (frappe.meta.has_field(fnf_doc.reference_document_type, 'employee')) { - filters['employee'] = frm.doc.employee; - } - }); - - return { - filters: filters - }; - }); - }, - - employee: function(frm) { - frm.events.get_outstanding_statements(frm); - }, - - get_outstanding_statements: function(frm) { - if (frm.doc.employee) { - frappe.call({ - method: "get_outstanding_statements", - doc: frm.doc, - callback: function() { - frm.refresh(); - } - }); - } - }, - - create_journal_entry: function(frm) { - frappe.call({ - method: "create_journal_entry", - doc: frm.doc, - callback: function(r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }); - } -}); - -frappe.ui.form.on("Full and Final Outstanding Statement", { - reference_document: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if (child.reference_document_type && child.reference_document) { - frappe.call({ - method: "erpnext.hr.doctype.full_and_final_statement.full_and_final_statement.get_account_and_amount", - args: { - ref_doctype: child.reference_document_type, - ref_document: child.reference_document - }, - callback: function(r) { - if (r.message) { - frappe.model.set_value(cdt, cdn, "account", r.message[0]); - frappe.model.set_value(cdt, cdn, "amount", r.message[1]); - } - } - }); - } - }, - - amount: function(frm) { - var total_payable_amount = 0; - var total_receivable_amount = 0; - - frm.doc.payables.forEach(element => { - total_payable_amount = total_payable_amount + element.amount; - }); - - frm.doc.receivables.forEach(element => { - total_receivable_amount = total_receivable_amount + element.amount; - }); - frm.set_value("total_payable_amount", flt(total_payable_amount)); - frm.set_value("total_receivable_amount", flt(total_receivable_amount)); - } -}); diff --git a/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.json b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.json deleted file mode 100644 index ebcf36dfd9..0000000000 --- a/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.json +++ /dev/null @@ -1,231 +0,0 @@ -{ - "actions": [], - "autoname": "HR-FNF-.YYYY.-.#####", - "creation": "2021-06-28 13:17:36.050459", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "transaction_date", - "column_break_12", - "company", - "status", - "amended_from", - "employee_details_section", - "date_of_joining", - "relieving_date", - "column_break_4", - "designation", - "department", - "section_break_8", - "payables", - "section_break_10", - "receivables", - "totals_section", - "total_payable_amount", - "column_break_21", - "total_receivable_amount", - "section_break_15", - "assets_allocated" - ], - "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.designation", - "fieldname": "designation", - "fieldtype": "Link", - "label": "Designation", - "options": "Designation", - "read_only": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "default": "Unpaid", - "fieldname": "status", - "fieldtype": "Select", - "label": "Status", - "options": "Paid\nUnpaid", - "read_only": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Full and Final Statement", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "label": "Payables" - }, - { - "fieldname": "section_break_10", - "fieldtype": "Section Break", - "label": "Receivables" - }, - { - "fieldname": "assets_allocated", - "fieldtype": "Table", - "options": "Full and Final Asset" - }, - { - "fetch_from": "employee.relieving_date", - "fieldname": "relieving_date", - "fieldtype": "Date", - "label": "Relieving Date ", - "read_only": 1, - "reqd": 1 - }, - { - "fetch_from": "employee.date_of_joining", - "fieldname": "date_of_joining", - "fieldtype": "Date", - "label": "Date of Joining", - "read_only": 1 - }, - { - "fieldname": "section_break_15", - "fieldtype": "Section Break", - "label": "Assets Allocated" - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Company", - "options": "Company", - "read_only": 1 - }, - { - "fieldname": "column_break_12", - "fieldtype": "Column Break" - }, - { - "fieldname": "payables", - "fieldtype": "Table", - "options": "Full and Final Outstanding Statement" - }, - { - "fieldname": "receivables", - "fieldtype": "Table", - "options": "Full and Final Outstanding Statement" - }, - { - "fieldname": "employee_details_section", - "fieldtype": "Section Break", - "label": "Employee Details" - }, - { - "fieldname": "transaction_date", - "fieldtype": "Date", - "in_standard_filter": 1, - "label": "Transaction Date", - "reqd": 1 - }, - { - "fieldname": "totals_section", - "fieldtype": "Section Break", - "label": "Totals" - }, - { - "fieldname": "total_payable_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Total Payable Amount", - "read_only": 1 - }, - { - "fieldname": "column_break_21", - "fieldtype": "Column Break" - }, - { - "fieldname": "total_receivable_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Total Receivable Amount", - "read_only": 1 - } - ], - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2021-08-30 21:11:09.892560", - "modified_by": "Administrator", - "module": "HR", - "name": "Full and Final Statement", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.py b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.py deleted file mode 100644 index 8137a0de14..0000000000 --- a/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import flt, get_link_to_form, today - - -class FullandFinalStatement(Document): - def validate(self): - self.get_outstanding_statements() - if self.docstatus == 1: - self.validate_settlement("payables") - self.validate_settlement("receivables") - self.validate_asset() - - def validate_settlement(self, component_type): - for data in self.get(component_type, []): - if data.status == "Unsettled": - frappe.throw(_("Settle all Payables and Receivables before submission")) - - def validate_asset(self): - for data in self.assets_allocated: - if data.status == "Owned": - frappe.throw(_("All allocated assets should be returned before submission")) - - @frappe.whitelist() - def get_outstanding_statements(self): - if self.relieving_date: - if not len(self.get("payables", [])): - components = self.get_payable_component() - self.create_component_row(components, "payables") - if not len(self.get("receivables", [])): - components = self.get_receivable_component() - self.create_component_row(components, "receivables") - - if not len(self.get("assets_allocated", [])): - for data in self.get_assets_movement(): - self.append("assets_allocated", data) - else: - frappe.throw( - _("Set Relieving Date for Employee: {0}").format(get_link_to_form("Employee", self.employee)) - ) - - def create_component_row(self, components, component_type): - for component in components: - self.append( - component_type, - { - "status": "Unsettled", - "reference_document_type": component if component != "Bonus" else "Additional Salary", - "component": component, - }, - ) - - def get_payable_component(self): - return [ - "Salary Slip", - "Gratuity", - "Expense Claim", - "Bonus", - "Leave Encashment", - ] - - def get_receivable_component(self): - return [ - "Loan", - "Employee Advance", - ] - - def get_assets_movement(self): - asset_movements = frappe.get_all( - "Asset Movement Item", - filters={"docstatus": 1}, - fields=["asset", "from_employee", "to_employee", "parent", "asset_name"], - or_filters={"from_employee": self.employee, "to_employee": self.employee}, - ) - - data = [] - inward_movements = [] - outward_movements = [] - for movement in asset_movements: - if movement.to_employee and movement.to_employee == self.employee: - inward_movements.append(movement) - - if movement.from_employee and movement.from_employee == self.employee: - outward_movements.append(movement) - - for movement in inward_movements: - outwards_count = [movement.asset for movement in outward_movements].count(movement.asset) - inwards_counts = [movement.asset for movement in inward_movements].count(movement.asset) - - if inwards_counts > outwards_count: - data.append( - { - "reference": movement.parent, - "asset_name": movement.asset_name, - "date": frappe.db.get_value("Asset Movement", movement.parent, "transaction_date"), - "status": "Owned", - } - ) - return data - - @frappe.whitelist() - def create_journal_entry(self): - precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency") - jv = frappe.new_doc("Journal Entry") - jv.company = self.company - jv.voucher_type = "Bank Entry" - jv.posting_date = today() - - difference = self.total_payable_amount - self.total_receivable_amount - - for data in self.payables: - if data.amount > 0 and not data.paid_via_salary_slip: - account_dict = { - "account": data.account, - "debit_in_account_currency": flt(data.amount, precision), - } - if data.reference_document_type == "Expense Claim": - account_dict["party_type"] = "Employee" - account_dict["party"] = self.employee - - jv.append("accounts", account_dict) - - for data in self.receivables: - if data.amount > 0: - account_dict = { - "account": data.account, - "credit_in_account_currency": flt(data.amount, precision), - } - if data.reference_document_type == "Employee Advance": - account_dict["party_type"] = "Employee" - account_dict["party"] = self.employee - - jv.append("accounts", account_dict) - - jv.append( - "accounts", - { - "credit_in_account_currency": difference if difference > 0 else 0, - "debit_in_account_currency": -(difference) if difference < 0 else 0, - "reference_type": self.doctype, - "reference_name": self.name, - }, - ) - return jv - - -@frappe.whitelist() -def get_account_and_amount(ref_doctype, ref_document): - if not ref_doctype or not ref_document: - return None - - if ref_doctype == "Salary Slip": - salary_details = frappe.db.get_value( - "Salary Slip", ref_document, ["payroll_entry", "net_pay"], as_dict=1 - ) - amount = salary_details.net_pay - payable_account = ( - frappe.db.get_value("Payroll Entry", salary_details.payroll_entry, "payroll_payable_account") - if salary_details.payroll_entry - else None - ) - return [payable_account, amount] - - if ref_doctype == "Gratuity": - payable_account, amount = frappe.db.get_value( - "Gratuity", ref_document, ["payable_account", "amount"] - ) - return [payable_account, amount] - - if ref_doctype == "Expense Claim": - details = frappe.db.get_value( - "Expense Claim", - ref_document, - ["payable_account", "grand_total", "total_amount_reimbursed", "total_advance_amount"], - as_dict=True, - ) - payable_account = details.payable_account - amount = details.grand_total - (details.total_amount_reimbursed + details.total_advance_amount) - return [payable_account, amount] - - if ref_doctype == "Loan": - details = frappe.db.get_value( - "Loan", ref_document, ["payment_account", "total_payment", "total_amount_paid"], as_dict=1 - ) - payment_account = details.payment_account - amount = details.total_payment - details.total_amount_paid - return [payment_account, amount] - - if ref_doctype == "Employee Advance": - details = frappe.db.get_value( - "Employee Advance", - ref_document, - ["advance_account", "paid_amount", "claimed_amount", "return_amount"], - as_dict=1, - ) - payment_account = details.advance_account - amount = details.paid_amount - (details.claimed_amount + details.return_amount) - return [payment_account, amount] diff --git a/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement_list.js b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement_list.js deleted file mode 100644 index 4aedec7c89..0000000000 --- a/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement_list.js +++ /dev/null @@ -1,11 +0,0 @@ -frappe.listview_settings["Full and Final Statement"] = { - get_indicator: function(doc) { - var colors = { - "Draft": "red", - "Unpaid": "orange", - "Paid": "green", - "Cancelled": "red" - }; - return [__(doc.status), colors[doc.status], "status,=," + doc.status]; - } -}; diff --git a/erpnext/hr/doctype/full_and_final_statement/test_full_and_final_statement.py b/erpnext/hr/doctype/full_and_final_statement/test_full_and_final_statement.py deleted file mode 100644 index 8c6723f60e..0000000000 --- a/erpnext/hr/doctype/full_and_final_statement/test_full_and_final_statement.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import add_days, today - -from erpnext.assets.doctype.asset.test_asset import create_asset_data -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt - - -class TestFullandFinalStatement(unittest.TestCase): - def setUp(self): - create_asset_data() - - def tearDown(self): - frappe.db.sql("Delete from `tabFull and Final Statement`") - frappe.db.sql("Delete from `tabAsset`") - frappe.db.sql("Delete from `tabAsset Movement`") - - def test_check_bootstraped_data_asset_movement_and_jv_creation(self): - employee = make_employee("test_fnf@example.com", company="_Test Company") - movement = create_asset_movement(employee) - frappe.db.set_value("Employee", employee, "relieving_date", add_days(today(), 30)) - fnf = create_full_and_final_statement(employee) - - payables_bootstraped_component = [ - "Salary Slip", - "Gratuity", - "Expense Claim", - "Bonus", - "Leave Encashment", - ] - - receivable_bootstraped_component = ["Loan", "Employee Advance"] - - # checking payable s and receivables bootstraped value - self.assertEqual([payable.component for payable in fnf.payables], payables_bootstraped_component) - self.assertEqual( - [receivable.component for receivable in fnf.receivables], receivable_bootstraped_component - ) - - # checking allocated asset - self.assertIn(movement, [asset.reference for asset in fnf.assets_allocated]) - - -def create_full_and_final_statement(employee): - fnf = frappe.new_doc("Full and Final Statement") - fnf.employee = employee - fnf.transaction_date = today() - fnf.save() - return fnf - - -def create_asset_movement(employee): - asset_name = create_asset() - movement = frappe.new_doc("Asset Movement") - movement.company = "_Test Company" - movement.purpose = "Issue" - movement.transaction_date = today() - - movement.append("assets", {"asset": asset_name, "to_employee": employee}) - - movement.save() - movement.submit() - return movement.name - - -def create_asset(): - pr = make_purchase_receipt( - item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location" - ) - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name") - asset = frappe.get_doc("Asset", asset_name) - asset.calculate_depreciation = 0 - asset.available_for_use_date = today() - asset.submit() - return asset_name diff --git a/erpnext/hr/doctype/grievance_type/__init__.py b/erpnext/hr/doctype/grievance_type/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/grievance_type/grievance_type.js b/erpnext/hr/doctype/grievance_type/grievance_type.js deleted file mode 100644 index 425f2fd5b5..0000000000 --- a/erpnext/hr/doctype/grievance_type/grievance_type.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Grievance Type', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/hr/doctype/grievance_type/grievance_type.json b/erpnext/hr/doctype/grievance_type/grievance_type.json deleted file mode 100644 index 1dce00a0e2..0000000000 --- a/erpnext/hr/doctype/grievance_type/grievance_type.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "actions": [], - "autoname": "Prompt", - "creation": "2021-05-11 12:41:50.256071", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "section_break_5", - "description" - ], - "fields": [ - { - "fieldname": "section_break_5", - "fieldtype": "Section Break" - }, - { - "fieldname": "description", - "fieldtype": "Text", - "label": "Description" - } - ], - "index_web_pages_for_search": 1, - "links": [], - "modified": "2021-06-21 12:54:37.764712", - "modified_by": "Administrator", - "module": "HR", - "name": "Grievance 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 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/grievance_type/grievance_type.py b/erpnext/hr/doctype/grievance_type/grievance_type.py deleted file mode 100644 index 5d8d41cb73..0000000000 --- a/erpnext/hr/doctype/grievance_type/grievance_type.py +++ /dev/null @@ -1,9 +0,0 @@ -# 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 GrievanceType(Document): - pass diff --git a/erpnext/hr/doctype/grievance_type/test_grievance_type.py b/erpnext/hr/doctype/grievance_type/test_grievance_type.py deleted file mode 100644 index 481f4e58a7..0000000000 --- a/erpnext/hr/doctype/grievance_type/test_grievance_type.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -import unittest - - -class TestGrievanceType(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/hr_settings/__init__.py b/erpnext/hr/doctype/hr_settings/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js deleted file mode 100644 index 6e26a1fa71..0000000000 --- a/erpnext/hr/doctype/hr_settings/hr_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('HR Settings', { -}); - -frappe.tour['HR Settings'] = [ - { - fieldname: 'emp_created_by', - title: 'Employee Naming By', - description: __('Employee can be named by Employee ID if you assign one, or via Naming Series. Select your preference here.'), - }, - { - fieldname: 'standard_working_hours', - title: 'Standard Working Hours', - description: __('Enter the Standard Working Hours for a normal work day. These hours will be used in calculations of reports such as Employee Hours Utilization and Project Profitability analysis.'), - }, - { - fieldname: 'leave_and_expense_claim_settings', - title: 'Leave and Expense Clain Settings', - description: __('Review various other settings related to Employee Leaves and Expense Claim') - } -]; diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json deleted file mode 100644 index f9a3e05fc3..0000000000 --- a/erpnext/hr/doctype/hr_settings/hr_settings.json +++ /dev/null @@ -1,279 +0,0 @@ -{ - "actions": [], - "creation": "2013-08-02 13:45:23", - "doctype": "DocType", - "document_type": "Other", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee_settings", - "emp_created_by", - "standard_working_hours", - "column_break_9", - "retirement_age", - "reminders_section", - "send_birthday_reminders", - "column_break_11", - "send_work_anniversary_reminders", - "column_break_18", - "send_holiday_reminders", - "frequency", - "leave_and_expense_claim_settings", - "send_leave_notification", - "leave_approval_notification_template", - "leave_status_notification_template", - "leave_approver_mandatory_in_leave_application", - "restrict_backdated_leave_application", - "role_allowed_to_create_backdated_leave_application", - "column_break_29", - "expense_approver_mandatory_in_expense_claim", - "show_leaves_of_all_department_members_in_calendar", - "auto_leave_encashment", - "hiring_settings_section", - "check_vacancies", - "send_interview_reminder", - "interview_reminder_template", - "remind_before", - "column_break_4", - "send_interview_feedback_reminder", - "feedback_reminder_notification_template", - "employee_exit_section", - "exit_questionnaire_web_form", - "column_break_34", - "exit_questionnaire_notification_template" - ], - "fields": [ - { - "fieldname": "employee_settings", - "fieldtype": "Section Break", - "label": "Employee Settings" - }, - { - "fieldname": "retirement_age", - "fieldtype": "Data", - "label": "Retirement Age (In Years)" - }, - { - "default": "Naming Series", - "description": "Employee records are created using the selected option", - "fieldname": "emp_created_by", - "fieldtype": "Select", - "label": "Employee Naming By", - "options": "Naming Series\nEmployee Number\nFull Name" - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "default": "1", - "fieldname": "expense_approver_mandatory_in_expense_claim", - "fieldtype": "Check", - "label": "Expense Approver Mandatory In Expense Claim" - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" - }, - { - "default": "1", - "fieldname": "leave_approver_mandatory_in_leave_application", - "fieldtype": "Check", - "label": "Leave Approver Mandatory In Leave Application" - }, - { - "default": "0", - "fieldname": "show_leaves_of_all_department_members_in_calendar", - "fieldtype": "Check", - "label": "Show Leaves Of All Department Members In Calendar" - }, - { - "default": "0", - "fieldname": "auto_leave_encashment", - "fieldtype": "Check", - "label": "Auto Leave Encashment" - }, - { - "depends_on": "eval:doc.restrict_backdated_leave_application == 1", - "fieldname": "role_allowed_to_create_backdated_leave_application", - "fieldtype": "Link", - "label": "Role Allowed to Create Backdated Leave Application", - "mandatory_depends_on": "eval:doc.restrict_backdated_leave_application == 1", - "options": "Role" - }, - { - "default": "1", - "fieldname": "send_leave_notification", - "fieldtype": "Check", - "label": "Send Leave Notification" - }, - { - "depends_on": "eval: doc.send_leave_notification == 1", - "fieldname": "leave_approval_notification_template", - "fieldtype": "Link", - "label": "Leave Approval Notification Template", - "mandatory_depends_on": "eval: doc.send_leave_notification == 1", - "options": "Email Template" - }, - { - "depends_on": "eval: doc.send_leave_notification == 1", - "fieldname": "leave_status_notification_template", - "fieldtype": "Link", - "label": "Leave Status Notification Template", - "mandatory_depends_on": "eval: doc.send_leave_notification == 1", - "options": "Email Template" - }, - { - "fieldname": "standard_working_hours", - "fieldtype": "Int", - "label": "Standard Working Hours" - }, - { - "collapsible": 1, - "fieldname": "leave_and_expense_claim_settings", - "fieldtype": "Section Break", - "label": "Leave and Expense Claim Settings" - }, - { - "default": "00:15:00", - "depends_on": "send_interview_reminder", - "fieldname": "remind_before", - "fieldtype": "Time", - "label": "Remind Before" - }, - { - "collapsible": 1, - "fieldname": "reminders_section", - "fieldtype": "Section Break", - "label": "Reminders" - }, - { - "default": "1", - "fieldname": "send_holiday_reminders", - "fieldtype": "Check", - "label": "Holidays" - }, - { - "default": "1", - "fieldname": "send_work_anniversary_reminders", - "fieldtype": "Check", - "label": "Work Anniversaries " - }, - { - "default": "Weekly", - "depends_on": "eval:doc.send_holiday_reminders", - "fieldname": "frequency", - "fieldtype": "Select", - "label": "Set the frequency for holiday reminders", - "mandatory_depends_on": "send_holiday_reminders", - "options": "Weekly\nMonthly" - }, - { - "default": "1", - "fieldname": "send_birthday_reminders", - "fieldtype": "Check", - "label": "Birthdays" - }, - { - "fieldname": "column_break_9", - "fieldtype": "Column Break" - }, - { - "fieldname": "column_break_11", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "send_interview_reminder", - "fieldtype": "Check", - "label": "Send Interview Reminder" - }, - { - "default": "0", - "fieldname": "send_interview_feedback_reminder", - "fieldtype": "Check", - "label": "Send Interview Feedback Reminder" - }, - { - "fieldname": "column_break_29", - "fieldtype": "Column Break" - }, - { - "depends_on": "send_interview_feedback_reminder", - "fieldname": "feedback_reminder_notification_template", - "fieldtype": "Link", - "label": "Feedback Reminder Notification Template", - "mandatory_depends_on": "send_interview_feedback_reminder", - "options": "Email Template" - }, - { - "depends_on": "send_interview_reminder", - "fieldname": "interview_reminder_template", - "fieldtype": "Link", - "label": "Interview Reminder Notification Template", - "mandatory_depends_on": "send_interview_reminder", - "options": "Email Template" - }, - { - "default": "0", - "fieldname": "restrict_backdated_leave_application", - "fieldtype": "Check", - "label": "Restrict Backdated Leave Application" - }, - { - "fieldname": "hiring_settings_section", - "fieldtype": "Section Break", - "label": "Hiring Settings" - }, - { - "default": "0", - "fieldname": "check_vacancies", - "fieldtype": "Check", - "label": "Check Vacancies On Job Offer Creation" - }, - { - "fieldname": "employee_exit_section", - "fieldtype": "Section Break", - "label": "Employee Exit Settings" - }, - { - "fieldname": "exit_questionnaire_web_form", - "fieldtype": "Link", - "label": "Exit Questionnaire Web Form", - "options": "Web Form" - }, - { - "fieldname": "exit_questionnaire_notification_template", - "fieldtype": "Link", - "label": "Exit Questionnaire Notification Template", - "options": "Email Template" - }, - { - "fieldname": "column_break_34", - "fieldtype": "Column Break" - } - ], - "icon": "fa fa-cog", - "idx": 1, - "issingle": 1, - "links": [], - "modified": "2021-12-05 14:48:10.884253", - "modified_by": "Administrator", - "module": "HR", - "name": "HR Settings", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.py b/erpnext/hr/doctype/hr_settings/hr_settings.py deleted file mode 100644 index b56f3dbe0d..0000000000 --- a/erpnext/hr/doctype/hr_settings/hr_settings.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -# For license information, please see license.txt - -import frappe -from frappe.model.document import Document -from frappe.utils import format_date - -# Wether to proceed with frequency change -PROCEED_WITH_FREQUENCY_CHANGE = False - - -class HRSettings(Document): - def validate(self): - self.set_naming_series() - - # Based on proceed flag - global PROCEED_WITH_FREQUENCY_CHANGE - if not PROCEED_WITH_FREQUENCY_CHANGE: - self.validate_frequency_change() - PROCEED_WITH_FREQUENCY_CHANGE = False - - def set_naming_series(self): - from erpnext.utilities.naming import set_by_naming_series - - set_by_naming_series( - "Employee", - "employee_number", - self.get("emp_created_by") == "Naming Series", - hide_name_field=True, - ) - - def validate_frequency_change(self): - weekly_job, monthly_job = None, None - - try: - weekly_job = frappe.get_doc( - "Scheduled Job Type", "employee_reminders.send_reminders_in_advance_weekly" - ) - - monthly_job = frappe.get_doc( - "Scheduled Job Type", "employee_reminders.send_reminders_in_advance_monthly" - ) - except frappe.DoesNotExistError: - return - - next_weekly_trigger = weekly_job.get_next_execution() - next_monthly_trigger = monthly_job.get_next_execution() - - if self.freq_changed_from_monthly_to_weekly(): - if next_monthly_trigger < next_weekly_trigger: - self.show_freq_change_warning(next_monthly_trigger, next_weekly_trigger) - - elif self.freq_changed_from_weekly_to_monthly(): - if next_monthly_trigger > next_weekly_trigger: - self.show_freq_change_warning(next_weekly_trigger, next_monthly_trigger) - - def freq_changed_from_weekly_to_monthly(self): - return self.has_value_changed("frequency") and self.frequency == "Monthly" - - def freq_changed_from_monthly_to_weekly(self): - return self.has_value_changed("frequency") and self.frequency == "Weekly" - - def show_freq_change_warning(self, from_date, to_date): - from_date = frappe.bold(format_date(from_date)) - to_date = frappe.bold(format_date(to_date)) - frappe.msgprint( - msg=frappe._( - "Employees will miss holiday reminders from {} until {}.
Do you want to proceed with this change?" - ).format(from_date, to_date), - title="Confirm change in Frequency", - primary_action={ - "label": frappe._("Yes, Proceed"), - "client_action": "erpnext.proceed_save_with_reminders_frequency_change", - }, - raise_exception=frappe.ValidationError, - ) - - -@frappe.whitelist() -def set_proceed_with_frequency_change(): - """Enables proceed with frequency change""" - global PROCEED_WITH_FREQUENCY_CHANGE - PROCEED_WITH_FREQUENCY_CHANGE = True diff --git a/erpnext/hr/doctype/hr_settings/test_hr_settings.py b/erpnext/hr/doctype/hr_settings/test_hr_settings.py deleted file mode 100644 index 7e13213ff3..0000000000 --- a/erpnext/hr/doctype/hr_settings/test_hr_settings.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestHRSettings(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/identification_document_type/__init__.py b/erpnext/hr/doctype/identification_document_type/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/identification_document_type/identification_document_type.js b/erpnext/hr/doctype/identification_document_type/identification_document_type.js deleted file mode 100644 index 351cf9d9ff..0000000000 --- a/erpnext/hr/doctype/identification_document_type/identification_document_type.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Identification Document Type', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/identification_document_type/identification_document_type.json b/erpnext/hr/doctype/identification_document_type/identification_document_type.json deleted file mode 100644 index 33cbde7409..0000000000 --- a/erpnext/hr/doctype/identification_document_type/identification_document_type.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:identification_document_type", - "beta": 0, - "creation": "2018-05-15 07:13:28.620570", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "identification_document_type", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Identification Document Type", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-05-16 04:34:00.448680", - "modified_by": "Administrator", - "module": "HR", - "name": "Identification Document Type", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/identification_document_type/identification_document_type.py b/erpnext/hr/doctype/identification_document_type/identification_document_type.py deleted file mode 100644 index 3bfcfaadcc..0000000000 --- a/erpnext/hr/doctype/identification_document_type/identification_document_type.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class IdentificationDocumentType(Document): - pass diff --git a/erpnext/hr/doctype/identification_document_type/test_identification_document_type.py b/erpnext/hr/doctype/identification_document_type/test_identification_document_type.py deleted file mode 100644 index 3e8f7ab0d6..0000000000 --- a/erpnext/hr/doctype/identification_document_type/test_identification_document_type.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestIdentificationDocumentType(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/interest/__init__.py b/erpnext/hr/doctype/interest/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/interest/interest.js b/erpnext/hr/doctype/interest/interest.js deleted file mode 100644 index 70e1b6aaaa..0000000000 --- a/erpnext/hr/doctype/interest/interest.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Interest', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/interest/interest.json b/erpnext/hr/doctype/interest/interest.json deleted file mode 100644 index d6d2342ab6..0000000000 --- a/erpnext/hr/doctype/interest/interest.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "allow_copy": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "field:interest", - "beta": 0, - "creation": "2016-07-25 07:12:33.600702", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "interest", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Interest", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2016-08-01 05:47:29.479141", - "modified_by": "Administrator", - "module": "HR", - "name": "Interest", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Academics User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/interest/interest.py b/erpnext/hr/doctype/interest/interest.py deleted file mode 100644 index 3563f7f3a0..0000000000 --- a/erpnext/hr/doctype/interest/interest.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class Interest(Document): - pass diff --git a/erpnext/hr/doctype/interest/test_interest.py b/erpnext/hr/doctype/interest/test_interest.py deleted file mode 100644 index eacb57f758..0000000000 --- a/erpnext/hr/doctype/interest/test_interest.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -# test_records = frappe.get_test_records('Interest') - - -class TestInterest(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/interview/__init__.py b/erpnext/hr/doctype/interview/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/interview/interview.js b/erpnext/hr/doctype/interview/interview.js deleted file mode 100644 index 6341e3a62b..0000000000 --- a/erpnext/hr/doctype/interview/interview.js +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Interview', { - onload: function (frm) { - frm.events.set_job_applicant_query(frm); - - frm.set_query('interviewer', 'interview_details', function () { - return { - query: 'erpnext.hr.doctype.interview.interview.get_interviewer_list' - }; - }); - }, - - refresh: function (frm) { - if (frm.doc.docstatus != 2 && !frm.doc.__islocal) { - if (frm.doc.status === 'Pending') { - frm.add_custom_button(__('Reschedule Interview'), function() { - frm.events.show_reschedule_dialog(frm); - frm.refresh(); - }); - } - - let allowed_interviewers = []; - frm.doc.interview_details.forEach(values => { - allowed_interviewers.push(values.interviewer); - }); - - if ((allowed_interviewers.includes(frappe.session.user))) { - frappe.db.get_value('Interview Feedback', {'interviewer': frappe.session.user, 'interview': frm.doc.name, 'docstatus': 1}, 'name', (r) => { - if (Object.keys(r).length === 0) { - frm.add_custom_button(__('Submit Feedback'), function () { - frappe.call({ - method: 'erpnext.hr.doctype.interview.interview.get_expected_skill_set', - args: { - interview_round: frm.doc.interview_round - }, - callback: function (r) { - frm.events.show_feedback_dialog(frm, r.message); - frm.refresh(); - } - }); - }).addClass('btn-primary'); - } - }); - } - } - }, - - show_reschedule_dialog: function (frm) { - let d = new frappe.ui.Dialog({ - title: 'Reschedule Interview', - fields: [ - { - label: 'Schedule On', - fieldname: 'scheduled_on', - fieldtype: 'Date', - reqd: 1 - }, - { - label: 'From Time', - fieldname: 'from_time', - fieldtype: 'Time', - reqd: 1 - }, - { - label: 'To Time', - fieldname: 'to_time', - fieldtype: 'Time', - reqd: 1 - } - ], - primary_action_label: 'Reschedule', - primary_action(values) { - frm.call({ - method: 'reschedule_interview', - doc: frm.doc, - args: { - scheduled_on: values.scheduled_on, - from_time: values.from_time, - to_time: values.to_time - } - }).then(() => { - frm.refresh(); - d.hide(); - }); - } - }); - d.show(); - }, - - show_feedback_dialog: function (frm, data) { - let fields = frm.events.get_fields_for_feedback(); - - let d = new frappe.ui.Dialog({ - title: __('Submit Feedback'), - fields: [ - { - fieldname: 'skill_set', - fieldtype: 'Table', - label: __('Skill Assessment'), - cannot_add_rows: false, - in_editable_grid: true, - reqd: 1, - fields: fields, - data: data - }, - { - fieldname: 'result', - fieldtype: 'Select', - options: ['', 'Cleared', 'Rejected'], - label: __('Result') - }, - { - fieldname: 'feedback', - fieldtype: 'Small Text', - label: __('Feedback') - } - ], - size: 'large', - minimizable: true, - primary_action: function(values) { - frappe.call({ - method: 'erpnext.hr.doctype.interview.interview.create_interview_feedback', - args: { - data: values, - interview_name: frm.doc.name, - interviewer: frappe.session.user, - job_applicant: frm.doc.job_applicant - } - }).then(() => { - frm.refresh(); - }); - d.hide(); - } - }); - d.show(); - }, - - get_fields_for_feedback: function () { - return [{ - fieldtype: 'Link', - fieldname: 'skill', - options: 'Skill', - in_list_view: 1, - label: __('Skill') - }, { - fieldtype: 'Rating', - fieldname: 'rating', - label: __('Rating'), - in_list_view: 1, - reqd: 1, - }]; - }, - - set_job_applicant_query: function (frm) { - frm.set_query('job_applicant', function () { - let job_applicant_filters = { - status: ['!=', 'Rejected'] - }; - if (frm.doc.designation) { - job_applicant_filters.designation = frm.doc.designation; - } - return { - filters: job_applicant_filters - }; - }); - }, - - interview_round: async function (frm) { - frm.events.reset_values(frm); - frm.set_value('job_applicant', ''); - - let round_data = (await frappe.db.get_value('Interview Round', frm.doc.interview_round, 'designation')).message; - frm.set_value('designation', round_data.designation); - frm.events.set_job_applicant_query(frm); - - if (frm.doc.interview_round) { - frm.events.set_interview_details(frm); - } else { - frm.set_value('interview_details', []); - } - }, - - set_interview_details: function (frm) { - frappe.call({ - method: 'erpnext.hr.doctype.interview.interview.get_interviewers', - args: { - interview_round: frm.doc.interview_round - }, - callback: function (data) { - let interview_details = data.message; - frm.set_value('interview_details', []); - if (data.message.length) { - frm.set_value('interview_details', interview_details); - } - } - }); - }, - - job_applicant: function (frm) { - if (!frm.doc.interview_round) { - frm.doc.job_applicant = ''; - frm.refresh(); - frappe.throw(__('Select Interview Round First')); - } - - if (frm.doc.job_applicant) { - frm.events.set_designation_and_job_opening(frm); - } else { - frm.events.reset_values(frm); - } - }, - - set_designation_and_job_opening: async function (frm) { - let round_data = (await frappe.db.get_value('Interview Round', frm.doc.interview_round, 'designation')).message; - frm.set_value('designation', round_data.designation); - frm.events.set_job_applicant_query(frm); - - let job_applicant_data = (await frappe.db.get_value( - 'Job Applicant', frm.doc.job_applicant, ['designation', 'job_title', 'resume_link'], - )).message; - - if (!round_data.designation) { - frm.set_value('designation', job_applicant_data.designation); - } - - frm.set_value('job_opening', job_applicant_data.job_title); - frm.set_value('resume_link', job_applicant_data.resume_link); - }, - - reset_values: function (frm) { - frm.set_value('designation', ''); - frm.set_value('job_opening', ''); - frm.set_value('resume_link', ''); - } -}); diff --git a/erpnext/hr/doctype/interview/interview.json b/erpnext/hr/doctype/interview/interview.json deleted file mode 100644 index 0d393e7556..0000000000 --- a/erpnext/hr/doctype/interview/interview.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "actions": [], - "autoname": "HR-INT-.YYYY.-.####", - "creation": "2021-04-12 15:03:11.524090", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "interview_details_section", - "interview_round", - "job_applicant", - "job_opening", - "designation", - "resume_link", - "column_break_4", - "status", - "scheduled_on", - "from_time", - "to_time", - "interview_feedback_section", - "interview_details", - "ratings_section", - "expected_average_rating", - "column_break_12", - "average_rating", - "section_break_13", - "interview_summary", - "reminded", - "amended_from" - ], - "fields": [ - { - "fieldname": "job_applicant", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Job Applicant", - "options": "Job Applicant", - "reqd": 1 - }, - { - "fieldname": "job_opening", - "fieldtype": "Link", - "label": "Job Opening", - "options": "Job Opening", - "read_only": 1 - }, - { - "fieldname": "interview_round", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Interview Round", - "options": "Interview Round", - "reqd": 1 - }, - { - "default": "Pending", - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Status", - "options": "Pending\nUnder Review\nCleared\nRejected", - "reqd": 1 - }, - { - "fieldname": "ratings_section", - "fieldtype": "Section Break", - "label": "Ratings" - }, - { - "allow_on_submit": 1, - "fieldname": "average_rating", - "fieldtype": "Rating", - "in_list_view": 1, - "label": "Obtained Average Rating", - "read_only": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "interview_summary", - "fieldtype": "Text" - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "fieldname": "resume_link", - "fieldtype": "Data", - "label": "Resume link" - }, - { - "fieldname": "interview_details_section", - "fieldtype": "Section Break", - "label": "Details" - }, - { - "fetch_from": "interview_round.expected_average_rating", - "fieldname": "expected_average_rating", - "fieldtype": "Rating", - "label": "Expected Average Rating", - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "section_break_13", - "fieldtype": "Section Break", - "label": "Interview Summary" - }, - { - "fieldname": "column_break_12", - "fieldtype": "Column Break" - }, - { - "fetch_from": "interview_round.designation", - "fieldname": "designation", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Designation", - "options": "Designation", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Interview", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "scheduled_on", - "fieldtype": "Date", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Scheduled On", - "reqd": 1, - "set_only_once": 1 - }, - { - "default": "0", - "fieldname": "reminded", - "fieldtype": "Check", - "hidden": 1, - "label": "Reminded" - }, - { - "allow_on_submit": 1, - "fieldname": "interview_details", - "fieldtype": "Table", - "options": "Interview Detail" - }, - { - "fieldname": "interview_feedback_section", - "fieldtype": "Section Break", - "label": "Feedback" - }, - { - "fieldname": "from_time", - "fieldtype": "Time", - "in_list_view": 1, - "label": "From Time", - "reqd": 1, - "set_only_once": 1 - }, - { - "fieldname": "to_time", - "fieldtype": "Time", - "in_list_view": 1, - "label": "To Time", - "reqd": 1, - "set_only_once": 1 - } - ], - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [ - { - "link_doctype": "Interview Feedback", - "link_fieldname": "interview" - } - ], - "modified": "2021-09-30 13:30:05.421035", - "modified_by": "Administrator", - "module": "HR", - "name": "Interview", - "owner": "Administrator", - "permissions": [ - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Interviewer", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "job_applicant", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/interview/interview.py b/erpnext/hr/doctype/interview/interview.py deleted file mode 100644 index a6e9af2679..0000000000 --- a/erpnext/hr/doctype/interview/interview.py +++ /dev/null @@ -1,355 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import datetime - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import cstr, flt, get_datetime, get_link_to_form - - -class DuplicateInterviewRoundError(frappe.ValidationError): - pass - - -class Interview(Document): - def validate(self): - self.validate_duplicate_interview() - self.validate_designation() - self.validate_overlap() - self.set_average_rating() - - def on_submit(self): - if self.status not in ["Cleared", "Rejected"]: - frappe.throw( - _("Only Interviews with Cleared or Rejected status can be submitted."), title=_("Not Allowed") - ) - - def validate_duplicate_interview(self): - duplicate_interview = frappe.db.exists( - "Interview", - {"job_applicant": self.job_applicant, "interview_round": self.interview_round, "docstatus": 1}, - ) - - if duplicate_interview: - frappe.throw( - _( - "Job Applicants are not allowed to appear twice for the same Interview round. Interview {0} already scheduled for Job Applicant {1}" - ).format( - frappe.bold(get_link_to_form("Interview", duplicate_interview)), - frappe.bold(self.job_applicant), - ) - ) - - def validate_designation(self): - applicant_designation = frappe.db.get_value("Job Applicant", self.job_applicant, "designation") - if self.designation: - if self.designation != applicant_designation: - frappe.throw( - _( - "Interview Round {0} is only for Designation {1}. Job Applicant has applied for the role {2}" - ).format( - self.interview_round, frappe.bold(self.designation), applicant_designation - ), - exc=DuplicateInterviewRoundError, - ) - else: - self.designation = applicant_designation - - def validate_overlap(self): - interviewers = [entry.interviewer for entry in self.interview_details] or [""] - - overlaps = frappe.db.sql( - """ - SELECT interview.name - FROM `tabInterview` as interview - INNER JOIN `tabInterview Detail` as detail - WHERE - interview.scheduled_on = %s and interview.name != %s and interview.docstatus != 2 - and (interview.job_applicant = %s or detail.interviewer IN %s) and - ((from_time < %s and to_time > %s) or - (from_time > %s and to_time < %s) or - (from_time = %s)) - """, - ( - self.scheduled_on, - self.name, - self.job_applicant, - interviewers, - self.from_time, - self.to_time, - self.from_time, - self.to_time, - self.from_time, - ), - ) - - if overlaps: - overlapping_details = _("Interview overlaps with {0}").format( - get_link_to_form("Interview", overlaps[0][0]) - ) - frappe.throw(overlapping_details, title=_("Overlap")) - - def set_average_rating(self): - total_rating = 0 - for entry in self.interview_details: - if entry.average_rating: - total_rating += entry.average_rating - - self.average_rating = flt( - total_rating / len(self.interview_details) if len(self.interview_details) else 0 - ) - - @frappe.whitelist() - def reschedule_interview(self, scheduled_on, from_time, to_time): - original_date = self.scheduled_on - from_time = self.from_time - to_time = self.to_time - - self.db_set({"scheduled_on": scheduled_on, "from_time": from_time, "to_time": to_time}) - self.notify_update() - - recipients = get_recipients(self.name) - - try: - frappe.sendmail( - recipients=recipients, - subject=_("Interview: {0} Rescheduled").format(self.name), - message=_("Your Interview session is rescheduled from {0} {1} - {2} to {3} {4} - {5}").format( - original_date, from_time, to_time, self.scheduled_on, self.from_time, self.to_time - ), - reference_doctype=self.doctype, - reference_name=self.name, - ) - except Exception: - frappe.msgprint( - _("Failed to send the Interview Reschedule notification. Please configure your email account.") - ) - - frappe.msgprint(_("Interview Rescheduled successfully"), indicator="green") - - -def get_recipients(name, for_feedback=0): - interview = frappe.get_doc("Interview", name) - - if for_feedback: - recipients = [d.interviewer for d in interview.interview_details if not d.interview_feedback] - else: - recipients = [d.interviewer for d in interview.interview_details] - recipients.append(frappe.db.get_value("Job Applicant", interview.job_applicant, "email_id")) - - return recipients - - -@frappe.whitelist() -def get_interviewers(interview_round): - return frappe.get_all( - "Interviewer", filters={"parent": interview_round}, fields=["user as interviewer"] - ) - - -def send_interview_reminder(): - reminder_settings = frappe.db.get_value( - "HR Settings", - "HR Settings", - ["send_interview_reminder", "interview_reminder_template"], - as_dict=True, - ) - - if not reminder_settings.send_interview_reminder: - return - - remind_before = cstr(frappe.db.get_single_value("HR Settings", "remind_before")) or "01:00:00" - remind_before = datetime.datetime.strptime(remind_before, "%H:%M:%S") - reminder_date_time = datetime.datetime.now() + datetime.timedelta( - hours=remind_before.hour, minutes=remind_before.minute, seconds=remind_before.second - ) - - interviews = frappe.get_all( - "Interview", - filters={ - "scheduled_on": ["between", (datetime.datetime.now(), reminder_date_time)], - "status": "Pending", - "reminded": 0, - "docstatus": ["!=", 2], - }, - ) - - interview_template = frappe.get_doc( - "Email Template", reminder_settings.interview_reminder_template - ) - - for d in interviews: - doc = frappe.get_doc("Interview", d.name) - context = doc.as_dict() - message = frappe.render_template(interview_template.response, context) - recipients = get_recipients(doc.name) - - frappe.sendmail( - recipients=recipients, - subject=interview_template.subject, - message=message, - reference_doctype=doc.doctype, - reference_name=doc.name, - ) - - doc.db_set("reminded", 1) - - -def send_daily_feedback_reminder(): - reminder_settings = frappe.db.get_value( - "HR Settings", - "HR Settings", - ["send_interview_feedback_reminder", "feedback_reminder_notification_template"], - as_dict=True, - ) - - if not reminder_settings.send_interview_feedback_reminder: - return - - interview_feedback_template = frappe.get_doc( - "Email Template", reminder_settings.feedback_reminder_notification_template - ) - interviews = frappe.get_all( - "Interview", filters={"status": ["in", ["Under Review", "Pending"]], "docstatus": ["!=", 2]} - ) - - for entry in interviews: - recipients = get_recipients(entry.name, for_feedback=1) - - doc = frappe.get_doc("Interview", entry.name) - context = doc.as_dict() - - message = frappe.render_template(interview_feedback_template.response, context) - - if len(recipients): - frappe.sendmail( - recipients=recipients, - subject=interview_feedback_template.subject, - message=message, - reference_doctype="Interview", - reference_name=entry.name, - ) - - -@frappe.whitelist() -def get_expected_skill_set(interview_round): - return frappe.get_all("Expected Skill Set", filters={"parent": interview_round}, fields=["skill"]) - - -@frappe.whitelist() -def create_interview_feedback(data, interview_name, interviewer, job_applicant): - import json - - if isinstance(data, str): - data = frappe._dict(json.loads(data)) - - if frappe.session.user != interviewer: - frappe.throw(_("Only Interviewer Are allowed to submit Interview Feedback")) - - interview_feedback = frappe.new_doc("Interview Feedback") - interview_feedback.interview = interview_name - interview_feedback.interviewer = interviewer - interview_feedback.job_applicant = job_applicant - - for d in data.skill_set: - d = frappe._dict(d) - interview_feedback.append("skill_assessment", {"skill": d.skill, "rating": d.rating}) - - interview_feedback.feedback = data.feedback - interview_feedback.result = data.result - - interview_feedback.save() - interview_feedback.submit() - - frappe.msgprint( - _("Interview Feedback {0} submitted successfully").format( - get_link_to_form("Interview Feedback", interview_feedback.name) - ) - ) - - -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def get_interviewer_list(doctype, txt, searchfield, start, page_len, filters): - filters = [ - ["Has Role", "parent", "like", "%{}%".format(txt)], - ["Has Role", "role", "=", "interviewer"], - ["Has Role", "parenttype", "=", "User"], - ] - - if filters and isinstance(filters, list): - filters.extend(filters) - - return frappe.get_all( - "Has Role", - limit_start=start, - limit_page_length=page_len, - filters=filters, - fields=["parent"], - as_list=1, - ) - - -@frappe.whitelist() -def get_events(start, end, filters=None): - """Returns events for Gantt / Calendar view rendering. - - :param start: Start date-time. - :param end: End date-time. - :param filters: Filters (JSON). - """ - from frappe.desk.calendar import get_event_conditions - - events = [] - - event_color = { - "Pending": "#fff4f0", - "Under Review": "#d3e8fc", - "Cleared": "#eaf5ed", - "Rejected": "#fce7e7", - } - - conditions = get_event_conditions("Interview", filters) - - interviews = frappe.db.sql( - """ - SELECT DISTINCT - `tabInterview`.name, `tabInterview`.job_applicant, `tabInterview`.interview_round, - `tabInterview`.scheduled_on, `tabInterview`.status, `tabInterview`.from_time as from_time, - `tabInterview`.to_time as to_time - from - `tabInterview` - where - (`tabInterview`.scheduled_on between %(start)s and %(end)s) - and docstatus != 2 - {conditions} - """.format( - conditions=conditions - ), - {"start": start, "end": end}, - as_dict=True, - update={"allDay": 0}, - ) - - for d in interviews: - subject_data = [] - for field in ["name", "job_applicant", "interview_round"]: - if not d.get(field): - continue - subject_data.append(d.get(field)) - - color = event_color.get(d.status) - interview_data = { - "from": get_datetime("%s %s" % (d.scheduled_on, d.from_time or "00:00:00")), - "to": get_datetime("%s %s" % (d.scheduled_on, d.to_time or "00:00:00")), - "name": d.name, - "subject": "\n".join(subject_data), - "color": color if color else "#89bcde", - } - - events.append(interview_data) - - return events diff --git a/erpnext/hr/doctype/interview/interview_calendar.js b/erpnext/hr/doctype/interview/interview_calendar.js deleted file mode 100644 index b46b72ecb2..0000000000 --- a/erpnext/hr/doctype/interview/interview_calendar.js +++ /dev/null @@ -1,14 +0,0 @@ - -frappe.views.calendar['Interview'] = { - field_map: { - 'start': 'from', - 'end': 'to', - 'id': 'name', - 'title': 'subject', - 'allDay': 'allDay', - 'color': 'color' - }, - order_by: 'scheduled_on', - gantt: true, - get_events_method: 'erpnext.hr.doctype.interview.interview.get_events' -}; diff --git a/erpnext/hr/doctype/interview/interview_feedback_reminder_template.html b/erpnext/hr/doctype/interview/interview_feedback_reminder_template.html deleted file mode 100644 index 8d39fb54ef..0000000000 --- a/erpnext/hr/doctype/interview/interview_feedback_reminder_template.html +++ /dev/null @@ -1,5 +0,0 @@ -

Interview Feedback Reminder

- -

- Interview Feedback for Interview {{ name }} is not submitted yet. Please submit your feedback. Thank you, good day! -

diff --git a/erpnext/hr/doctype/interview/interview_list.js b/erpnext/hr/doctype/interview/interview_list.js deleted file mode 100644 index b1f072f0d4..0000000000 --- a/erpnext/hr/doctype/interview/interview_list.js +++ /dev/null @@ -1,12 +0,0 @@ -frappe.listview_settings['Interview'] = { - has_indicator_for_draft: 1, - get_indicator: function(doc) { - let status_color = { - 'Pending': 'orange', - 'Under Review': 'blue', - 'Cleared': 'green', - 'Rejected': 'red', - }; - return [__(doc.status), status_color[doc.status], 'status,=,'+doc.status]; - } -}; diff --git a/erpnext/hr/doctype/interview/interview_reminder_notification_template.html b/erpnext/hr/doctype/interview/interview_reminder_notification_template.html deleted file mode 100644 index 76de46e28d..0000000000 --- a/erpnext/hr/doctype/interview/interview_reminder_notification_template.html +++ /dev/null @@ -1,5 +0,0 @@ -

Interview Reminder

- -

- Interview: {{name}} is scheduled on {{scheduled_on}} from {{from_time}} to {{to_time}} -

diff --git a/erpnext/hr/doctype/interview/test_interview.py b/erpnext/hr/doctype/interview/test_interview.py deleted file mode 100644 index ae8493ab3e..0000000000 --- a/erpnext/hr/doctype/interview/test_interview.py +++ /dev/null @@ -1,209 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import datetime -import os -import unittest - -import frappe -from frappe import _ -from frappe.core.doctype.user_permission.test_user_permission import create_user -from frappe.utils import add_days, getdate, nowtime - -from erpnext.hr.doctype.designation.test_designation import create_designation -from erpnext.hr.doctype.interview.interview import DuplicateInterviewRoundError -from erpnext.hr.doctype.job_applicant.job_applicant import get_interview_details -from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant - - -class TestInterview(unittest.TestCase): - def test_validations_for_designation(self): - job_applicant = create_job_applicant() - interview = create_interview_and_dependencies( - job_applicant.name, designation="_Test_Sales_manager", save=0 - ) - self.assertRaises(DuplicateInterviewRoundError, interview.save) - - def test_notification_on_rescheduling(self): - job_applicant = create_job_applicant() - interview = create_interview_and_dependencies( - job_applicant.name, scheduled_on=add_days(getdate(), -4) - ) - - previous_scheduled_date = interview.scheduled_on - frappe.db.sql("DELETE FROM `tabEmail Queue`") - - interview.reschedule_interview( - add_days(getdate(previous_scheduled_date), 2), from_time=nowtime(), to_time=nowtime() - ) - interview.reload() - - self.assertEqual(interview.scheduled_on, add_days(getdate(previous_scheduled_date), 2)) - - notification = frappe.get_all( - "Email Queue", filters={"message": ("like", "%Your Interview session is rescheduled from%")} - ) - self.assertIsNotNone(notification) - - def test_notification_for_scheduling(self): - from erpnext.hr.doctype.interview.interview import send_interview_reminder - - setup_reminder_settings() - - job_applicant = create_job_applicant() - scheduled_on = datetime.datetime.now() + datetime.timedelta(minutes=10) - - interview = create_interview_and_dependencies(job_applicant.name, scheduled_on=scheduled_on) - - frappe.db.sql("DELETE FROM `tabEmail Queue`") - send_interview_reminder() - - interview.reload() - - email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) - self.assertTrue("Subject: Interview Reminder" in email_queue[0].message) - - def test_notification_for_feedback_submission(self): - from erpnext.hr.doctype.interview.interview import send_daily_feedback_reminder - - setup_reminder_settings() - - job_applicant = create_job_applicant() - scheduled_on = add_days(getdate(), -4) - create_interview_and_dependencies(job_applicant.name, scheduled_on=scheduled_on) - - frappe.db.sql("DELETE FROM `tabEmail Queue`") - send_daily_feedback_reminder() - - email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) - self.assertTrue("Subject: Interview Feedback Reminder" in email_queue[0].message) - - def test_get_interview_details_for_applicant_dashboard(self): - job_applicant = create_job_applicant() - interview = create_interview_and_dependencies(job_applicant.name) - - details = get_interview_details(job_applicant.name) - self.assertEqual(details.get("stars"), 5) - self.assertEqual( - details.get("interviews").get(interview.name), - { - "name": interview.name, - "interview_round": interview.interview_round, - "expected_average_rating": interview.expected_average_rating * 5, - "average_rating": interview.average_rating * 5, - "status": "Pending", - }, - ) - - def tearDown(self): - frappe.db.rollback() - - -def create_interview_and_dependencies( - job_applicant, scheduled_on=None, from_time=None, to_time=None, designation=None, save=1 -): - if designation: - designation = create_designation(designation_name="_Test_Sales_manager").name - - interviewer_1 = create_user("test_interviewer1@example.com", "Interviewer") - interviewer_2 = create_user("test_interviewer2@example.com", "Interviewer") - - interview_round = create_interview_round( - "Technical Round", ["Python", "JS"], designation=designation, save=True - ) - - interview = frappe.new_doc("Interview") - interview.interview_round = interview_round.name - interview.job_applicant = job_applicant - interview.scheduled_on = scheduled_on or getdate() - interview.from_time = from_time or nowtime() - interview.to_time = to_time or nowtime() - - interview.append("interview_details", {"interviewer": interviewer_1.name}) - interview.append("interview_details", {"interviewer": interviewer_2.name}) - - if save: - interview.save() - - return interview - - -def create_interview_round(name, skill_set, interviewers=[], designation=None, save=True): - create_skill_set(skill_set) - interview_round = frappe.new_doc("Interview Round") - interview_round.round_name = name - interview_round.interview_type = create_interview_type() - # average rating = 4 - interview_round.expected_average_rating = 0.8 - if designation: - interview_round.designation = designation - - for skill in skill_set: - interview_round.append("expected_skill_set", {"skill": skill}) - - for interviewer in interviewers: - interview_round.append("interviewer", {"user": interviewer}) - - if save: - interview_round.save() - - return interview_round - - -def create_skill_set(skill_set): - for skill in skill_set: - if not frappe.db.exists("Skill", skill): - doc = frappe.new_doc("Skill") - doc.skill_name = skill - doc.save() - - -def create_interview_type(name="test_interview_type"): - if frappe.db.exists("Interview Type", name): - return frappe.get_doc("Interview Type", name).name - else: - doc = frappe.new_doc("Interview Type") - doc.name = name - doc.description = "_Test_Description" - doc.save() - - return doc.name - - -def setup_reminder_settings(): - if not frappe.db.exists("Email Template", _("Interview Reminder")): - base_path = frappe.get_app_path("erpnext", "hr", "doctype") - response = frappe.read_file( - os.path.join(base_path, "interview/interview_reminder_notification_template.html") - ) - - frappe.get_doc( - { - "doctype": "Email Template", - "name": _("Interview Reminder"), - "response": response, - "subject": _("Interview Reminder"), - "owner": frappe.session.user, - } - ).insert(ignore_permissions=True) - - if not frappe.db.exists("Email Template", _("Interview Feedback Reminder")): - base_path = frappe.get_app_path("erpnext", "hr", "doctype") - response = frappe.read_file( - os.path.join(base_path, "interview/interview_feedback_reminder_template.html") - ) - - frappe.get_doc( - { - "doctype": "Email Template", - "name": _("Interview Feedback Reminder"), - "response": response, - "subject": _("Interview Feedback Reminder"), - "owner": frappe.session.user, - } - ).insert(ignore_permissions=True) - - hr_settings = frappe.get_doc("HR Settings") - hr_settings.interview_reminder_template = _("Interview Reminder") - hr_settings.feedback_reminder_notification_template = _("Interview Feedback Reminder") - hr_settings.save() diff --git a/erpnext/hr/doctype/interview_detail/__init__.py b/erpnext/hr/doctype/interview_detail/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/interview_detail/interview_detail.js b/erpnext/hr/doctype/interview_detail/interview_detail.js deleted file mode 100644 index 88518ca4cc..0000000000 --- a/erpnext/hr/doctype/interview_detail/interview_detail.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Interview Detail', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/hr/doctype/interview_detail/interview_detail.json b/erpnext/hr/doctype/interview_detail/interview_detail.json deleted file mode 100644 index b5b49c0993..0000000000 --- a/erpnext/hr/doctype/interview_detail/interview_detail.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "actions": [], - "creation": "2021-04-12 16:24:10.382863", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "interviewer", - "interview_feedback", - "average_rating", - "result", - "column_break_4", - "comments" - ], - "fields": [ - { - "fieldname": "interviewer", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Interviewer", - "options": "User" - }, - { - "allow_on_submit": 1, - "fieldname": "interview_feedback", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Interview Feedback", - "options": "Interview Feedback", - "read_only": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "average_rating", - "fieldtype": "Rating", - "in_list_view": 1, - "label": "Average Rating", - "read_only": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "allow_on_submit": 1, - "fetch_from": "interview_feedback.feedback", - "fieldname": "comments", - "fieldtype": "Text", - "label": "Comments", - "read_only": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "result", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Result", - "options": "\nCleared\nRejected", - "read_only": 1 - } - ], - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2021-09-29 13:13:25.865063", - "modified_by": "Administrator", - "module": "HR", - "name": "Interview Detail", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/interview_detail/interview_detail.py b/erpnext/hr/doctype/interview_detail/interview_detail.py deleted file mode 100644 index d44e29a9c1..0000000000 --- a/erpnext/hr/doctype/interview_detail/interview_detail.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 InterviewDetail(Document): - pass diff --git a/erpnext/hr/doctype/interview_detail/test_interview_detail.py b/erpnext/hr/doctype/interview_detail/test_interview_detail.py deleted file mode 100644 index 68a1f72485..0000000000 --- a/erpnext/hr/doctype/interview_detail/test_interview_detail.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -import unittest - - -class TestInterviewDetail(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/interview_feedback/__init__.py b/erpnext/hr/doctype/interview_feedback/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/interview_feedback/interview_feedback.js b/erpnext/hr/doctype/interview_feedback/interview_feedback.js deleted file mode 100644 index dec559fcea..0000000000 --- a/erpnext/hr/doctype/interview_feedback/interview_feedback.js +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Interview Feedback', { - onload: function(frm) { - frm.ignore_doctypes_on_cancel_all = ['Interview']; - - frm.set_query('interview', function() { - return { - filters: { - docstatus: ['!=', 2] - } - }; - }); - }, - - interview_round: function(frm) { - frappe.call({ - method: 'erpnext.hr.doctype.interview.interview.get_expected_skill_set', - args: { - interview_round: frm.doc.interview_round - }, - callback: function(r) { - frm.set_value('skill_assessment', r.message); - } - }); - }, - - interview: function(frm) { - frappe.call({ - method: 'erpnext.hr.doctype.interview_feedback.interview_feedback.get_applicable_interviewers', - args: { - interview: frm.doc.interview || '' - }, - callback: function(r) { - frm.set_query('interviewer', function() { - return { - filters: { - name: ['in', r.message] - } - }; - }); - } - }); - - }, - - interviewer: function(frm) { - if (!frm.doc.interview) { - frappe.throw(__('Select Interview first')); - frm.set_value('interviewer', ''); - } - } -}); diff --git a/erpnext/hr/doctype/interview_feedback/interview_feedback.json b/erpnext/hr/doctype/interview_feedback/interview_feedback.json deleted file mode 100644 index 6a2f7e8696..0000000000 --- a/erpnext/hr/doctype/interview_feedback/interview_feedback.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "actions": [], - "autoname": "HR-INT-FEED-.####", - "creation": "2021-04-12 17:03:13.833285", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "details_section", - "interview", - "interview_round", - "job_applicant", - "column_break_3", - "interviewer", - "result", - "section_break_4", - "skill_assessment", - "average_rating", - "section_break_7", - "feedback", - "amended_from" - ], - "fields": [ - { - "allow_in_quick_entry": 1, - "fieldname": "interview", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Interview", - "options": "Interview", - "reqd": 1 - }, - { - "allow_in_quick_entry": 1, - "fetch_from": "interview.interview_round", - "fieldname": "interview_round", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Interview Round", - "options": "Interview Round", - "read_only": 1, - "reqd": 1 - }, - { - "allow_in_quick_entry": 1, - "fieldname": "interviewer", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Interviewer", - "options": "User", - "reqd": 1 - }, - { - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "label": "Skill Assessment" - }, - { - "allow_in_quick_entry": 1, - "fieldname": "skill_assessment", - "fieldtype": "Table", - "options": "Skill Assessment", - "reqd": 1 - }, - { - "allow_in_quick_entry": 1, - "fieldname": "average_rating", - "fieldtype": "Rating", - "in_list_view": 1, - "label": "Average Rating", - "read_only": 1 - }, - { - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "label": "Feedback" - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Interview Feedback", - "print_hide": 1, - "read_only": 1 - }, - { - "allow_in_quick_entry": 1, - "fieldname": "feedback", - "fieldtype": "Text" - }, - { - "fieldname": "result", - "fieldtype": "Select", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Result", - "options": "\nCleared\nRejected", - "reqd": 1 - }, - { - "fieldname": "details_section", - "fieldtype": "Section Break", - "label": "Details" - }, - { - "fetch_from": "interview.job_applicant", - "fieldname": "job_applicant", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Job Applicant", - "options": "Job Applicant", - "read_only": 1 - } - ], - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2021-09-30 13:30:49.955352", - "modified_by": "Administrator", - "module": "HR", - "name": "Interview Feedback", - "owner": "Administrator", - "permissions": [ - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Interviewer", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "interviewer", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/interview_feedback/interview_feedback.py b/erpnext/hr/doctype/interview_feedback/interview_feedback.py deleted file mode 100644 index 5bb498fa54..0000000000 --- a/erpnext/hr/doctype/interview_feedback/interview_feedback.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import flt, get_link_to_form, getdate - - -class InterviewFeedback(Document): - def validate(self): - self.validate_interviewer() - self.validate_interview_date() - self.validate_duplicate() - self.calculate_average_rating() - - def on_submit(self): - self.update_interview_details() - - def on_cancel(self): - self.update_interview_details() - - def validate_interviewer(self): - applicable_interviewers = get_applicable_interviewers(self.interview) - if self.interviewer not in applicable_interviewers: - frappe.throw( - _("{0} is not allowed to submit Interview Feedback for the Interview: {1}").format( - frappe.bold(self.interviewer), frappe.bold(self.interview) - ) - ) - - def validate_interview_date(self): - scheduled_date = frappe.db.get_value("Interview", self.interview, "scheduled_on") - - if getdate() < getdate(scheduled_date) and self.docstatus == 1: - frappe.throw( - _("{0} submission before {1} is not allowed").format( - frappe.bold("Interview Feedback"), frappe.bold("Interview Scheduled Date") - ) - ) - - def validate_duplicate(self): - duplicate_feedback = frappe.db.exists( - "Interview Feedback", - {"interviewer": self.interviewer, "interview": self.interview, "docstatus": 1}, - ) - - if duplicate_feedback: - frappe.throw( - _( - "Feedback already submitted for the Interview {0}. Please cancel the previous Interview Feedback {1} to continue." - ).format( - self.interview, get_link_to_form("Interview Feedback", duplicate_feedback) - ) - ) - - def calculate_average_rating(self): - total_rating = 0 - for d in self.skill_assessment: - if d.rating: - total_rating += d.rating - - self.average_rating = flt( - total_rating / len(self.skill_assessment) if len(self.skill_assessment) else 0 - ) - - def update_interview_details(self): - doc = frappe.get_doc("Interview", self.interview) - - if self.docstatus == 2: - for entry in doc.interview_details: - if entry.interview_feedback == self.name: - entry.average_rating = entry.interview_feedback = entry.comments = entry.result = None - break - else: - for entry in doc.interview_details: - if entry.interviewer == self.interviewer: - entry.average_rating = self.average_rating - entry.interview_feedback = self.name - entry.comments = self.feedback - entry.result = self.result - - doc.save() - doc.notify_update() - - -@frappe.whitelist() -def get_applicable_interviewers(interview): - data = frappe.get_all("Interview Detail", filters={"parent": interview}, fields=["interviewer"]) - return [d.interviewer for d in data] diff --git a/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py b/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py deleted file mode 100644 index 63d4775195..0000000000 --- a/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import add_days, flt, getdate - -from erpnext.hr.doctype.interview.test_interview import ( - create_interview_and_dependencies, - create_skill_set, -) -from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant - - -class TestInterviewFeedback(unittest.TestCase): - def test_validation_for_skill_set(self): - frappe.set_user("Administrator") - job_applicant = create_job_applicant() - interview = create_interview_and_dependencies( - job_applicant.name, scheduled_on=add_days(getdate(), -1) - ) - skill_ratings = get_skills_rating(interview.interview_round) - - interviewer = interview.interview_details[0].interviewer - create_skill_set(["Leadership"]) - - interview_feedback = create_interview_feedback(interview.name, interviewer, skill_ratings) - interview_feedback.append("skill_assessment", {"skill": "Leadership", "rating": 0.8}) - frappe.set_user(interviewer) - - self.assertRaises(frappe.ValidationError, interview_feedback.save) - - frappe.set_user("Administrator") - - def test_average_ratings_on_feedback_submission_and_cancellation(self): - job_applicant = create_job_applicant() - interview = create_interview_and_dependencies( - job_applicant.name, scheduled_on=add_days(getdate(), -1) - ) - skill_ratings = get_skills_rating(interview.interview_round) - - # For First Interviewer Feedback - interviewer = interview.interview_details[0].interviewer - frappe.set_user(interviewer) - - # calculating Average - feedback_1 = create_interview_feedback(interview.name, interviewer, skill_ratings) - - total_rating = 0 - for d in feedback_1.skill_assessment: - if d.rating: - total_rating += d.rating - - avg_rating = flt( - total_rating / len(feedback_1.skill_assessment) if len(feedback_1.skill_assessment) else 0 - ) - - self.assertEqual(flt(avg_rating, 2), flt(feedback_1.average_rating, 2)) - - avg_on_interview_detail = frappe.db.get_value( - "Interview Detail", - { - "parent": feedback_1.interview, - "interviewer": feedback_1.interviewer, - "interview_feedback": feedback_1.name, - }, - "average_rating", - ) - - # 1. average should be reflected in Interview Detail. - self.assertEqual(flt(avg_on_interview_detail, 2), flt(feedback_1.average_rating, 2)) - - """For Second Interviewer Feedback""" - interviewer = interview.interview_details[1].interviewer - frappe.set_user(interviewer) - - feedback_2 = create_interview_feedback(interview.name, interviewer, skill_ratings) - interview.reload() - - feedback_2.cancel() - interview.reload() - - frappe.set_user("Administrator") - - def tearDown(self): - frappe.db.rollback() - - -def create_interview_feedback(interview, interviewer, skills_ratings): - interview_feedback = frappe.new_doc("Interview Feedback") - interview_feedback.interview = interview - interview_feedback.interviewer = interviewer - interview_feedback.result = "Cleared" - - for rating in skills_ratings: - interview_feedback.append("skill_assessment", rating) - - interview_feedback.save() - interview_feedback.submit() - - return interview_feedback - - -def get_skills_rating(interview_round): - import random - - skills = frappe.get_all( - "Expected Skill Set", filters={"parent": interview_round}, fields=["skill"] - ) - for d in skills: - d["rating"] = random.random() - return skills diff --git a/erpnext/hr/doctype/interview_round/__init__.py b/erpnext/hr/doctype/interview_round/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/interview_round/interview_round.js b/erpnext/hr/doctype/interview_round/interview_round.js deleted file mode 100644 index 6a608b03d2..0000000000 --- a/erpnext/hr/doctype/interview_round/interview_round.js +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on("Interview Round", { - refresh: function(frm) { - if (!frm.doc.__islocal) { - frm.add_custom_button(__("Create Interview"), function() { - frm.events.create_interview(frm); - }); - } - }, - create_interview: function(frm) { - frappe.call({ - method: "erpnext.hr.doctype.interview_round.interview_round.create_interview", - args: { - doc: frm.doc - }, - callback: function (r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }); - } -}); diff --git a/erpnext/hr/doctype/interview_round/interview_round.json b/erpnext/hr/doctype/interview_round/interview_round.json deleted file mode 100644 index 9c95185e9c..0000000000 --- a/erpnext/hr/doctype/interview_round/interview_round.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "actions": [], - "allow_rename": 1, - "autoname": "field:round_name", - "creation": "2021-04-12 12:57:19.902866", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "round_name", - "interview_type", - "interviewers", - "column_break_3", - "designation", - "expected_average_rating", - "expected_skills_section", - "expected_skill_set" - ], - "fields": [ - { - "fieldname": "round_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Round Name", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "designation", - "fieldtype": "Link", - "label": "Designation", - "options": "Designation" - }, - { - "fieldname": "expected_skills_section", - "fieldtype": "Section Break", - "label": "Expected Skillset" - }, - { - "fieldname": "expected_skill_set", - "fieldtype": "Table", - "options": "Expected Skill Set", - "reqd": 1 - }, - { - "fieldname": "expected_average_rating", - "fieldtype": "Rating", - "label": "Expected Average Rating", - "reqd": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "interview_type", - "fieldtype": "Link", - "label": "Interview Type", - "options": "Interview Type", - "reqd": 1 - }, - { - "fieldname": "interviewers", - "fieldtype": "Table MultiSelect", - "label": "Interviewers", - "options": "Interviewer" - } - ], - "index_web_pages_for_search": 1, - "links": [], - "modified": "2021-09-30 13:01:25.666660", - "modified_by": "Administrator", - "module": "HR", - "name": "Interview Round", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Interviewer", - "select": 1, - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/interview_round/interview_round.py b/erpnext/hr/doctype/interview_round/interview_round.py deleted file mode 100644 index 83dbf0ea98..0000000000 --- a/erpnext/hr/doctype/interview_round/interview_round.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import json - -import frappe -from frappe.model.document import Document - - -class InterviewRound(Document): - pass - - -@frappe.whitelist() -def create_interview(doc): - if isinstance(doc, str): - doc = json.loads(doc) - doc = frappe.get_doc(doc) - - interview = frappe.new_doc("Interview") - interview.interview_round = doc.name - interview.designation = doc.designation - - if doc.interviewers: - interview.interview_details = [] - for data in doc.interviewers: - interview.append("interview_details", {"interviewer": data.user}) - return interview diff --git a/erpnext/hr/doctype/interview_round/test_interview_round.py b/erpnext/hr/doctype/interview_round/test_interview_round.py deleted file mode 100644 index 9568165374..0000000000 --- a/erpnext/hr/doctype/interview_round/test_interview_round.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -# import frappe - - -class TestInterviewRound(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/interview_type/__init__.py b/erpnext/hr/doctype/interview_type/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/interview_type/interview_type.js b/erpnext/hr/doctype/interview_type/interview_type.js deleted file mode 100644 index af77b527d4..0000000000 --- a/erpnext/hr/doctype/interview_type/interview_type.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Interview Type', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/hr/doctype/interview_type/interview_type.json b/erpnext/hr/doctype/interview_type/interview_type.json deleted file mode 100644 index 14636a18cb..0000000000 --- a/erpnext/hr/doctype/interview_type/interview_type.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "actions": [], - "allow_rename": 1, - "autoname": "Prompt", - "creation": "2021-04-12 14:44:40.664034", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "description" - ], - "fields": [ - { - "fieldname": "description", - "fieldtype": "Text", - "in_list_view": 1, - "label": "Description" - } - ], - "index_web_pages_for_search": 1, - "links": [ - { - "link_doctype": "Interview Round", - "link_fieldname": "interview_type" - } - ], - "modified": "2021-09-30 13:00:16.471518", - "modified_by": "Administrator", - "module": "HR", - "name": "Interview 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 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/interview_type/interview_type.py b/erpnext/hr/doctype/interview_type/interview_type.py deleted file mode 100644 index f5ebda427b..0000000000 --- a/erpnext/hr/doctype/interview_type/interview_type.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 InterviewType(Document): - pass diff --git a/erpnext/hr/doctype/interview_type/test_interview_type.py b/erpnext/hr/doctype/interview_type/test_interview_type.py deleted file mode 100644 index 96fdfcad68..0000000000 --- a/erpnext/hr/doctype/interview_type/test_interview_type.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -import unittest - - -class TestInterviewType(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/interviewer/__init__.py b/erpnext/hr/doctype/interviewer/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/interviewer/interviewer.json b/erpnext/hr/doctype/interviewer/interviewer.json deleted file mode 100644 index a37b8b0e4e..0000000000 --- a/erpnext/hr/doctype/interviewer/interviewer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "actions": [], - "creation": "2021-04-12 17:38:19.354734", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "user" - ], - "fields": [ - { - "fieldname": "user", - "fieldtype": "Link", - "in_list_view": 1, - "label": "User", - "options": "User" - } - ], - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2021-04-13 13:41:35.817568", - "modified_by": "Administrator", - "module": "HR", - "name": "Interviewer", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/interviewer/interviewer.py b/erpnext/hr/doctype/interviewer/interviewer.py deleted file mode 100644 index 2dc4a14325..0000000000 --- a/erpnext/hr/doctype/interviewer/interviewer.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 Interviewer(Document): - pass diff --git a/erpnext/hr/doctype/job_applicant/README.md b/erpnext/hr/doctype/job_applicant/README.md deleted file mode 100644 index 8eb2edbda0..0000000000 --- a/erpnext/hr/doctype/job_applicant/README.md +++ /dev/null @@ -1 +0,0 @@ -Applicant for Job. \ No newline at end of file diff --git a/erpnext/hr/doctype/job_applicant/__init__.py b/erpnext/hr/doctype/job_applicant/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.js b/erpnext/hr/doctype/job_applicant/job_applicant.js deleted file mode 100644 index c1e8257168..0000000000 --- a/erpnext/hr/doctype/job_applicant/job_applicant.js +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -// For license information, please see license.txt - -// for communication -cur_frm.email_field = "email_id"; - -frappe.ui.form.on("Job Applicant", { - refresh: function(frm) { - frm.set_query("job_title", function() { - return { - filters: { - 'status': 'Open' - } - }; - }); - frm.events.create_custom_buttons(frm); - frm.events.make_dashboard(frm); - }, - - create_custom_buttons: function(frm) { - if (!frm.doc.__islocal && frm.doc.status !== "Rejected" && frm.doc.status !== "Accepted") { - frm.add_custom_button(__("Interview"), function() { - frm.events.create_dialog(frm); - }, __("Create")); - } - - if (!frm.doc.__islocal) { - if (frm.doc.__onload && frm.doc.__onload.job_offer) { - $('[data-doctype="Employee Onboarding"]').find("button").show(); - $('[data-doctype="Job Offer"]').find("button").hide(); - frm.add_custom_button(__("Job Offer"), function() { - frappe.set_route("Form", "Job Offer", frm.doc.__onload.job_offer); - }, __("View")); - } else { - $('[data-doctype="Employee Onboarding"]').find("button").hide(); - $('[data-doctype="Job Offer"]').find("button").show(); - frm.add_custom_button(__("Job Offer"), function() { - frappe.route_options = { - "job_applicant": frm.doc.name, - "applicant_name": frm.doc.applicant_name, - "designation": frm.doc.job_opening || frm.doc.designation, - }; - frappe.new_doc("Job Offer"); - }, __("Create")); - } - } - }, - - make_dashboard: function(frm) { - frappe.call({ - method: "erpnext.hr.doctype.job_applicant.job_applicant.get_interview_details", - args: { - job_applicant: frm.doc.name - }, - callback: function(r) { - if (r.message) { - $("div").remove(".form-dashboard-section.custom"); - frm.dashboard.add_section( - frappe.render_template("job_applicant_dashboard", { - data: r.message.interviews, - number_of_stars: r.message.stars - }), - __("Interview Summary") - ); - } - } - }); - }, - - create_dialog: function(frm) { - let d = new frappe.ui.Dialog({ - title: 'Enter Interview Round', - fields: [ - { - label: 'Interview Round', - fieldname: 'interview_round', - fieldtype: 'Link', - options: 'Interview Round' - }, - ], - primary_action_label: 'Create Interview', - primary_action(values) { - frm.events.create_interview(frm, values); - d.hide(); - } - }); - d.show(); - }, - - create_interview: function (frm, values) { - frappe.call({ - method: "erpnext.hr.doctype.job_applicant.job_applicant.create_interview", - args: { - doc: frm.doc, - interview_round: values.interview_round - }, - callback: function (r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }); - } -}); diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.json b/erpnext/hr/doctype/job_applicant/job_applicant.json deleted file mode 100644 index 66b609cf99..0000000000 --- a/erpnext/hr/doctype/job_applicant/job_applicant.json +++ /dev/null @@ -1,221 +0,0 @@ -{ - "actions": [], - "allow_rename": 1, - "autoname": "HR-APP-.YYYY.-.#####", - "creation": "2013-01-29 19:25:37", - "description": "Applicant for a Job", - "doctype": "DocType", - "document_type": "Document", - "email_append_to": 1, - "engine": "InnoDB", - "field_order": [ - "details_section", - "applicant_name", - "email_id", - "phone_number", - "country", - "column_break_3", - "job_title", - "designation", - "status", - "source_and_rating_section", - "source", - "source_name", - "employee_referral", - "column_break_13", - "applicant_rating", - "section_break_6", - "notes", - "cover_letter", - "resume_attachment", - "resume_link", - "section_break_16", - "currency", - "column_break_18", - "lower_range", - "upper_range" - ], - "fields": [ - { - "bold": 1, - "fieldname": "applicant_name", - "fieldtype": "Data", - "in_global_search": 1, - "label": "Applicant Name", - "reqd": 1 - }, - { - "bold": 1, - "fieldname": "email_id", - "fieldtype": "Data", - "label": "Email Address", - "options": "Email", - "reqd": 1 - }, - { - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Status", - "options": "Open\nReplied\nRejected\nHold\nAccepted", - "reqd": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "width": "50%" - }, - { - "fieldname": "job_title", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Job Opening", - "options": "Job Opening" - }, - { - "fieldname": "source", - "fieldtype": "Link", - "label": "Source", - "options": "Job Applicant Source" - }, - { - "depends_on": "eval: doc.source==\"Employee Referral\" ", - "fieldname": "source_name", - "fieldtype": "Link", - "label": "Source Name", - "options": "Employee" - }, - { - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "label": "Resume" - }, - { - "fieldname": "cover_letter", - "fieldtype": "Text", - "label": "Cover Letter" - }, - { - "fieldname": "resume_attachment", - "fieldtype": "Attach", - "label": "Resume Attachment" - }, - { - "fieldname": "notes", - "fieldtype": "Data", - "label": "Notes", - "read_only": 1 - }, - { - "fieldname": "phone_number", - "fieldtype": "Data", - "label": "Phone Number", - "options": "Phone" - }, - { - "fieldname": "country", - "fieldtype": "Link", - "label": "Country", - "options": "Country" - }, - { - "fieldname": "resume_link", - "fieldtype": "Data", - "label": "Resume Link" - }, - { - "fieldname": "applicant_rating", - "fieldtype": "Rating", - "in_list_view": 1, - "label": "Applicant Rating" - }, - { - "fieldname": "section_break_16", - "fieldtype": "Section Break", - "label": "Salary Expectation" - }, - { - "fieldname": "lower_range", - "fieldtype": "Currency", - "label": "Lower Range", - "options": "currency", - "precision": "0" - }, - { - "fieldname": "upper_range", - "fieldtype": "Currency", - "label": "Upper Range", - "options": "currency", - "precision": "0" - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" - }, - { - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency" - }, - { - "fieldname": "employee_referral", - "fieldtype": "Link", - "label": "Employee Referral", - "options": "Employee Referral", - "read_only": 1 - }, - { - "fieldname": "details_section", - "fieldtype": "Section Break", - "label": "Details" - }, - { - "fieldname": "source_and_rating_section", - "fieldtype": "Section Break", - "label": "Source and Rating" - }, - { - "fieldname": "column_break_13", - "fieldtype": "Column Break" - }, - { - "fetch_from": "job_opening.designation", - "fieldname": "designation", - "fieldtype": "Link", - "label": "Designation", - "options": "Designation" - } - ], - "icon": "fa fa-user", - "idx": 1, - "index_web_pages_for_search": 1, - "links": [], - "modified": "2022-01-12 16:28:53.196881", - "modified_by": "Administrator", - "module": "HR", - "name": "Job Applicant", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - } - ], - "search_fields": "applicant_name, email_id, job_title, phone_number", - "sender_field": "email_id", - "sort_field": "modified", - "sort_order": "ASC", - "states": [], - "subject_field": "notes", - "title_field": "applicant_name" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.py b/erpnext/hr/doctype/job_applicant/job_applicant.py deleted file mode 100644 index 5b0a4cafbf..0000000000 --- a/erpnext/hr/doctype/job_applicant/job_applicant.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.model.naming import append_number_if_name_exists -from frappe.utils import validate_email_address - -from erpnext.hr.doctype.interview.interview import get_interviewers - - -class DuplicationError(frappe.ValidationError): - pass - - -class JobApplicant(Document): - def onload(self): - job_offer = frappe.get_all("Job Offer", filters={"job_applicant": self.name}) - if job_offer: - self.get("__onload").job_offer = job_offer[0].name - - def autoname(self): - self.name = self.email_id - - # applicant can apply more than once for a different job title or reapply - if frappe.db.exists("Job Applicant", self.name): - self.name = append_number_if_name_exists("Job Applicant", self.name) - - def validate(self): - if self.email_id: - validate_email_address(self.email_id, True) - - if self.employee_referral: - self.set_status_for_employee_referral() - - if not self.applicant_name and self.email_id: - guess = self.email_id.split("@")[0] - self.applicant_name = " ".join([p.capitalize() for p in guess.split(".")]) - - def set_status_for_employee_referral(self): - emp_ref = frappe.get_doc("Employee Referral", self.employee_referral) - if self.status in ["Open", "Replied", "Hold"]: - emp_ref.db_set("status", "In Process") - elif self.status in ["Accepted", "Rejected"]: - emp_ref.db_set("status", self.status) - - -@frappe.whitelist() -def create_interview(doc, interview_round): - import json - - if isinstance(doc, str): - doc = json.loads(doc) - doc = frappe.get_doc(doc) - - round_designation = frappe.db.get_value("Interview Round", interview_round, "designation") - - if round_designation and doc.designation and round_designation != doc.designation: - frappe.throw( - _("Interview Round {0} is only applicable for the Designation {1}").format( - interview_round, round_designation - ) - ) - - interview = frappe.new_doc("Interview") - interview.interview_round = interview_round - interview.job_applicant = doc.name - interview.designation = doc.designation - interview.resume_link = doc.resume_link - interview.job_opening = doc.job_title - interviewer_detail = get_interviewers(interview_round) - - for d in interviewer_detail: - interview.append("interview_details", {"interviewer": d.interviewer}) - return interview - - -@frappe.whitelist() -def get_interview_details(job_applicant): - interview_details = frappe.db.get_all( - "Interview", - filters={"job_applicant": job_applicant, "docstatus": ["!=", 2]}, - fields=["name", "interview_round", "expected_average_rating", "average_rating", "status"], - ) - interview_detail_map = {} - meta = frappe.get_meta("Interview") - number_of_stars = meta.get_options("expected_average_rating") or 5 - - for detail in interview_details: - detail.expected_average_rating = ( - detail.expected_average_rating * number_of_stars if detail.expected_average_rating else 0 - ) - detail.average_rating = detail.average_rating * number_of_stars if detail.average_rating else 0 - - interview_detail_map[detail.name] = detail - - return {"interviews": interview_detail_map, "stars": number_of_stars} diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html deleted file mode 100644 index 734b2fe5e8..0000000000 --- a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html +++ /dev/null @@ -1,53 +0,0 @@ - -{% if not jQuery.isEmptyObject(data) %} - - - - - - - - - - - - - {% for(const [key, value] of Object.entries(data)) { %} - - - - - {% let right_class = ''; %} - {% let left_class = ''; %} - - {% $.each([value["expected_average_rating"], value["average_rating"]], (_, val) => { %} - - {% }); %} - - {% } %} - -
{{ __("Interview") }}{{ __("Interview Round") }}{{ __("Status") }}{{ __("Expected Rating") }}{{ __("Rating") }}
{%= key %} {%= value["interview_round"] %} {%= value["status"] %} -
- {% for (let i = 1; i <= number_of_stars; i++) { %} - {% if (i <= val) { %} - {% right_class = 'star-click'; %} - {% } else { %} - {% right_class = ''; %} - {% } %} - - {% if ((i <= val) || ((i - 0.5) == val)) { %} - {% left_class = 'star-click'; %} - {% } else { %} - {% left_class = ''; %} - {% } %} - - - - - - {% } %} -
-
-{% else %} -

No Interview has been scheduled.

-{% endif %} diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py deleted file mode 100644 index 14b944ac61..0000000000 --- a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py +++ /dev/null @@ -1,9 +0,0 @@ -def get_data(): - return { - "fieldname": "job_applicant", - "transactions": [ - {"items": ["Employee", "Employee Onboarding"]}, - {"items": ["Job Offer", "Appointment Letter"]}, - {"items": ["Interview"]}, - ], - } diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_list.js b/erpnext/hr/doctype/job_applicant/job_applicant_list.js deleted file mode 100644 index 2ad0d591d8..0000000000 --- a/erpnext/hr/doctype/job_applicant/job_applicant_list.js +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -// MIT License. See license.txt - -frappe.listview_settings['Job Applicant'] = { - add_fields: ["status"], - get_indicator: function (doc) { - if (doc.status == "Accepted") { - return [__(doc.status), "green", "status,=," + doc.status]; - } else if (["Open", "Replied"].includes(doc.status)) { - return [__(doc.status), "orange", "status,=," + doc.status]; - } else if (["Hold", "Rejected"].includes(doc.status)) { - return [__(doc.status), "red", "status,=," + doc.status]; - } - } -}; diff --git a/erpnext/hr/doctype/job_applicant/test_job_applicant.py b/erpnext/hr/doctype/job_applicant/test_job_applicant.py deleted file mode 100644 index 99d1161978..0000000000 --- a/erpnext/hr/doctype/job_applicant/test_job_applicant.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors -# See license.txt - -import unittest - -import frappe - -from erpnext.hr.doctype.designation.test_designation import create_designation - - -class TestJobApplicant(unittest.TestCase): - def test_job_applicant_naming(self): - applicant = frappe.get_doc( - { - "doctype": "Job Applicant", - "status": "Open", - "applicant_name": "_Test Applicant", - "email_id": "job_applicant_naming@example.com", - } - ).insert() - self.assertEqual(applicant.name, "job_applicant_naming@example.com") - - applicant = frappe.get_doc( - { - "doctype": "Job Applicant", - "status": "Open", - "applicant_name": "_Test Applicant", - "email_id": "job_applicant_naming@example.com", - } - ).insert() - self.assertEqual(applicant.name, "job_applicant_naming@example.com-1") - - def tearDown(self): - frappe.db.rollback() - - -def create_job_applicant(**args): - args = frappe._dict(args) - - filters = { - "applicant_name": args.applicant_name or "_Test Applicant", - "email_id": args.email_id or "test_applicant@example.com", - } - - if frappe.db.exists("Job Applicant", filters): - return frappe.get_doc("Job Applicant", filters) - - job_applicant = frappe.get_doc( - { - "doctype": "Job Applicant", - "status": args.status or "Open", - "designation": create_designation().name, - } - ) - - job_applicant.update(filters) - job_applicant.save() - - return job_applicant diff --git a/erpnext/hr/doctype/job_applicant_source/__init__.py b/erpnext/hr/doctype/job_applicant_source/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/job_applicant_source/job_applicant_source.js b/erpnext/hr/doctype/job_applicant_source/job_applicant_source.js deleted file mode 100644 index 8725f2312b..0000000000 --- a/erpnext/hr/doctype/job_applicant_source/job_applicant_source.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Job Applicant Source', { - refresh: function() { - - } -}); diff --git a/erpnext/hr/doctype/job_applicant_source/job_applicant_source.json b/erpnext/hr/doctype/job_applicant_source/job_applicant_source.json deleted file mode 100644 index c3bb1eb9f7..0000000000 --- a/erpnext/hr/doctype/job_applicant_source/job_applicant_source.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:source_name", - "beta": 0, - "creation": "2018-06-16 12:28:26.432651", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "source_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Source Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "details", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-06-16 14:53:52.918474", - "modified_by": "Administrator", - "module": "HR", - "name": "Job Applicant Source", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/job_applicant_source/job_applicant_source.py b/erpnext/hr/doctype/job_applicant_source/job_applicant_source.py deleted file mode 100644 index 1f208c14c6..0000000000 --- a/erpnext/hr/doctype/job_applicant_source/job_applicant_source.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class JobApplicantSource(Document): - pass diff --git a/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.py b/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.py deleted file mode 100644 index cee5daf783..0000000000 --- a/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestJobApplicantSource(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/job_offer/__init__.py b/erpnext/hr/doctype/job_offer/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/job_offer/job_offer.js b/erpnext/hr/doctype/job_offer/job_offer.js deleted file mode 100755 index 558f4954d3..0000000000 --- a/erpnext/hr/doctype/job_offer/job_offer.js +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.provide("erpnext.job_offer"); - -frappe.ui.form.on("Job Offer", { - onload: function (frm) { - frm.set_query("select_terms", function() { - return { filters: { hr: 1 } }; - }); - }, - - setup: function (frm) { - frm.email_field = "applicant_email"; - }, - - select_terms: function (frm) { - erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function (r) { - if (!r.exc) { - frm.set_value("terms", r.message); - } - }); - }, - - refresh: function (frm) { - if ((!frm.doc.__islocal) && (frm.doc.status == 'Accepted') - && (frm.doc.docstatus === 1) && (!frm.doc.__onload || !frm.doc.__onload.employee)) { - frm.add_custom_button(__('Create Employee'), - function () { - erpnext.job_offer.make_employee(frm); - } - ); - } - - if(frm.doc.__onload && frm.doc.__onload.employee) { - frm.add_custom_button(__('Show Employee'), - function () { - frappe.set_route("Form", "Employee", frm.doc.__onload.employee); - } - ); - } - } - -}); - -erpnext.job_offer.make_employee = function (frm) { - frappe.model.open_mapped_doc({ - method: "erpnext.hr.doctype.job_offer.job_offer.make_employee", - frm: frm - }); -}; diff --git a/erpnext/hr/doctype/job_offer/job_offer.json b/erpnext/hr/doctype/job_offer/job_offer.json deleted file mode 100644 index c0b7f69e1b..0000000000 --- a/erpnext/hr/doctype/job_offer/job_offer.json +++ /dev/null @@ -1,190 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "HR-OFF-.YYYY.-.#####", - "creation": "2015-03-04 14:20:17.662207", - "doctype": "DocType", - "document_type": "Document", - "engine": "InnoDB", - "field_order": [ - "job_applicant", - "applicant_name", - "applicant_email", - "column_break_3", - "status", - "offer_date", - "designation", - "company", - "section_break_4", - "offer_terms", - "section_break_14", - "select_terms", - "terms", - "printing_details", - "letter_head", - "column_break_16", - "select_print_heading", - "amended_from" - ], - "fields": [ - { - "fieldname": "job_applicant", - "fieldtype": "Link", - "label": "Job Applicant", - "options": "Job Applicant", - "print_hide": 1, - "reqd": 1 - }, - { - "fetch_from": "job_applicant.applicant_name", - "fieldname": "applicant_name", - "fieldtype": "Data", - "in_global_search": 1, - "in_list_view": 1, - "label": "Applicant Name", - "read_only": 1, - "reqd": 1 - }, - { - "fetch_from": "job_applicant.email_id", - "fieldname": "applicant_email", - "fieldtype": "Data", - "in_global_search": 1, - "label": "Applicant Email Address", - "options": "Email", - "read_only": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "allow_on_submit": 1, - "fieldname": "status", - "fieldtype": "Select", - "in_standard_filter": 1, - "label": "Status", - "no_copy": 1, - "options": "Awaiting Response\nAccepted\nRejected", - "print_hide": 1 - }, - { - "fieldname": "offer_date", - "fieldtype": "Date", - "label": "Offer Date", - "reqd": 1 - }, - { - "fieldname": "designation", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Designation", - "options": "Designation", - "reqd": 1 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Company", - "options": "Company", - "print_hide": 1, - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "fieldname": "section_break_4", - "fieldtype": "Section Break" - }, - { - "fieldname": "offer_terms", - "fieldtype": "Table", - "label": "Job Offer Terms", - "options": "Job Offer Term" - }, - { - "fieldname": "section_break_14", - "fieldtype": "Section Break" - }, - { - "fieldname": "select_terms", - "fieldtype": "Link", - "label": "Select Terms and Conditions", - "options": "Terms and Conditions", - "print_hide": 1 - }, - { - "fieldname": "terms", - "fieldtype": "Text Editor", - "label": "Terms and Conditions" - }, - { - "collapsible": 1, - "fieldname": "printing_details", - "fieldtype": "Section Break", - "label": "Printing Details" - }, - { - "allow_on_submit": 1, - "fetch_from": "company.default_letter_head", - "fieldname": "letter_head", - "fieldtype": "Link", - "label": "Letter Head", - "options": "Letter Head", - "print_hide": 1 - }, - { - "fieldname": "column_break_16", - "fieldtype": "Column Break", - "print_hide": 1, - "width": "50%" - }, - { - "allow_on_submit": 1, - "fieldname": "select_print_heading", - "fieldtype": "Link", - "label": "Print Heading", - "options": "Print Heading", - "print_hide": 1, - "report_hide": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Job Offer", - "print_hide": 1, - "read_only": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2020-06-25 00:56:24.756395", - "modified_by": "Administrator", - "module": "HR", - "name": "Job Offer", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "import": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "applicant_name" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py deleted file mode 100644 index b46930a117..0000000000 --- a/erpnext/hr/doctype/job_offer/job_offer.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.model.mapper import get_mapped_doc -from frappe.utils import cint -from frappe.utils.data import get_link_to_form - - -class JobOffer(Document): - def onload(self): - employee = frappe.db.get_value("Employee", {"job_applicant": self.job_applicant}, "name") or "" - self.set_onload("employee", employee) - - def validate(self): - self.validate_vacancies() - job_offer = frappe.db.exists( - "Job Offer", {"job_applicant": self.job_applicant, "docstatus": ["!=", 2]} - ) - if job_offer and job_offer != self.name: - frappe.throw( - _("Job Offer: {0} is already for Job Applicant: {1}").format( - frappe.bold(job_offer), frappe.bold(self.job_applicant) - ) - ) - - def validate_vacancies(self): - staffing_plan = get_staffing_plan_detail(self.designation, self.company, self.offer_date) - check_vacancies = frappe.get_single("HR Settings").check_vacancies - if staffing_plan and check_vacancies: - job_offers = self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date) - if not staffing_plan.get("vacancies") or cint(staffing_plan.vacancies) - len(job_offers) <= 0: - error_variable = "for " + frappe.bold(self.designation) - if staffing_plan.get("parent"): - error_variable = frappe.bold(get_link_to_form("Staffing Plan", staffing_plan.parent)) - - frappe.throw(_("There are no vacancies under staffing plan {0}").format(error_variable)) - - def on_change(self): - update_job_applicant(self.status, self.job_applicant) - - def get_job_offer(self, from_date, to_date): - """Returns job offer created during a time period""" - return frappe.get_all( - "Job Offer", - filters={ - "offer_date": ["between", (from_date, to_date)], - "designation": self.designation, - "company": self.company, - "docstatus": 1, - }, - fields=["name"], - ) - - -def update_job_applicant(status, job_applicant): - if status in ("Accepted", "Rejected"): - frappe.set_value("Job Applicant", job_applicant, "status", status) - - -def get_staffing_plan_detail(designation, company, offer_date): - detail = frappe.db.sql( - """ - SELECT DISTINCT spd.parent, - sp.from_date as from_date, - sp.to_date as to_date, - sp.name, - sum(spd.vacancies) as vacancies, - spd.designation - FROM `tabStaffing Plan Detail` spd, `tabStaffing Plan` sp - WHERE - sp.docstatus=1 - AND spd.designation=%s - AND sp.company=%s - AND spd.parent = sp.name - AND %s between sp.from_date and sp.to_date - """, - (designation, company, offer_date), - as_dict=1, - ) - - return frappe._dict(detail[0]) if (detail and detail[0].parent) else None - - -@frappe.whitelist() -def make_employee(source_name, target_doc=None): - def set_missing_values(source, target): - target.personal_email, target.first_name = frappe.db.get_value( - "Job Applicant", source.job_applicant, ["email_id", "applicant_name"] - ) - - doc = get_mapped_doc( - "Job Offer", - source_name, - { - "Job Offer": { - "doctype": "Employee", - "field_map": {"applicant_name": "employee_name", "offer_date": "scheduled_confirmation_date"}, - } - }, - target_doc, - set_missing_values, - ) - return doc diff --git a/erpnext/hr/doctype/job_offer/job_offer_list.js b/erpnext/hr/doctype/job_offer/job_offer_list.js deleted file mode 100644 index 4fa5be7cc8..0000000000 --- a/erpnext/hr/doctype/job_offer/job_offer_list.js +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -// MIT License. See license.txt - -frappe.listview_settings['Job Offer'] = { - add_fields: ["company", "designation", "job_applicant", "status"], - get_indicator: function (doc) { - if (doc.status == "Accepted") { - return [__(doc.status), "green", "status,=," + doc.status]; - } else if (doc.status == "Awaiting Response") { - return [__(doc.status), "orange", "status,=," + doc.status]; - } else if (doc.status == "Rejected") { - return [__(doc.status), "red", "status,=," + doc.status]; - } - } -}; diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.py b/erpnext/hr/doctype/job_offer/test_job_offer.py deleted file mode 100644 index 7d8ef115d1..0000000000 --- a/erpnext/hr/doctype/job_offer/test_job_offer.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import add_days, nowdate - -from erpnext.hr.doctype.designation.test_designation import create_designation -from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant -from erpnext.hr.doctype.staffing_plan.test_staffing_plan import make_company - -# test_records = frappe.get_test_records('Job Offer') - - -class TestJobOffer(unittest.TestCase): - def test_job_offer_creation_against_vacancies(self): - frappe.db.set_value("HR Settings", None, "check_vacancies", 1) - job_applicant = create_job_applicant(email_id="test_job_offer@example.com") - job_offer = create_job_offer(job_applicant=job_applicant.name, designation="UX Designer") - - create_staffing_plan( - name="Test No Vacancies", - staffing_details=[ - {"designation": "UX Designer", "vacancies": 0, "estimated_cost_per_position": 5000} - ], - ) - self.assertRaises(frappe.ValidationError, job_offer.submit) - - # test creation of job offer when vacancies are not present - frappe.db.set_value("HR Settings", None, "check_vacancies", 0) - job_offer.submit() - self.assertTrue(frappe.db.exists("Job Offer", job_offer.name)) - - def test_job_applicant_update(self): - frappe.db.set_value("HR Settings", None, "check_vacancies", 0) - create_staffing_plan() - job_applicant = create_job_applicant(email_id="test_job_applicants@example.com") - job_offer = create_job_offer(job_applicant=job_applicant.name) - job_offer.submit() - job_applicant.reload() - self.assertEqual(job_applicant.status, "Accepted") - - # status update after rejection - job_offer.status = "Rejected" - job_offer.submit() - job_applicant.reload() - self.assertEquals(job_applicant.status, "Rejected") - frappe.db.set_value("HR Settings", None, "check_vacancies", 1) - - def tearDown(self): - frappe.db.sql("DELETE FROM `tabJob Offer` WHERE 1") - - -def create_job_offer(**args): - args = frappe._dict(args) - if not args.job_applicant: - job_applicant = create_job_applicant() - - if not frappe.db.exists("Designation", args.designation): - designation = create_designation(designation_name=args.designation) - - job_offer = frappe.get_doc( - { - "doctype": "Job Offer", - "job_applicant": args.job_applicant or job_applicant.name, - "offer_date": args.offer_date or nowdate(), - "designation": args.designation or "Researcher", - "status": args.status or "Accepted", - } - ) - return job_offer - - -def create_staffing_plan(**args): - args = frappe._dict(args) - make_company() - frappe.db.set_value("Company", "_Test Company", "is_group", 1) - if frappe.db.exists("Staffing Plan", args.name or "Test"): - return - staffing_plan = frappe.get_doc( - { - "doctype": "Staffing Plan", - "name": args.name or "Test", - "from_date": args.from_date or nowdate(), - "to_date": args.to_date or add_days(nowdate(), 10), - "staffing_details": args.staffing_details - or [{"designation": "Researcher", "vacancies": 1, "estimated_cost_per_position": 50000}], - } - ) - staffing_plan.insert() - staffing_plan.submit() - return staffing_plan diff --git a/erpnext/hr/doctype/job_offer_term/__init__.py b/erpnext/hr/doctype/job_offer_term/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/job_offer_term/job_offer_term.json b/erpnext/hr/doctype/job_offer_term/job_offer_term.json deleted file mode 100644 index dcd723e033..0000000000 --- a/erpnext/hr/doctype/job_offer_term/job_offer_term.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2015-03-05 12:53:45.342292", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "offer_term", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Offer Term", - "length": 0, - "no_copy": 0, - "options": "Offer Term", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "value", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Value / Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-02-15 03:30:56.020668", - "modified_by": "Administrator", - "module": "HR", - "name": "Job Offer Term", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/job_offer_term/job_offer_term.py b/erpnext/hr/doctype/job_offer_term/job_offer_term.py deleted file mode 100644 index d2eae467e4..0000000000 --- a/erpnext/hr/doctype/job_offer_term/job_offer_term.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class JobOfferTerm(Document): - pass diff --git a/erpnext/hr/doctype/job_opening/README.md b/erpnext/hr/doctype/job_opening/README.md deleted file mode 100644 index 5a19f33c8e..0000000000 --- a/erpnext/hr/doctype/job_opening/README.md +++ /dev/null @@ -1 +0,0 @@ -Open position for Job. \ No newline at end of file diff --git a/erpnext/hr/doctype/job_opening/__init__.py b/erpnext/hr/doctype/job_opening/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/job_opening/job_opening.js b/erpnext/hr/doctype/job_opening/job_opening.js deleted file mode 100644 index 04aab7e0da..0000000000 --- a/erpnext/hr/doctype/job_opening/job_opening.js +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Job Opening', { - onload: function(frm) { - frm.set_query("department", function() { - return { - "filters": { - "company": frm.doc.company, - } - }; - }); - }, - designation: function(frm) { - if(frm.doc.designation && frm.doc.company){ - frappe.call({ - "method": "erpnext.hr.doctype.staffing_plan.staffing_plan.get_active_staffing_plan_details", - args: { - company: frm.doc.company, - designation: frm.doc.designation, - date: frappe.datetime.now_date() // ToDo - Date in Job Opening? - }, - callback: function (data) { - if(data.message){ - frm.set_value('staffing_plan', data.message[0].name); - frm.set_value('planned_vacancies', data.message[0].vacancies); - } else { - frm.set_value('staffing_plan', ""); - frm.set_value('planned_vacancies', 0); - frappe.show_alert({ - indicator: 'orange', - message: __('No Staffing Plans found for this Designation') - }); - } - } - }); - } - else{ - frm.set_value('staffing_plan', ""); - frm.set_value('planned_vacancies', 0); - } - }, - company: function(frm) { - frm.set_value('designation', ""); - } -}); diff --git a/erpnext/hr/doctype/job_opening/job_opening.json b/erpnext/hr/doctype/job_opening/job_opening.json deleted file mode 100644 index b8f6df6f7a..0000000000 --- a/erpnext/hr/doctype/job_opening/job_opening.json +++ /dev/null @@ -1,188 +0,0 @@ -{ - "actions": [], - "autoname": "field:route", - "creation": "2013-01-15 16:13:36", - "description": "Description of a Job Opening", - "doctype": "DocType", - "document_type": "Document", - "engine": "InnoDB", - "field_order": [ - "job_title", - "company", - "status", - "column_break_5", - "designation", - "department", - "staffing_plan", - "planned_vacancies", - "section_break_6", - "publish", - "route", - "column_break_12", - "job_application_route", - "section_break_14", - "description", - "section_break_16", - "currency", - "lower_range", - "upper_range", - "column_break_20", - "publish_salary_range" - ], - "fields": [ - { - "fieldname": "job_title", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Job Title", - "reqd": 1 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Status", - "options": "Open\nClosed" - }, - { - "fieldname": "column_break_5", - "fieldtype": "Column Break" - }, - { - "fieldname": "designation", - "fieldtype": "Link", - "label": "Designation", - "options": "Designation", - "reqd": 1 - }, - { - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department" - }, - { - "fieldname": "staffing_plan", - "fieldtype": "Link", - "label": "Staffing Plan", - "options": "Staffing Plan", - "read_only": 1 - }, - { - "depends_on": "staffing_plan", - "fieldname": "planned_vacancies", - "fieldtype": "Int", - "label": "Planned number of Positions", - "read_only": 1 - }, - { - "fieldname": "section_break_6", - "fieldtype": "Section Break" - }, - { - "default": "0", - "fieldname": "publish", - "fieldtype": "Check", - "label": "Publish on website" - }, - { - "depends_on": "publish", - "fieldname": "route", - "fieldtype": "Data", - "label": "Route", - "unique": 1 - }, - { - "description": "Job profile, qualifications required etc.", - "fieldname": "description", - "fieldtype": "Text Editor", - "in_list_view": 1, - "label": "Description" - }, - { - "fieldname": "column_break_12", - "fieldtype": "Column Break" - }, - { - "fieldname": "section_break_14", - "fieldtype": "Section Break" - }, - { - "collapsible": 1, - "fieldname": "section_break_16", - "fieldtype": "Section Break" - }, - { - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency" - }, - { - "fieldname": "lower_range", - "fieldtype": "Currency", - "label": "Lower Range", - "options": "currency", - "precision": "0" - }, - { - "fieldname": "upper_range", - "fieldtype": "Currency", - "label": "Upper Range", - "options": "currency", - "precision": "0" - }, - { - "fieldname": "column_break_20", - "fieldtype": "Column Break" - }, - { - "depends_on": "publish", - "description": "Route to the custom Job Application Webform", - "fieldname": "job_application_route", - "fieldtype": "Data", - "label": "Job Application Route" - }, - { - "default": "0", - "fieldname": "publish_salary_range", - "fieldtype": "Check", - "label": "Publish Salary Range" - } - ], - "icon": "fa fa-bookmark", - "idx": 1, - "links": [], - "modified": "2020-09-18 11:23:29.488923", - "modified_by": "Administrator", - "module": "HR", - "name": "Job Opening", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - }, - { - "read": 1, - "role": "Guest" - } - ], - "sort_field": "modified", - "sort_order": "ASC" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/job_opening/job_opening.py b/erpnext/hr/doctype/job_opening/job_opening.py deleted file mode 100644 index ce7caa33c6..0000000000 --- a/erpnext/hr/doctype/job_opening/job_opening.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.utils import get_link_to_form -from frappe.website.website_generator import WebsiteGenerator - -from erpnext.hr.doctype.staffing_plan.staffing_plan import ( - get_active_staffing_plan_details, - get_designation_counts, -) - - -class JobOpening(WebsiteGenerator): - website = frappe._dict( - template="templates/generators/job_opening.html", - condition_field="publish", - page_title_field="job_title", - ) - - def validate(self): - if not self.route: - self.route = frappe.scrub(self.job_title).replace("_", "-") - self.validate_current_vacancies() - - def validate_current_vacancies(self): - if not self.staffing_plan: - staffing_plan = get_active_staffing_plan_details(self.company, self.designation) - if staffing_plan: - self.staffing_plan = staffing_plan[0].name - self.planned_vacancies = staffing_plan[0].vacancies - elif not self.planned_vacancies: - self.planned_vacancies = frappe.db.get_value( - "Staffing Plan Detail", - {"parent": self.staffing_plan, "designation": self.designation}, - "vacancies", - ) - - if self.staffing_plan and self.planned_vacancies: - staffing_plan_company = frappe.db.get_value("Staffing Plan", self.staffing_plan, "company") - - designation_counts = get_designation_counts(self.designation, self.company, self.name) - current_count = designation_counts["employee_count"] + designation_counts["job_openings"] - - number_of_positions = frappe.db.get_value( - "Staffing Plan Detail", - {"parent": self.staffing_plan, "designation": self.designation}, - "number_of_positions", - ) - - if number_of_positions <= current_count: - frappe.throw( - _( - "Job Openings for the designation {0} are already open or the hiring is complete as per the Staffing Plan {1}" - ).format( - frappe.bold(self.designation), get_link_to_form("Staffing Plan", self.staffing_plan) - ), - title=_("Vacancies fulfilled"), - ) - - def get_context(self, context): - context.parents = [{"route": "jobs", "title": _("All Jobs")}] - - -def get_list_context(context): - context.title = _("Jobs") - context.introduction = _("Current Job Openings") - context.get_list = get_job_openings - - -def get_job_openings( - doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by=None -): - fields = [ - "name", - "status", - "job_title", - "description", - "publish_salary_range", - "lower_range", - "upper_range", - "currency", - "job_application_route", - ] - - filters = filters or {} - filters.update({"status": "Open"}) - - if txt: - filters.update( - {"job_title": ["like", "%{0}%".format(txt)], "description": ["like", "%{0}%".format(txt)]} - ) - - return frappe.get_all( - doctype, filters, fields, start=limit_start, page_length=limit_page_length, order_by=order_by - ) diff --git a/erpnext/hr/doctype/job_opening/job_opening_dashboard.py b/erpnext/hr/doctype/job_opening/job_opening_dashboard.py deleted file mode 100644 index a30932870d..0000000000 --- a/erpnext/hr/doctype/job_opening/job_opening_dashboard.py +++ /dev/null @@ -1,5 +0,0 @@ -def get_data(): - return { - "fieldname": "job_title", - "transactions": [{"items": ["Job Applicant"]}], - } diff --git a/erpnext/hr/doctype/job_opening/templates/job_opening_row.html b/erpnext/hr/doctype/job_opening/templates/job_opening_row.html deleted file mode 100644 index 69bf49bef7..0000000000 --- a/erpnext/hr/doctype/job_opening/templates/job_opening_row.html +++ /dev/null @@ -1,18 +0,0 @@ -
-

{{ doc.job_title }}

-

{{ doc.description }}

- {%- if doc.publish_salary_range -%} -

{{_("Salary range per month")}}: {{ frappe.format_value(frappe.utils.flt(doc.lower_range), currency=doc.currency) }} - {{ frappe.format_value(frappe.utils.flt(doc.upper_range), currency=doc.currency) }}

- {% endif %} -
- {%- if doc.job_application_route -%} - - {{ _("Apply Now") }} - {% else %} - - {{ _("Apply Now") }} - {% endif %} -
-
diff --git a/erpnext/hr/doctype/job_opening/test_job_opening.py b/erpnext/hr/doctype/job_opening/test_job_opening.py deleted file mode 100644 index e991054f62..0000000000 --- a/erpnext/hr/doctype/job_opening/test_job_opening.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days, getdate - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.staffing_plan.test_staffing_plan import make_company - - -class TestJobOpening(FrappeTestCase): - def setUp(self): - frappe.db.delete("Staffing Plan") - frappe.db.delete("Staffing Plan Detail") - frappe.db.delete("Job Opening") - - make_company("_Test Opening Company", "_TOC") - frappe.db.delete("Employee", {"company": "_Test Opening Company"}) - - def test_vacancies_fulfilled(self): - make_employee( - "test_job_opening@example.com", company="_Test Opening Company", designation="Designer" - ) - - staffing_plan = frappe.get_doc( - { - "doctype": "Staffing Plan", - "company": "_Test Opening Company", - "name": "Test", - "from_date": getdate(), - "to_date": add_days(getdate(), 10), - } - ) - - staffing_plan.append( - "staffing_details", - {"designation": "Designer", "vacancies": 1, "estimated_cost_per_position": 50000}, - ) - staffing_plan.insert() - staffing_plan.submit() - - self.assertEqual(staffing_plan.staffing_details[0].number_of_positions, 2) - - # allows creating 1 job opening as per vacancy - opening_1 = get_job_opening() - opening_1.insert() - - # vacancies as per staffing plan already fulfilled via job opening and existing employee count - opening_2 = get_job_opening(job_title="Designer New") - self.assertRaises(frappe.ValidationError, opening_2.insert) - - # allows updating existing job opening - opening_1.status = "Closed" - opening_1.save() - - -def get_job_opening(**args): - args = frappe._dict(args) - - opening = frappe.db.exists("Job Opening", {"job_title": args.job_title or "Designer"}) - if opening: - return frappe.get_doc("Job Opening", opening) - - opening = frappe.get_doc( - { - "doctype": "Job Opening", - "job_title": "Designer", - "designation": "Designer", - "company": "_Test Opening Company", - "status": "Open", - } - ) - - opening.update(args) - - return opening diff --git a/erpnext/hr/doctype/leave_allocation/README.md b/erpnext/hr/doctype/leave_allocation/README.md deleted file mode 100644 index 870f9a501a..0000000000 --- a/erpnext/hr/doctype/leave_allocation/README.md +++ /dev/null @@ -1 +0,0 @@ -Leave Allocated to an Employee at the beginning of the period. \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_allocation/__init__.py b/erpnext/hr/doctype/leave_allocation/__init__.py deleted file mode 100755 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js deleted file mode 100755 index 9742387c16..0000000000 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -cur_frm.add_fetch('employee', 'employee_name', 'employee_name'); - -frappe.ui.form.on("Leave Allocation", { - onload: function(frm) { - // Ignore cancellation of doctype on cancel all. - frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"]; - - if (!frm.doc.from_date) frm.set_value("from_date", frappe.datetime.get_today()); - - frm.set_query("employee", function() { - return { - query: "erpnext.controllers.queries.employee_query" - }; - }); - frm.set_query("leave_type", function() { - return { - filters: { - is_lwp: 0 - } - }; - }); - }, - - refresh: function(frm) { - if (frm.doc.docstatus === 1 && frm.doc.expired) { - var valid_expiry = moment(frappe.datetime.get_today()).isBetween(frm.doc.from_date, frm.doc.to_date); - if (valid_expiry) { - // expire current allocation - frm.add_custom_button(__('Expire Allocation'), function() { - frm.trigger("expire_allocation"); - }); - } - } - }, - - expire_allocation: function(frm) { - frappe.call({ - method: 'erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.expire_allocation', - args: { - 'allocation': frm.doc, - 'expiry_date': frappe.datetime.get_today() - }, - freeze: true, - callback: function(r) { - if (!r.exc) { - frappe.msgprint(__("Allocation Expired!")); - } - frm.refresh(); - } - }); - }, - - employee: function(frm) { - frm.trigger("calculate_total_leaves_allocated"); - }, - - leave_type: function(frm) { - frm.trigger("leave_policy"); - frm.trigger("calculate_total_leaves_allocated"); - }, - - carry_forward: function(frm) { - frm.trigger("calculate_total_leaves_allocated"); - }, - - unused_leaves: function(frm) { - frm.set_value("total_leaves_allocated", - flt(frm.doc.unused_leaves) + flt(frm.doc.new_leaves_allocated)); - }, - - new_leaves_allocated: function(frm) { - frm.set_value("total_leaves_allocated", - flt(frm.doc.unused_leaves) + flt(frm.doc.new_leaves_allocated)); - }, - - leave_policy: function(frm) { - if (frm.doc.leave_policy && frm.doc.leave_type) { - frappe.db.get_value("Leave Policy Detail", { - 'parent': frm.doc.leave_policy, - 'leave_type': frm.doc.leave_type - }, 'annual_allocation', (r) => { - if (r && !r.exc) frm.set_value("new_leaves_allocated", flt(r.annual_allocation)); - }, "Leave Policy"); - } - }, - calculate_total_leaves_allocated: function(frm) { - if (cint(frm.doc.carry_forward) == 1 && frm.doc.leave_type && frm.doc.employee) { - return frappe.call({ - method: "set_total_leaves_allocated", - doc: frm.doc, - callback: function() { - frm.refresh_fields(); - } - }); - } else if (cint(frm.doc.carry_forward) == 0) { - frm.set_value("unused_leaves", 0); - frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated)); - } - } -}); - -frappe.tour["Leave Allocation"] = [ - { - fieldname: "employee", - title: "Employee", - description: __("Select the Employee for which you want to allocate leaves.") - }, - { - fieldname: "leave_type", - title: "Leave Type", - description: __("Select the Leave Type like Sick leave, Privilege Leave, Casual Leave, etc.") - }, - { - fieldname: "from_date", - title: "From Date", - description: __("Select the date from which this Leave Allocation will be valid.") - }, - { - fieldname: "to_date", - title: "To Date", - description: __("Select the date after which this Leave Allocation will expire.") - }, - { - fieldname: "new_leaves_allocated", - title: "New Leaves Allocated", - description: __("Enter the number of leaves you want to allocate for the period.") - } -]; diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json deleted file mode 100644 index 9d1db9b17f..0000000000 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ /dev/null @@ -1,286 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "naming_series:", - "creation": "2013-02-20 19:10:38", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "naming_series", - "employee", - "employee_name", - "department", - "company", - "column_break1", - "leave_type", - "from_date", - "to_date", - "section_break_6", - "new_leaves_allocated", - "carry_forward", - "unused_leaves", - "total_leaves_allocated", - "total_leaves_encashed", - "column_break_10", - "compensatory_request", - "leave_period", - "leave_policy", - "leave_policy_assignment", - "carry_forwarded_leaves_count", - "expired", - "amended_from", - "notes", - "description" - ], - "fields": [ - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "no_copy": 1, - "options": "HR-LAL-.YYYY.-", - "print_hide": 1, - "reqd": 1, - "set_only_once": 1 - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Employee", - "oldfieldname": "employee", - "oldfieldtype": "Link", - "options": "Employee", - "reqd": 1, - "search_index": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "in_global_search": 1, - "in_list_view": 1, - "label": "Employee Name", - "read_only": 1, - "search_index": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "width": "50%" - }, - { - "fieldname": "leave_type", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Leave Type", - "oldfieldname": "leave_type", - "oldfieldtype": "Link", - "options": "Leave Type", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "from_date", - "fieldtype": "Date", - "label": "From Date", - "reqd": 1 - }, - { - "fieldname": "to_date", - "fieldtype": "Date", - "label": "To Date", - "reqd": 1 - }, - { - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "label": "Allocation" - }, - { - "allow_on_submit": 1, - "bold": 1, - "fieldname": "new_leaves_allocated", - "fieldtype": "Float", - "label": "New Leaves Allocated" - }, - { - "default": "0", - "fieldname": "carry_forward", - "fieldtype": "Check", - "label": "Add unused leaves from previous allocations" - }, - { - "depends_on": "carry_forward", - "fieldname": "unused_leaves", - "fieldtype": "Float", - "label": "Unused leaves", - "read_only": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "total_leaves_allocated", - "fieldtype": "Float", - "label": "Total Leaves Allocated", - "read_only": 1, - "reqd": 1 - }, - { - "depends_on": "eval:doc.total_leaves_encashed>0", - "fieldname": "total_leaves_encashed", - "fieldtype": "Float", - "label": "Total Leaves Encashed", - "read_only": 1 - }, - { - "fieldname": "column_break_10", - "fieldtype": "Column Break" - }, - { - "fieldname": "compensatory_request", - "fieldtype": "Link", - "label": "Compensatory Leave Request", - "options": "Compensatory Leave Request", - "read_only": 1 - }, - { - "fieldname": "leave_period", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Leave Period", - "options": "Leave Period", - "read_only": 1 - }, - { - "fetch_from": "leave_policy_assignment.leave_policy", - "fieldname": "leave_policy", - "fieldtype": "Link", - "hidden": 1, - "in_standard_filter": 1, - "label": "Leave Policy", - "options": "Leave Policy", - "read_only": 1 - }, - { - "default": "0", - "fieldname": "expired", - "fieldtype": "Check", - "hidden": 1, - "in_standard_filter": 1, - "label": "Expired", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Amended From", - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Leave Allocation", - "print_hide": 1, - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "notes", - "fieldtype": "Section Break", - "label": "Notes" - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "label": "Description", - "oldfieldname": "reason", - "oldfieldtype": "Small Text", - "width": "300px" - }, - { - "depends_on": "carry_forwarded_leaves_count", - "fieldname": "carry_forwarded_leaves_count", - "fieldtype": "Float", - "label": "Carry Forwarded Leaves", - "read_only": 1 - }, - { - "fieldname": "leave_policy_assignment", - "fieldtype": "Link", - "label": "Leave Policy Assignment", - "options": "Leave Policy Assignment", - "read_only": 1 - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "read_only": 1, - "reqd": 1 - } - ], - "icon": "fa fa-ok", - "idx": 1, - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2022-04-07 09:50:33.145825", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Allocation", - "naming_rule": "By \"Naming Series\" field", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "import": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "employee,employee_name,leave_type,total_leaves_allocated", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "timeline_field": "employee", - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py deleted file mode 100755 index 8fae2a9a88..0000000000 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ /dev/null @@ -1,370 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import add_days, date_diff, flt, formatdate, getdate - -from erpnext.hr.doctype.leave_application.leave_application import get_approved_leaves_for_period -from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import ( - create_leave_ledger_entry, - expire_allocation, -) -from erpnext.hr.utils import get_leave_period, set_employee_name - - -class OverlapError(frappe.ValidationError): - pass - - -class BackDatedAllocationError(frappe.ValidationError): - pass - - -class OverAllocationError(frappe.ValidationError): - pass - - -class LessAllocationError(frappe.ValidationError): - pass - - -class ValueMultiplierError(frappe.ValidationError): - pass - - -class LeaveAllocation(Document): - def validate(self): - self.validate_period() - self.validate_allocation_overlap() - self.validate_lwp() - set_employee_name(self) - self.set_total_leaves_allocated() - self.validate_leave_days_and_dates() - - def validate_leave_days_and_dates(self): - # all validations that should run on save as well as on update after submit - self.validate_back_dated_allocation() - self.validate_total_leaves_allocated() - self.validate_leave_allocation_days() - - def validate_leave_allocation_days(self): - company = frappe.db.get_value("Employee", self.employee, "company") - leave_period = get_leave_period(self.from_date, self.to_date, company) - max_leaves_allowed = flt( - frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") - ) - if max_leaves_allowed > 0: - leave_allocated = 0 - if leave_period: - leave_allocated = get_leave_allocation_for_period( - self.employee, - self.leave_type, - leave_period[0].from_date, - leave_period[0].to_date, - exclude_allocation=self.name, - ) - leave_allocated += flt(self.new_leaves_allocated) - if leave_allocated > max_leaves_allowed: - frappe.throw( - _( - "Total allocated leaves are more than maximum allocation allowed for {0} leave type for employee {1} in the period" - ).format(self.leave_type, self.employee), - OverAllocationError, - ) - - def on_submit(self): - self.create_leave_ledger_entry() - - # expire all unused leaves in the ledger on creation of carry forward allocation - allocation = get_previous_allocation(self.from_date, self.leave_type, self.employee) - if self.carry_forward and allocation: - expire_allocation(allocation) - - def on_cancel(self): - self.create_leave_ledger_entry(submit=False) - if self.leave_policy_assignment: - self.update_leave_policy_assignments_when_no_allocations_left() - if self.carry_forward: - self.set_carry_forwarded_leaves_in_previous_allocation(on_cancel=True) - - def on_update_after_submit(self): - if self.has_value_changed("new_leaves_allocated"): - self.validate_against_leave_applications() - - # recalculate total leaves allocated - self.total_leaves_allocated = flt(self.unused_leaves) + flt(self.new_leaves_allocated) - # run required validations again since total leaves are being updated - self.validate_leave_days_and_dates() - - leaves_to_be_added = self.new_leaves_allocated - self.get_existing_leave_count() - args = { - "leaves": leaves_to_be_added, - "from_date": self.from_date, - "to_date": self.to_date, - "is_carry_forward": 0, - } - create_leave_ledger_entry(self, args, True) - self.db_update() - - def get_existing_leave_count(self): - ledger_entries = frappe.get_all( - "Leave Ledger Entry", - filters={ - "transaction_type": "Leave Allocation", - "transaction_name": self.name, - "employee": self.employee, - "company": self.company, - "leave_type": self.leave_type, - }, - pluck="leaves", - ) - total_existing_leaves = 0 - for entry in ledger_entries: - total_existing_leaves += entry - - return total_existing_leaves - - def validate_against_leave_applications(self): - leaves_taken = get_approved_leaves_for_period( - self.employee, self.leave_type, self.from_date, self.to_date - ) - if flt(leaves_taken) > flt(self.total_leaves_allocated): - if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"): - frappe.msgprint( - _( - "Note: Total allocated leaves {0} shouldn't be less than already approved leaves {1} for the period" - ).format(self.total_leaves_allocated, leaves_taken) - ) - else: - frappe.throw( - _( - "Total allocated leaves {0} cannot be less than already approved leaves {1} for the period" - ).format(self.total_leaves_allocated, leaves_taken), - LessAllocationError, - ) - - def update_leave_policy_assignments_when_no_allocations_left(self): - allocations = frappe.db.get_list( - "Leave Allocation", - filters={"docstatus": 1, "leave_policy_assignment": self.leave_policy_assignment}, - ) - if len(allocations) == 0: - frappe.db.set_value( - "Leave Policy Assignment", self.leave_policy_assignment, "leaves_allocated", 0 - ) - - def validate_period(self): - if date_diff(self.to_date, self.from_date) <= 0: - frappe.throw(_("To date cannot be before from date")) - - def validate_lwp(self): - if frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"): - frappe.throw( - _("Leave Type {0} cannot be allocated since it is leave without pay").format(self.leave_type) - ) - - def validate_allocation_overlap(self): - leave_allocation = frappe.db.sql( - """ - SELECT - name - FROM `tabLeave Allocation` - WHERE - employee=%s AND leave_type=%s - AND name <> %s AND docstatus=1 - AND to_date >= %s AND from_date <= %s""", - (self.employee, self.leave_type, self.name, self.from_date, self.to_date), - ) - - if leave_allocation: - frappe.msgprint( - _("{0} already allocated for Employee {1} for period {2} to {3}").format( - self.leave_type, self.employee, formatdate(self.from_date), formatdate(self.to_date) - ) - ) - - frappe.throw( - _("Reference") - + ': {0}'.format(leave_allocation[0][0]), - OverlapError, - ) - - def validate_back_dated_allocation(self): - future_allocation = frappe.db.sql( - """select name, from_date from `tabLeave Allocation` - where employee=%s and leave_type=%s and docstatus=1 and from_date > %s - and carry_forward=1""", - (self.employee, self.leave_type, self.to_date), - as_dict=1, - ) - - if future_allocation: - frappe.throw( - _( - "Leave cannot be allocated before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}" - ).format(formatdate(future_allocation[0].from_date), future_allocation[0].name), - BackDatedAllocationError, - ) - - @frappe.whitelist() - def set_total_leaves_allocated(self): - self.unused_leaves = get_carry_forwarded_leaves( - self.employee, self.leave_type, self.from_date, self.carry_forward - ) - - self.total_leaves_allocated = flt(self.unused_leaves) + flt(self.new_leaves_allocated) - - self.limit_carry_forward_based_on_max_allowed_leaves() - - if self.carry_forward: - self.set_carry_forwarded_leaves_in_previous_allocation() - - if ( - not self.total_leaves_allocated - and not frappe.db.get_value("Leave Type", self.leave_type, "is_earned_leave") - and not frappe.db.get_value("Leave Type", self.leave_type, "is_compensatory") - ): - frappe.throw( - _("Total leaves allocated is mandatory for Leave Type {0}").format(self.leave_type) - ) - - def limit_carry_forward_based_on_max_allowed_leaves(self): - max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") - if max_leaves_allowed and self.total_leaves_allocated > flt(max_leaves_allowed): - self.total_leaves_allocated = flt(max_leaves_allowed) - self.unused_leaves = max_leaves_allowed - flt(self.new_leaves_allocated) - - def set_carry_forwarded_leaves_in_previous_allocation(self, on_cancel=False): - """Set carry forwarded leaves in previous allocation""" - previous_allocation = get_previous_allocation(self.from_date, self.leave_type, self.employee) - if on_cancel: - self.unused_leaves = 0.0 - if previous_allocation: - frappe.db.set_value( - "Leave Allocation", - previous_allocation.name, - "carry_forwarded_leaves_count", - self.unused_leaves, - ) - - def validate_total_leaves_allocated(self): - # Adding a day to include To Date in the difference - date_difference = date_diff(self.to_date, self.from_date) + 1 - if date_difference < self.total_leaves_allocated: - if frappe.db.get_value("Leave Type", self.leave_type, "allow_over_allocation"): - frappe.msgprint( - _("Total Leaves Allocated are more than the number of days in the allocation period"), - indicator="orange", - alert=True, - ) - else: - frappe.throw( - _("Total Leaves Allocated are more than the number of days in the allocation period"), - exc=OverAllocationError, - title=_("Over Allocation"), - ) - - def create_leave_ledger_entry(self, submit=True): - if self.unused_leaves: - expiry_days = frappe.db.get_value( - "Leave Type", self.leave_type, "expire_carry_forwarded_leaves_after_days" - ) - end_date = add_days(self.from_date, expiry_days - 1) if expiry_days else self.to_date - args = dict( - leaves=self.unused_leaves, - from_date=self.from_date, - to_date=min(getdate(end_date), getdate(self.to_date)), - is_carry_forward=1, - ) - create_leave_ledger_entry(self, args, submit) - - args = dict( - leaves=self.new_leaves_allocated, - from_date=self.from_date, - to_date=self.to_date, - is_carry_forward=0, - ) - create_leave_ledger_entry(self, args, submit) - - -def get_previous_allocation(from_date, leave_type, employee): - """Returns document properties of previous allocation""" - return frappe.db.get_value( - "Leave Allocation", - filters={ - "to_date": ("<", from_date), - "leave_type": leave_type, - "employee": employee, - "docstatus": 1, - }, - order_by="to_date DESC", - fieldname=["name", "from_date", "to_date", "employee", "leave_type"], - as_dict=1, - ) - - -def get_leave_allocation_for_period( - employee, leave_type, from_date, to_date, exclude_allocation=None -): - from frappe.query_builder.functions import Sum - - Allocation = frappe.qb.DocType("Leave Allocation") - return ( - frappe.qb.from_(Allocation) - .select(Sum(Allocation.total_leaves_allocated).as_("total_allocated_leaves")) - .where( - (Allocation.employee == employee) - & (Allocation.leave_type == leave_type) - & (Allocation.docstatus == 1) - & (Allocation.name != exclude_allocation) - & ( - (Allocation.from_date.between(from_date, to_date)) - | (Allocation.to_date.between(from_date, to_date)) - | ((Allocation.from_date < from_date) & (Allocation.to_date > to_date)) - ) - ) - ).run()[0][0] or 0.0 - - -@frappe.whitelist() -def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None): - """Returns carry forwarded leaves for the given employee""" - unused_leaves = 0.0 - previous_allocation = get_previous_allocation(date, leave_type, employee) - if carry_forward and previous_allocation: - validate_carry_forward(leave_type) - unused_leaves = get_unused_leaves( - employee, leave_type, previous_allocation.from_date, previous_allocation.to_date - ) - if unused_leaves: - max_carry_forwarded_leaves = frappe.db.get_value( - "Leave Type", leave_type, "maximum_carry_forwarded_leaves" - ) - if max_carry_forwarded_leaves and unused_leaves > flt(max_carry_forwarded_leaves): - unused_leaves = flt(max_carry_forwarded_leaves) - - return unused_leaves - - -def get_unused_leaves(employee, leave_type, from_date, to_date): - """Returns unused leaves between the given period while skipping leave allocation expiry""" - leaves = frappe.get_all( - "Leave Ledger Entry", - filters={ - "employee": employee, - "leave_type": leave_type, - "from_date": (">=", from_date), - "to_date": ("<=", to_date), - }, - or_filters={"is_expired": 0, "is_carry_forward": 1}, - fields=["sum(leaves) as leaves"], - ) - return flt(leaves[0]["leaves"]) - - -def validate_carry_forward(leave_type): - if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"): - frappe.throw(_("Leave Type {0} cannot be carry-forwarded").format(leave_type)) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py b/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py deleted file mode 100644 index 96e81db617..0000000000 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py +++ /dev/null @@ -1,6 +0,0 @@ -def get_data(): - return { - "fieldname": "leave_allocation", - "transactions": [{"items": ["Compensatory Leave Request"]}, {"items": ["Leave Encashment"]}], - "reports": [{"items": ["Employee Leave Balance"]}], - } diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js b/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js deleted file mode 100644 index 3ab176f809..0000000000 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -// render -frappe.listview_settings['Leave Allocation'] = { - get_indicator: function(doc) { - if(doc.status==="Expired") { - return [__("Expired"), "gray", "expired, =, 1"]; - } - }, -}; diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py deleted file mode 100644 index b4a42d375c..0000000000 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ /dev/null @@ -1,433 +0,0 @@ -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days, add_months, getdate, nowdate - -import erpnext -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.leave_allocation.leave_allocation import ( - BackDatedAllocationError, - OverAllocationError, -) -from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation -from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type - - -class TestLeaveAllocation(FrappeTestCase): - def setUp(self): - frappe.db.delete("Leave Period") - frappe.db.delete("Leave Allocation") - frappe.db.delete("Leave Ledger Entry") - - emp_id = make_employee("test_emp_leave_allocation@salary.com", company="_Test Company") - self.employee = frappe.get_doc("Employee", emp_id) - - def test_overlapping_allocation(self): - leaves = [ - { - "doctype": "Leave Allocation", - "__islocal": 1, - "employee": self.employee.name, - "employee_name": self.employee.employee_name, - "leave_type": "_Test Leave Type", - "from_date": getdate("2015-10-01"), - "to_date": getdate("2015-10-31"), - "new_leaves_allocated": 5, - "docstatus": 1, - }, - { - "doctype": "Leave Allocation", - "__islocal": 1, - "employee": self.employee.name, - "employee_name": self.employee.employee_name, - "leave_type": "_Test Leave Type", - "from_date": getdate("2015-09-01"), - "to_date": getdate("2015-11-30"), - "new_leaves_allocated": 5, - }, - ] - - frappe.get_doc(leaves[0]).save() - self.assertRaises(frappe.ValidationError, frappe.get_doc(leaves[1]).save) - - def test_invalid_period(self): - doc = frappe.get_doc( - { - "doctype": "Leave Allocation", - "__islocal": 1, - "employee": self.employee.name, - "employee_name": self.employee.employee_name, - "leave_type": "_Test Leave Type", - "from_date": getdate("2015-09-30"), - "to_date": getdate("2015-09-1"), - "new_leaves_allocated": 5, - } - ) - - # invalid period - self.assertRaises(frappe.ValidationError, doc.save) - - def test_validation_for_over_allocation(self): - leave_type = create_leave_type(leave_type_name="Test Over Allocation", is_carry_forward=1) - leave_type.save() - - doc = frappe.get_doc( - { - "doctype": "Leave Allocation", - "__islocal": 1, - "employee": self.employee.name, - "employee_name": self.employee.employee_name, - "leave_type": leave_type.name, - "from_date": getdate("2015-09-1"), - "to_date": getdate("2015-09-30"), - "new_leaves_allocated": 35, - "carry_forward": 1, - } - ) - - # allocated leave more than period - self.assertRaises(OverAllocationError, doc.save) - - leave_type.allow_over_allocation = 1 - leave_type.save() - - # allows creating a leave allocation with more leave days than period days - doc = frappe.get_doc( - { - "doctype": "Leave Allocation", - "__islocal": 1, - "employee": self.employee.name, - "employee_name": self.employee.employee_name, - "leave_type": leave_type.name, - "from_date": getdate("2015-09-1"), - "to_date": getdate("2015-09-30"), - "new_leaves_allocated": 35, - "carry_forward": 1, - } - ).insert() - - def test_validation_for_over_allocation_post_submission(self): - allocation = frappe.get_doc( - { - "doctype": "Leave Allocation", - "__islocal": 1, - "employee": self.employee.name, - "employee_name": self.employee.employee_name, - "leave_type": "_Test Leave Type", - "from_date": getdate("2015-09-1"), - "to_date": getdate("2015-09-30"), - "new_leaves_allocated": 15, - } - ).submit() - allocation.reload() - # allocated leaves more than period after submission - allocation.new_leaves_allocated = 35 - self.assertRaises(OverAllocationError, allocation.save) - - def test_validation_for_over_allocation_based_on_leave_setup(self): - frappe.delete_doc_if_exists("Leave Period", "Test Allocation Period") - leave_period = frappe.get_doc( - dict( - name="Test Allocation Period", - doctype="Leave Period", - from_date=add_months(nowdate(), -6), - to_date=add_months(nowdate(), 6), - company="_Test Company", - is_active=1, - ) - ).insert() - - leave_type = create_leave_type(leave_type_name="_Test Allocation Validation", is_carry_forward=1) - leave_type.max_leaves_allowed = 25 - leave_type.save() - - # 15 leaves allocated in this period - allocation = create_leave_allocation( - leave_type=leave_type.name, - employee=self.employee.name, - employee_name=self.employee.employee_name, - from_date=leave_period.from_date, - to_date=nowdate(), - ) - allocation.submit() - - # trying to allocate additional 15 leaves - allocation = create_leave_allocation( - leave_type=leave_type.name, - employee=self.employee.name, - employee_name=self.employee.employee_name, - from_date=add_days(nowdate(), 1), - to_date=leave_period.to_date, - ) - self.assertRaises(OverAllocationError, allocation.save) - - def test_validation_for_over_allocation_based_on_leave_setup_post_submission(self): - frappe.delete_doc_if_exists("Leave Period", "Test Allocation Period") - leave_period = frappe.get_doc( - dict( - name="Test Allocation Period", - doctype="Leave Period", - from_date=add_months(nowdate(), -6), - to_date=add_months(nowdate(), 6), - company="_Test Company", - is_active=1, - ) - ).insert() - - leave_type = create_leave_type(leave_type_name="_Test Allocation Validation", is_carry_forward=1) - leave_type.max_leaves_allowed = 30 - leave_type.save() - - # 15 leaves allocated - allocation = create_leave_allocation( - leave_type=leave_type.name, - employee=self.employee.name, - employee_name=self.employee.employee_name, - from_date=leave_period.from_date, - to_date=nowdate(), - ) - allocation.submit() - allocation.reload() - - # allocate additional 15 leaves - allocation = create_leave_allocation( - leave_type=leave_type.name, - employee=self.employee.name, - employee_name=self.employee.employee_name, - from_date=add_days(nowdate(), 1), - to_date=leave_period.to_date, - ) - allocation.submit() - allocation.reload() - - # trying to allocate 25 leaves in 2nd alloc within leave period - # total leaves = 40 which is more than `max_leaves_allowed` setting i.e. 30 - allocation.new_leaves_allocated = 25 - self.assertRaises(OverAllocationError, allocation.save) - - def test_validate_back_dated_allocation_update(self): - leave_type = create_leave_type(leave_type_name="_Test_CF_leave", is_carry_forward=1) - leave_type.save() - - # initial leave allocation = 15 - leave_allocation = create_leave_allocation( - employee=self.employee.name, - employee_name=self.employee.employee_name, - leave_type="_Test_CF_leave", - from_date=add_months(nowdate(), -12), - to_date=add_months(nowdate(), -1), - carry_forward=0, - ) - leave_allocation.submit() - - # new_leaves = 15, carry_forwarded = 10 - leave_allocation_1 = create_leave_allocation( - employee=self.employee.name, - employee_name=self.employee.employee_name, - leave_type="_Test_CF_leave", - carry_forward=1, - ) - leave_allocation_1.submit() - - # try updating initial leave allocation - leave_allocation.reload() - leave_allocation.new_leaves_allocated = 20 - self.assertRaises(BackDatedAllocationError, leave_allocation.save) - - def test_carry_forward_calculation(self): - leave_type = create_leave_type(leave_type_name="_Test_CF_leave", is_carry_forward=1) - leave_type.maximum_carry_forwarded_leaves = 10 - leave_type.max_leaves_allowed = 30 - leave_type.save() - - # initial leave allocation = 15 - leave_allocation = create_leave_allocation( - employee=self.employee.name, - employee_name=self.employee.employee_name, - leave_type="_Test_CF_leave", - from_date=add_months(nowdate(), -12), - to_date=add_months(nowdate(), -1), - carry_forward=0, - ) - leave_allocation.submit() - - # carry forwarded leaves considering maximum_carry_forwarded_leaves - # new_leaves = 15, carry_forwarded = 10 - leave_allocation_1 = create_leave_allocation( - employee=self.employee.name, - employee_name=self.employee.employee_name, - leave_type="_Test_CF_leave", - carry_forward=1, - ) - leave_allocation_1.submit() - leave_allocation_1.reload() - - self.assertEqual(leave_allocation_1.unused_leaves, 10) - self.assertEqual(leave_allocation_1.total_leaves_allocated, 25) - - leave_allocation_1.cancel() - - # carry forwarded leaves considering max_leave_allowed - # max_leave_allowed = 30, new_leaves = 25, carry_forwarded = 5 - leave_allocation_2 = create_leave_allocation( - employee=self.employee.name, - employee_name=self.employee.employee_name, - leave_type="_Test_CF_leave", - carry_forward=1, - new_leaves_allocated=25, - ) - leave_allocation_2.submit() - - self.assertEqual(leave_allocation_2.unused_leaves, 5) - - def test_carry_forward_leaves_expiry(self): - leave_type = create_leave_type( - leave_type_name="_Test_CF_leave_expiry", - is_carry_forward=1, - expire_carry_forwarded_leaves_after_days=90, - ) - leave_type.save() - - # initial leave allocation - leave_allocation = create_leave_allocation( - employee=self.employee.name, - employee_name=self.employee.employee_name, - leave_type="_Test_CF_leave_expiry", - from_date=add_months(nowdate(), -24), - to_date=add_months(nowdate(), -12), - carry_forward=0, - ) - leave_allocation.submit() - - leave_allocation = create_leave_allocation( - employee=self.employee.name, - employee_name=self.employee.employee_name, - leave_type="_Test_CF_leave_expiry", - from_date=add_days(nowdate(), -90), - to_date=add_days(nowdate(), 100), - carry_forward=1, - ) - leave_allocation.submit() - - # expires all the carry forwarded leaves after 90 days - process_expired_allocation() - - # leave allocation with carry forward of only new leaves allocated - leave_allocation_1 = create_leave_allocation( - employee=self.employee.name, - employee_name=self.employee.employee_name, - leave_type="_Test_CF_leave_expiry", - carry_forward=1, - from_date=add_months(nowdate(), 6), - to_date=add_months(nowdate(), 12), - ) - leave_allocation_1.submit() - - self.assertEqual(leave_allocation_1.unused_leaves, leave_allocation.new_leaves_allocated) - - def test_creation_of_leave_ledger_entry_on_submit(self): - leave_allocation = create_leave_allocation( - employee=self.employee.name, employee_name=self.employee.employee_name - ) - leave_allocation.submit() - - leave_ledger_entry = frappe.get_all( - "Leave Ledger Entry", fields="*", filters=dict(transaction_name=leave_allocation.name) - ) - - self.assertEqual(len(leave_ledger_entry), 1) - self.assertEqual(leave_ledger_entry[0].employee, leave_allocation.employee) - self.assertEqual(leave_ledger_entry[0].leave_type, leave_allocation.leave_type) - self.assertEqual(leave_ledger_entry[0].leaves, leave_allocation.new_leaves_allocated) - - # check if leave ledger entry is deleted on cancellation - leave_allocation.cancel() - self.assertFalse( - frappe.db.exists("Leave Ledger Entry", {"transaction_name": leave_allocation.name}) - ) - - def test_leave_addition_after_submit(self): - leave_allocation = create_leave_allocation( - employee=self.employee.name, employee_name=self.employee.employee_name - ) - leave_allocation.submit() - leave_allocation.reload() - self.assertTrue(leave_allocation.total_leaves_allocated, 15) - - leave_allocation.new_leaves_allocated = 40 - leave_allocation.submit() - leave_allocation.reload() - self.assertTrue(leave_allocation.total_leaves_allocated, 40) - - def test_leave_subtraction_after_submit(self): - leave_allocation = create_leave_allocation( - employee=self.employee.name, employee_name=self.employee.employee_name - ) - leave_allocation.submit() - leave_allocation.reload() - self.assertTrue(leave_allocation.total_leaves_allocated, 15) - - leave_allocation.new_leaves_allocated = 10 - leave_allocation.submit() - leave_allocation.reload() - self.assertTrue(leave_allocation.total_leaves_allocated, 10) - - def test_validation_against_leave_application_after_submit(self): - from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list - - make_holiday_list() - frappe.db.set_value( - "Company", self.employee.company, "default_holiday_list", "Salary Slip Test Holiday List" - ) - - leave_allocation = create_leave_allocation( - employee=self.employee.name, employee_name=self.employee.employee_name - ) - leave_allocation.submit() - self.assertTrue(leave_allocation.total_leaves_allocated, 15) - - leave_application = frappe.get_doc( - { - "doctype": "Leave Application", - "employee": self.employee.name, - "leave_type": "_Test Leave Type", - "from_date": add_months(nowdate(), 2), - "to_date": add_months(add_days(nowdate(), 10), 2), - "company": self.employee.company, - "docstatus": 1, - "status": "Approved", - "leave_approver": "test@example.com", - } - ) - leave_application.submit() - leave_application.reload() - - # allocate less leaves than the ones which are already approved - leave_allocation.new_leaves_allocated = leave_application.total_leave_days - 1 - leave_allocation.total_leaves_allocated = leave_application.total_leave_days - 1 - self.assertRaises(frappe.ValidationError, leave_allocation.submit) - - -def create_leave_allocation(**args): - args = frappe._dict(args) - - emp_id = make_employee("test_emp_leave_allocation@salary.com") - employee = frappe.get_doc("Employee", emp_id) - - return frappe.get_doc( - { - "doctype": "Leave Allocation", - "__islocal": 1, - "employee": args.employee or employee.name, - "employee_name": args.employee_name or employee.employee_name, - "leave_type": args.leave_type or "_Test Leave Type", - "from_date": args.from_date or nowdate(), - "new_leaves_allocated": args.new_leaves_allocated or 15, - "carry_forward": args.carry_forward or 0, - "to_date": args.to_date or add_months(nowdate(), 12), - } - ) - - -test_dependencies = ["Employee", "Leave Type"] diff --git a/erpnext/hr/doctype/leave_allocation/test_records.json b/erpnext/hr/doctype/leave_allocation/test_records.json deleted file mode 100644 index 23acbb026e..0000000000 --- a/erpnext/hr/doctype/leave_allocation/test_records.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "docstatus": 1, - "doctype": "Leave Allocation", - "employee": "_T-Employee-00001", - "from_date": "2013-01-01", - "to_date": "2013-12-31", - "leave_type": "_Test Leave Type", - "new_leaves_allocated": 15 - }, - { - "docstatus": 1, - "doctype": "Leave Allocation", - "employee": "_T-Employee-00002", - "from_date": "2013-01-01", - "to_date": "2013-12-31", - "leave_type": "_Test Leave Type", - "new_leaves_allocated": 15 - } -] \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_application/README.md b/erpnext/hr/doctype/leave_application/README.md deleted file mode 100644 index e78e37a1c2..0000000000 --- a/erpnext/hr/doctype/leave_application/README.md +++ /dev/null @@ -1 +0,0 @@ -Application for Leave by an Employee. \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_application/__init__.py b/erpnext/hr/doctype/leave_application/__init__.py deleted file mode 100755 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js deleted file mode 100755 index ee00e6719c..0000000000 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -cur_frm.add_fetch('employee', 'employee_name', 'employee_name'); -cur_frm.add_fetch('employee', 'company', 'company'); - -frappe.ui.form.on("Leave Application", { - setup: function(frm) { - frm.set_query("leave_approver", function() { - return { - query: "erpnext.hr.doctype.department_approver.department_approver.get_approvers", - filters: { - employee: frm.doc.employee, - doctype: frm.doc.doctype - } - }; - }); - - frm.set_query("employee", erpnext.queries.employee); - }, - onload: function(frm) { - // Ignore cancellation of doctype on cancel all. - frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"]; - - if (!frm.doc.posting_date) { - frm.set_value("posting_date", frappe.datetime.get_today()); - } - if (frm.doc.docstatus == 0) { - return frappe.call({ - method: "erpnext.hr.doctype.leave_application.leave_application.get_mandatory_approval", - args: { - doctype: frm.doc.doctype, - }, - callback: function(r) { - if (!r.exc && r.message) { - frm.toggle_reqd("leave_approver", true); - } - } - }); - } - }, - - validate: function(frm) { - if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1) { - frm.doc.half_day_date = frm.doc.from_date; - } else if (frm.doc.half_day == 0) { - frm.doc.half_day_date = ""; - } - frm.toggle_reqd("half_day_date", frm.doc.half_day == 1); - }, - - make_dashboard: function(frm) { - var leave_details; - let lwps; - if (frm.doc.employee && frm.doc.from_date) { - frappe.call({ - method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_details", - async: false, - args: { - employee: frm.doc.employee, - date: frm.doc.from_date || frm.doc.posting_date - }, - callback: function(r) { - if (!r.exc && r.message['leave_allocation']) { - leave_details = r.message['leave_allocation']; - } - if (!r.exc && r.message['leave_approver']) { - frm.set_value('leave_approver', r.message['leave_approver']); - } - lwps = r.message["lwps"]; - } - }); - $("div").remove(".form-dashboard-section.custom"); - frm.dashboard.add_section( - frappe.render_template('leave_application_dashboard', { - data: leave_details - }), - __("Allocated Leaves") - ); - frm.dashboard.show(); - let allowed_leave_types = Object.keys(leave_details); - - // lwps should be allowed, lwps don't have any allocation - allowed_leave_types = allowed_leave_types.concat(lwps); - - frm.set_query('leave_type', function() { - return { - filters: [ - ['leave_type_name', 'in', allowed_leave_types] - ] - }; - }); - } - }, - - refresh: function(frm) { - if (frm.is_new()) { - frm.trigger("calculate_total_days"); - } - cur_frm.set_intro(""); - if (frm.doc.__islocal && !in_list(frappe.user_roles, "Employee")) { - frm.set_intro(__("Fill the form and save it")); - } - - if (!frm.doc.employee && frappe.defaults.get_user_permissions()) { - const perm = frappe.defaults.get_user_permissions(); - if (perm && perm['Employee']) { - frm.set_value('employee', perm['Employee'].map(perm_doc => perm_doc.doc)[0]); - } - } - }, - - employee: function(frm) { - frm.trigger("make_dashboard"); - frm.trigger("get_leave_balance"); - frm.trigger("set_leave_approver"); - }, - - leave_approver: function(frm) { - if (frm.doc.leave_approver) { - frm.set_value("leave_approver_name", frappe.user.full_name(frm.doc.leave_approver)); - } - }, - - leave_type: function(frm) { - frm.trigger("get_leave_balance"); - }, - - half_day: function(frm) { - if (frm.doc.half_day) { - if (frm.doc.from_date == frm.doc.to_date) { - frm.set_value("half_day_date", frm.doc.from_date); - } else { - frm.trigger("half_day_datepicker"); - } - } else { - frm.set_value("half_day_date", ""); - } - frm.trigger("calculate_total_days"); - }, - - from_date: function(frm) { - frm.trigger("make_dashboard"); - frm.trigger("half_day_datepicker"); - frm.trigger("calculate_total_days"); - }, - - to_date: function(frm) { - frm.trigger("make_dashboard"); - frm.trigger("half_day_datepicker"); - frm.trigger("calculate_total_days"); - }, - - half_day_date(frm) { - frm.trigger("calculate_total_days"); - }, - - half_day_datepicker: function(frm) { - frm.set_value('half_day_date', ''); - var half_day_datepicker = frm.fields_dict.half_day_date.datepicker; - half_day_datepicker.update({ - minDate: frappe.datetime.str_to_obj(frm.doc.from_date), - maxDate: frappe.datetime.str_to_obj(frm.doc.to_date) - }); - }, - - get_leave_balance: function(frm) { - if (frm.doc.docstatus === 0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date && frm.doc.to_date) { - return frappe.call({ - method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_balance_on", - args: { - employee: frm.doc.employee, - date: frm.doc.from_date, - to_date: frm.doc.to_date, - leave_type: frm.doc.leave_type, - consider_all_leaves_in_the_allocation_period: 1 - }, - callback: function (r) { - if (!r.exc && r.message) { - frm.set_value('leave_balance', r.message); - } else { - frm.set_value('leave_balance', "0"); - } - } - }); - } - }, - - calculate_total_days: function(frm) { - if (frm.doc.from_date && frm.doc.to_date && frm.doc.employee && frm.doc.leave_type) { - - var from_date = Date.parse(frm.doc.from_date); - var to_date = Date.parse(frm.doc.to_date); - - if (to_date < from_date) { - frappe.msgprint(__("To Date cannot be less than From Date")); - frm.set_value('to_date', ''); - return; - } - // server call is done to include holidays in leave days calculations - return frappe.call({ - method: 'erpnext.hr.doctype.leave_application.leave_application.get_number_of_leave_days', - args: { - "employee": frm.doc.employee, - "leave_type": frm.doc.leave_type, - "from_date": frm.doc.from_date, - "to_date": frm.doc.to_date, - "half_day": frm.doc.half_day, - "half_day_date": frm.doc.half_day_date, - }, - callback: function(r) { - if (r && r.message) { - frm.set_value('total_leave_days', r.message); - frm.trigger("get_leave_balance"); - } - } - }); - } - }, - - set_leave_approver: function(frm) { - if (frm.doc.employee) { - // server call is done to include holidays in leave days calculations - return frappe.call({ - method: 'erpnext.hr.doctype.leave_application.leave_application.get_leave_approver', - args: { - "employee": frm.doc.employee, - }, - callback: function(r) { - if (r && r.message) { - frm.set_value('leave_approver', r.message); - } - } - }); - } - } -}); - -frappe.tour["Leave Application"] = [ - { - fieldname: "employee", - title: "Employee", - description: __("Select the Employee.") - }, - { - fieldname: "leave_type", - title: "Leave Type", - description: __("Select type of leave the employee wants to apply for, like Sick Leave, Privilege Leave, Casual Leave, etc.") - }, - { - fieldname: "from_date", - title: "From Date", - description: __("Select the start date for your Leave Application.") - }, - { - fieldname: "to_date", - title: "To Date", - description: __("Select the end date for your Leave Application.") - }, - { - fieldname: "half_day", - title: "Half Day", - description: __("To apply for a Half Day check 'Half Day' and select the Half Day Date") - }, - { - fieldname: "leave_approver", - title: "Leave Approver", - description: __("Select your Leave Approver i.e. the person who approves or rejects your leaves.") - } -]; diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json deleted file mode 100644 index 7f50ace766..0000000000 --- a/erpnext/hr/doctype/leave_application/leave_application.json +++ /dev/null @@ -1,338 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "naming_series:", - "creation": "2013-02-20 11:18:11", - "description": "Apply / Approve Leaves", - "doctype": "DocType", - "document_type": "Document", - "engine": "InnoDB", - "field_order": [ - "naming_series", - "employee", - "employee_name", - "column_break_4", - "leave_type", - "department", - "leave_balance", - "section_break_5", - "from_date", - "to_date", - "half_day", - "half_day_date", - "total_leave_days", - "column_break1", - "description", - "section_break_7", - "leave_approver", - "leave_approver_name", - "column_break_18", - "status", - "salary_slip", - "sb10", - "posting_date", - "follow_via_email", - "color", - "column_break_17", - "company", - "letter_head", - "amended_from" - ], - "fields": [ - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "no_copy": 1, - "options": "HR-LAP-.YYYY.-", - "print_hide": 1, - "reqd": 1, - "set_only_once": 1 - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "in_global_search": 1, - "in_standard_filter": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "employee_name", - "fieldtype": "Data", - "in_global_search": 1, - "label": "Employee Name", - "read_only": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "fieldname": "leave_type", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "in_standard_filter": 1, - "label": "Leave Type", - "options": "Leave Type", - "reqd": 1, - "search_index": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "leave_balance", - "fieldtype": "Float", - "label": "Leave Balance Before Application", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "section_break_5", - "fieldtype": "Section Break" - }, - { - "fieldname": "from_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "From Date", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "to_date", - "fieldtype": "Date", - "label": "To Date", - "reqd": 1, - "search_index": 1 - }, - { - "default": "0", - "fieldname": "half_day", - "fieldtype": "Check", - "label": "Half Day" - }, - { - "depends_on": "eval:doc.half_day && (doc.from_date != doc.to_date)", - "fieldname": "half_day_date", - "fieldtype": "Date", - "label": "Half Day Date" - }, - { - "fieldname": "total_leave_days", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Total Leave Days", - "no_copy": 1, - "precision": "1", - "read_only": 1 - }, - { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "print_width": "50%", - "width": "50%" - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "label": "Reason" - }, - { - "fieldname": "section_break_7", - "fieldtype": "Section Break" - }, - { - "fieldname": "leave_approver", - "fieldtype": "Link", - "label": "Leave Approver", - "options": "User" - }, - { - "fieldname": "leave_approver_name", - "fieldtype": "Data", - "label": "Leave Approver Name", - "read_only": 1 - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" - }, - { - "default": "Open", - "fieldname": "status", - "fieldtype": "Select", - "in_standard_filter": 1, - "label": "Status", - "no_copy": 1, - "options": "Open\nApproved\nRejected\nCancelled", - "permlevel": 1, - "reqd": 1 - }, - { - "fieldname": "sb10", - "fieldtype": "Section Break" - }, - { - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "label": "Posting Date", - "no_copy": 1, - "reqd": 1 - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "read_only": 1, - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "allow_on_submit": 1, - "default": "1", - "fieldname": "follow_via_email", - "fieldtype": "Check", - "label": "Follow via Email", - "print_hide": 1 - }, - { - "fieldname": "column_break_17", - "fieldtype": "Column Break" - }, - { - "fieldname": "salary_slip", - "fieldtype": "Link", - "label": "Salary Slip", - "options": "Salary Slip", - "print_hide": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "letter_head", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Letter Head", - "options": "Letter Head", - "print_hide": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "color", - "fieldtype": "Color", - "label": "Color", - "print_hide": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Amended From", - "no_copy": 1, - "options": "Leave Application", - "print_hide": 1, - "read_only": 1 - } - ], - "icon": "fa fa-calendar", - "idx": 1, - "is_submittable": 1, - "links": [], - "max_attachments": 3, - "modified": "2020-05-18 13:00:41.577327", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Application", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 1, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "permlevel": 1, - "read": 1, - "role": "All" - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 1, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Leave Approver", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "permlevel": 1, - "read": 1, - "report": 1, - "role": "HR User", - "write": 1 - }, - { - "permlevel": 1, - "read": 1, - "report": 1, - "role": "Leave Approver", - "write": 1 - } - ], - "search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days", - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "employee", - "title_field": "employee_name" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py deleted file mode 100755 index 43c2bb37b2..0000000000 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ /dev/null @@ -1,1246 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from typing import Dict, Optional, Tuple - -import frappe -from frappe import _ -from frappe.query_builder.functions import Max, Min, Sum -from frappe.utils import ( - add_days, - cint, - cstr, - date_diff, - flt, - formatdate, - get_fullname, - get_link_to_form, - getdate, - nowdate, -) - -from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange -from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee -from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates -from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry -from erpnext.hr.utils import ( - get_holiday_dates_for_employee, - get_leave_period, - set_employee_name, - share_doc_with_approver, - validate_active_employee, -) - - -class LeaveDayBlockedError(frappe.ValidationError): - pass - - -class OverlapError(frappe.ValidationError): - pass - - -class AttendanceAlreadyMarkedError(frappe.ValidationError): - pass - - -class NotAnOptionalHoliday(frappe.ValidationError): - pass - - -class InsufficientLeaveBalanceError(frappe.ValidationError): - pass - - -class LeaveAcrossAllocationsError(frappe.ValidationError): - pass - - -from frappe.model.document import Document - - -class LeaveApplication(Document): - def get_feed(self): - return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type) - - def validate(self): - validate_active_employee(self.employee) - set_employee_name(self) - self.validate_dates() - self.validate_balance_leaves() - self.validate_leave_overlap() - self.validate_max_days() - self.show_block_day_warning() - self.validate_block_days() - self.validate_salary_processed_days() - self.validate_attendance() - self.set_half_day_date() - if frappe.db.get_value("Leave Type", self.leave_type, "is_optional_leave"): - self.validate_optional_leave() - self.validate_applicable_after() - - def on_update(self): - if self.status == "Open" and self.docstatus < 1: - # notify leave approver about creation - if frappe.db.get_single_value("HR Settings", "send_leave_notification"): - self.notify_leave_approver() - - share_doc_with_approver(self, self.leave_approver) - - def on_submit(self): - if self.status in ["Open", "Cancelled"]: - frappe.throw( - _("Only Leave Applications with status 'Approved' and 'Rejected' can be submitted") - ) - - self.validate_back_dated_application() - self.update_attendance() - - # notify leave applier about approval - if frappe.db.get_single_value("HR Settings", "send_leave_notification"): - self.notify_employee() - - self.create_leave_ledger_entry() - self.reload() - - def before_cancel(self): - self.status = "Cancelled" - - def on_cancel(self): - self.create_leave_ledger_entry(submit=False) - # notify leave applier about cancellation - if frappe.db.get_single_value("HR Settings", "send_leave_notification"): - self.notify_employee() - self.cancel_attendance() - - def validate_applicable_after(self): - if self.leave_type: - leave_type = frappe.get_doc("Leave Type", self.leave_type) - if leave_type.applicable_after > 0: - date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") - leave_days = get_approved_leaves_for_period( - self.employee, False, date_of_joining, self.from_date - ) - number_of_days = date_diff(getdate(self.from_date), date_of_joining) - if number_of_days >= 0: - holidays = 0 - if not frappe.db.get_value("Leave Type", self.leave_type, "include_holiday"): - holidays = get_holidays(self.employee, date_of_joining, self.from_date) - number_of_days = number_of_days - leave_days - holidays - if number_of_days < leave_type.applicable_after: - frappe.throw( - _("{0} applicable after {1} working days").format( - self.leave_type, leave_type.applicable_after - ) - ) - - def validate_dates(self): - if frappe.db.get_single_value("HR Settings", "restrict_backdated_leave_application"): - if self.from_date and getdate(self.from_date) < getdate(): - allowed_role = frappe.db.get_single_value( - "HR Settings", "role_allowed_to_create_backdated_leave_application" - ) - user = frappe.get_doc("User", frappe.session.user) - user_roles = [d.role for d in user.roles] - if not allowed_role: - frappe.throw( - _("Backdated Leave Application is restricted. Please set the {} in {}").format( - frappe.bold("Role Allowed to Create Backdated Leave Application"), - get_link_to_form("HR Settings", "HR Settings"), - ) - ) - - if allowed_role and allowed_role not in user_roles: - frappe.throw( - _("Only users with the {0} role can create backdated leave applications").format( - allowed_role - ) - ) - - if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)): - frappe.throw(_("To date cannot be before from date")) - - if ( - self.half_day - and self.half_day_date - and ( - getdate(self.half_day_date) < getdate(self.from_date) - or getdate(self.half_day_date) > getdate(self.to_date) - ) - ): - - frappe.throw(_("Half Day Date should be between From Date and To Date")) - - if not is_lwp(self.leave_type): - self.validate_dates_across_allocation() - self.validate_back_dated_application() - - def validate_dates_across_allocation(self): - if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"): - return - - alloc_on_from_date, alloc_on_to_date = self.get_allocation_based_on_application_dates() - - if not (alloc_on_from_date or alloc_on_to_date): - frappe.throw(_("Application period cannot be outside leave allocation period")) - elif self.is_separate_ledger_entry_required(alloc_on_from_date, alloc_on_to_date): - frappe.throw( - _("Application period cannot be across two allocation records"), - exc=LeaveAcrossAllocationsError, - ) - - def get_allocation_based_on_application_dates(self) -> Tuple[Dict, Dict]: - """Returns allocation name, from and to dates for application dates""" - - def _get_leave_allocation_record(date): - LeaveAllocation = frappe.qb.DocType("Leave Allocation") - allocation = ( - frappe.qb.from_(LeaveAllocation) - .select(LeaveAllocation.name, LeaveAllocation.from_date, LeaveAllocation.to_date) - .where( - (LeaveAllocation.employee == self.employee) - & (LeaveAllocation.leave_type == self.leave_type) - & (LeaveAllocation.docstatus == 1) - & ((date >= LeaveAllocation.from_date) & (date <= LeaveAllocation.to_date)) - ) - ).run(as_dict=True) - - return allocation and allocation[0] - - allocation_based_on_from_date = _get_leave_allocation_record(self.from_date) - allocation_based_on_to_date = _get_leave_allocation_record(self.to_date) - - return allocation_based_on_from_date, allocation_based_on_to_date - - def validate_back_dated_application(self): - future_allocation = frappe.db.sql( - """select name, from_date from `tabLeave Allocation` - where employee=%s and leave_type=%s and docstatus=1 and from_date > %s - and carry_forward=1""", - (self.employee, self.leave_type, self.to_date), - as_dict=1, - ) - - if future_allocation: - frappe.throw( - _( - "Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}" - ).format(formatdate(future_allocation[0].from_date), future_allocation[0].name) - ) - - def update_attendance(self): - if self.status != "Approved": - return - - holiday_dates = [] - if not frappe.db.get_value("Leave Type", self.leave_type, "include_holiday"): - holiday_dates = get_holiday_dates_for_employee(self.employee, self.from_date, self.to_date) - - for dt in daterange(getdate(self.from_date), getdate(self.to_date)): - date = dt.strftime("%Y-%m-%d") - attendance_name = frappe.db.exists( - "Attendance", dict(employee=self.employee, attendance_date=date, docstatus=("!=", 2)) - ) - - # don't mark attendance for holidays - # if leave type does not include holidays within leaves as leaves - if date in holiday_dates: - if attendance_name: - # cancel and delete existing attendance for holidays - attendance = frappe.get_doc("Attendance", attendance_name) - attendance.flags.ignore_permissions = True - if attendance.docstatus == 1: - attendance.cancel() - frappe.delete_doc("Attendance", attendance_name, force=1) - continue - - self.create_or_update_attendance(attendance_name, date) - - def create_or_update_attendance(self, attendance_name, date): - status = ( - "Half Day" - if self.half_day_date and getdate(date) == getdate(self.half_day_date) - else "On Leave" - ) - - if attendance_name: - # update existing attendance, change absent to on leave - doc = frappe.get_doc("Attendance", attendance_name) - if doc.status != status: - doc.db_set({"status": status, "leave_type": self.leave_type, "leave_application": self.name}) - else: - # make new attendance and submit it - doc = frappe.new_doc("Attendance") - doc.employee = self.employee - doc.employee_name = self.employee_name - doc.attendance_date = date - doc.company = self.company - doc.leave_type = self.leave_type - doc.leave_application = self.name - doc.status = status - doc.flags.ignore_validate = True - doc.insert(ignore_permissions=True) - doc.submit() - - def cancel_attendance(self): - if self.docstatus == 2: - attendance = frappe.db.sql( - """select name from `tabAttendance` where employee = %s\ - and (attendance_date between %s and %s) and docstatus < 2 and status in ('On Leave', 'Half Day')""", - (self.employee, self.from_date, self.to_date), - as_dict=1, - ) - for name in attendance: - frappe.db.set_value("Attendance", name, "docstatus", 2) - - def validate_salary_processed_days(self): - if not frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"): - return - - last_processed_pay_slip = frappe.db.sql( - """ - select start_date, end_date from `tabSalary Slip` - where docstatus = 1 and employee = %s - and ((%s between start_date and end_date) or (%s between start_date and end_date)) - order by modified desc limit 1 - """, - (self.employee, self.to_date, self.from_date), - ) - - if last_processed_pay_slip: - frappe.throw( - _( - "Salary already processed for period between {0} and {1}, Leave application period cannot be between this date range." - ).format( - formatdate(last_processed_pay_slip[0][0]), formatdate(last_processed_pay_slip[0][1]) - ) - ) - - def show_block_day_warning(self): - block_dates = get_applicable_block_dates( - self.from_date, self.to_date, self.employee, self.company, all_lists=True - ) - - if block_dates: - frappe.msgprint(_("Warning: Leave application contains following block dates") + ":") - for d in block_dates: - frappe.msgprint(formatdate(d.block_date) + ": " + d.reason) - - def validate_block_days(self): - block_dates = get_applicable_block_dates( - self.from_date, self.to_date, self.employee, self.company - ) - - if block_dates and self.status == "Approved": - frappe.throw(_("You are not authorized to approve leaves on Block Dates"), LeaveDayBlockedError) - - def validate_balance_leaves(self): - if self.from_date and self.to_date: - self.total_leave_days = get_number_of_leave_days( - self.employee, self.leave_type, self.from_date, self.to_date, self.half_day, self.half_day_date - ) - - if self.total_leave_days <= 0: - frappe.throw( - _( - "The day(s) on which you are applying for leave are holidays. You need not apply for leave." - ) - ) - - if not is_lwp(self.leave_type): - leave_balance = get_leave_balance_on( - self.employee, - self.leave_type, - self.from_date, - self.to_date, - consider_all_leaves_in_the_allocation_period=True, - for_consumption=True, - ) - self.leave_balance = leave_balance.get("leave_balance") - leave_balance_for_consumption = leave_balance.get("leave_balance_for_consumption") - - if self.status != "Rejected" and ( - leave_balance_for_consumption < self.total_leave_days or not leave_balance_for_consumption - ): - self.show_insufficient_balance_message(leave_balance_for_consumption) - - def show_insufficient_balance_message(self, leave_balance_for_consumption: float) -> None: - alloc_on_from_date, alloc_on_to_date = self.get_allocation_based_on_application_dates() - - if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"): - if leave_balance_for_consumption != self.leave_balance: - msg = _("Warning: Insufficient leave balance for Leave Type {0} in this allocation.").format( - frappe.bold(self.leave_type) - ) - msg += "

" - msg += _( - "Actual balances aren't available because the leave application spans over different leave allocations. You can still apply for leaves which would be compensated during the next allocation." - ) - else: - msg = _("Warning: Insufficient leave balance for Leave Type {0}.").format( - frappe.bold(self.leave_type) - ) - - frappe.msgprint(msg, title=_("Warning"), indicator="orange") - else: - frappe.throw( - _("Insufficient leave balance for Leave Type {0}").format(frappe.bold(self.leave_type)), - exc=InsufficientLeaveBalanceError, - title=_("Insufficient Balance"), - ) - - def validate_leave_overlap(self): - if not self.name: - # hack! if name is null, it could cause problems with != - self.name = "New Leave Application" - - for d in frappe.db.sql( - """ - select - name, leave_type, posting_date, from_date, to_date, total_leave_days, half_day_date - from `tabLeave Application` - where employee = %(employee)s and docstatus < 2 and status in ("Open", "Approved") - and to_date >= %(from_date)s and from_date <= %(to_date)s - and name != %(name)s""", - { - "employee": self.employee, - "from_date": self.from_date, - "to_date": self.to_date, - "name": self.name, - }, - as_dict=1, - ): - - if ( - cint(self.half_day) == 1 - and getdate(self.half_day_date) == getdate(d.half_day_date) - and ( - flt(self.total_leave_days) == 0.5 - or getdate(self.from_date) == getdate(d.to_date) - or getdate(self.to_date) == getdate(d.from_date) - ) - ): - - total_leaves_on_half_day = self.get_total_leaves_on_half_day() - if total_leaves_on_half_day >= 1: - self.throw_overlap_error(d) - else: - self.throw_overlap_error(d) - - def throw_overlap_error(self, d): - form_link = get_link_to_form("Leave Application", d.name) - msg = _("Employee {0} has already applied for {1} between {2} and {3} : {4}").format( - self.employee, d["leave_type"], formatdate(d["from_date"]), formatdate(d["to_date"]), form_link - ) - frappe.throw(msg, OverlapError) - - def get_total_leaves_on_half_day(self): - leave_count_on_half_day_date = frappe.db.sql( - """select count(name) from `tabLeave Application` - where employee = %(employee)s - and docstatus < 2 - and status in ("Open", "Approved") - and half_day = 1 - and half_day_date = %(half_day_date)s - and name != %(name)s""", - {"employee": self.employee, "half_day_date": self.half_day_date, "name": self.name}, - )[0][0] - - return leave_count_on_half_day_date * 0.5 - - def validate_max_days(self): - max_days = frappe.db.get_value("Leave Type", self.leave_type, "max_continuous_days_allowed") - if max_days and self.total_leave_days > cint(max_days): - frappe.throw(_("Leave of type {0} cannot be longer than {1}").format(self.leave_type, max_days)) - - def validate_attendance(self): - attendance = frappe.db.sql( - """select name from `tabAttendance` where employee = %s and (attendance_date between %s and %s) - and status = "Present" and docstatus = 1""", - (self.employee, self.from_date, self.to_date), - ) - if attendance: - frappe.throw( - _("Attendance for employee {0} is already marked for this day").format(self.employee), - AttendanceAlreadyMarkedError, - ) - - def validate_optional_leave(self): - leave_period = get_leave_period(self.from_date, self.to_date, self.company) - if not leave_period: - frappe.throw(_("Cannot find active Leave Period")) - optional_holiday_list = frappe.db.get_value( - "Leave Period", leave_period[0]["name"], "optional_holiday_list" - ) - if not optional_holiday_list: - frappe.throw( - _("Optional Holiday List not set for leave period {0}").format(leave_period[0]["name"]) - ) - day = getdate(self.from_date) - while day <= getdate(self.to_date): - if not frappe.db.exists( - {"doctype": "Holiday", "parent": optional_holiday_list, "holiday_date": day} - ): - frappe.throw( - _("{0} is not in Optional Holiday List").format(formatdate(day)), NotAnOptionalHoliday - ) - day = add_days(day, 1) - - def set_half_day_date(self): - if self.from_date == self.to_date and self.half_day == 1: - self.half_day_date = self.from_date - - if self.half_day == 0: - self.half_day_date = None - - def notify_employee(self): - employee = frappe.get_doc("Employee", self.employee) - if not employee.user_id: - return - - parent_doc = frappe.get_doc("Leave Application", self.name) - args = parent_doc.as_dict() - - template = frappe.db.get_single_value("HR Settings", "leave_status_notification_template") - if not template: - frappe.msgprint(_("Please set default template for Leave Status Notification in HR Settings.")) - return - email_template = frappe.get_doc("Email Template", template) - message = frappe.render_template(email_template.response, args) - - self.notify( - { - # for post in messages - "message": message, - "message_to": employee.user_id, - # for email - "subject": email_template.subject, - "notify": "employee", - } - ) - - def notify_leave_approver(self): - if self.leave_approver: - parent_doc = frappe.get_doc("Leave Application", self.name) - args = parent_doc.as_dict() - - template = frappe.db.get_single_value("HR Settings", "leave_approval_notification_template") - if not template: - frappe.msgprint( - _("Please set default template for Leave Approval Notification in HR Settings.") - ) - return - email_template = frappe.get_doc("Email Template", template) - message = frappe.render_template(email_template.response, args) - - self.notify( - { - # for post in messages - "message": message, - "message_to": self.leave_approver, - # for email - "subject": email_template.subject, - } - ) - - def notify(self, args): - args = frappe._dict(args) - # args -> message, message_to, subject - if cint(self.follow_via_email): - contact = args.message_to - if not isinstance(contact, list): - if not args.notify == "employee": - contact = frappe.get_doc("User", contact).email or contact - - sender = dict() - sender["email"] = frappe.get_doc("User", frappe.session.user).email - sender["full_name"] = get_fullname(sender["email"]) - - try: - frappe.sendmail( - recipients=contact, - sender=sender["email"], - subject=args.subject, - message=args.message, - ) - frappe.msgprint(_("Email sent to {0}").format(contact)) - except frappe.OutgoingEmailError: - pass - - def create_leave_ledger_entry(self, submit=True): - if self.status != "Approved" and submit: - return - - expiry_date = get_allocation_expiry_for_cf_leaves( - self.employee, self.leave_type, self.to_date, self.from_date - ) - lwp = frappe.db.get_value("Leave Type", self.leave_type, "is_lwp") - - if expiry_date: - self.create_ledger_entry_for_intermediate_allocation_expiry(expiry_date, submit, lwp) - else: - alloc_on_from_date, alloc_on_to_date = self.get_allocation_based_on_application_dates() - if self.is_separate_ledger_entry_required(alloc_on_from_date, alloc_on_to_date): - # required only if negative balance is allowed for leave type - # else will be stopped in validation itself - self.create_separate_ledger_entries(alloc_on_from_date, alloc_on_to_date, submit, lwp) - else: - raise_exception = False if frappe.flags.in_patch else True - args = dict( - leaves=self.total_leave_days * -1, - from_date=self.from_date, - to_date=self.to_date, - is_lwp=lwp, - holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) - or "", - ) - create_leave_ledger_entry(self, args, submit) - - def is_separate_ledger_entry_required( - self, alloc_on_from_date: Optional[Dict] = None, alloc_on_to_date: Optional[Dict] = None - ) -> bool: - """Checks if application dates fall in separate allocations""" - if ( - (alloc_on_from_date and not alloc_on_to_date) - or (not alloc_on_from_date and alloc_on_to_date) - or ( - alloc_on_from_date and alloc_on_to_date and alloc_on_from_date.name != alloc_on_to_date.name - ) - ): - return True - return False - - def create_separate_ledger_entries(self, alloc_on_from_date, alloc_on_to_date, submit, lwp): - """Creates separate ledger entries for application period falling into separate allocations""" - # for creating separate ledger entries existing allocation periods should be consecutive - if ( - submit - and alloc_on_from_date - and alloc_on_to_date - and add_days(alloc_on_from_date.to_date, 1) != alloc_on_to_date.from_date - ): - frappe.throw( - _( - "Leave Application period cannot be across two non-consecutive leave allocations {0} and {1}." - ).format( - get_link_to_form("Leave Allocation", alloc_on_from_date.name), - get_link_to_form("Leave Allocation", alloc_on_to_date), - ) - ) - - raise_exception = False if frappe.flags.in_patch else True - - if alloc_on_from_date: - first_alloc_end = alloc_on_from_date.to_date - second_alloc_start = add_days(alloc_on_from_date.to_date, 1) - else: - first_alloc_end = add_days(alloc_on_to_date.from_date, -1) - second_alloc_start = alloc_on_to_date.from_date - - leaves_in_first_alloc = get_number_of_leave_days( - self.employee, - self.leave_type, - self.from_date, - first_alloc_end, - self.half_day, - self.half_day_date, - ) - leaves_in_second_alloc = get_number_of_leave_days( - self.employee, - self.leave_type, - second_alloc_start, - self.to_date, - self.half_day, - self.half_day_date, - ) - - args = dict( - is_lwp=lwp, - holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) - or "", - ) - - if leaves_in_first_alloc: - args.update( - dict(from_date=self.from_date, to_date=first_alloc_end, leaves=leaves_in_first_alloc * -1) - ) - create_leave_ledger_entry(self, args, submit) - - if leaves_in_second_alloc: - args.update( - dict(from_date=second_alloc_start, to_date=self.to_date, leaves=leaves_in_second_alloc * -1) - ) - create_leave_ledger_entry(self, args, submit) - - def create_ledger_entry_for_intermediate_allocation_expiry(self, expiry_date, submit, lwp): - """Splits leave application into two ledger entries to consider expiry of allocation""" - raise_exception = False if frappe.flags.in_patch else True - - leaves = get_number_of_leave_days( - self.employee, self.leave_type, self.from_date, expiry_date, self.half_day, self.half_day_date - ) - - if leaves: - args = dict( - from_date=self.from_date, - to_date=expiry_date, - leaves=leaves * -1, - is_lwp=lwp, - holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) - or "", - ) - create_leave_ledger_entry(self, args, submit) - - if getdate(expiry_date) != getdate(self.to_date): - start_date = add_days(expiry_date, 1) - leaves = get_number_of_leave_days( - self.employee, self.leave_type, start_date, self.to_date, self.half_day, self.half_day_date - ) - - if leaves: - args.update(dict(from_date=start_date, to_date=self.to_date, leaves=leaves * -1)) - create_leave_ledger_entry(self, args, submit) - - -def get_allocation_expiry_for_cf_leaves( - employee: str, leave_type: str, to_date: str, from_date: str -) -> str: - """Returns expiry of carry forward allocation in leave ledger entry""" - expiry = frappe.get_all( - "Leave Ledger Entry", - filters={ - "employee": employee, - "leave_type": leave_type, - "is_carry_forward": 1, - "transaction_type": "Leave Allocation", - "to_date": ["between", (from_date, to_date)], - "docstatus": 1, - }, - fields=["to_date"], - ) - return expiry[0]["to_date"] if expiry else "" - - -@frappe.whitelist() -def get_number_of_leave_days( - employee: str, - leave_type: str, - from_date: str, - to_date: str, - half_day: Optional[int] = None, - half_day_date: Optional[str] = None, - holiday_list: Optional[str] = None, -) -> float: - """Returns number of leave days between 2 dates after considering half day and holidays - (Based on the include_holiday setting in Leave Type)""" - number_of_days = 0 - if cint(half_day) == 1: - if getdate(from_date) == getdate(to_date): - number_of_days = 0.5 - elif half_day_date and getdate(from_date) <= getdate(half_day_date) <= getdate(to_date): - number_of_days = date_diff(to_date, from_date) + 0.5 - else: - number_of_days = date_diff(to_date, from_date) + 1 - else: - number_of_days = date_diff(to_date, from_date) + 1 - - if not frappe.db.get_value("Leave Type", leave_type, "include_holiday"): - number_of_days = flt(number_of_days) - flt( - get_holidays(employee, from_date, to_date, holiday_list=holiday_list) - ) - return number_of_days - - -@frappe.whitelist() -def get_leave_details(employee, date): - allocation_records = get_leave_allocation_records(employee, date) - leave_allocation = {} - for d in allocation_records: - allocation = allocation_records.get(d, frappe._dict()) - remaining_leaves = get_leave_balance_on( - employee, d, date, to_date=allocation.to_date, consider_all_leaves_in_the_allocation_period=True - ) - - end_date = allocation.to_date - leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, end_date) * -1 - leaves_pending = get_leaves_pending_approval_for_period( - employee, d, allocation.from_date, end_date - ) - expired_leaves = allocation.total_leaves_allocated - (remaining_leaves + leaves_taken) - - leave_allocation[d] = { - "total_leaves": allocation.total_leaves_allocated, - "expired_leaves": expired_leaves if expired_leaves > 0 else 0, - "leaves_taken": leaves_taken, - "leaves_pending_approval": leaves_pending, - "remaining_leaves": remaining_leaves, - } - - # is used in set query - lwp = frappe.get_list("Leave Type", filters={"is_lwp": 1}, pluck="name") - - return { - "leave_allocation": leave_allocation, - "leave_approver": get_leave_approver(employee), - "lwps": lwp, - } - - -@frappe.whitelist() -def get_leave_balance_on( - employee: str, - leave_type: str, - date: str, - to_date: str = None, - consider_all_leaves_in_the_allocation_period: bool = False, - for_consumption: bool = False, -): - """ - Returns leave balance till date - :param employee: employee name - :param leave_type: leave type - :param date: date to check balance on - :param to_date: future date to check for allocation expiry - :param consider_all_leaves_in_the_allocation_period: consider all leaves taken till the allocation end date - :param for_consumption: flag to check if leave balance is required for consumption or display - eg: employee has leave balance = 10 but allocation is expiring in 1 day so employee can only consume 1 leave - in this case leave_balance = 10 but leave_balance_for_consumption = 1 - if True, returns a dict eg: {'leave_balance': 10, 'leave_balance_for_consumption': 1} - else, returns leave_balance (in this case 10) - """ - - if not to_date: - to_date = nowdate() - - allocation_records = get_leave_allocation_records(employee, date, leave_type) - allocation = allocation_records.get(leave_type, frappe._dict()) - - end_date = allocation.to_date if cint(consider_all_leaves_in_the_allocation_period) else date - cf_expiry = get_allocation_expiry_for_cf_leaves(employee, leave_type, to_date, date) - - leaves_taken = get_leaves_for_period(employee, leave_type, allocation.from_date, end_date) - - remaining_leaves = get_remaining_leaves(allocation, leaves_taken, date, cf_expiry) - - if for_consumption: - return remaining_leaves - else: - return remaining_leaves.get("leave_balance") - - -def get_leave_allocation_records(employee, date, leave_type=None): - """Returns the total allocated leaves and carry forwarded leaves based on ledger entries""" - Ledger = frappe.qb.DocType("Leave Ledger Entry") - - cf_leave_case = ( - frappe.qb.terms.Case().when(Ledger.is_carry_forward == "1", Ledger.leaves).else_(0) - ) - sum_cf_leaves = Sum(cf_leave_case).as_("cf_leaves") - - new_leaves_case = ( - frappe.qb.terms.Case().when(Ledger.is_carry_forward == "0", Ledger.leaves).else_(0) - ) - sum_new_leaves = Sum(new_leaves_case).as_("new_leaves") - - query = ( - frappe.qb.from_(Ledger) - .select( - sum_cf_leaves, - sum_new_leaves, - Min(Ledger.from_date).as_("from_date"), - Max(Ledger.to_date).as_("to_date"), - Ledger.leave_type, - ) - .where( - (Ledger.from_date <= date) - & (Ledger.to_date >= date) - & (Ledger.docstatus == 1) - & (Ledger.transaction_type == "Leave Allocation") - & (Ledger.employee == employee) - & (Ledger.is_expired == 0) - & (Ledger.is_lwp == 0) - ) - ) - - if leave_type: - query = query.where((Ledger.leave_type == leave_type)) - query = query.groupby(Ledger.employee, Ledger.leave_type) - - allocation_details = query.run(as_dict=True) - - allocated_leaves = frappe._dict() - for d in allocation_details: - allocated_leaves.setdefault( - d.leave_type, - frappe._dict( - { - "from_date": d.from_date, - "to_date": d.to_date, - "total_leaves_allocated": flt(d.cf_leaves) + flt(d.new_leaves), - "unused_leaves": d.cf_leaves, - "new_leaves_allocated": d.new_leaves, - "leave_type": d.leave_type, - } - ), - ) - return allocated_leaves - - -def get_leaves_pending_approval_for_period( - employee: str, leave_type: str, from_date: str, to_date: str -) -> float: - """Returns leaves that are pending for approval""" - leaves = frappe.get_all( - "Leave Application", - filters={"employee": employee, "leave_type": leave_type, "status": "Open"}, - or_filters={ - "from_date": ["between", (from_date, to_date)], - "to_date": ["between", (from_date, to_date)], - }, - fields=["SUM(total_leave_days) as leaves"], - )[0] - return leaves["leaves"] if leaves["leaves"] else 0.0 - - -def get_remaining_leaves( - allocation: Dict, leaves_taken: float, date: str, cf_expiry: str -) -> Dict[str, float]: - """Returns a dict of leave_balance and leave_balance_for_consumption - leave_balance returns the available leave balance - leave_balance_for_consumption returns the minimum leaves remaining after comparing with remaining days for allocation expiry - """ - - def _get_remaining_leaves(remaining_leaves, end_date): - """Returns minimum leaves remaining after comparing with remaining days for allocation expiry""" - if remaining_leaves > 0: - remaining_days = date_diff(end_date, date) + 1 - remaining_leaves = min(remaining_days, remaining_leaves) - - return remaining_leaves - - leave_balance = leave_balance_for_consumption = flt(allocation.total_leaves_allocated) + flt( - leaves_taken - ) - - # balance for carry forwarded leaves - if cf_expiry and allocation.unused_leaves: - cf_leaves = flt(allocation.unused_leaves) + flt(leaves_taken) - remaining_cf_leaves = _get_remaining_leaves(cf_leaves, cf_expiry) - - leave_balance = flt(allocation.new_leaves_allocated) + flt(cf_leaves) - leave_balance_for_consumption = flt(allocation.new_leaves_allocated) + flt(remaining_cf_leaves) - - remaining_leaves = _get_remaining_leaves(leave_balance_for_consumption, allocation.to_date) - return frappe._dict(leave_balance=leave_balance, leave_balance_for_consumption=remaining_leaves) - - -def get_leaves_for_period( - employee: str, leave_type: str, from_date: str, to_date: str, skip_expired_leaves: bool = True -) -> float: - leave_entries = get_leave_entries(employee, leave_type, from_date, to_date) - leave_days = 0 - - for leave_entry in leave_entries: - inclusive_period = leave_entry.from_date >= getdate( - from_date - ) and leave_entry.to_date <= getdate(to_date) - - if inclusive_period and leave_entry.transaction_type == "Leave Encashment": - leave_days += leave_entry.leaves - - elif ( - inclusive_period - and leave_entry.transaction_type == "Leave Allocation" - and leave_entry.is_expired - and not skip_expired_leaves - ): - leave_days += leave_entry.leaves - - elif leave_entry.transaction_type == "Leave Application": - if leave_entry.from_date < getdate(from_date): - leave_entry.from_date = from_date - if leave_entry.to_date > getdate(to_date): - leave_entry.to_date = to_date - - half_day = 0 - half_day_date = None - # fetch half day date for leaves with half days - if leave_entry.leaves % 1: - half_day = 1 - half_day_date = frappe.db.get_value( - "Leave Application", {"name": leave_entry.transaction_name}, ["half_day_date"] - ) - - leave_days += ( - get_number_of_leave_days( - employee, - leave_type, - leave_entry.from_date, - leave_entry.to_date, - half_day, - half_day_date, - holiday_list=leave_entry.holiday_list, - ) - * -1 - ) - - return leave_days - - -def get_leave_entries(employee, leave_type, from_date, to_date): - """Returns leave entries between from_date and to_date.""" - return frappe.db.sql( - """ - SELECT - employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, holiday_list, - is_carry_forward, is_expired - FROM `tabLeave Ledger Entry` - WHERE employee=%(employee)s AND leave_type=%(leave_type)s - AND docstatus=1 - AND (leaves<0 - OR is_expired=1) - AND (from_date between %(from_date)s AND %(to_date)s - OR to_date between %(from_date)s AND %(to_date)s - OR (from_date < %(from_date)s AND to_date > %(to_date)s)) - """, - {"from_date": from_date, "to_date": to_date, "employee": employee, "leave_type": leave_type}, - as_dict=1, - ) - - -@frappe.whitelist() -def get_holidays(employee, from_date, to_date, holiday_list=None): - """get holidays between two dates for the given employee""" - if not holiday_list: - holiday_list = get_holiday_list_for_employee(employee) - - holidays = frappe.db.sql( - """select count(distinct holiday_date) from `tabHoliday` h1, `tabHoliday List` h2 - where h1.parent = h2.name and h1.holiday_date between %s and %s - and h2.name = %s""", - (from_date, to_date, holiday_list), - )[0][0] - - return holidays - - -def is_lwp(leave_type): - lwp = frappe.db.sql("select is_lwp from `tabLeave Type` where name = %s", leave_type) - return lwp and cint(lwp[0][0]) or 0 - - -@frappe.whitelist() -def get_events(start, end, filters=None): - from frappe.desk.reportview import get_filters_cond - - events = [] - - employee = frappe.db.get_value( - "Employee", filters={"user_id": frappe.session.user}, fieldname=["name", "company"], as_dict=True - ) - - if employee: - employee, company = employee.name, employee.company - else: - employee = "" - company = frappe.db.get_value("Global Defaults", None, "default_company") - - conditions = get_filters_cond("Leave Application", filters, []) - # show department leaves for employee - if "Employee" in frappe.get_roles(): - add_department_leaves(events, start, end, employee, company) - - add_leaves(events, start, end, conditions) - add_block_dates(events, start, end, employee, company) - add_holidays(events, start, end, employee, company) - - return events - - -def add_department_leaves(events, start, end, employee, company): - department = frappe.db.get_value("Employee", employee, "department") - - if not department: - return - - # department leaves - department_employees = frappe.db.sql_list( - """select name from tabEmployee where department=%s - and company=%s""", - (department, company), - ) - - filter_conditions = ' and employee in ("%s")' % '", "'.join(department_employees) - add_leaves(events, start, end, filter_conditions=filter_conditions) - - -def add_leaves(events, start, end, filter_conditions=None): - from frappe.desk.reportview import build_match_conditions - - conditions = [] - - if not cint( - frappe.db.get_value("HR Settings", None, "show_leaves_of_all_department_members_in_calendar") - ): - match_conditions = build_match_conditions("Leave Application") - - if match_conditions: - conditions.append(match_conditions) - - query = """SELECT - docstatus, - name, - employee, - employee_name, - leave_type, - from_date, - to_date, - half_day, - status, - color - FROM `tabLeave Application` - WHERE - from_date <= %(end)s AND to_date >= %(start)s <= to_date - AND docstatus < 2 - AND status in ('Approved', 'Open') - """ - - if conditions: - query += " AND " + " AND ".join(conditions) - - if filter_conditions: - query += filter_conditions - - for d in frappe.db.sql(query, {"start": start, "end": end}, as_dict=True): - e = { - "name": d.name, - "doctype": "Leave Application", - "from_date": d.from_date, - "to_date": d.to_date, - "docstatus": d.docstatus, - "color": d.color, - "all_day": int(not d.half_day), - "title": cstr(d.employee_name) - + f" ({cstr(d.leave_type)})" - + (" " + _("(Half Day)") if d.half_day else ""), - } - if e not in events: - events.append(e) - - -def add_block_dates(events, start, end, employee, company): - # block days - from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates - - cnt = 0 - block_dates = get_applicable_block_dates(start, end, employee, company, all_lists=True) - - for block_date in block_dates: - events.append( - { - "doctype": "Leave Block List Date", - "from_date": block_date.block_date, - "to_date": block_date.block_date, - "title": _("Leave Blocked") + ": " + block_date.reason, - "name": "_" + str(cnt), - } - ) - cnt += 1 - - -def add_holidays(events, start, end, employee, company): - applicable_holiday_list = get_holiday_list_for_employee(employee, company) - if not applicable_holiday_list: - return - - for holiday in frappe.db.sql( - """select name, holiday_date, description - from `tabHoliday` where parent=%s and holiday_date between %s and %s""", - (applicable_holiday_list, start, end), - as_dict=True, - ): - events.append( - { - "doctype": "Holiday", - "from_date": holiday.holiday_date, - "to_date": holiday.holiday_date, - "title": _("Holiday") + ": " + cstr(holiday.description), - "name": holiday.name, - } - ) - - -@frappe.whitelist() -def get_mandatory_approval(doctype): - mandatory = "" - if doctype == "Leave Application": - mandatory = frappe.db.get_single_value( - "HR Settings", "leave_approver_mandatory_in_leave_application" - ) - else: - mandatory = frappe.db.get_single_value( - "HR Settings", "expense_approver_mandatory_in_expense_claim" - ) - - return mandatory - - -def get_approved_leaves_for_period(employee, leave_type, from_date, to_date): - LeaveApplication = frappe.qb.DocType("Leave Application") - query = ( - frappe.qb.from_(LeaveApplication) - .select( - LeaveApplication.employee, - LeaveApplication.leave_type, - LeaveApplication.from_date, - LeaveApplication.to_date, - LeaveApplication.total_leave_days, - ) - .where( - (LeaveApplication.employee == employee) - & (LeaveApplication.docstatus == 1) - & (LeaveApplication.status == "Approved") - & ( - (LeaveApplication.from_date.between(from_date, to_date)) - | (LeaveApplication.to_date.between(from_date, to_date)) - | ((LeaveApplication.from_date < from_date) & (LeaveApplication.to_date > to_date)) - ) - ) - ) - - if leave_type: - query = query.where(LeaveApplication.leave_type == leave_type) - - leave_applications = query.run(as_dict=True) - - leave_days = 0 - for leave_app in leave_applications: - if leave_app.from_date >= getdate(from_date) and leave_app.to_date <= getdate(to_date): - leave_days += leave_app.total_leave_days - else: - if leave_app.from_date < getdate(from_date): - leave_app.from_date = from_date - if leave_app.to_date > getdate(to_date): - leave_app.to_date = to_date - - leave_days += get_number_of_leave_days( - employee, leave_type, leave_app.from_date, leave_app.to_date - ) - - return leave_days - - -@frappe.whitelist() -def get_leave_approver(employee): - leave_approver, department = frappe.db.get_value( - "Employee", employee, ["leave_approver", "department"] - ) - - if not leave_approver and department: - leave_approver = frappe.db.get_value( - "Department Approver", - {"parent": department, "parentfield": "leave_approvers", "idx": 1}, - "approver", - ) - - return leave_approver diff --git a/erpnext/hr/doctype/leave_application/leave_application_calendar.js b/erpnext/hr/doctype/leave_application/leave_application_calendar.js deleted file mode 100644 index 0ba0285552..0000000000 --- a/erpnext/hr/doctype/leave_application/leave_application_calendar.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.views.calendar["Leave Application"] = { - field_map: { - "start": "from_date", - "end": "to_date", - "id": "name", - "title": "title", - "docstatus": 1, - "color": "color", - "allDay": "all_day" - }, - options: { - header: { - left: 'prev,next today', - center: 'title', - right: 'month' - } - }, - get_events_method: "erpnext.hr.doctype.leave_application.leave_application.get_events" -} diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html deleted file mode 100644 index e755322efd..0000000000 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html +++ /dev/null @@ -1,29 +0,0 @@ - -{% if not jQuery.isEmptyObject(data) %} - - - - - - - - - - - - - {% for(const [key, value] of Object.entries(data)) { %} - - - - - - - - - {% } %} - -
{{ __("Leave Type") }}{{ __("Total Allocated Leave(s)") }}{{ __("Expired Leave(s)") }}{{ __("Used Leave(s)") }}{{ __("Leave(s) Pending Approval") }}{{ __("Available Leave(s)") }}
{%= key %} {%= value["total_leaves"] %} {%= value["expired_leaves"] %} {%= value["leaves_taken"] %} {%= value["leaves_pending_approval"] %} {%= value["remaining_leaves"] %}
-{% else %} -

No Leave has been allocated.

-{% endif %} diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.py b/erpnext/hr/doctype/leave_application/leave_application_dashboard.py deleted file mode 100644 index ee5cbe99f3..0000000000 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.py +++ /dev/null @@ -1,9 +0,0 @@ -from frappe import _ - - -def get_data(): - return { - "fieldname": "leave_application", - "transactions": [{"items": ["Attendance"]}], - "reports": [{"label": _("Reports"), "items": ["Employee Leave Balance"]}], - } diff --git a/erpnext/hr/doctype/leave_application/leave_application_email_template.html b/erpnext/hr/doctype/leave_application/leave_application_email_template.html deleted file mode 100644 index dae9084f79..0000000000 --- a/erpnext/hr/doctype/leave_application/leave_application_email_template.html +++ /dev/null @@ -1,30 +0,0 @@ -

Leave Application Notification

-

Details:

- - - - - - - - - - - - - - - - - - - - - - -
Employee{{employee_name}}
Leave Type{{leave_type}}
From Date{{from_date}}
To Date{{to_date}}
Status{{status}}
- - {% set doc_link = frappe.utils.get_url_to_form('Leave Application', name) %} - -

- {{ _('Open Now') }} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_application/leave_application_list.js b/erpnext/hr/doctype/leave_application/leave_application_list.js deleted file mode 100644 index 157271a5a0..0000000000 --- a/erpnext/hr/doctype/leave_application/leave_application_list.js +++ /dev/null @@ -1,14 +0,0 @@ -frappe.listview_settings["Leave Application"] = { - add_fields: ["leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"], - has_indicator_for_draft: 1, - get_indicator: function (doc) { - let status_color = { - "Approved": "green", - "Rejected": "red", - "Open": "orange", - "Cancelled": "red", - "Submitted": "blue" - }; - return [__(doc.status), status_color[doc.status], "status,=," + doc.status]; - } -}; diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py deleted file mode 100644 index 27c54109de..0000000000 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ /dev/null @@ -1,1155 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import unittest - -import frappe -from frappe.permissions import clear_user_permissions_for_doctype -from frappe.utils import ( - add_days, - add_months, - get_first_day, - get_last_day, - get_year_ending, - get_year_start, - getdate, - nowdate, -) - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list -from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation -from erpnext.hr.doctype.leave_application.leave_application import ( - InsufficientLeaveBalanceError, - LeaveAcrossAllocationsError, - LeaveDayBlockedError, - NotAnOptionalHoliday, - OverlapError, - get_leave_allocation_records, - get_leave_balance_on, - get_leave_details, -) -from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import ( - create_assignment_for_multiple_employees, -) -from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type -from erpnext.payroll.doctype.salary_slip.test_salary_slip import ( - make_holiday_list, - make_leave_application, -) - -test_dependencies = ["Leave Type", "Leave Allocation", "Leave Block List", "Employee"] - -_test_records = [ - { - "company": "_Test Company", - "doctype": "Leave Application", - "employee": "_T-Employee-00001", - "from_date": "2013-05-01", - "description": "_Test Reason", - "leave_type": "_Test Leave Type", - "posting_date": "2013-01-02", - "to_date": "2013-05-05", - }, - { - "company": "_Test Company", - "doctype": "Leave Application", - "employee": "_T-Employee-00002", - "from_date": "2013-05-01", - "description": "_Test Reason", - "leave_type": "_Test Leave Type", - "posting_date": "2013-01-02", - "to_date": "2013-05-05", - }, - { - "company": "_Test Company", - "doctype": "Leave Application", - "employee": "_T-Employee-00001", - "from_date": "2013-01-15", - "description": "_Test Reason", - "leave_type": "_Test Leave Type LWP", - "posting_date": "2013-01-02", - "to_date": "2013-01-15", - }, -] - - -class TestLeaveApplication(unittest.TestCase): - def setUp(self): - for dt in [ - "Leave Application", - "Leave Allocation", - "Salary Slip", - "Leave Ledger Entry", - "Leave Period", - "Leave Policy Assignment", - ]: - frappe.db.delete(dt) - - frappe.set_user("Administrator") - set_leave_approver() - - frappe.db.delete("Attendance", {"employee": "_T-Employee-00001"}) - frappe.db.set_value("Employee", "_T-Employee-00001", "holiday_list", "") - - from_date = get_year_start(getdate()) - to_date = get_year_ending(getdate()) - self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date) - - if not frappe.db.exists("Leave Type", "_Test Leave Type"): - frappe.get_doc( - dict(leave_type_name="_Test Leave Type", doctype="Leave Type", include_holiday=True) - ).insert() - - def tearDown(self): - frappe.db.rollback() - frappe.set_user("Administrator") - - def _clear_roles(self): - frappe.db.sql( - """delete from `tabHas Role` where parent in - ("test@example.com", "test1@example.com", "test2@example.com")""" - ) - - def _clear_applications(self): - frappe.db.sql("""delete from `tabLeave Application`""") - - def get_application(self, doc): - application = frappe.copy_doc(doc) - application.from_date = "2013-01-01" - application.to_date = "2013-01-05" - return application - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_validate_application_across_allocations(self): - # Test validation for application dates when negative balance is disabled - frappe.delete_doc_if_exists("Leave Type", "Test Leave Validation", force=1) - leave_type = frappe.get_doc( - dict(leave_type_name="Test Leave Validation", doctype="Leave Type", allow_negative=False) - ).insert() - - employee = get_employee() - date = getdate() - first_sunday = get_first_sunday(self.holiday_list, for_date=get_year_start(date)) - - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee.name, - leave_type=leave_type.name, - from_date=add_days(first_sunday, 1), - to_date=add_days(first_sunday, 4), - company="_Test Company", - status="Approved", - leave_approver="test@example.com", - ) - ) - # Application period cannot be outside leave allocation period - self.assertRaises(frappe.ValidationError, leave_application.insert) - - make_allocation_record( - leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date) - ) - - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee.name, - leave_type=leave_type.name, - from_date=add_days(first_sunday, -10), - to_date=add_days(first_sunday, 1), - company="_Test Company", - status="Approved", - leave_approver="test@example.com", - ) - ) - - # Application period cannot be across two allocation records - self.assertRaises(LeaveAcrossAllocationsError, leave_application.insert) - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_insufficient_leave_balance_validation(self): - # CASE 1: Validation when allow negative is disabled - frappe.delete_doc_if_exists("Leave Type", "Test Leave Validation", force=1) - leave_type = frappe.get_doc( - dict(leave_type_name="Test Leave Validation", doctype="Leave Type", allow_negative=False) - ).insert() - - employee = get_employee() - date = getdate() - first_sunday = get_first_sunday(self.holiday_list, for_date=get_year_start(date)) - - # allocate 2 leaves, apply for more - make_allocation_record( - leave_type=leave_type.name, - from_date=get_year_start(date), - to_date=get_year_ending(date), - leaves=2, - ) - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee.name, - leave_type=leave_type.name, - from_date=add_days(first_sunday, 1), - to_date=add_days(first_sunday, 3), - company="_Test Company", - status="Approved", - leave_approver="test@example.com", - ) - ) - self.assertRaises(InsufficientLeaveBalanceError, leave_application.insert) - - # CASE 2: Allows creating application with a warning message when allow negative is enabled - frappe.db.set_value("Leave Type", "Test Leave Validation", "allow_negative", True) - make_leave_application( - employee.name, add_days(first_sunday, 1), add_days(first_sunday, 3), leave_type.name - ) - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_separate_leave_ledger_entry_for_boundary_applications(self): - # When application falls in 2 different allocations and Allow Negative is enabled - # creates separate leave ledger entries - frappe.delete_doc_if_exists("Leave Type", "Test Leave Validation", force=1) - leave_type = frappe.get_doc( - dict( - leave_type_name="Test Leave Validation", - doctype="Leave Type", - allow_negative=True, - include_holiday=True, - ) - ).insert() - - employee = get_employee() - date = getdate() - year_start = getdate(get_year_start(date)) - year_end = getdate(get_year_ending(date)) - - make_allocation_record(leave_type=leave_type.name, from_date=year_start, to_date=year_end) - # application across allocations - - # CASE 1: from date has no allocation, to date has an allocation / both dates have allocation - start_date = add_days(year_start, -10) - application = make_leave_application( - employee.name, - start_date, - add_days(year_start, 3), - leave_type.name, - half_day=1, - half_day_date=start_date, - ) - - # 2 separate leave ledger entries - ledgers = frappe.db.get_all( - "Leave Ledger Entry", - {"transaction_type": "Leave Application", "transaction_name": application.name}, - ["leaves", "from_date", "to_date"], - order_by="from_date", - ) - self.assertEqual(len(ledgers), 2) - - self.assertEqual(ledgers[0].from_date, application.from_date) - self.assertEqual(ledgers[0].to_date, add_days(year_start, -1)) - - self.assertEqual(ledgers[1].from_date, year_start) - self.assertEqual(ledgers[1].to_date, application.to_date) - - # CASE 2: from date has an allocation, to date has no allocation - application = make_leave_application( - employee.name, add_days(year_end, -3), add_days(year_end, 5), leave_type.name - ) - - # 2 separate leave ledger entries - ledgers = frappe.db.get_all( - "Leave Ledger Entry", - {"transaction_type": "Leave Application", "transaction_name": application.name}, - ["leaves", "from_date", "to_date"], - order_by="from_date", - ) - self.assertEqual(len(ledgers), 2) - - self.assertEqual(ledgers[0].from_date, application.from_date) - self.assertEqual(ledgers[0].to_date, year_end) - - self.assertEqual(ledgers[1].from_date, add_days(year_end, 1)) - self.assertEqual(ledgers[1].to_date, application.to_date) - - def test_overwrite_attendance(self): - """check attendance is automatically created on leave approval""" - make_allocation_record() - application = self.get_application(_test_records[0]) - application.status = "Approved" - application.from_date = "2018-01-01" - application.to_date = "2018-01-03" - application.insert() - application.submit() - - attendance = frappe.get_all( - "Attendance", - ["name", "status", "attendance_date"], - dict(attendance_date=("between", ["2018-01-01", "2018-01-03"]), docstatus=("!=", 2)), - ) - - # attendance created for all 3 days - self.assertEqual(len(attendance), 3) - - # all on leave - self.assertTrue(all([d.status == "On Leave" for d in attendance])) - - # dates - dates = [d.attendance_date for d in attendance] - for d in ("2018-01-01", "2018-01-02", "2018-01-03"): - self.assertTrue(getdate(d) in dates) - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_attendance_for_include_holidays(self): - # Case 1: leave type with 'Include holidays within leaves as leaves' enabled - frappe.delete_doc_if_exists("Leave Type", "Test Include Holidays", force=1) - leave_type = frappe.get_doc( - dict(leave_type_name="Test Include Holidays", doctype="Leave Type", include_holiday=True) - ).insert() - - date = getdate() - make_allocation_record( - leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date) - ) - - employee = get_employee() - first_sunday = get_first_sunday(self.holiday_list) - - leave_application = make_leave_application( - employee.name, first_sunday, add_days(first_sunday, 3), leave_type.name - ) - leave_application.reload() - self.assertEqual(leave_application.total_leave_days, 4) - self.assertEqual(frappe.db.count("Attendance", {"leave_application": leave_application.name}), 4) - - leave_application.cancel() - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_attendance_update_for_exclude_holidays(self): - # Case 2: leave type with 'Include holidays within leaves as leaves' disabled - frappe.delete_doc_if_exists("Leave Type", "Test Do Not Include Holidays", force=1) - leave_type = frappe.get_doc( - dict( - leave_type_name="Test Do Not Include Holidays", doctype="Leave Type", include_holiday=False - ) - ).insert() - - date = getdate() - make_allocation_record( - leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date) - ) - - employee = get_employee() - first_sunday = get_first_sunday(self.holiday_list) - - # already marked attendance on a holiday should be deleted in this case - config = {"doctype": "Attendance", "employee": employee.name, "status": "Present"} - attendance_on_holiday = frappe.get_doc(config) - attendance_on_holiday.attendance_date = first_sunday - attendance_on_holiday.flags.ignore_validate = True - attendance_on_holiday.save() - - # already marked attendance on a non-holiday should be updated - attendance = frappe.get_doc(config) - attendance.attendance_date = add_days(first_sunday, 3) - attendance.flags.ignore_validate = True - attendance.save() - - leave_application = make_leave_application( - employee.name, first_sunday, add_days(first_sunday, 3), leave_type.name, employee.company - ) - leave_application.reload() - - # holiday should be excluded while marking attendance - self.assertEqual(leave_application.total_leave_days, 3) - self.assertEqual(frappe.db.count("Attendance", {"leave_application": leave_application.name}), 3) - - # attendance on holiday deleted - self.assertFalse(frappe.db.exists("Attendance", attendance_on_holiday.name)) - - # attendance on non-holiday updated - self.assertEqual(frappe.db.get_value("Attendance", attendance.name, "status"), "On Leave") - - def test_block_list(self): - self._clear_roles() - - from frappe.utils.user import add_role - - add_role("test@example.com", "HR User") - clear_user_permissions_for_doctype("Employee") - - frappe.db.set_value( - "Department", "_Test Department - _TC", "leave_block_list", "_Test Leave Block List" - ) - - make_allocation_record() - - application = self.get_application(_test_records[0]) - application.insert() - application.reload() - application.status = "Approved" - self.assertRaises(LeaveDayBlockedError, application.submit) - - frappe.set_user("test@example.com") - - # clear other applications - frappe.db.sql("delete from `tabLeave Application`") - - application = self.get_application(_test_records[0]) - self.assertTrue(application.insert()) - - def test_overlap(self): - self._clear_roles() - self._clear_applications() - - from frappe.utils.user import add_role - - add_role("test@example.com", "Employee") - frappe.set_user("test@example.com") - - make_allocation_record() - - application = self.get_application(_test_records[0]) - application.insert() - - application = self.get_application(_test_records[0]) - self.assertRaises(OverlapError, application.insert) - - def test_overlap_with_half_day_1(self): - self._clear_roles() - self._clear_applications() - - from frappe.utils.user import add_role - - add_role("test@example.com", "Employee") - frappe.set_user("test@example.com") - - make_allocation_record() - - # leave from 1-5, half day on 3rd - application = self.get_application(_test_records[0]) - application.half_day = 1 - application.half_day_date = "2013-01-03" - application.insert() - - # Apply again for a half day leave on 3rd - application = self.get_application(_test_records[0]) - application.from_date = "2013-01-03" - application.to_date = "2013-01-03" - application.half_day = 1 - application.half_day_date = "2013-01-03" - application.insert() - - # Apply again for a half day leave on 3rd - application = self.get_application(_test_records[0]) - application.from_date = "2013-01-03" - application.to_date = "2013-01-03" - application.half_day = 1 - application.half_day_date = "2013-01-03" - - self.assertRaises(OverlapError, application.insert) - - def test_overlap_with_half_day_2(self): - self._clear_roles() - self._clear_applications() - - from frappe.utils.user import add_role - - add_role("test@example.com", "Employee") - - frappe.set_user("test@example.com") - - make_allocation_record() - - # leave from 1-5, no half day - application = self.get_application(_test_records[0]) - application.insert() - - # Apply again for a half day leave on 1st - application = self.get_application(_test_records[0]) - application.half_day = 1 - application.half_day_date = application.from_date - - self.assertRaises(OverlapError, application.insert) - - def test_overlap_with_half_day_3(self): - self._clear_roles() - self._clear_applications() - - from frappe.utils.user import add_role - - add_role("test@example.com", "Employee") - - frappe.set_user("test@example.com") - - make_allocation_record() - - # leave from 1-5, half day on 5th - application = self.get_application(_test_records[0]) - application.half_day = 1 - application.half_day_date = "2013-01-05" - application.insert() - - # Apply leave from 4-7, half day on 5th - application = self.get_application(_test_records[0]) - application.from_date = "2013-01-04" - application.to_date = "2013-01-07" - application.half_day = 1 - application.half_day_date = "2013-01-05" - - self.assertRaises(OverlapError, application.insert) - - # Apply leave from 5-7, half day on 5th - application = self.get_application(_test_records[0]) - application.from_date = "2013-01-05" - application.to_date = "2013-01-07" - application.half_day = 1 - application.half_day_date = "2013-01-05" - application.insert() - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_optional_leave(self): - leave_period = get_leave_period() - today = nowdate() - holiday_list = "Test Holiday List for Optional Holiday" - employee = get_employee() - - first_sunday = get_first_sunday(self.holiday_list) - optional_leave_date = add_days(first_sunday, 1) - - if not frappe.db.exists("Holiday List", holiday_list): - frappe.get_doc( - dict( - doctype="Holiday List", - holiday_list_name=holiday_list, - from_date=add_months(today, -6), - to_date=add_months(today, 6), - holidays=[dict(holiday_date=optional_leave_date, description="Test")], - ) - ).insert() - - frappe.db.set_value("Leave Period", leave_period.name, "optional_holiday_list", holiday_list) - leave_type = "Test Optional Type" - if not frappe.db.exists("Leave Type", leave_type): - frappe.get_doc( - dict(leave_type_name=leave_type, doctype="Leave Type", is_optional_leave=1) - ).insert() - - allocate_leaves(employee, leave_period, leave_type, 10) - - date = add_days(first_sunday, 2) - - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee.name, - company="_Test Company", - description="_Test Reason", - leave_type=leave_type, - from_date=date, - to_date=date, - ) - ) - - # can only apply on optional holidays - self.assertRaises(NotAnOptionalHoliday, leave_application.insert) - - leave_application.from_date = optional_leave_date - leave_application.to_date = optional_leave_date - leave_application.status = "Approved" - leave_application.insert() - leave_application.submit() - - # check leave balance is reduced - self.assertEqual(get_leave_balance_on(employee.name, leave_type, optional_leave_date), 9) - - def test_leaves_allowed(self): - employee = get_employee() - leave_period = get_leave_period() - frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1) - leave_type = frappe.get_doc( - dict(leave_type_name="Test Leave Type", doctype="Leave Type", max_leaves_allowed=5) - ).insert() - - date = add_days(nowdate(), -7) - - allocate_leaves(employee, leave_period, leave_type.name, 5) - - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee.name, - leave_type=leave_type.name, - description="_Test Reason", - from_date=date, - to_date=add_days(date, 2), - company="_Test Company", - docstatus=1, - status="Approved", - ) - ) - leave_application.submit() - - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee.name, - leave_type=leave_type.name, - description="_Test Reason", - from_date=add_days(date, 4), - to_date=add_days(date, 8), - company="_Test Company", - docstatus=1, - status="Approved", - ) - ) - self.assertRaises(frappe.ValidationError, leave_application.insert) - - def test_applicable_after(self): - employee = get_employee() - leave_period = get_leave_period() - frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1) - leave_type = frappe.get_doc( - dict(leave_type_name="Test Leave Type", doctype="Leave Type", applicable_after=15) - ).insert() - date = add_days(nowdate(), -7) - frappe.db.set_value("Employee", employee.name, "date_of_joining", date) - allocate_leaves(employee, leave_period, leave_type.name, 10) - - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee.name, - leave_type=leave_type.name, - description="_Test Reason", - from_date=date, - to_date=add_days(date, 4), - company="_Test Company", - docstatus=1, - status="Approved", - ) - ) - - self.assertRaises(frappe.ValidationError, leave_application.insert) - - frappe.delete_doc_if_exists("Leave Type", "Test Leave Type 1", force=1) - leave_type_1 = frappe.get_doc( - dict(leave_type_name="Test Leave Type 1", doctype="Leave Type") - ).insert() - - allocate_leaves(employee, leave_period, leave_type_1.name, 10) - - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee.name, - leave_type=leave_type_1.name, - description="_Test Reason", - from_date=date, - to_date=add_days(date, 4), - company="_Test Company", - docstatus=1, - status="Approved", - ) - ) - - self.assertTrue(leave_application.insert()) - frappe.db.set_value("Employee", employee.name, "date_of_joining", "2010-01-01") - - def test_max_continuous_leaves(self): - employee = get_employee() - leave_period = get_leave_period() - frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1) - leave_type = frappe.get_doc( - dict( - leave_type_name="Test Leave Type", - doctype="Leave Type", - max_leaves_allowed=15, - max_continuous_days_allowed=3, - ) - ).insert() - - date = add_days(nowdate(), -7) - - allocate_leaves(employee, leave_period, leave_type.name, 10) - - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee.name, - leave_type=leave_type.name, - description="_Test Reason", - from_date=date, - to_date=add_days(date, 4), - company="_Test Company", - docstatus=1, - status="Approved", - ) - ) - - self.assertRaises(frappe.ValidationError, leave_application.insert) - - def test_leave_balance_near_allocaton_expiry(self): - employee = get_employee() - leave_type = create_leave_type( - leave_type_name="_Test_CF_leave_expiry", - is_carry_forward=1, - expire_carry_forwarded_leaves_after_days=90, - ) - leave_type.insert() - - create_carry_forwarded_allocation(employee, leave_type) - details = get_leave_balance_on( - employee.name, leave_type.name, nowdate(), add_days(nowdate(), 8), for_consumption=True - ) - - self.assertEqual(details.leave_balance_for_consumption, 21) - self.assertEqual(details.leave_balance, 30) - - def test_earned_leaves_creation(self): - from erpnext.hr.utils import allocate_earned_leaves - - leave_period = get_leave_period() - employee = get_employee() - leave_type = "Test Earned Leave Type" - make_policy_assignment(employee, leave_type, leave_period) - - for i in range(0, 14): - allocate_earned_leaves() - - self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 6) - - # validate earned leaves creation without maximum leaves - frappe.db.set_value("Leave Type", leave_type, "max_leaves_allowed", 0) - - for i in range(0, 6): - allocate_earned_leaves() - - self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 9) - - # test to not consider current leave in leave balance while submitting - def test_current_leave_on_submit(self): - employee = get_employee() - - leave_type = "Sick Leave" - if not frappe.db.exists("Leave Type", leave_type): - frappe.get_doc(dict(leave_type_name=leave_type, doctype="Leave Type")).insert() - - allocation = frappe.get_doc( - dict( - doctype="Leave Allocation", - employee=employee.name, - leave_type=leave_type, - from_date="2018-10-01", - to_date="2018-10-10", - new_leaves_allocated=1, - ) - ) - allocation.insert(ignore_permissions=True) - allocation.submit() - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee.name, - leave_type=leave_type, - description="_Test Reason", - from_date="2018-10-02", - to_date="2018-10-02", - company="_Test Company", - status="Approved", - leave_approver="test@example.com", - ) - ) - self.assertTrue(leave_application.insert()) - leave_application.submit() - self.assertEqual(leave_application.docstatus, 1) - - def test_creation_of_leave_ledger_entry_on_submit(self): - employee = get_employee() - - leave_type = create_leave_type(leave_type_name="Test Leave Type 1") - leave_type.save() - - leave_allocation = create_leave_allocation( - employee=employee.name, employee_name=employee.employee_name, leave_type=leave_type.name - ) - leave_allocation.submit() - - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee.name, - leave_type=leave_type.name, - from_date=add_days(nowdate(), 1), - to_date=add_days(nowdate(), 4), - description="_Test Reason", - company="_Test Company", - docstatus=1, - status="Approved", - ) - ) - leave_application.submit() - leave_ledger_entry = frappe.get_all( - "Leave Ledger Entry", fields="*", filters=dict(transaction_name=leave_application.name) - ) - - self.assertEqual(leave_ledger_entry[0].employee, leave_application.employee) - self.assertEqual(leave_ledger_entry[0].leave_type, leave_application.leave_type) - self.assertEqual(leave_ledger_entry[0].leaves, leave_application.total_leave_days * -1) - - # check if leave ledger entry is deleted on cancellation - leave_application.cancel() - self.assertFalse( - frappe.db.exists("Leave Ledger Entry", {"transaction_name": leave_application.name}) - ) - - def test_ledger_entry_creation_on_intermediate_allocation_expiry(self): - employee = get_employee() - leave_type = create_leave_type( - leave_type_name="_Test_CF_leave_expiry", - is_carry_forward=1, - expire_carry_forwarded_leaves_after_days=90, - include_holiday=True, - ) - leave_type.submit() - - create_carry_forwarded_allocation(employee, leave_type) - - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee.name, - leave_type=leave_type.name, - from_date=add_days(nowdate(), -3), - to_date=add_days(nowdate(), 7), - half_day=1, - half_day_date=add_days(nowdate(), -3), - description="_Test Reason", - company="_Test Company", - docstatus=1, - status="Approved", - ) - ) - leave_application.submit() - - leave_ledger_entry = frappe.get_all( - "Leave Ledger Entry", "*", filters=dict(transaction_name=leave_application.name) - ) - - self.assertEqual(len(leave_ledger_entry), 2) - self.assertEqual(leave_ledger_entry[0].employee, leave_application.employee) - self.assertEqual(leave_ledger_entry[0].leave_type, leave_application.leave_type) - self.assertEqual(leave_ledger_entry[0].leaves, -8.5) - self.assertEqual(leave_ledger_entry[1].leaves, -2) - - def test_leave_application_creation_after_expiry(self): - # test leave balance for carry forwarded allocation - employee = get_employee() - leave_type = create_leave_type( - leave_type_name="_Test_CF_leave_expiry", - is_carry_forward=1, - expire_carry_forwarded_leaves_after_days=90, - ) - leave_type.submit() - - create_carry_forwarded_allocation(employee, leave_type) - - self.assertEqual( - get_leave_balance_on( - employee.name, leave_type.name, add_days(nowdate(), -85), add_days(nowdate(), -84) - ), - 0, - ) - - def test_leave_approver_perms(self): - employee = get_employee() - user = "test_approver_perm_emp@example.com" - make_employee(user, "_Test Company") - - # set approver for employee - employee.reload() - employee.leave_approver = user - employee.save() - self.assertTrue("Leave Approver" in frappe.get_roles(user)) - - make_allocation_record(employee.name) - - application = self.get_application(_test_records[0]) - application.from_date = "2018-01-01" - application.to_date = "2018-01-03" - application.leave_approver = user - application.insert() - self.assertTrue(application.name in frappe.share.get_shared("Leave Application", user)) - - # check shared doc revoked - application.reload() - application.leave_approver = "test@example.com" - application.save() - self.assertTrue(application.name not in frappe.share.get_shared("Leave Application", user)) - - application.reload() - application.leave_approver = user - application.save() - - frappe.set_user(user) - application.reload() - application.status = "Approved" - application.submit() - - # unset leave approver - frappe.set_user("Administrator") - employee.reload() - employee.leave_approver = "" - employee.save() - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_get_leave_details_for_dashboard(self): - employee = get_employee() - date = getdate() - year_start = getdate(get_year_start(date)) - year_end = getdate(get_year_ending(date)) - - # ALLOCATION = 30 - allocation = make_allocation_record( - employee=employee.name, from_date=year_start, to_date=year_end - ) - - # USED LEAVES = 4 - first_sunday = get_first_sunday(self.holiday_list) - leave_application = make_leave_application( - employee.name, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type" - ) - leave_application.reload() - - # LEAVES PENDING APPROVAL = 1 - leave_application = make_leave_application( - employee.name, - add_days(first_sunday, 5), - add_days(first_sunday, 5), - "_Test Leave Type", - submit=False, - ) - leave_application.status = "Open" - leave_application.save() - - details = get_leave_details(employee.name, allocation.from_date) - leave_allocation = details["leave_allocation"]["_Test Leave Type"] - self.assertEqual(leave_allocation["total_leaves"], 30) - self.assertEqual(leave_allocation["leaves_taken"], 4) - self.assertEqual(leave_allocation["expired_leaves"], 0) - self.assertEqual(leave_allocation["leaves_pending_approval"], 1) - self.assertEqual(leave_allocation["remaining_leaves"], 26) - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_get_earned_leave_details_for_dashboard(self): - from erpnext.hr.utils import allocate_earned_leaves - - leave_period = get_leave_period() - employee = get_employee() - leave_type = "Test Earned Leave Type" - leave_policy_assignments = make_policy_assignment(employee, leave_type, leave_period) - allocation = frappe.db.get_value( - "Leave Allocation", - {"leave_policy_assignment": leave_policy_assignments[0]}, - "name", - ) - allocation = frappe.get_doc("Leave Allocation", allocation) - allocation.new_leaves_allocated = 2 - allocation.save() - - for i in range(0, 6): - allocate_earned_leaves() - - first_sunday = get_first_sunday(self.holiday_list) - make_leave_application( - employee.name, add_days(first_sunday, 1), add_days(first_sunday, 1), leave_type - ) - - details = get_leave_details(employee.name, allocation.from_date) - leave_allocation = details["leave_allocation"][leave_type] - expected = { - "total_leaves": 2.0, - "expired_leaves": 0.0, - "leaves_taken": 1.0, - "leaves_pending_approval": 0.0, - "remaining_leaves": 1.0, - } - self.assertEqual(leave_allocation, expected) - - details = get_leave_details(employee.name, getdate()) - leave_allocation = details["leave_allocation"][leave_type] - - expected = { - "total_leaves": 5.0, - "expired_leaves": 0.0, - "leaves_taken": 1.0, - "leaves_pending_approval": 0.0, - "remaining_leaves": 4.0, - } - self.assertEqual(leave_allocation, expected) - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_get_leave_allocation_records(self): - employee = get_employee() - leave_type = create_leave_type( - leave_type_name="_Test_CF_leave_expiry", - is_carry_forward=1, - expire_carry_forwarded_leaves_after_days=90, - ) - leave_type.insert() - - leave_alloc = create_carry_forwarded_allocation(employee, leave_type) - details = get_leave_allocation_records(employee.name, getdate(), leave_type.name) - expected_data = { - "from_date": getdate(leave_alloc.from_date), - "to_date": getdate(leave_alloc.to_date), - "total_leaves_allocated": 30.0, - "unused_leaves": 15.0, - "new_leaves_allocated": 15.0, - "leave_type": leave_type.name, - } - self.assertEqual(details.get(leave_type.name), expected_data) - - -def create_carry_forwarded_allocation(employee, leave_type): - # initial leave allocation - leave_allocation = create_leave_allocation( - leave_type="_Test_CF_leave_expiry", - employee=employee.name, - employee_name=employee.employee_name, - from_date=add_months(nowdate(), -24), - to_date=add_months(nowdate(), -12), - carry_forward=0, - ) - leave_allocation.submit() - - leave_allocation = create_leave_allocation( - leave_type="_Test_CF_leave_expiry", - employee=employee.name, - employee_name=employee.employee_name, - from_date=add_days(nowdate(), -84), - to_date=add_days(nowdate(), 100), - carry_forward=1, - ) - leave_allocation.submit() - - return leave_allocation - - -def make_allocation_record( - employee=None, leave_type=None, from_date=None, to_date=None, carry_forward=False, leaves=None -): - allocation = frappe.get_doc( - { - "doctype": "Leave Allocation", - "employee": employee or "_T-Employee-00001", - "leave_type": leave_type or "_Test Leave Type", - "from_date": from_date or "2013-01-01", - "to_date": to_date or "2019-12-31", - "new_leaves_allocated": leaves or 30, - "carry_forward": carry_forward, - } - ) - - allocation.insert(ignore_permissions=True) - allocation.submit() - - return allocation - - -def get_employee(): - return frappe.get_doc("Employee", "_T-Employee-00001") - - -def set_leave_approver(): - employee = get_employee() - dept_doc = frappe.get_doc("Department", employee.department) - dept_doc.append("leave_approvers", {"approver": "test@example.com"}) - dept_doc.save(ignore_permissions=True) - - -def get_leave_period(): - leave_period_name = frappe.db.get_value("Leave Period", {"company": "_Test Company"}) - if leave_period_name: - return frappe.get_doc("Leave Period", leave_period_name) - else: - return frappe.get_doc( - dict( - name="Test Leave Period", - doctype="Leave Period", - from_date=add_months(nowdate(), -6), - to_date=add_months(nowdate(), 6), - company="_Test Company", - is_active=1, - ) - ).insert() - - -def allocate_leaves(employee, leave_period, leave_type, new_leaves_allocated, eligible_leaves=0): - allocate_leave = frappe.get_doc( - { - "doctype": "Leave Allocation", - "__islocal": 1, - "employee": employee.name, - "employee_name": employee.employee_name, - "leave_type": leave_type, - "from_date": leave_period.from_date, - "to_date": leave_period.to_date, - "new_leaves_allocated": new_leaves_allocated, - "docstatus": 1, - } - ).insert() - - allocate_leave.submit() - - -def get_first_sunday(holiday_list, for_date=None): - date = for_date or getdate() - month_start_date = get_first_day(date) - month_end_date = get_last_day(date) - first_sunday = frappe.db.sql( - """ - select holiday_date from `tabHoliday` - where parent = %s - and holiday_date between %s and %s - order by holiday_date - """, - (holiday_list, month_start_date, month_end_date), - )[0][0] - - return first_sunday - - -def make_policy_assignment(employee, leave_type, leave_period): - frappe.delete_doc_if_exists("Leave Type", leave_type, force=1) - frappe.get_doc( - dict( - leave_type_name=leave_type, - doctype="Leave Type", - is_earned_leave=1, - earned_leave_frequency="Monthly", - rounding=0.5, - max_leaves_allowed=6, - ) - ).insert() - - leave_policy = frappe.get_doc( - { - "doctype": "Leave Policy", - "title": "Test Leave Policy", - "leave_policy_details": [{"leave_type": leave_type, "annual_allocation": 6}], - } - ).insert() - - data = { - "assignment_based_on": "Leave Period", - "leave_policy": leave_policy.name, - "leave_period": leave_period.name, - } - - leave_policy_assignments = create_assignment_for_multiple_employees( - [employee.name], frappe._dict(data) - ) - return leave_policy_assignments diff --git a/erpnext/hr/doctype/leave_application/test_records.json b/erpnext/hr/doctype/leave_application/test_records.json deleted file mode 100644 index fe51488c70..0000000000 --- a/erpnext/hr/doctype/leave_application/test_records.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/erpnext/hr/doctype/leave_block_list/README.md b/erpnext/hr/doctype/leave_block_list/README.md deleted file mode 100644 index b23396c4a3..0000000000 --- a/erpnext/hr/doctype/leave_block_list/README.md +++ /dev/null @@ -1 +0,0 @@ -List of days on which leaves can only be approved by special users. \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_block_list/__init__.py b/erpnext/hr/doctype/leave_block_list/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_block_list/leave_block_list.js b/erpnext/hr/doctype/leave_block_list/leave_block_list.js deleted file mode 100644 index 7ccf59d2f6..0000000000 --- a/erpnext/hr/doctype/leave_block_list/leave_block_list.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Leave Block List', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/leave_block_list/leave_block_list.json b/erpnext/hr/doctype/leave_block_list/leave_block_list.json deleted file mode 100644 index fdb975ba85..0000000000 --- a/erpnext/hr/doctype/leave_block_list/leave_block_list.json +++ /dev/null @@ -1,248 +0,0 @@ -{ - "allow_copy": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "field:leave_block_list_name", - "beta": 0, - "creation": "2013-02-18 17:43:12", - "custom": 0, - "description": "Block Holidays on important days.", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "leave_block_list_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Leave Block List Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If not checked, the list will have to be added to each Department where it has to be applied.", - "fieldname": "applies_to_all_departments", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Applies to Company", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Stop users from making Leave Applications on following days.", - "fieldname": "block_days", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Block Days", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "leave_block_list_dates", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Leave Block List Dates", - "length": 0, - "no_copy": 0, - "options": "Leave Block List Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Allow the following users to approve Leave Applications for block days.", - "fieldname": "allow_list", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Allow Users", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "leave_block_list_allowed", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Leave Block List Allowed", - "length": 0, - "no_copy": 0, - "options": "Leave Block List Allow", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-calendar", - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2016-11-03 16:01:42.171113", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Block List", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "sort_order": "ASC", - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_block_list/leave_block_list.py b/erpnext/hr/doctype/leave_block_list/leave_block_list.py deleted file mode 100644 index a57ba84e38..0000000000 --- a/erpnext/hr/doctype/leave_block_list/leave_block_list.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document - - -class LeaveBlockList(Document): - def validate(self): - dates = [] - for d in self.get("leave_block_list_dates"): - - # date is not repeated - if d.block_date in dates: - frappe.msgprint(_("Date is repeated") + ":" + d.block_date, raise_exception=1) - dates.append(d.block_date) - - -@frappe.whitelist() -def get_applicable_block_dates(from_date, to_date, employee=None, company=None, all_lists=False): - block_dates = [] - for block_list in get_applicable_block_lists(employee, company, all_lists): - block_dates.extend( - frappe.db.sql( - """select block_date, reason - from `tabLeave Block List Date` where parent=%s - and block_date between %s and %s""", - (block_list, from_date, to_date), - as_dict=1, - ) - ) - - return block_dates - - -def get_applicable_block_lists(employee=None, company=None, all_lists=False): - block_lists = [] - - if not employee: - employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user}) - if not employee: - return [] - - if not company: - company = frappe.db.get_value("Employee", employee, "company") - - def add_block_list(block_list): - if block_list: - if all_lists or not is_user_in_allow_list(block_list): - block_lists.append(block_list) - - # per department - department = frappe.db.get_value("Employee", employee, "department") - if department: - block_list = frappe.db.get_value("Department", department, "leave_block_list") - add_block_list(block_list) - - # global - for block_list in frappe.db.sql_list( - """select name from `tabLeave Block List` - where applies_to_all_departments=1 and company=%s""", - company, - ): - add_block_list(block_list) - - return list(set(block_lists)) - - -def is_user_in_allow_list(block_list): - return frappe.session.user in frappe.db.sql_list( - """select allow_user - from `tabLeave Block List Allow` where parent=%s""", - block_list, - ) diff --git a/erpnext/hr/doctype/leave_block_list/leave_block_list_dashboard.py b/erpnext/hr/doctype/leave_block_list/leave_block_list_dashboard.py deleted file mode 100644 index afeb5ded39..0000000000 --- a/erpnext/hr/doctype/leave_block_list/leave_block_list_dashboard.py +++ /dev/null @@ -1,2 +0,0 @@ -def get_data(): - return {"fieldname": "leave_block_list", "transactions": [{"items": ["Department"]}]} diff --git a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.py b/erpnext/hr/doctype/leave_block_list/test_leave_block_list.py deleted file mode 100644 index be85a35414..0000000000 --- a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import unittest - -import frappe -from frappe.utils import getdate - -from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates - - -class TestLeaveBlockList(unittest.TestCase): - def tearDown(self): - frappe.set_user("Administrator") - - def test_get_applicable_block_dates(self): - frappe.set_user("test@example.com") - frappe.db.set_value( - "Department", "_Test Department - _TC", "leave_block_list", "_Test Leave Block List" - ) - self.assertTrue( - getdate("2013-01-02") - in [d.block_date for d in get_applicable_block_dates("2013-01-01", "2013-01-03")] - ) - - def test_get_applicable_block_dates_for_allowed_user(self): - frappe.set_user("test1@example.com") - frappe.db.set_value( - "Department", "_Test Department 1 - _TC", "leave_block_list", "_Test Leave Block List" - ) - self.assertEqual( - [], [d.block_date for d in get_applicable_block_dates("2013-01-01", "2013-01-03")] - ) - - def test_get_applicable_block_dates_all_lists(self): - frappe.set_user("test1@example.com") - frappe.db.set_value( - "Department", "_Test Department 1 - _TC", "leave_block_list", "_Test Leave Block List" - ) - self.assertTrue( - getdate("2013-01-02") - in [ - d.block_date for d in get_applicable_block_dates("2013-01-01", "2013-01-03", all_lists=True) - ] - ) - - -test_dependencies = ["Employee"] - -test_records = frappe.get_test_records("Leave Block List") diff --git a/erpnext/hr/doctype/leave_block_list/test_records.json b/erpnext/hr/doctype/leave_block_list/test_records.json deleted file mode 100644 index 04565116d1..0000000000 --- a/erpnext/hr/doctype/leave_block_list/test_records.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "company": "_Test Company", - "doctype": "Leave Block List", - "leave_block_list_allowed": [ - { - "allow_user": "test1@example.com", - "doctype": "Leave Block List Allow", - "parent": "_Test Leave Block List", - "parentfield": "leave_block_list_allowed", - "parenttype": "Leave Block List" - } - ], - "leave_block_list_dates": [ - { - "block_date": "2013-01-02", - "doctype": "Leave Block List Date", - "parent": "_Test Leave Block List", - "parentfield": "leave_block_list_dates", - "parenttype": "Leave Block List", - "reason": "First work day" - } - ], - "leave_block_list_name": "_Test Leave Block List", - "year": "_Test Fiscal Year 2013" - } -] \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_block_list_allow/README.md b/erpnext/hr/doctype/leave_block_list_allow/README.md deleted file mode 100644 index a735039a1a..0000000000 --- a/erpnext/hr/doctype/leave_block_list_allow/README.md +++ /dev/null @@ -1 +0,0 @@ -User allowed to approve leave on blocked date. \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_block_list_allow/__init__.py b/erpnext/hr/doctype/leave_block_list_allow/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.json b/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.json deleted file mode 100644 index fe10c7823c..0000000000 --- a/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-02-22 01:27:47", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "allow_user", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Allow User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "200px", - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "200px" - } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2016-07-11 03:28:02.032277", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Block List Allow", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.py b/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.py deleted file mode 100644 index 50dc125650..0000000000 --- a/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class LeaveBlockListAllow(Document): - pass diff --git a/erpnext/hr/doctype/leave_block_list_date/README.md b/erpnext/hr/doctype/leave_block_list_date/README.md deleted file mode 100644 index 658f35985d..0000000000 --- a/erpnext/hr/doctype/leave_block_list_date/README.md +++ /dev/null @@ -1 +0,0 @@ -Date blocked on parent Leave Block List. \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_block_list_date/__init__.py b/erpnext/hr/doctype/leave_block_list_date/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.json b/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.json deleted file mode 100644 index dbb903969f..0000000000 --- a/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-02-22 01:27:47", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "block_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Block Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "200px", - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "200px" - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "reason", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Reason", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "200px", - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "200px" - } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2016-07-11 03:28:02.099296", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Block List Date", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.py b/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.py deleted file mode 100644 index 36550ccd89..0000000000 --- a/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class LeaveBlockListDate(Document): - pass diff --git a/erpnext/hr/doctype/leave_control_panel/README.md b/erpnext/hr/doctype/leave_control_panel/README.md deleted file mode 100644 index f7d3357570..0000000000 --- a/erpnext/hr/doctype/leave_control_panel/README.md +++ /dev/null @@ -1 +0,0 @@ -Tool to allocate leaves in bulk. \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_control_panel/__init__.py b/erpnext/hr/doctype/leave_control_panel/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.js b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.js deleted file mode 100644 index 4a450807cc..0000000000 --- a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.js +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.ui.form.on("Leave Control Panel", { - onload: function(frm) { - if (!frm.doc.from_date) { - frm.set_value('from_date', frappe.datetime.get_today()); - } - }, - refresh: function(frm) { - frm.disable_save(); - }, - company: function(frm) { - if(frm.doc.company) { - frm.set_query("department", function() { - return { - "filters": { - "company": frm.doc.company, - } - }; - }); - } - } -}); diff --git a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.json b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.json deleted file mode 100644 index d9c592aa64..0000000000 --- a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "actions": [], - "allow_copy": 1, - "creation": "2013-01-10 16:34:15", - "doctype": "DocType", - "engine": "InnoDB", - "field_order": [ - "select_employees_section", - "company", - "employment_type", - "branch", - "department", - "column_break1", - "designation", - "employee_grade", - "employee", - "allocate_leaves_section", - "from_date", - "to_date", - "carry_forward", - "no_of_days", - "allocate", - "column_break_16", - "leave_policy", - "leave_type" - ], - "fields": [ - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company" - }, - { - "fieldname": "employment_type", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Employment Type (optional)", - "options": "Employment Type" - }, - { - "fieldname": "branch", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Branch (optional)", - "options": "Branch" - }, - { - "fieldname": "department", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Department (optional)", - "options": "Department" - }, - { - "fieldname": "designation", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Designation (optional)", - "options": "Designation" - }, - { - "fieldname": "employee_grade", - "fieldtype": "Link", - "label": "Employee Grade (optional)", - "options": "Employee Grade" - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "label": "Employee (optional)", - "options": "Employee" - }, - { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "width": "50%" - }, - { - "fieldname": "from_date", - "fieldtype": "Date", - "label": "From Date", - "reqd": 1 - }, - { - "fieldname": "to_date", - "fieldtype": "Date", - "label": "To Date", - "reqd": 1 - }, - { - "fieldname": "leave_policy", - "fieldtype": "Link", - "label": "Leave Policy", - "options": "Leave Policy" - }, - { - "fieldname": "leave_type", - "fieldtype": "Link", - "label": "Leave Type", - "options": "Leave Type" - }, - { - "default": "0", - "description": "Please select Carry Forward if you also want to include previous fiscal year's balance leaves to this fiscal year", - "fieldname": "carry_forward", - "fieldtype": "Check", - "label": "Carry Forward" - }, - { - "fieldname": "no_of_days", - "fieldtype": "Float", - "label": "New Leaves Allocated (In Days)", - "reqd": 1 - }, - { - "fieldname": "allocate", - "fieldtype": "Button", - "label": "Allocate", - "options": "allocate_leave" - }, - { - "fieldname": "select_employees_section", - "fieldtype": "Section Break", - "label": "Select Employees" - }, - { - "fieldname": "allocate_leaves_section", - "fieldtype": "Section Break", - "label": "Allocate Leaves" - }, - { - "fieldname": "column_break_16", - "fieldtype": "Column Break" - } - ], - "hide_toolbar": 1, - "icon": "fa fa-cog", - "idx": 1, - "issingle": 1, - "links": [], - "modified": "2019-12-12 18:51:41.573349", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Control Panel", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "read": 1, - "role": "HR User", - "write": 1 - } - ], - "read_only": 1, - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.py b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.py deleted file mode 100644 index c57f8ae72b..0000000000 --- a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe import _, msgprint -from frappe.model.document import Document -from frappe.utils import cint, comma_and, cstr, flt - - -class LeaveControlPanel(Document): - def get_employees(self): - conditions, values = [], [] - for field in ["company", "employment_type", "branch", "designation", "department"]: - if self.get(field): - conditions.append("{0}=%s".format(field)) - values.append(self.get(field)) - - condition_str = " and " + " and ".join(conditions) if len(conditions) else "" - - e = frappe.db.sql( - "select name from tabEmployee where status='Active' {condition}".format( - condition=condition_str - ), - tuple(values), - ) - - return e - - def validate_values(self): - for f in ["from_date", "to_date", "leave_type", "no_of_days"]: - if not self.get(f): - frappe.throw(_("{0} is required").format(self.meta.get_label(f))) - self.validate_from_to_dates("from_date", "to_date") - - @frappe.whitelist() - def allocate_leave(self): - self.validate_values() - leave_allocated_for = [] - employees = self.get_employees() - if not employees: - frappe.throw(_("No employee found")) - - for d in self.get_employees(): - try: - la = frappe.new_doc("Leave Allocation") - la.set("__islocal", 1) - la.employee = cstr(d[0]) - la.employee_name = frappe.db.get_value("Employee", cstr(d[0]), "employee_name") - la.leave_type = self.leave_type - la.from_date = self.from_date - la.to_date = self.to_date - la.carry_forward = cint(self.carry_forward) - la.new_leaves_allocated = flt(self.no_of_days) - la.docstatus = 1 - la.save() - leave_allocated_for.append(d[0]) - except Exception: - pass - if leave_allocated_for: - msgprint(_("Leaves Allocated Successfully for {0}").format(comma_and(leave_allocated_for))) diff --git a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.py b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.py deleted file mode 100644 index d5a9bc0486..0000000000 --- a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestLeaveControlPanel(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/leave_encashment/__init__.py b/erpnext/hr/doctype/leave_encashment/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.js b/erpnext/hr/doctype/leave_encashment/leave_encashment.js deleted file mode 100644 index 81936a4a38..0000000000 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.js +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Leave Encashment', { - onload: function(frm) { - // Ignore cancellation of doctype on cancel all. - frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"]; - }, - setup: function(frm) { - frm.set_query("leave_type", function() { - return { - filters: { - allow_encashment: 1 - } - } - }) - }, - refresh: function(frm) { - cur_frm.set_intro(""); - if(frm.doc.__islocal && !in_list(frappe.user_roles, "Employee")) { - frm.set_intro(__("Fill the form and save it")); - } - }, - employee: function(frm) { - if (frm.doc.employee) { - frappe.run_serially([ - () => frm.trigger('get_employee_currency'), - () => frm.trigger('get_leave_details_for_encashment') - ]); - } - }, - leave_type: function(frm) { - frm.trigger("get_leave_details_for_encashment"); - }, - encashment_date: function(frm) { - frm.trigger("get_leave_details_for_encashment"); - }, - get_leave_details_for_encashment: function(frm) { - if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type) { - return frappe.call({ - method: "get_leave_details_for_encashment", - doc: frm.doc, - callback: function(r) { - frm.refresh_fields(); - } - }); - } - }, - - get_employee_currency: function(frm) { - frappe.call({ - method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", - args: { - employee: frm.doc.employee, - }, - callback: function(r) { - if (r.message) { - frm.set_value('currency', r.message); - frm.refresh_fields(); - } - } - }); - }, -}); diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.json b/erpnext/hr/doctype/leave_encashment/leave_encashment.json deleted file mode 100644 index cc4e53eb90..0000000000 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.json +++ /dev/null @@ -1,228 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-ENC-.YYYY.-.#####", - "creation": "2018-04-13 15:31:51.197046", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "leave_period", - "employee", - "employee_name", - "department", - "company", - "column_break_4", - "leave_type", - "leave_allocation", - "leave_balance", - "encashable_days", - "amended_from", - "payroll", - "encashment_date", - "additional_salary", - "column_break_14", - "currency", - "encashment_amount" - ], - "fields": [ - { - "fieldname": "leave_period", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Leave Period", - "options": "Leave Period", - "reqd": 1 - }, - { - "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 - }, - { - "fieldname": "leave_type", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Leave Type", - "options": "Leave Type", - "reqd": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "fieldname": "leave_allocation", - "fieldtype": "Link", - "label": "Leave Allocation", - "no_copy": 1, - "options": "Leave Allocation", - "read_only": 1 - }, - { - "fieldname": "leave_balance", - "fieldtype": "Float", - "label": "Leave Balance", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "encashable_days", - "fieldtype": "Float", - "label": "Encashable days", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Leave Encashment", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "payroll", - "fieldtype": "Section Break", - "label": "Payroll" - }, - { - "fieldname": "encashment_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Encashment Amount", - "no_copy": 1, - "options": "currency", - "read_only": 1 - }, - { - "default": "Today", - "fieldname": "encashment_date", - "fieldtype": "Date", - "label": "Encashment Date" - }, - { - "fieldname": "additional_salary", - "fieldtype": "Link", - "label": "Additional Salary", - "no_copy": 1, - "options": "Additional Salary", - "read_only": 1 - }, - { - "depends_on": "eval:(doc.docstatus==1 || doc.employee)", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "print_hide": 1, - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "column_break_14", - "fieldtype": "Column Break" - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-18 19:16:52.414356", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Encashment", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "write": 1 - } - ], - "search_fields": "employee,employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py deleted file mode 100644 index 7c0f0db197..0000000000 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import getdate, nowdate - -from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period -from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry -from erpnext.hr.utils import set_employee_name, validate_active_employee -from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import ( - get_assigned_salary_structure, -) - - -class LeaveEncashment(Document): - def validate(self): - set_employee_name(self) - validate_active_employee(self.employee) - self.get_leave_details_for_encashment() - self.validate_salary_structure() - - if not self.encashment_date: - self.encashment_date = getdate(nowdate()) - - def validate_salary_structure(self): - if not frappe.db.exists("Salary Structure Assignment", {"employee": self.employee}): - frappe.throw( - _("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format( - self.employee - ) - ) - - def before_submit(self): - if self.encashment_amount <= 0: - frappe.throw(_("You can only submit Leave Encashment for a valid encashment amount")) - - def on_submit(self): - if not self.leave_allocation: - self.leave_allocation = self.get_leave_allocation().get("name") - additional_salary = frappe.new_doc("Additional Salary") - additional_salary.company = frappe.get_value("Employee", self.employee, "company") - additional_salary.employee = self.employee - additional_salary.currency = self.currency - earning_component = frappe.get_value("Leave Type", self.leave_type, "earning_component") - if not earning_component: - frappe.throw(_("Please set Earning Component for Leave type: {0}.").format(self.leave_type)) - additional_salary.salary_component = earning_component - additional_salary.payroll_date = self.encashment_date - additional_salary.amount = self.encashment_amount - additional_salary.ref_doctype = self.doctype - additional_salary.ref_docname = self.name - additional_salary.submit() - - # Set encashed leaves in Allocation - frappe.db.set_value( - "Leave Allocation", - self.leave_allocation, - "total_leaves_encashed", - frappe.db.get_value("Leave Allocation", self.leave_allocation, "total_leaves_encashed") - + self.encashable_days, - ) - - self.create_leave_ledger_entry() - - def on_cancel(self): - if self.additional_salary: - frappe.get_doc("Additional Salary", self.additional_salary).cancel() - self.db_set("additional_salary", "") - - if self.leave_allocation: - frappe.db.set_value( - "Leave Allocation", - self.leave_allocation, - "total_leaves_encashed", - frappe.db.get_value("Leave Allocation", self.leave_allocation, "total_leaves_encashed") - - self.encashable_days, - ) - self.create_leave_ledger_entry(submit=False) - - @frappe.whitelist() - def get_leave_details_for_encashment(self): - salary_structure = get_assigned_salary_structure( - self.employee, self.encashment_date or getdate(nowdate()) - ) - if not salary_structure: - frappe.throw( - _("No Salary Structure assigned for Employee {0} on given date {1}").format( - self.employee, self.encashment_date - ) - ) - - if not frappe.db.get_value("Leave Type", self.leave_type, "allow_encashment"): - frappe.throw(_("Leave Type {0} is not encashable").format(self.leave_type)) - - allocation = self.get_leave_allocation() - - if not allocation: - frappe.throw( - _("No Leaves Allocated to Employee: {0} for Leave Type: {1}").format( - self.employee, self.leave_type - ) - ) - - self.leave_balance = ( - allocation.total_leaves_allocated - - allocation.carry_forwarded_leaves_count - # adding this because the function returns a -ve number - + get_leaves_for_period( - self.employee, self.leave_type, allocation.from_date, self.encashment_date - ) - ) - - encashable_days = self.leave_balance - frappe.db.get_value( - "Leave Type", self.leave_type, "encashment_threshold_days" - ) - self.encashable_days = encashable_days if encashable_days > 0 else 0 - - per_day_encashment = frappe.db.get_value( - "Salary Structure", salary_structure, "leave_encashment_amount_per_day" - ) - self.encashment_amount = ( - self.encashable_days * per_day_encashment if per_day_encashment > 0 else 0 - ) - - self.leave_allocation = allocation.name - return True - - def get_leave_allocation(self): - date = self.encashment_date or getdate() - - LeaveAllocation = frappe.qb.DocType("Leave Allocation") - leave_allocation = ( - frappe.qb.from_(LeaveAllocation) - .select( - LeaveAllocation.name, - LeaveAllocation.from_date, - LeaveAllocation.to_date, - LeaveAllocation.total_leaves_allocated, - LeaveAllocation.carry_forwarded_leaves_count, - ) - .where( - ((LeaveAllocation.from_date <= date) & (date <= LeaveAllocation.to_date)) - & (LeaveAllocation.docstatus == 1) - & (LeaveAllocation.leave_type == self.leave_type) - & (LeaveAllocation.employee == self.employee) - ) - ).run(as_dict=True) - - return leave_allocation[0] if leave_allocation else None - - def create_leave_ledger_entry(self, submit=True): - args = frappe._dict( - leaves=self.encashable_days * -1, - from_date=self.encashment_date, - to_date=self.encashment_date, - is_carry_forward=0, - ) - create_leave_ledger_entry(self, args, submit) - - # create reverse entry for expired leaves - leave_allocation = self.get_leave_allocation() - if not leave_allocation: - return - - to_date = leave_allocation.get("to_date") - if to_date < getdate(nowdate()): - args = frappe._dict( - leaves=self.encashable_days, from_date=to_date, to_date=to_date, is_carry_forward=0 - ) - create_leave_ledger_entry(self, args, submit) - - -def create_leave_encashment(leave_allocation): - """Creates leave encashment for the given allocations""" - for allocation in leave_allocation: - if not get_assigned_salary_structure(allocation.employee, allocation.to_date): - continue - leave_encashment = frappe.get_doc( - dict( - doctype="Leave Encashment", - leave_period=allocation.leave_period, - employee=allocation.employee, - leave_type=allocation.leave_type, - encashment_date=allocation.to_date, - ) - ) - leave_encashment.insert(ignore_permissions=True) diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py deleted file mode 100644 index d06b6a3764..0000000000 --- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days, get_year_ending, get_year_start, getdate - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list -from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period -from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy -from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import ( - create_assignment_for_multiple_employees, -) -from erpnext.payroll.doctype.salary_slip.test_salary_slip import ( - make_holiday_list, - make_leave_application, -) -from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - -test_records = frappe.get_test_records("Leave Type") - - -class TestLeaveEncashment(FrappeTestCase): - def setUp(self): - frappe.db.delete("Leave Period") - frappe.db.delete("Leave Policy Assignment") - frappe.db.delete("Leave Allocation") - frappe.db.delete("Leave Ledger Entry") - frappe.db.delete("Additional Salary") - frappe.db.delete("Leave Encashment") - - if not frappe.db.exists("Leave Type", "_Test Leave Type Encashment"): - frappe.get_doc(test_records[2]).insert() - - date = getdate() - year_start = getdate(get_year_start(date)) - year_end = getdate(get_year_ending(date)) - - make_holiday_list("_Test Leave Encashment", year_start, year_end) - - # create the leave policy - leave_policy = create_leave_policy( - leave_type="_Test Leave Type Encashment", annual_allocation=10 - ) - leave_policy.submit() - - # create employee, salary structure and assignment - self.employee = make_employee("test_employee_encashment@example.com", company="_Test Company") - - self.leave_period = create_leave_period(year_start, year_end, "_Test Company") - - data = { - "assignment_based_on": "Leave Period", - "leave_policy": leave_policy.name, - "leave_period": self.leave_period.name, - } - - leave_policy_assignments = create_assignment_for_multiple_employees( - [self.employee], frappe._dict(data) - ) - - salary_structure = make_salary_structure( - "Salary Structure for Encashment", - "Monthly", - self.employee, - other_details={"leave_encashment_amount_per_day": 50}, - ) - - @set_holiday_list("_Test Leave Encashment", "_Test Company") - def test_leave_balance_value_and_amount(self): - leave_encashment = frappe.get_doc( - dict( - doctype="Leave Encashment", - employee=self.employee, - leave_type="_Test Leave Type Encashment", - leave_period=self.leave_period.name, - encashment_date=self.leave_period.to_date, - currency="INR", - ) - ).insert() - - self.assertEqual(leave_encashment.leave_balance, 10) - self.assertEqual(leave_encashment.encashable_days, 5) - self.assertEqual(leave_encashment.encashment_amount, 250) - - leave_encashment.submit() - - # assert links - add_sal = frappe.get_all("Additional Salary", filters={"ref_docname": leave_encashment.name})[0] - self.assertTrue(add_sal) - - @set_holiday_list("_Test Leave Encashment", "_Test Company") - def test_leave_balance_value_with_leaves_and_amount(self): - date = self.leave_period.from_date - leave_application = make_leave_application( - self.employee, date, add_days(date, 3), "_Test Leave Type Encashment" - ) - leave_application.reload() - - leave_encashment = frappe.get_doc( - dict( - doctype="Leave Encashment", - employee=self.employee, - leave_type="_Test Leave Type Encashment", - leave_period=self.leave_period.name, - encashment_date=self.leave_period.to_date, - currency="INR", - ) - ).insert() - - self.assertEqual(leave_encashment.leave_balance, 10 - leave_application.total_leave_days) - # encashable days threshold is 5, total leaves are 6, so encashable days = 6-5 = 1 - # with charge of 50 per day - self.assertEqual(leave_encashment.encashable_days, leave_encashment.leave_balance - 5) - self.assertEqual(leave_encashment.encashment_amount, 50) - - leave_encashment.submit() - - # assert links - add_sal = frappe.get_all("Additional Salary", filters={"ref_docname": leave_encashment.name})[0] - self.assertTrue(add_sal) - - @set_holiday_list("_Test Leave Encashment", "_Test Company") - def test_creation_of_leave_ledger_entry_on_submit(self): - leave_encashment = frappe.get_doc( - dict( - doctype="Leave Encashment", - employee=self.employee, - leave_type="_Test Leave Type Encashment", - leave_period=self.leave_period.name, - encashment_date=self.leave_period.to_date, - currency="INR", - ) - ).insert() - - leave_encashment.submit() - - leave_ledger_entry = frappe.get_all( - "Leave Ledger Entry", fields="*", filters=dict(transaction_name=leave_encashment.name) - ) - - self.assertEqual(len(leave_ledger_entry), 1) - self.assertEqual(leave_ledger_entry[0].employee, leave_encashment.employee) - self.assertEqual(leave_ledger_entry[0].leave_type, leave_encashment.leave_type) - self.assertEqual(leave_ledger_entry[0].leaves, leave_encashment.encashable_days * -1) - - # check if leave ledger entry is deleted on cancellation - - frappe.db.sql( - "Delete from `tabAdditional Salary` WHERE ref_docname = %s", (leave_encashment.name) - ) - - leave_encashment.cancel() - self.assertFalse( - frappe.db.exists("Leave Ledger Entry", {"transaction_name": leave_encashment.name}) - ) diff --git a/erpnext/hr/doctype/leave_ledger_entry/__init__.py b/erpnext/hr/doctype/leave_ledger_entry/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.js b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.js deleted file mode 100644 index c68d518f67..0000000000 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Leave Ledger Entry', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json deleted file mode 100644 index d74760a5cf..0000000000 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json +++ /dev/null @@ -1,190 +0,0 @@ -{ - "actions": [], - "creation": "2019-05-09 15:47:39.760406", - "doctype": "DocType", - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "leave_type", - "transaction_type", - "transaction_name", - "company", - "leaves", - "column_break_7", - "from_date", - "to_date", - "holiday_list", - "is_carry_forward", - "is_expired", - "is_lwp", - "amended_from" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Employee", - "options": "Employee" - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "label": "Employee Name" - }, - { - "fieldname": "leave_type", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Leave Type", - "options": "Leave Type" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Leave Ledger Entry", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "transaction_type", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Transaction Type", - "options": "DocType" - }, - { - "fieldname": "transaction_name", - "fieldtype": "Dynamic Link", - "label": "Transaction Name", - "options": "transaction_type" - }, - { - "fieldname": "leaves", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Leaves" - }, - { - "fieldname": "from_date", - "fieldtype": "Date", - "label": "From Date" - }, - { - "fieldname": "to_date", - "fieldtype": "Date", - "label": "To Date" - }, - { - "default": "0", - "fieldname": "is_carry_forward", - "fieldtype": "Check", - "label": "Is Carry Forward" - }, - { - "default": "0", - "fieldname": "is_expired", - "fieldtype": "Check", - "label": "Is Expired" - }, - { - "fieldname": "column_break_7", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "is_lwp", - "fieldtype": "Check", - "label": "Is Leave Without Pay" - }, - { - "fieldname": "holiday_list", - "fieldtype": "Link", - "label": "Holiday List", - "options": "Holiday List" - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "read_only": 1, - "reqd": 1 - } - ], - "in_create": 1, - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2021-01-04 18:47:45.146652", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Ledger Entry", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "All", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "ASC", - "title_field": "employee" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py deleted file mode 100644 index fed9f770df..0000000000 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ /dev/null @@ -1,227 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import DATE_FORMAT, flt, getdate, today - - -class LeaveLedgerEntry(Document): - def validate(self): - if getdate(self.from_date) > getdate(self.to_date): - frappe.throw(_("To date needs to be before from date")) - - def on_cancel(self): - # allow cancellation of expiry leaves - if self.is_expired: - frappe.db.set_value("Leave Allocation", self.transaction_name, "expired", 0) - else: - frappe.throw(_("Only expired allocation can be cancelled")) - - -def validate_leave_allocation_against_leave_application(ledger): - """Checks that leave allocation has no leave application against it""" - leave_application_records = frappe.db.sql_list( - """ - SELECT transaction_name - FROM `tabLeave Ledger Entry` - WHERE - employee=%s - AND leave_type=%s - AND transaction_type='Leave Application' - AND from_date>=%s - AND to_date<=%s - """, - (ledger.employee, ledger.leave_type, ledger.from_date, ledger.to_date), - ) - - if leave_application_records: - frappe.throw( - _("Leave allocation {0} is linked with the Leave Application {1}").format( - ledger.transaction_name, ", ".join(leave_application_records) - ) - ) - - -def create_leave_ledger_entry(ref_doc, args, submit=True): - ledger = frappe._dict( - doctype="Leave Ledger Entry", - employee=ref_doc.employee, - employee_name=ref_doc.employee_name, - leave_type=ref_doc.leave_type, - transaction_type=ref_doc.doctype, - transaction_name=ref_doc.name, - is_carry_forward=0, - is_expired=0, - is_lwp=0, - ) - ledger.update(args) - - if submit: - doc = frappe.get_doc(ledger) - doc.flags.ignore_permissions = 1 - doc.submit() - else: - delete_ledger_entry(ledger) - - -def delete_ledger_entry(ledger): - """Delete ledger entry on cancel of leave application/allocation/encashment""" - if ledger.transaction_type == "Leave Allocation": - validate_leave_allocation_against_leave_application(ledger) - - expired_entry = get_previous_expiry_ledger_entry(ledger) - frappe.db.sql( - """DELETE - FROM `tabLeave Ledger Entry` - WHERE - `transaction_name`=%s - OR `name`=%s""", - (ledger.transaction_name, expired_entry), - ) - - -def get_previous_expiry_ledger_entry(ledger): - """Returns the expiry ledger entry having same creation date as the ledger entry to be cancelled""" - creation_date = frappe.db.get_value( - "Leave Ledger Entry", - filters={ - "transaction_name": ledger.transaction_name, - "is_expired": 0, - "transaction_type": "Leave Allocation", - }, - fieldname=["creation"], - ) - - creation_date = creation_date.strftime(DATE_FORMAT) if creation_date else "" - - return frappe.db.get_value( - "Leave Ledger Entry", - filters={ - "creation": ("like", creation_date + "%"), - "employee": ledger.employee, - "leave_type": ledger.leave_type, - "is_expired": 1, - "docstatus": 1, - "is_carry_forward": 0, - }, - fieldname=["name"], - ) - - -def process_expired_allocation(): - """Check if a carry forwarded allocation has expired and create a expiry ledger entry - Case 1: carry forwarded expiry period is set for the leave type, - create a separate leave expiry entry against each entry of carry forwarded and non carry forwarded leaves - Case 2: leave type has no specific expiry period for carry forwarded leaves - and there is no carry forwarded leave allocation, create a single expiry against the remaining leaves. - """ - - # fetch leave type records that has carry forwarded leaves expiry - leave_type_records = frappe.db.get_values( - "Leave Type", filters={"expire_carry_forwarded_leaves_after_days": (">", 0)}, fieldname=["name"] - ) - - leave_type = [record[0] for record in leave_type_records] or [""] - - # fetch non expired leave ledger entry of transaction_type allocation - expire_allocation = frappe.db.sql( - """ - SELECT - leaves, to_date, employee, leave_type, - is_carry_forward, transaction_name as name, transaction_type - FROM `tabLeave Ledger Entry` l - WHERE (NOT EXISTS - (SELECT name - FROM `tabLeave Ledger Entry` - WHERE - transaction_name = l.transaction_name - AND transaction_type = 'Leave Allocation' - AND name<>l.name - AND docstatus = 1 - AND ( - is_carry_forward=l.is_carry_forward - OR (is_carry_forward = 0 AND leave_type not in %s) - ))) - AND transaction_type = 'Leave Allocation' - AND to_date < %s""", - (leave_type, today()), - as_dict=1, - ) - - if expire_allocation: - create_expiry_ledger_entry(expire_allocation) - - -def create_expiry_ledger_entry(allocations): - """Create ledger entry for expired allocation""" - for allocation in allocations: - if allocation.is_carry_forward: - expire_carried_forward_allocation(allocation) - else: - expire_allocation(allocation) - - -def get_remaining_leaves(allocation): - """Returns remaining leaves from the given allocation""" - return frappe.db.get_value( - "Leave Ledger Entry", - filters={ - "employee": allocation.employee, - "leave_type": allocation.leave_type, - "to_date": ("<=", allocation.to_date), - "docstatus": 1, - }, - fieldname=["SUM(leaves)"], - ) - - -@frappe.whitelist() -def expire_allocation(allocation, expiry_date=None): - """expires non-carry forwarded allocation""" - leaves = get_remaining_leaves(allocation) - expiry_date = expiry_date if expiry_date else allocation.to_date - - # allows expired leaves entry to be created/reverted - if leaves: - args = dict( - leaves=flt(leaves) * -1, - transaction_name=allocation.name, - transaction_type="Leave Allocation", - from_date=expiry_date, - to_date=expiry_date, - is_carry_forward=0, - is_expired=1, - ) - create_leave_ledger_entry(allocation, args) - - frappe.db.set_value("Leave Allocation", allocation.name, "expired", 1) - - -def expire_carried_forward_allocation(allocation): - """Expires remaining leaves in the on carried forward allocation""" - from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period - - leaves_taken = get_leaves_for_period( - allocation.employee, - allocation.leave_type, - allocation.from_date, - allocation.to_date, - skip_expired_leaves=False, - ) - leaves = flt(allocation.leaves) + flt(leaves_taken) - - # allow expired leaves entry to be created - if leaves > 0: - args = frappe._dict( - transaction_name=allocation.name, - transaction_type="Leave Allocation", - leaves=allocation.leaves * -1, - is_carry_forward=allocation.is_carry_forward, - is_expired=1, - from_date=allocation.to_date, - to_date=allocation.to_date, - ) - create_leave_ledger_entry(allocation, args) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry_list.js b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry_list.js deleted file mode 100644 index 889325bf2b..0000000000 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry_list.js +++ /dev/null @@ -1,13 +0,0 @@ -frappe.listview_settings['Leave Ledger Entry'] = { - onload: function(listview) { - if(listview.page.fields_dict.transaction_type) { - listview.page.fields_dict.transaction_type.get_query = function() { - return { - "filters": { - "name": ["in", ["Leave Allocation", "Leave Application", "Leave Encashment"]], - } - }; - }; - } - } -}; diff --git a/erpnext/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py deleted file mode 100644 index 3121109183..0000000000 --- a/erpnext/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -import unittest - - -class TestLeaveLedgerEntry(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/leave_period/__init__.py b/erpnext/hr/doctype/leave_period/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_period/leave_period.js b/erpnext/hr/doctype/leave_period/leave_period.js deleted file mode 100644 index 0e88bc1671..0000000000 --- a/erpnext/hr/doctype/leave_period/leave_period.js +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Leave Period', { - from_date: (frm)=>{ - if (frm.doc.from_date && !frm.doc.to_date) { - var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12); - frm.set_value("to_date", frappe.datetime.add_days(a_year_from_start, -1)); - } - }, - onload: (frm) => { - frm.set_query("department", function() { - return { - "filters": { - "company": frm.doc.company, - } - }; - }); - }, -}); diff --git a/erpnext/hr/doctype/leave_period/leave_period.json b/erpnext/hr/doctype/leave_period/leave_period.json deleted file mode 100644 index 84ce1147e9..0000000000 --- a/erpnext/hr/doctype/leave_period/leave_period.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-LPR-.YYYY.-.#####", - "creation": "2018-04-13 15:20:52.864288", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "from_date", - "to_date", - "is_active", - "column_break_3", - "company", - "optional_holiday_list" - ], - "fields": [ - { - "fieldname": "from_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "From Date", - "reqd": 1 - }, - { - "fieldname": "to_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "To Date", - "reqd": 1 - }, - { - "default": "0", - "fieldname": "is_active", - "fieldtype": "Check", - "label": "Is Active" - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "optional_holiday_list", - "fieldtype": "Link", - "label": "Holiday List for Optional Leave", - "options": "Holiday List" - } - ], - "links": [], - "modified": "2022-01-13 13:28:12.951025", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Period", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - } - ], - "search_fields": "from_date, to_date, company", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_period/leave_period.py b/erpnext/hr/doctype/leave_period/leave_period.py deleted file mode 100644 index 6e62bb5876..0000000000 --- a/erpnext/hr/doctype/leave_period/leave_period.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import getdate - -from erpnext.hr.utils import validate_overlap - - -class LeavePeriod(Document): - def validate(self): - self.validate_dates() - validate_overlap(self, self.from_date, self.to_date, self.company) - - def validate_dates(self): - if getdate(self.from_date) >= getdate(self.to_date): - frappe.throw(_("To date can not be equal or less than from date")) diff --git a/erpnext/hr/doctype/leave_period/leave_period_dashboard.py b/erpnext/hr/doctype/leave_period/leave_period_dashboard.py deleted file mode 100644 index 854f988f35..0000000000 --- a/erpnext/hr/doctype/leave_period/leave_period_dashboard.py +++ /dev/null @@ -1,8 +0,0 @@ -from frappe import _ - - -def get_data(): - return { - "fieldname": "leave_period", - "transactions": [{"label": _("Transactions"), "items": ["Leave Allocation"]}], - } diff --git a/erpnext/hr/doctype/leave_period/test_leave_period.py b/erpnext/hr/doctype/leave_period/test_leave_period.py deleted file mode 100644 index 09235741b6..0000000000 --- a/erpnext/hr/doctype/leave_period/test_leave_period.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe - -import erpnext - -test_dependencies = ["Employee", "Leave Type", "Leave Policy"] - - -class TestLeavePeriod(unittest.TestCase): - pass - - -def create_leave_period(from_date, to_date, company=None): - leave_period = frappe.db.get_value( - "Leave Period", - dict( - company=company or erpnext.get_default_company(), - from_date=from_date, - to_date=to_date, - is_active=1, - ), - "name", - ) - if leave_period: - return frappe.get_doc("Leave Period", leave_period) - - leave_period = frappe.get_doc( - { - "doctype": "Leave Period", - "company": company or erpnext.get_default_company(), - "from_date": from_date, - "to_date": to_date, - "is_active": 1, - } - ).insert() - return leave_period diff --git a/erpnext/hr/doctype/leave_policy/__init__.py b/erpnext/hr/doctype/leave_policy/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_policy/leave_policy.js b/erpnext/hr/doctype/leave_policy/leave_policy.js deleted file mode 100644 index fdf8e0cdbb..0000000000 --- a/erpnext/hr/doctype/leave_policy/leave_policy.js +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Leave Policy', { -}); - -frappe.ui.form.on('Leave Policy Detail',{ - leave_type: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if(child.leave_type){ - frappe.call({ - method: "frappe.client.get_value", - args: { - doctype: "Leave Type", - fieldname: "max_leaves_allowed", - filters: { name: child.leave_type } - }, - callback: function(r) { - if (r.message) { - child.annual_allocation = r.message.max_leaves_allowed; - refresh_field("leave_policy_details"); - } - } - }); - } - else{ - child.annual_allocation = ""; - refresh_field("leave_policy_details"); - } - } -}); diff --git a/erpnext/hr/doctype/leave_policy/leave_policy.json b/erpnext/hr/doctype/leave_policy/leave_policy.json deleted file mode 100644 index 6ac8f20ea2..0000000000 --- a/erpnext/hr/doctype/leave_policy/leave_policy.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "actions": [], - "autoname": "HR-LPOL-.YYYY.-.#####", - "creation": "2018-04-13 16:06:19.507624", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "title", - "leave_allocations_section", - "leave_policy_details", - "amended_from" - ], - "fields": [ - { - "allow_in_quick_entry": 1, - "fieldname": "leave_allocations_section", - "fieldtype": "Section Break", - "label": "Leave Allocations" - }, - { - "fieldname": "leave_policy_details", - "fieldtype": "Table", - "label": "Leave Policy Details", - "options": "Leave Policy Detail", - "reqd": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Leave Policy", - "print_hide": 1, - "read_only": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "title", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Title", - "reqd": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-19 13:07:40.556500", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Policy", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "title", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "title", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_policy/leave_policy.py b/erpnext/hr/doctype/leave_policy/leave_policy.py deleted file mode 100644 index 33c949354c..0000000000 --- a/erpnext/hr/doctype/leave_policy/leave_policy.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document - - -class LeavePolicy(Document): - def validate(self): - if self.leave_policy_details: - for lp_detail in self.leave_policy_details: - max_leaves_allowed = frappe.db.get_value( - "Leave Type", lp_detail.leave_type, "max_leaves_allowed" - ) - if max_leaves_allowed > 0 and lp_detail.annual_allocation > max_leaves_allowed: - frappe.throw( - _("Maximum leave allowed in the leave type {0} is {1}").format( - lp_detail.leave_type, max_leaves_allowed - ) - ) diff --git a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py deleted file mode 100644 index 57ea93ee46..0000000000 --- a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py +++ /dev/null @@ -1,10 +0,0 @@ -from frappe import _ - - -def get_data(): - return { - "fieldname": "leave_policy", - "transactions": [ - {"label": _("Leaves"), "items": ["Leave Policy Assignment", "Leave Allocation"]}, - ], - } diff --git a/erpnext/hr/doctype/leave_policy/test_leave_policy.py b/erpnext/hr/doctype/leave_policy/test_leave_policy.py deleted file mode 100644 index 33d5508705..0000000000 --- a/erpnext/hr/doctype/leave_policy/test_leave_policy.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe - - -class TestLeavePolicy(unittest.TestCase): - def test_max_leave_allowed(self): - random_leave_type = frappe.get_all("Leave Type", fields=["name", "max_leaves_allowed"]) - if random_leave_type: - random_leave_type = random_leave_type[0] - leave_type = frappe.get_doc("Leave Type", random_leave_type.name) - leave_type.max_leaves_allowed = 2 - leave_type.save() - - leave_policy = create_leave_policy( - leave_type=leave_type.name, annual_allocation=leave_type.max_leaves_allowed + 1 - ) - - self.assertRaises(frappe.ValidationError, leave_policy.insert) - - -def create_leave_policy(**args): - """Returns an object of leave policy""" - args = frappe._dict(args) - return frappe.get_doc( - { - "doctype": "Leave Policy", - "title": "Test Leave Policy", - "leave_policy_details": [ - { - "leave_type": args.leave_type or "_Test Leave Type", - "annual_allocation": args.annual_allocation or 10, - } - ], - } - ) diff --git a/erpnext/hr/doctype/leave_policy_assignment/__init__.py b/erpnext/hr/doctype/leave_policy_assignment/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.js b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.js deleted file mode 100644 index 0aaf4cf616..0000000000 --- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.js +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Leave Policy Assignment', { - onload: function(frm) { - frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"]; - - frm.set_query('leave_policy', function() { - return { - filters: { - "docstatus": 1 - } - }; - }); - frm.set_query('leave_period', function() { - return { - filters: { - "is_active": 1, - "company": frm.doc.company - } - }; - }); - }, - - assignment_based_on: function(frm) { - if (frm.doc.assignment_based_on) { - frm.events.set_effective_date(frm); - } else { - frm.set_value("effective_from", ''); - frm.set_value("effective_to", ''); - } - }, - - leave_period: function(frm) { - if (frm.doc.leave_period) { - frm.events.set_effective_date(frm); - } - }, - - set_effective_date: function(frm) { - if (frm.doc.assignment_based_on == "Leave Period" && frm.doc.leave_period) { - frappe.model.with_doc("Leave Period", frm.doc.leave_period, function () { - let from_date = frappe.model.get_value("Leave Period", frm.doc.leave_period, "from_date"); - let to_date = frappe.model.get_value("Leave Period", frm.doc.leave_period, "to_date"); - frm.set_value("effective_from", from_date); - frm.set_value("effective_to", to_date); - - }); - } else if (frm.doc.assignment_based_on == "Joining Date" && frm.doc.employee) { - frappe.model.with_doc("Employee", frm.doc.employee, function () { - let from_date = frappe.model.get_value("Employee", frm.doc.employee, "date_of_joining"); - frm.set_value("effective_from", from_date); - frm.set_value("effective_to", frappe.datetime.add_months(frm.doc.effective_from, 12)); - }); - } - frm.refresh(); - } - -}); diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json deleted file mode 100644 index 27f0540b24..0000000000 --- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "actions": [], - "autoname": "HR-LPOL-ASSGN-.#####", - "creation": "2020-08-19 13:02:43.343666", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "company", - "leave_policy", - "carry_forward", - "column_break_5", - "assignment_based_on", - "leave_period", - "effective_from", - "effective_to", - "leaves_allocated", - "amended_from" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "label": "Employee name", - "read_only": 1 - }, - { - "fieldname": "leave_policy", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Leave Policy", - "options": "Leave Policy", - "reqd": 1 - }, - { - "fieldname": "assignment_based_on", - "fieldtype": "Select", - "label": "Assignment based on", - "options": "\nLeave Period\nJoining Date" - }, - { - "depends_on": "eval:doc.assignment_based_on == \"Leave Period\"", - "fieldname": "leave_period", - "fieldtype": "Link", - "label": "Leave Period", - "mandatory_depends_on": "eval:doc.assignment_based_on == \"Leave Period\"", - "options": "Leave Period" - }, - { - "fieldname": "effective_from", - "fieldtype": "Date", - "label": "Effective From", - "read_only_depends_on": "eval:doc.assignment_based_on", - "reqd": 1 - }, - { - "fieldname": "effective_to", - "fieldtype": "Date", - "label": "Effective To", - "read_only_depends_on": "eval:doc.assignment_based_on == \"Leave Period\"", - "reqd": 1 - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Company", - "options": "Company", - "read_only": 1 - }, - { - "fieldname": "column_break_5", - "fieldtype": "Column Break" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Leave Policy Assignment", - "print_hide": 1, - "read_only": 1 - }, - { - "default": "0", - "fieldname": "carry_forward", - "fieldtype": "Check", - "label": "Add unused leaves from previous allocations" - }, - { - "default": "0", - "fieldname": "leaves_allocated", - "fieldtype": "Check", - "hidden": 1, - "label": "Leaves Allocated", - "no_copy": 1, - "print_hide": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-13 13:37:11.218882", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Policy Assignment", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py deleted file mode 100644 index 2ed86f301c..0000000000 --- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import json -from math import ceil - -import frappe -from frappe import _, bold -from frappe.model.document import Document -from frappe.utils import date_diff, flt, formatdate, get_last_day, get_link_to_form, getdate - - -class LeavePolicyAssignment(Document): - def validate(self): - self.set_dates() - self.validate_policy_assignment_overlap() - self.warn_about_carry_forwarding() - - def on_submit(self): - self.grant_leave_alloc_for_employee() - - def set_dates(self): - if self.assignment_based_on == "Leave Period": - self.effective_from, self.effective_to = frappe.db.get_value( - "Leave Period", self.leave_period, ["from_date", "to_date"] - ) - elif self.assignment_based_on == "Joining Date": - self.effective_from = frappe.db.get_value("Employee", self.employee, "date_of_joining") - - def validate_policy_assignment_overlap(self): - leave_policy_assignments = frappe.get_all( - "Leave Policy Assignment", - filters={ - "employee": self.employee, - "name": ("!=", self.name), - "docstatus": 1, - "effective_to": (">=", self.effective_from), - "effective_from": ("<=", self.effective_to), - }, - ) - - if len(leave_policy_assignments): - frappe.throw( - _("Leave Policy: {0} already assigned for Employee {1} for period {2} to {3}").format( - bold(self.leave_policy), - bold(self.employee), - bold(formatdate(self.effective_from)), - bold(formatdate(self.effective_to)), - ) - ) - - def warn_about_carry_forwarding(self): - if not self.carry_forward: - return - - leave_types = get_leave_type_details() - leave_policy = frappe.get_doc("Leave Policy", self.leave_policy) - - for policy in leave_policy.leave_policy_details: - leave_type = leave_types.get(policy.leave_type) - if not leave_type.is_carry_forward: - msg = _( - "Leaves for the Leave Type {0} won't be carry-forwarded since carry-forwarding is disabled." - ).format(frappe.bold(get_link_to_form("Leave Type", leave_type.name))) - frappe.msgprint(msg, indicator="orange", alert=True) - - @frappe.whitelist() - def grant_leave_alloc_for_employee(self): - if self.leaves_allocated: - frappe.throw(_("Leave already have been assigned for this Leave Policy Assignment")) - else: - leave_allocations = {} - leave_type_details = get_leave_type_details() - - leave_policy = frappe.get_doc("Leave Policy", self.leave_policy) - date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") - - for leave_policy_detail in leave_policy.leave_policy_details: - if not leave_type_details.get(leave_policy_detail.leave_type).is_lwp: - leave_allocation, new_leaves_allocated = self.create_leave_allocation( - leave_policy_detail.leave_type, - leave_policy_detail.annual_allocation, - leave_type_details, - date_of_joining, - ) - leave_allocations[leave_policy_detail.leave_type] = { - "name": leave_allocation, - "leaves": new_leaves_allocated, - } - self.db_set("leaves_allocated", 1) - return leave_allocations - - def create_leave_allocation( - self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining - ): - # Creates leave allocation for the given employee in the provided leave period - carry_forward = self.carry_forward - if self.carry_forward and not leave_type_details.get(leave_type).is_carry_forward: - carry_forward = 0 - - new_leaves_allocated = self.get_new_leaves( - leave_type, new_leaves_allocated, leave_type_details, date_of_joining - ) - - allocation = frappe.get_doc( - dict( - doctype="Leave Allocation", - employee=self.employee, - leave_type=leave_type, - from_date=self.effective_from, - to_date=self.effective_to, - new_leaves_allocated=new_leaves_allocated, - leave_period=self.leave_period if self.assignment_based_on == "Leave Policy" else "", - leave_policy_assignment=self.name, - leave_policy=self.leave_policy, - carry_forward=carry_forward, - ) - ) - allocation.save(ignore_permissions=True) - allocation.submit() - return allocation.name, new_leaves_allocated - - def get_new_leaves(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining): - from frappe.model.meta import get_field_precision - - precision = get_field_precision( - frappe.get_meta("Leave Allocation").get_field("new_leaves_allocated") - ) - - # Earned Leaves and Compensatory Leaves are allocated by scheduler, initially allocate 0 - if leave_type_details.get(leave_type).is_compensatory == 1: - new_leaves_allocated = 0 - - elif leave_type_details.get(leave_type).is_earned_leave == 1: - if not self.assignment_based_on: - new_leaves_allocated = 0 - else: - # get leaves for past months if assignment is based on Leave Period / Joining Date - new_leaves_allocated = self.get_leaves_for_passed_months( - leave_type, new_leaves_allocated, leave_type_details, date_of_joining - ) - - # Calculate leaves at pro-rata basis for employees joining after the beginning of the given leave period - elif getdate(date_of_joining) > getdate(self.effective_from): - remaining_period = (date_diff(self.effective_to, date_of_joining) + 1) / ( - date_diff(self.effective_to, self.effective_from) + 1 - ) - new_leaves_allocated = ceil(new_leaves_allocated * remaining_period) - - return flt(new_leaves_allocated, precision) - - def get_leaves_for_passed_months( - self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining - ): - from erpnext.hr.utils import get_monthly_earned_leave - - current_date = frappe.flags.current_date or getdate() - if current_date > getdate(self.effective_to): - current_date = getdate(self.effective_to) - - from_date = getdate(self.effective_from) - if getdate(date_of_joining) > from_date: - from_date = getdate(date_of_joining) - - months_passed = 0 - based_on_doj = leave_type_details.get(leave_type).based_on_date_of_joining - - if current_date.year == from_date.year and current_date.month >= from_date.month: - months_passed = current_date.month - from_date.month - months_passed = add_current_month_if_applicable(months_passed, date_of_joining, based_on_doj) - - elif current_date.year > from_date.year: - months_passed = (12 - from_date.month) + current_date.month - months_passed = add_current_month_if_applicable(months_passed, date_of_joining, based_on_doj) - - if months_passed > 0: - monthly_earned_leave = get_monthly_earned_leave( - new_leaves_allocated, - leave_type_details.get(leave_type).earned_leave_frequency, - leave_type_details.get(leave_type).rounding, - ) - new_leaves_allocated = monthly_earned_leave * months_passed - else: - new_leaves_allocated = 0 - - return new_leaves_allocated - - -def add_current_month_if_applicable(months_passed, date_of_joining, based_on_doj): - date = getdate(frappe.flags.current_date) or getdate() - - if based_on_doj: - # if leave type allocation is based on DOJ, and the date of assignment creation is same as DOJ, - # then the month should be considered - if date.day == date_of_joining.day: - months_passed += 1 - else: - last_day_of_month = get_last_day(date) - # if its the last day of the month, then that month should be considered - if last_day_of_month == date: - months_passed += 1 - - return months_passed - - -@frappe.whitelist() -def create_assignment_for_multiple_employees(employees, data): - - if isinstance(employees, str): - employees = json.loads(employees) - - if isinstance(data, str): - data = frappe._dict(json.loads(data)) - - docs_name = [] - for employee in employees: - assignment = frappe.new_doc("Leave Policy Assignment") - assignment.employee = employee - assignment.assignment_based_on = data.assignment_based_on or None - assignment.leave_policy = data.leave_policy - assignment.effective_from = getdate(data.effective_from) or None - assignment.effective_to = getdate(data.effective_to) or None - assignment.leave_period = data.leave_period or None - assignment.carry_forward = data.carry_forward - assignment.save() - try: - assignment.submit() - except frappe.exceptions.ValidationError: - continue - - frappe.db.commit() - - docs_name.append(assignment.name) - - return docs_name - - -def get_leave_type_details(): - leave_type_details = frappe._dict() - leave_types = frappe.get_all( - "Leave Type", - fields=[ - "name", - "is_lwp", - "is_earned_leave", - "is_compensatory", - "based_on_date_of_joining", - "is_carry_forward", - "expire_carry_forwarded_leaves_after_days", - "earned_leave_frequency", - "rounding", - ], - ) - for d in leave_types: - leave_type_details.setdefault(d.name, d) - return leave_type_details diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py deleted file mode 100644 index 13b39c7ee6..0000000000 --- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py +++ /dev/null @@ -1,10 +0,0 @@ -from frappe import _ - - -def get_data(): - return { - "fieldname": "leave_policy_assignment", - "transactions": [ - {"label": _("Leaves"), "items": ["Leave Allocation"]}, - ], - } diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js deleted file mode 100644 index 6b75817cba..0000000000 --- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js +++ /dev/null @@ -1,117 +0,0 @@ -frappe.listview_settings['Leave Policy Assignment'] = { - onload: function (list_view) { - let me = this; - list_view.page.add_inner_button(__("Bulk Leave Policy Assignment"), function () { - me.dialog = new frappe.ui.form.MultiSelectDialog({ - doctype: "Employee", - target: cur_list, - setters: { - employee_name: '', - company: '', - department: '', - }, - data_fields: [{ - fieldname: 'leave_policy', - fieldtype: 'Link', - options: 'Leave Policy', - label: __('Leave Policy'), - reqd: 1 - }, - { - fieldname: 'assignment_based_on', - fieldtype: 'Select', - options: ["", "Leave Period"], - label: __('Assignment Based On'), - onchange: () => { - if (cur_dialog.fields_dict.assignment_based_on.value === "Leave Period") { - cur_dialog.set_df_property("effective_from", "read_only", 1); - cur_dialog.set_df_property("leave_period", "reqd", 1); - cur_dialog.set_df_property("effective_to", "read_only", 1); - } else { - cur_dialog.set_df_property("effective_from", "read_only", 0); - cur_dialog.set_df_property("leave_period", "reqd", 0); - cur_dialog.set_df_property("effective_to", "read_only", 0); - cur_dialog.set_value("effective_from", ""); - cur_dialog.set_value("effective_to", ""); - } - } - }, - { - fieldname: "leave_period", - fieldtype: 'Link', - options: "Leave Period", - label: __('Leave Period'), - depends_on: doc => { - return doc.assignment_based_on == 'Leave Period'; - }, - onchange: () => { - if (cur_dialog.fields_dict.leave_period.value) { - me.set_effective_date(); - } - }, - get_query() { - let filters = {"is_active": 1}; - if (cur_dialog.fields_dict.company.value) - filters["company"] = cur_dialog.fields_dict.company.value; - - return { - filters: filters - }; - }, - }, - { - fieldtype: "Column Break" - }, - { - fieldname: 'effective_from', - fieldtype: 'Date', - label: __('Effective From'), - reqd: 1 - }, - { - fieldname: 'effective_to', - fieldtype: 'Date', - label: __('Effective To'), - reqd: 1 - }, - { - fieldname: 'carry_forward', - fieldtype: 'Check', - label: __('Add unused leaves from previous allocations') - } - ], - get_query() { - return { - filters: { - status: ['=', 'Active'] - } - }; - }, - add_filters_group: 1, - primary_action_label: "Assign", - action(employees, data) { - frappe.call({ - method: 'erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment.create_assignment_for_multiple_employees', - async: false, - args: { - employees: employees, - data: data - } - }); - cur_dialog.hide(); - } - }); - }); - }, - - set_effective_date: function () { - if (cur_dialog.fields_dict.assignment_based_on.value === "Leave Period" && cur_dialog.fields_dict.leave_period.value) { - frappe.model.with_doc("Leave Period", cur_dialog.fields_dict.leave_period.value, function () { - let from_date = frappe.model.get_value("Leave Period", cur_dialog.fields_dict.leave_period.value, "from_date"); - let to_date = frappe.model.get_value("Leave Period", cur_dialog.fields_dict.leave_period.value, "to_date"); - cur_dialog.set_value("effective_from", from_date); - cur_dialog.set_value("effective_to", to_date); - }); - } - } -}; diff --git a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py deleted file mode 100644 index 031ed0e901..0000000000 --- a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py +++ /dev/null @@ -1,379 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days, add_months, get_first_day, get_last_day, getdate - -from erpnext.hr.doctype.leave_application.test_leave_application import ( - get_employee, - get_leave_period, -) -from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy -from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import ( - create_assignment_for_multiple_employees, -) - -test_dependencies = ["Employee"] - - -class TestLeavePolicyAssignment(FrappeTestCase): - def setUp(self): - for doctype in [ - "Leave Period", - "Leave Application", - "Leave Allocation", - "Leave Policy Assignment", - "Leave Ledger Entry", - ]: - frappe.db.delete(doctype) - - employee = get_employee() - self.original_doj = employee.date_of_joining - self.employee = employee - - def test_grant_leaves(self): - leave_period = get_leave_period() - # allocation = 10 - leave_policy = create_leave_policy() - leave_policy.submit() - - self.employee.date_of_joining = get_first_day(leave_period.from_date) - self.employee.save() - - data = { - "assignment_based_on": "Leave Period", - "leave_policy": leave_policy.name, - "leave_period": leave_period.name, - } - leave_policy_assignments = create_assignment_for_multiple_employees( - [self.employee.name], frappe._dict(data) - ) - self.assertEqual( - frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "leaves_allocated"), - 1, - ) - - leave_allocation = frappe.get_list( - "Leave Allocation", - filters={ - "employee": self.employee.name, - "leave_policy": leave_policy.name, - "leave_policy_assignment": leave_policy_assignments[0], - "docstatus": 1, - }, - )[0] - leave_alloc_doc = frappe.get_doc("Leave Allocation", leave_allocation) - - self.assertEqual(leave_alloc_doc.new_leaves_allocated, 10) - self.assertEqual(leave_alloc_doc.leave_type, "_Test Leave Type") - self.assertEqual(getdate(leave_alloc_doc.from_date), getdate(leave_period.from_date)) - self.assertEqual(getdate(leave_alloc_doc.to_date), getdate(leave_period.to_date)) - self.assertEqual(leave_alloc_doc.leave_policy, leave_policy.name) - self.assertEqual(leave_alloc_doc.leave_policy_assignment, leave_policy_assignments[0]) - - def test_allow_to_grant_all_leave_after_cancellation_of_every_leave_allocation(self): - leave_period = get_leave_period() - # create the leave policy with leave type "_Test Leave Type", allocation = 10 - leave_policy = create_leave_policy() - leave_policy.submit() - - data = { - "assignment_based_on": "Leave Period", - "leave_policy": leave_policy.name, - "leave_period": leave_period.name, - } - leave_policy_assignments = create_assignment_for_multiple_employees( - [self.employee.name], frappe._dict(data) - ) - - # every leave is allocated no more leave can be granted now - self.assertEqual( - frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "leaves_allocated"), - 1, - ) - leave_allocation = frappe.get_list( - "Leave Allocation", - filters={ - "employee": self.employee.name, - "leave_policy": leave_policy.name, - "leave_policy_assignment": leave_policy_assignments[0], - "docstatus": 1, - }, - )[0] - - leave_alloc_doc = frappe.get_doc("Leave Allocation", leave_allocation) - leave_alloc_doc.cancel() - leave_alloc_doc.delete() - self.assertEqual( - frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "leaves_allocated"), - 0, - ) - - def test_earned_leave_allocation(self): - leave_period = create_leave_period("Test Earned Leave Period") - leave_type = create_earned_leave_type("Test Earned Leave") - - leave_policy = frappe.get_doc( - { - "doctype": "Leave Policy", - "title": "Test Leave Policy", - "leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 6}], - } - ).submit() - - data = { - "assignment_based_on": "Leave Period", - "leave_policy": leave_policy.name, - "leave_period": leave_period.name, - } - - # second last day of the month - # leaves allocated should be 0 since it is an earned leave and allocation happens via scheduler based on set frequency - frappe.flags.current_date = add_days(get_last_day(getdate()), -1) - leave_policy_assignments = create_assignment_for_multiple_employees( - [self.employee.name], frappe._dict(data) - ) - - leaves_allocated = frappe.db.get_value( - "Leave Allocation", - {"leave_policy_assignment": leave_policy_assignments[0]}, - "total_leaves_allocated", - ) - self.assertEqual(leaves_allocated, 0) - - def test_earned_leave_alloc_for_passed_months_based_on_leave_period(self): - leave_period, leave_policy = setup_leave_period_and_policy( - get_first_day(add_months(getdate(), -1)) - ) - - # Case 1: assignment created one month after the leave period, should allocate 1 leave - frappe.flags.current_date = get_first_day(getdate()) - data = { - "assignment_based_on": "Leave Period", - "leave_policy": leave_policy.name, - "leave_period": leave_period.name, - } - leave_policy_assignments = create_assignment_for_multiple_employees( - [self.employee.name], frappe._dict(data) - ) - - leaves_allocated = frappe.db.get_value( - "Leave Allocation", - {"leave_policy_assignment": leave_policy_assignments[0]}, - "total_leaves_allocated", - ) - self.assertEqual(leaves_allocated, 1) - - def test_earned_leave_alloc_for_passed_months_on_month_end_based_on_leave_period(self): - leave_period, leave_policy = setup_leave_period_and_policy( - get_first_day(add_months(getdate(), -2)) - ) - # Case 2: assignment created on the last day of the leave period's latter month - # should allocate 1 leave for current month even though the month has not ended - # since the daily job might have already executed - frappe.flags.current_date = get_last_day(getdate()) - - data = { - "assignment_based_on": "Leave Period", - "leave_policy": leave_policy.name, - "leave_period": leave_period.name, - } - leave_policy_assignments = create_assignment_for_multiple_employees( - [self.employee.name], frappe._dict(data) - ) - - leaves_allocated = frappe.db.get_value( - "Leave Allocation", - {"leave_policy_assignment": leave_policy_assignments[0]}, - "total_leaves_allocated", - ) - self.assertEqual(leaves_allocated, 3) - - def test_earned_leave_alloc_for_passed_months_with_cf_leaves_based_on_leave_period(self): - from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation - - leave_period, leave_policy = setup_leave_period_and_policy( - get_first_day(add_months(getdate(), -2)) - ) - # initial leave allocation = 5 - leave_allocation = create_leave_allocation( - employee=self.employee.name, - employee_name=self.employee.employee_name, - leave_type="Test Earned Leave", - from_date=add_months(getdate(), -12), - to_date=add_months(getdate(), -3), - new_leaves_allocated=5, - carry_forward=0, - ) - leave_allocation.submit() - - # Case 3: assignment created on the last day of the leave period's latter month with carry forwarding - frappe.flags.current_date = get_last_day(add_months(getdate(), -1)) - data = { - "assignment_based_on": "Leave Period", - "leave_policy": leave_policy.name, - "leave_period": leave_period.name, - "carry_forward": 1, - } - # carry forwarded leaves = 5, 3 leaves allocated for passed months - leave_policy_assignments = create_assignment_for_multiple_employees( - [self.employee.name], frappe._dict(data) - ) - - details = frappe.db.get_value( - "Leave Allocation", - {"leave_policy_assignment": leave_policy_assignments[0]}, - ["total_leaves_allocated", "new_leaves_allocated", "unused_leaves", "name"], - as_dict=True, - ) - self.assertEqual(details.new_leaves_allocated, 2) - self.assertEqual(details.unused_leaves, 5) - self.assertEqual(details.total_leaves_allocated, 7) - - def test_earned_leave_alloc_for_passed_months_based_on_joining_date(self): - # tests leave alloc for earned leaves for assignment based on joining date in policy assignment - leave_type = create_earned_leave_type("Test Earned Leave") - leave_policy = frappe.get_doc( - { - "doctype": "Leave Policy", - "title": "Test Leave Policy", - "leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}], - } - ).submit() - - # joining date set to 2 months back - self.employee.date_of_joining = get_first_day(add_months(getdate(), -2)) - self.employee.save() - - # assignment created on the last day of the current month - frappe.flags.current_date = get_last_day(getdate()) - data = {"assignment_based_on": "Joining Date", "leave_policy": leave_policy.name} - leave_policy_assignments = create_assignment_for_multiple_employees( - [self.employee.name], frappe._dict(data) - ) - leaves_allocated = frappe.db.get_value( - "Leave Allocation", - {"leave_policy_assignment": leave_policy_assignments[0]}, - "total_leaves_allocated", - ) - effective_from = frappe.db.get_value( - "Leave Policy Assignment", leave_policy_assignments[0], "effective_from" - ) - self.assertEqual(effective_from, self.employee.date_of_joining) - self.assertEqual(leaves_allocated, 3) - - def test_grant_leaves_on_doj_for_earned_leaves_based_on_leave_period(self): - # tests leave alloc based on leave period for earned leaves with "based on doj" configuration in leave type - leave_period, leave_policy = setup_leave_period_and_policy( - get_first_day(add_months(getdate(), -2)), based_on_doj=True - ) - - # joining date set to 2 months back - self.employee.date_of_joining = get_first_day(add_months(getdate(), -2)) - self.employee.save() - - # assignment created on the same day of the current month, should allocate leaves including the current month - frappe.flags.current_date = get_first_day(getdate()) - - data = { - "assignment_based_on": "Leave Period", - "leave_policy": leave_policy.name, - "leave_period": leave_period.name, - } - leave_policy_assignments = create_assignment_for_multiple_employees( - [self.employee.name], frappe._dict(data) - ) - - leaves_allocated = frappe.db.get_value( - "Leave Allocation", - {"leave_policy_assignment": leave_policy_assignments[0]}, - "total_leaves_allocated", - ) - self.assertEqual(leaves_allocated, 3) - - def test_grant_leaves_on_doj_for_earned_leaves_based_on_joining_date(self): - # tests leave alloc based on joining date for earned leaves with "based on doj" configuration in leave type - leave_type = create_earned_leave_type("Test Earned Leave", based_on_doj=True) - leave_policy = frappe.get_doc( - { - "doctype": "Leave Policy", - "title": "Test Leave Policy", - "leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}], - } - ).submit() - - # joining date set to 2 months back - # leave should be allocated for current month too since this day is same as the joining day - self.employee.date_of_joining = get_first_day(add_months(getdate(), -2)) - self.employee.save() - - # assignment created on the first day of the current month - frappe.flags.current_date = get_first_day(getdate()) - data = {"assignment_based_on": "Joining Date", "leave_policy": leave_policy.name} - leave_policy_assignments = create_assignment_for_multiple_employees( - [self.employee.name], frappe._dict(data) - ) - leaves_allocated = frappe.db.get_value( - "Leave Allocation", - {"leave_policy_assignment": leave_policy_assignments[0]}, - "total_leaves_allocated", - ) - effective_from = frappe.db.get_value( - "Leave Policy Assignment", leave_policy_assignments[0], "effective_from" - ) - self.assertEqual(effective_from, self.employee.date_of_joining) - self.assertEqual(leaves_allocated, 3) - - def tearDown(self): - frappe.db.set_value("Employee", self.employee.name, "date_of_joining", self.original_doj) - frappe.flags.current_date = None - - -def create_earned_leave_type(leave_type, based_on_doj=False): - frappe.delete_doc_if_exists("Leave Type", leave_type, force=1) - - return frappe.get_doc( - dict( - leave_type_name=leave_type, - doctype="Leave Type", - is_earned_leave=1, - earned_leave_frequency="Monthly", - rounding=0.5, - is_carry_forward=1, - based_on_date_of_joining=based_on_doj, - ) - ).insert() - - -def create_leave_period(name, start_date=None): - frappe.delete_doc_if_exists("Leave Period", name, force=1) - if not start_date: - start_date = get_first_day(getdate()) - - return frappe.get_doc( - dict( - name=name, - doctype="Leave Period", - from_date=start_date, - to_date=add_months(start_date, 12), - company="_Test Company", - is_active=1, - ) - ).insert() - - -def setup_leave_period_and_policy(start_date, based_on_doj=False): - leave_type = create_earned_leave_type("Test Earned Leave", based_on_doj) - leave_period = create_leave_period("Test Earned Leave Period", start_date=start_date) - leave_policy = frappe.get_doc( - { - "doctype": "Leave Policy", - "title": "Test Leave Policy", - "leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}], - } - ).insert() - - return leave_period, leave_policy diff --git a/erpnext/hr/doctype/leave_policy_detail/__init__.py b/erpnext/hr/doctype/leave_policy_detail/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.js b/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.js deleted file mode 100644 index ee21f8d3ae..0000000000 --- a/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Leave Policy Detail', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.json b/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.json deleted file mode 100644 index 572b2f7953..0000000000 --- a/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 16:01:20.928853", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "fieldname": "leave_type", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Leave Type", - "length": 0, - "no_copy": 0, - "options": "Leave Type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "annual_allocation", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Annual Allocation", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-04-14 13:00:34.511109", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Policy Detail", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.py b/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.py deleted file mode 100644 index 8916d3d65a..0000000000 --- a/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class LeavePolicyDetail(Document): - pass diff --git a/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.py b/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.py deleted file mode 100644 index aacf64fd69..0000000000 --- a/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestLeavePolicyDetail(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/leave_type/README.md b/erpnext/hr/doctype/leave_type/README.md deleted file mode 100644 index 695e9ddafc..0000000000 --- a/erpnext/hr/doctype/leave_type/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Type of Leave. - -e.g. - -- Casual Leave -- Sick Leave \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_type/__init__.py b/erpnext/hr/doctype/leave_type/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/leave_type/leave_type.js b/erpnext/hr/doctype/leave_type/leave_type.js deleted file mode 100644 index b930dedaca..0000000000 --- a/erpnext/hr/doctype/leave_type/leave_type.js +++ /dev/null @@ -1,38 +0,0 @@ -frappe.ui.form.on("Leave Type", { - refresh: function(frm) { - } -}); - - -frappe.tour["Leave Type"] = [ - { - fieldname: "max_leaves_allowed", - title: "Maximum Leave Allocation Allowed", - description: __("This field allows you to set the maximum number of leaves that can be allocated annually for this Leave Type while creating the Leave Policy") - }, - { - fieldname: "max_continuous_days_allowed", - title: "Maximum Consecutive Leaves Allowed", - description: __("This field allows you to set the maximum number of consecutive leaves an Employee can apply for.") - }, - { - fieldname: "is_optional_leave", - title: "Is Optional Leave", - description: __("Optional Leaves are holidays that Employees can choose to avail from a list of holidays published by the company.") - }, - { - fieldname: "is_compensatory", - title: "Is Compensatory Leave", - description: __("Leaves you can avail against a holiday you worked on. You can claim Compensatory Off Leave using Compensatory Leave request. Click") + " here " + __('to know more') - }, - { - fieldname: "allow_encashment", - title: "Allow Encashment", - description: __("From here, you can enable encashment for the balance leaves.") - }, - { - fieldname: "is_earned_leave", - title: "Is Earned Leaves", - description: __("Earned Leaves are leaves earned by an Employee after working with the company for a certain amount of time. Enabling this will allocate leaves on pro-rata basis by automatically updating Leave Allocation for leaves of this type at intervals set by 'Earned Leave Frequency.") - } -]; \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json deleted file mode 100644 index d40ff09619..0000000000 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ /dev/null @@ -1,265 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:leave_type_name", - "creation": "2013-02-21 09:55:58", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "leave_type_name", - "max_leaves_allowed", - "applicable_after", - "max_continuous_days_allowed", - "column_break_3", - "is_carry_forward", - "is_lwp", - "is_ppl", - "fraction_of_daily_salary_per_leave", - "is_optional_leave", - "allow_negative", - "allow_over_allocation", - "include_holiday", - "is_compensatory", - "carry_forward_section", - "maximum_carry_forwarded_leaves", - "expire_carry_forwarded_leaves_after_days", - "encashment", - "allow_encashment", - "encashment_threshold_days", - "column_break_17", - "earning_component", - "earned_leave", - "is_earned_leave", - "earned_leave_frequency", - "column_break_22", - "based_on_date_of_joining", - "rounding" - ], - "fields": [ - { - "fieldname": "leave_type_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Leave Type Name", - "oldfieldname": "leave_type_name", - "oldfieldtype": "Data", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "max_leaves_allowed", - "fieldtype": "Int", - "label": "Maximum Leave Allocation Allowed" - }, - { - "fieldname": "applicable_after", - "fieldtype": "Int", - "label": "Applicable After (Working Days)" - }, - { - "fieldname": "max_continuous_days_allowed", - "fieldtype": "Int", - "in_list_view": 1, - "label": "Maximum Consecutive Leaves Allowed", - "oldfieldname": "max_days_allowed", - "oldfieldtype": "Data" - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "is_carry_forward", - "fieldtype": "Check", - "in_list_view": 1, - "label": "Is Carry Forward", - "oldfieldname": "is_carry_forward", - "oldfieldtype": "Check" - }, - { - "default": "0", - "depends_on": "eval:doc.is_ppl == 0", - "fieldname": "is_lwp", - "fieldtype": "Check", - "label": "Is Leave Without Pay" - }, - { - "default": "0", - "description": "These leaves are holidays permitted by the company however, availing it is optional for an Employee.", - "fieldname": "is_optional_leave", - "fieldtype": "Check", - "label": "Is Optional Leave" - }, - { - "default": "0", - "fieldname": "allow_negative", - "fieldtype": "Check", - "label": "Allow Negative Balance" - }, - { - "default": "0", - "fieldname": "include_holiday", - "fieldtype": "Check", - "label": "Include holidays within leaves as leaves" - }, - { - "default": "0", - "fieldname": "is_compensatory", - "fieldtype": "Check", - "label": "Is Compensatory" - }, - { - "collapsible": 1, - "depends_on": "eval: doc.is_carry_forward == 1", - "fieldname": "carry_forward_section", - "fieldtype": "Section Break", - "label": "Carry Forward" - }, - { - "description": "Calculated in days", - "fieldname": "expire_carry_forwarded_leaves_after_days", - "fieldtype": "Int", - "label": "Expire Carry Forwarded Leaves (Days)" - }, - { - "collapsible": 1, - "fieldname": "encashment", - "fieldtype": "Section Break", - "label": "Encashment" - }, - { - "default": "0", - "fieldname": "allow_encashment", - "fieldtype": "Check", - "label": "Allow Encashment" - }, - { - "depends_on": "allow_encashment", - "fieldname": "encashment_threshold_days", - "fieldtype": "Int", - "label": "Encashment Threshold Days" - }, - { - "depends_on": "allow_encashment", - "fieldname": "earning_component", - "fieldtype": "Link", - "label": "Earning Component", - "options": "Salary Component" - }, - { - "collapsible": 1, - "fieldname": "earned_leave", - "fieldtype": "Section Break", - "label": "Earned Leave" - }, - { - "default": "0", - "fieldname": "is_earned_leave", - "fieldtype": "Check", - "label": "Is Earned Leave" - }, - { - "depends_on": "is_earned_leave", - "fieldname": "earned_leave_frequency", - "fieldtype": "Select", - "label": "Earned Leave Frequency", - "options": "Monthly\nQuarterly\nHalf-Yearly\nYearly" - }, - { - "default": "0.5", - "depends_on": "is_earned_leave", - "fieldname": "rounding", - "fieldtype": "Select", - "label": "Rounding", - "options": "\n0.25\n0.5\n1.0" - }, - { - "depends_on": "is_carry_forward", - "fieldname": "maximum_carry_forwarded_leaves", - "fieldtype": "Float", - "label": "Maximum Carry Forwarded Leaves" - }, - { - "fieldname": "column_break_17", - "fieldtype": "Column Break" - }, - { - "fieldname": "column_break_22", - "fieldtype": "Column Break" - }, - { - "default": "0", - "depends_on": "eval:doc.is_earned_leave", - "description": "If checked, leave will be granted on the day of joining every month.", - "fieldname": "based_on_date_of_joining", - "fieldtype": "Check", - "label": "Based On Date Of Joining" - }, - { - "default": "0", - "depends_on": "eval:doc.is_lwp == 0", - "fieldname": "is_ppl", - "fieldtype": "Check", - "label": "Is Partially Paid Leave" - }, - { - "depends_on": "eval:doc.is_ppl == 1", - "description": "For a day of leave taken, if you still pay (say) 50% of the daily salary, then enter 0.50 in this field.", - "fieldname": "fraction_of_daily_salary_per_leave", - "fieldtype": "Float", - "label": "Fraction of Daily Salary per Leave", - "mandatory_depends_on": "eval:doc.is_ppl == 1" - }, - { - "default": "0", - "description": "Allows allocating more leaves than the number of days in the allocation period.", - "fieldname": "allow_over_allocation", - "fieldtype": "Check", - "label": "Allow Over Allocation" - } - ], - "icon": "fa fa-flag", - "idx": 1, - "links": [], - "modified": "2022-05-09 05:01:38.957545", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Type", - "naming_rule": "By fieldname", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "read": 1, - "role": "Employee" - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py deleted file mode 100644 index 82b9bd6575..0000000000 --- a/erpnext/hr/doctype/leave_type/leave_type.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import today - - -class LeaveType(Document): - def validate(self): - if self.is_lwp: - leave_allocation = frappe.get_all( - "Leave Allocation", - filters={"leave_type": self.name, "from_date": ("<=", today()), "to_date": (">=", today())}, - fields=["name"], - ) - leave_allocation = [l["name"] for l in leave_allocation] - if leave_allocation: - frappe.throw( - _( - "Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay" - ).format(", ".join(leave_allocation)) - ) # nosec - - if self.is_lwp and self.is_ppl: - frappe.throw(_("Leave Type can be either without pay or partial pay")) - - if self.is_ppl and ( - self.fraction_of_daily_salary_per_leave < 0 or self.fraction_of_daily_salary_per_leave > 1 - ): - frappe.throw(_("The fraction of Daily Salary per Leave should be between 0 and 1")) diff --git a/erpnext/hr/doctype/leave_type/leave_type_dashboard.py b/erpnext/hr/doctype/leave_type/leave_type_dashboard.py deleted file mode 100644 index 269a1ecc69..0000000000 --- a/erpnext/hr/doctype/leave_type/leave_type_dashboard.py +++ /dev/null @@ -1,10 +0,0 @@ -def get_data(): - return { - "fieldname": "leave_type", - "transactions": [ - { - "items": ["Leave Allocation", "Leave Application"], - }, - {"items": ["Attendance", "Leave Encashment"]}, - ], - } diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.py b/erpnext/hr/doctype/leave_type/test_leave_type.py deleted file mode 100644 index 69f9e12520..0000000000 --- a/erpnext/hr/doctype/leave_type/test_leave_type.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -test_records = frappe.get_test_records("Leave Type") - - -def create_leave_type(**args): - args = frappe._dict(args) - if frappe.db.exists("Leave Type", args.leave_type_name): - return frappe.get_doc("Leave Type", args.leave_type_name) - leave_type = frappe.get_doc( - { - "doctype": "Leave Type", - "leave_type_name": args.leave_type_name or "_Test Leave Type", - "include_holiday": args.include_holidays or 1, - "allow_encashment": args.allow_encashment or 0, - "is_earned_leave": args.is_earned_leave or 0, - "is_lwp": args.is_lwp or 0, - "is_ppl": args.is_ppl or 0, - "is_carry_forward": args.is_carry_forward or 0, - "expire_carry_forwarded_leaves_after_days": args.expire_carry_forwarded_leaves_after_days or 0, - "encashment_threshold_days": args.encashment_threshold_days or 5, - "earning_component": "Leave Encashment", - } - ) - - if leave_type.is_ppl: - leave_type.fraction_of_daily_salary_per_leave = args.fraction_of_daily_salary_per_leave or 0.5 - - return leave_type diff --git a/erpnext/hr/doctype/leave_type/test_records.json b/erpnext/hr/doctype/leave_type/test_records.json deleted file mode 100644 index f1f7d8fd14..0000000000 --- a/erpnext/hr/doctype/leave_type/test_records.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "doctype": "Leave Type", - "leave_type_name": "_Test Leave Type", - "include_holiday": 1 - }, - { - "doctype": "Leave Type", - "is_lwp": 1, - "leave_type_name": "_Test Leave Type LWP", - "include_holiday": 1 - }, - { - "doctype": "Leave Type", - "leave_type_name": "_Test Leave Type Encashment", - "include_holiday": 1, - "allow_encashment": 1, - "encashment_threshold_days": 5, - "earning_component": "Leave Encashment" - }, - { - "doctype": "Leave Type", - "leave_type_name": "_Test Leave Type Earned", - "include_holiday": 1, - "is_earned_leave": 1 - } -] \ No newline at end of file diff --git a/erpnext/hr/doctype/offer_term/__init__.py b/erpnext/hr/doctype/offer_term/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/offer_term/offer_term.js b/erpnext/hr/doctype/offer_term/offer_term.js deleted file mode 100644 index 3be6e7b5f9..0000000000 --- a/erpnext/hr/doctype/offer_term/offer_term.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Offer Term', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/offer_term/offer_term.json b/erpnext/hr/doctype/offer_term/offer_term.json deleted file mode 100644 index 3b7bd4250d..0000000000 --- a/erpnext/hr/doctype/offer_term/offer_term.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "allow_copy": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "field:offer_term", - "beta": 0, - "creation": "2015-03-05 13:00:30.900471", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "offer_term", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Offer Term", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2016-07-25 05:24:25.724664", - "modified_by": "Administrator", - "module": "HR", - "name": "Offer Term", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "HR User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/offer_term/offer_term.py b/erpnext/hr/doctype/offer_term/offer_term.py deleted file mode 100644 index cee6c4518b..0000000000 --- a/erpnext/hr/doctype/offer_term/offer_term.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class OfferTerm(Document): - pass diff --git a/erpnext/hr/doctype/offer_term/test_offer_term.py b/erpnext/hr/doctype/offer_term/test_offer_term.py deleted file mode 100644 index 2bea7b2597..0000000000 --- a/erpnext/hr/doctype/offer_term/test_offer_term.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors -# See license.txt - -import unittest - -# test_records = frappe.get_test_records('Offer Term') - - -class TestOfferTerm(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/purpose_of_travel/__init__.py b/erpnext/hr/doctype/purpose_of_travel/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.js b/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.js deleted file mode 100644 index a9424d6175..0000000000 --- a/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Purpose of Travel', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.json b/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.json deleted file mode 100644 index 68d2d6b570..0000000000 --- a/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:purpose_of_travel", - "beta": 0, - "creation": "2018-05-15 07:00:30.933908", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "purpose_of_travel", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Purpose of Travel", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-05-15 07:05:26.219209", - "modified_by": "Administrator", - "module": "HR", - "name": "Purpose of Travel", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.py b/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.py deleted file mode 100644 index c9d6e713fe..0000000000 --- a/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class PurposeofTravel(Document): - pass diff --git a/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.py b/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.py deleted file mode 100644 index 354663b78b..0000000000 --- a/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestPurposeofTravel(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/shift_assignment/__init__.py b/erpnext/hr/doctype/shift_assignment/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.js b/erpnext/hr/doctype/shift_assignment/shift_assignment.js deleted file mode 100644 index 74708b1aaa..0000000000 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Shift Assignment', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.json b/erpnext/hr/doctype/shift_assignment/shift_assignment.json deleted file mode 100644 index ce2a10f229..0000000000 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "HR-SHA-.YY.-.MM.-.#####", - "creation": "2018-04-13 16:25:04.562730", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "shift_type", - "status", - "column_break_3", - "company", - "start_date", - "end_date", - "shift_request", - "department", - "amended_from" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "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 - }, - { - "fieldname": "shift_type", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Shift Type", - "options": "Shift Type", - "reqd": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "shift_request", - "fieldtype": "Link", - "label": "Shift Request", - "options": "Shift Request", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Shift Assignment", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "start_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Start Date", - "reqd": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "end_date", - "fieldtype": "Date", - "label": "End Date", - "show_days": 1, - "show_seconds": 1 - }, - { - "allow_on_submit": 1, - "default": "Active", - "fieldname": "status", - "fieldtype": "Select", - "label": "Status", - "options": "Active\nInactive", - "show_days": 1, - "show_seconds": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2020-06-15 14:27:54.310773", - "modified_by": "Administrator", - "module": "HR", - "name": "Shift Assignment", - "owner": "Administrator", - "permissions": [ - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py deleted file mode 100644 index 51298deddb..0000000000 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py +++ /dev/null @@ -1,517 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from datetime import datetime, timedelta -from typing import Dict, List - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.query_builder import Criterion -from frappe.utils import cstr, get_datetime, get_link_to_form, get_time, getdate, now_datetime - -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 - - -class OverlappingShiftError(frappe.ValidationError): - pass - - -class ShiftAssignment(Document): - def validate(self): - validate_active_employee(self.employee) - self.validate_overlapping_shifts() - - if self.end_date: - self.validate_from_to_dates("start_date", "end_date") - - def validate_overlapping_shifts(self): - overlapping_dates = self.get_overlapping_dates() - if len(overlapping_dates): - # if dates are overlapping, check if timings are overlapping, else allow - overlapping_timings = has_overlapping_timings(self.shift_type, overlapping_dates[0].shift_type) - if overlapping_timings: - self.throw_overlap_error(overlapping_dates[0]) - - def get_overlapping_dates(self): - if not self.name: - self.name = "New Shift Assignment" - - shift = frappe.qb.DocType("Shift Assignment") - query = ( - frappe.qb.from_(shift) - .select(shift.name, shift.shift_type, shift.docstatus, shift.status) - .where( - (shift.employee == self.employee) - & (shift.docstatus == 1) - & (shift.name != self.name) - & (shift.status == "Active") - ) - ) - - if self.end_date: - query = query.where( - Criterion.any( - [ - Criterion.any( - [ - shift.end_date.isnull(), - ((self.start_date >= shift.start_date) & (self.start_date <= shift.end_date)), - ] - ), - Criterion.any( - [ - ((self.end_date >= shift.start_date) & (self.end_date <= shift.end_date)), - shift.start_date.between(self.start_date, self.end_date), - ] - ), - ] - ) - ) - else: - query = query.where( - shift.end_date.isnull() - | ((self.start_date >= shift.start_date) & (self.start_date <= shift.end_date)) - ) - - return query.run(as_dict=True) - - 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 an active Shift {1}: {2} that overlaps within this period." - ).format( - frappe.bold(self.employee), - frappe.bold(shift_details.shift_type), - get_link_to_form("Shift Assignment", shift_details.name), - ) - frappe.throw(msg, title=_("Overlapping Shifts"), exc=OverlappingShiftError) - - -def has_overlapping_timings(shift_1: str, shift_2: str) -> bool: - """ - Accepts two shift types and checks whether their timings are overlapping - """ - curr_shift = frappe.db.get_value("Shift Type", shift_1, ["start_time", "end_time"], as_dict=True) - overlapping_shift = frappe.db.get_value( - "Shift Type", shift_2, ["start_time", "end_time"], as_dict=True - ) - - if ( - ( - curr_shift.start_time > overlapping_shift.start_time - and curr_shift.start_time < overlapping_shift.end_time - ) - or ( - curr_shift.end_time > overlapping_shift.start_time - and curr_shift.end_time < overlapping_shift.end_time - ) - or ( - curr_shift.start_time <= overlapping_shift.start_time - and curr_shift.end_time >= overlapping_shift.end_time - ) - ): - return True - return False - - -@frappe.whitelist() -def get_events(start, end, filters=None): - from frappe.desk.calendar import get_event_conditions - - employee = frappe.db.get_value( - "Employee", {"user_id": frappe.session.user}, ["name", "company"], as_dict=True - ) - if employee: - employee, company = employee.name, employee.company - else: - employee = "" - company = frappe.db.get_value("Global Defaults", None, "default_company") - - conditions = get_event_conditions("Shift Assignment", filters) - events = add_assignments(start, end, conditions=conditions) - return events - - -def add_assignments(start, end, conditions=None): - events = [] - - query = """select name, start_date, end_date, employee_name, - employee, docstatus, shift_type - from `tabShift Assignment` where - ( - start_date >= %(start_date)s - or end_date <= %(end_date)s - or (%(start_date)s between start_date and end_date and %(end_date)s between start_date and end_date) - ) - and docstatus = 1""" - if conditions: - query += conditions - - records = frappe.db.sql(query, {"start_date": start, "end_date": end}, as_dict=True) - shift_timing_map = get_shift_type_timing([d.shift_type for d in records]) - - for d in records: - daily_event_start = d.start_date - daily_event_end = d.end_date if d.end_date else getdate() - delta = timedelta(days=1) - while daily_event_start <= daily_event_end: - start_timing = ( - frappe.utils.get_datetime(daily_event_start) + shift_timing_map[d.shift_type]["start_time"] - ) - end_timing = ( - frappe.utils.get_datetime(daily_event_start) + shift_timing_map[d.shift_type]["end_time"] - ) - daily_event_start += delta - e = { - "name": d.name, - "doctype": "Shift Assignment", - "start_date": start_timing, - "end_date": end_timing, - "title": cstr(d.employee_name) + ": " + cstr(d.shift_type), - "docstatus": d.docstatus, - "allDay": 0, - } - if e not in events: - events.append(e) - - return events - - -def get_shift_type_timing(shift_types): - shift_timing_map = {} - data = frappe.get_all( - "Shift Type", filters={"name": ("IN", shift_types)}, fields=["name", "start_time", "end_time"] - ) - - for d in data: - shift_timing_map[d.name] = d - - return shift_timing_map - - -def get_shift_for_time(shifts: List[Dict], for_timestamp: datetime) -> Dict: - """Returns shift with details for given timestamp""" - valid_shifts = [] - - for entry in shifts: - shift_details = get_shift_details(entry.shift_type, for_timestamp=for_timestamp) - - if ( - get_datetime(shift_details.actual_start) - <= get_datetime(for_timestamp) - <= get_datetime(shift_details.actual_end) - ): - valid_shifts.append(shift_details) - - valid_shifts.sort(key=lambda x: x["actual_start"]) - - if len(valid_shifts) > 1: - for i in range(len(valid_shifts) - 1): - # comparing 2 consecutive shifts and adjusting start and end times - # if they are overlapping within grace period - curr_shift = valid_shifts[i] - next_shift = valid_shifts[i + 1] - - if curr_shift and next_shift: - next_shift.actual_start = ( - curr_shift.end_datetime - if next_shift.actual_start < curr_shift.end_datetime - else next_shift.actual_start - ) - curr_shift.actual_end = ( - next_shift.actual_start - if curr_shift.actual_end > next_shift.actual_start - else curr_shift.actual_end - ) - - valid_shifts[i] = curr_shift - valid_shifts[i + 1] = next_shift - - return get_exact_shift(valid_shifts, for_timestamp) or {} - - return (valid_shifts and valid_shifts[0]) or {} - - -def get_shifts_for_date(employee: str, for_timestamp: datetime) -> List[Dict[str, str]]: - """Returns list of shifts with details for given date""" - assignment = frappe.qb.DocType("Shift Assignment") - - return ( - frappe.qb.from_(assignment) - .select(assignment.name, assignment.shift_type) - .where( - (assignment.employee == employee) - & (assignment.docstatus == 1) - & (assignment.status == "Active") - & (assignment.start_date <= getdate(for_timestamp.date())) - & ( - Criterion.any( - [ - assignment.end_date.isnull(), - (assignment.end_date.isnotnull() & (getdate(for_timestamp.date()) <= assignment.end_date)), - ] - ) - ) - ) - ).run(as_dict=True) - - -def get_shift_for_timestamp(employee: str, for_timestamp: datetime) -> Dict: - shifts = get_shifts_for_date(employee, for_timestamp) - if shifts: - return get_shift_for_time(shifts, for_timestamp) - return {} - - -def get_employee_shift( - employee: str, - for_timestamp: datetime = None, - consider_default_shift: bool = False, - next_shift_direction: str = None, -) -> Dict: - """Returns a Shift Type for the given employee on the given date. (excluding the holidays) - - :param employee: Employee for which shift is required. - :param for_timestamp: DateTime on which shift is required - :param consider_default_shift: If set to true, default shift is taken when no shift assignment is found. - :param next_shift_direction: One of: None, 'forward', 'reverse'. Direction to look for next shift if shift not found on given date. - """ - if for_timestamp is None: - for_timestamp = now_datetime() - - shift_details = get_shift_for_timestamp(employee, for_timestamp) - - # if shift assignment is not found, consider default shift - default_shift = frappe.db.get_value("Employee", employee, "default_shift") - if not shift_details and consider_default_shift: - shift_details = get_shift_details(default_shift, for_timestamp) - - # if its a holiday, reset - if shift_details and is_holiday_date(employee, shift_details): - shift_details = None - - # if no shift is found, find next or prev shift assignment based on direction - if not shift_details and next_shift_direction: - shift_details = get_prev_or_next_shift( - employee, for_timestamp, consider_default_shift, default_shift, next_shift_direction - ) - - return shift_details or {} - - -def get_prev_or_next_shift( - employee: str, - for_timestamp: datetime, - consider_default_shift: bool, - default_shift: str, - next_shift_direction: str, -) -> Dict: - """Returns a dict of shift details for the next or prev shift based on the next_shift_direction""" - MAX_DAYS = 366 - shift_details = {} - - if consider_default_shift and default_shift: - direction = -1 if next_shift_direction == "reverse" else 1 - for i in range(MAX_DAYS): - date = for_timestamp + timedelta(days=direction * (i + 1)) - shift_details = get_employee_shift(employee, date, consider_default_shift, None) - if shift_details: - break - else: - direction = "<" if next_shift_direction == "reverse" else ">" - sort_order = "desc" if next_shift_direction == "reverse" else "asc" - dates = frappe.db.get_all( - "Shift Assignment", - ["start_date", "end_date"], - { - "employee": employee, - "start_date": (direction, for_timestamp.date()), - "docstatus": 1, - "status": "Active", - }, - as_list=True, - limit=MAX_DAYS, - order_by="start_date " + sort_order, - ) - - if dates: - for date in dates: - if date[1] and date[1] < for_timestamp.date(): - continue - shift_details = get_employee_shift( - employee, datetime.combine(date[0], for_timestamp.time()), consider_default_shift, None - ) - if shift_details: - break - - return shift_details or {} - - -def is_holiday_date(employee: str, shift_details: Dict) -> bool: - holiday_list_name = frappe.db.get_value( - "Shift Type", shift_details.shift_type.name, "holiday_list" - ) - - if not holiday_list_name: - holiday_list_name = get_holiday_list_for_employee(employee, False) - - return holiday_list_name and is_holiday(holiday_list_name, shift_details.start_datetime.date()) - - -def get_employee_shift_timings( - employee: str, for_timestamp: datetime = None, consider_default_shift: bool = False -) -> List[Dict]: - """Returns previous shift, current/upcoming shift, next_shift for the given timestamp and employee""" - if for_timestamp is None: - for_timestamp = now_datetime() - - # write and verify a test case for midnight shift. - prev_shift = curr_shift = next_shift = None - curr_shift = get_employee_shift(employee, for_timestamp, consider_default_shift, "forward") - if curr_shift: - next_shift = get_employee_shift( - employee, curr_shift.start_datetime + timedelta(days=1), consider_default_shift, "forward" - ) - prev_shift = get_employee_shift( - employee, for_timestamp + timedelta(days=-1), consider_default_shift, "reverse" - ) - - if curr_shift: - # adjust actual start and end times if they are overlapping with grace period (before start and after end) - if prev_shift: - curr_shift.actual_start = ( - prev_shift.end_datetime - if curr_shift.actual_start < prev_shift.end_datetime - else curr_shift.actual_start - ) - prev_shift.actual_end = ( - curr_shift.actual_start - if prev_shift.actual_end > curr_shift.actual_start - else prev_shift.actual_end - ) - if next_shift: - next_shift.actual_start = ( - curr_shift.end_datetime - if next_shift.actual_start < curr_shift.end_datetime - else next_shift.actual_start - ) - curr_shift.actual_end = ( - next_shift.actual_start - if curr_shift.actual_end > next_shift.actual_start - else curr_shift.actual_end - ) - - return prev_shift, curr_shift, next_shift - - -def get_actual_start_end_datetime_of_shift( - employee: str, for_timestamp: datetime, consider_default_shift: bool = False -) -> Dict: - """Returns a Dict containing shift details with actual_start and actual_end datetime values - Here 'actual' means taking into account the "begin_check_in_before_shift_start_time" and "allow_check_out_after_shift_end_time". - Empty Dict is returned if the timestamp is outside any actual shift timings. - - :param employee (str): Employee name - :param for_timestamp (datetime, optional): Datetime value of checkin, if not provided considers current datetime - :param consider_default_shift (bool, optional): Flag (defaults to False) to specify whether to consider - default shift in employee master if no shift assignment is found - """ - shift_timings_as_per_timestamp = get_employee_shift_timings( - employee, for_timestamp, consider_default_shift - ) - return get_exact_shift(shift_timings_as_per_timestamp, for_timestamp) - - -def get_exact_shift(shifts: List, for_timestamp: datetime) -> Dict: - """Returns the shift details (dict) for the exact shift in which the 'for_timestamp' value falls among multiple shifts""" - shift_details = dict() - timestamp_list = [] - - for shift in shifts: - if shift: - timestamp_list.extend([shift.actual_start, shift.actual_end]) - else: - timestamp_list.extend([None, None]) - - timestamp_index = None - for index, timestamp in enumerate(timestamp_list): - if not timestamp: - continue - - if for_timestamp < timestamp: - timestamp_index = index - elif for_timestamp == timestamp: - # on timestamp boundary - if index % 2 == 1: - timestamp_index = index - else: - timestamp_index = index + 1 - - if timestamp_index: - break - - if timestamp_index and timestamp_index % 2 == 1: - shift_details = shifts[int((timestamp_index - 1) / 2)] - - return shift_details - - -def get_shift_details(shift_type_name: str, for_timestamp: datetime = None) -> Dict: - """Returns a Dict containing shift details with the following data: - 'shift_type' - Object of DocType Shift Type, - 'start_datetime' - datetime of shift start on given timestamp, - 'end_datetime' - datetime of shift end on given timestamp, - 'actual_start' - datetime of shift start after adding 'begin_check_in_before_shift_start_time', - 'actual_end' - datetime of shift end after adding 'allow_check_out_after_shift_end_time' (None is returned if this is zero) - - :param shift_type_name (str): shift type name for which shift_details are required. - :param for_timestamp (datetime, optional): Datetime value of checkin, if not provided considers current datetime - """ - if not shift_type_name: - return {} - - if for_timestamp is None: - for_timestamp = now_datetime() - - shift_type = frappe.get_doc("Shift Type", shift_type_name) - shift_actual_start = shift_type.start_time - timedelta( - minutes=shift_type.begin_check_in_before_shift_start_time - ) - - if shift_type.start_time > shift_type.end_time: - # shift spans accross 2 different days - if get_time(for_timestamp.time()) >= get_time(shift_actual_start): - # if for_timestamp is greater than start time, it's within the first day - start_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.start_time - for_timestamp = for_timestamp + timedelta(days=1) - end_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.end_time - - elif get_time(for_timestamp.time()) < get_time(shift_actual_start): - # if for_timestamp is less than start time, it's within the second day - end_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.end_time - for_timestamp = for_timestamp + timedelta(days=-1) - start_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.start_time - else: - # start and end timings fall on the same day - start_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.start_time - end_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.end_time - - actual_start = start_datetime - timedelta( - minutes=shift_type.begin_check_in_before_shift_start_time - ) - actual_end = end_datetime + timedelta(minutes=shift_type.allow_check_out_after_shift_end_time) - - return frappe._dict( - { - "shift_type": shift_type, - "start_datetime": start_datetime, - "end_datetime": end_datetime, - "actual_start": actual_start, - "actual_end": actual_end, - } - ) diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js b/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js deleted file mode 100644 index 5d2360f10f..0000000000 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.views.calendar["Shift Assignment"] = { - field_map: { - "start": "start_date", - "end": "end_date", - "id": "name", - "docstatus": 1, - "allDay": "allDay", - }, - get_events_method: "erpnext.hr.doctype.shift_assignment.shift_assignment.get_events" -} diff --git a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py b/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py deleted file mode 100644 index de82a2432b..0000000000 --- a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days, getdate, nowdate - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.shift_assignment.shift_assignment import OverlappingShiftError, get_events -from erpnext.hr.doctype.shift_type.test_shift_type import make_shift_assignment, setup_shift_type - -test_dependencies = ["Shift Type"] - - -class TestShiftAssignment(FrappeTestCase): - def setUp(self): - frappe.db.delete("Shift Assignment") - frappe.db.delete("Shift Type") - - def test_make_shift_assignment(self): - setup_shift_type(shift_type="Day Shift") - shift_assignment = frappe.get_doc( - { - "doctype": "Shift Assignment", - "shift_type": "Day Shift", - "company": "_Test Company", - "employee": "_T-Employee-00001", - "start_date": nowdate(), - } - ).insert() - shift_assignment.submit() - - self.assertEqual(shift_assignment.docstatus, 1) - - def test_overlapping_for_ongoing_shift(self): - # shift should be Ongoing if Only start_date is present and status = Active - setup_shift_type(shift_type="Day Shift") - shift_assignment_1 = frappe.get_doc( - { - "doctype": "Shift Assignment", - "shift_type": "Day Shift", - "company": "_Test Company", - "employee": "_T-Employee-00001", - "start_date": nowdate(), - "status": "Active", - } - ).insert() - shift_assignment_1.submit() - - self.assertEqual(shift_assignment_1.docstatus, 1) - - shift_assignment = frappe.get_doc( - { - "doctype": "Shift Assignment", - "shift_type": "Day Shift", - "company": "_Test Company", - "employee": "_T-Employee-00001", - "start_date": add_days(nowdate(), 2), - } - ) - - self.assertRaises(OverlappingShiftError, shift_assignment.save) - - def test_overlapping_for_fixed_period_shift(self): - # shift should is for Fixed period if Only start_date and end_date both are present and status = Active - setup_shift_type(shift_type="Day Shift") - shift_assignment_1 = frappe.get_doc( - { - "doctype": "Shift Assignment", - "shift_type": "Day Shift", - "company": "_Test Company", - "employee": "_T-Employee-00001", - "start_date": nowdate(), - "end_date": add_days(nowdate(), 30), - "status": "Active", - } - ).insert() - shift_assignment_1.submit() - - # it should not allowed within period of any shift. - shift_assignment_3 = frappe.get_doc( - { - "doctype": "Shift Assignment", - "shift_type": "Day Shift", - "company": "_Test Company", - "employee": "_T-Employee-00001", - "start_date": add_days(nowdate(), 10), - "end_date": add_days(nowdate(), 35), - "status": "Active", - } - ) - - self.assertRaises(OverlappingShiftError, shift_assignment_3.save) - - def test_overlapping_for_a_fixed_period_shift_and_ongoing_shift(self): - employee = make_employee("test_shift_assignment@example.com", company="_Test Company") - - # shift setup for 8-12 - shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00") - date = getdate() - # shift with end date - make_shift_assignment(shift_type.name, employee, date, add_days(date, 30)) - - # shift setup for 11-15 - shift_type = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="15:00:00") - date = getdate() - - # shift assignment without end date - shift2 = frappe.get_doc( - { - "doctype": "Shift Assignment", - "shift_type": shift_type.name, - "company": "_Test Company", - "employee": employee, - "start_date": date, - } - ) - self.assertRaises(OverlappingShiftError, shift2.insert) - - def test_overlap_validation_for_shifts_on_same_day_with_overlapping_timeslots(self): - employee = make_employee("test_shift_assignment@example.com", company="_Test Company") - - # shift setup for 8-12 - shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00") - date = getdate() - make_shift_assignment(shift_type.name, employee, date) - - # shift setup for 11-15 - shift_type = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="15:00:00") - date = getdate() - - shift2 = frappe.get_doc( - { - "doctype": "Shift Assignment", - "shift_type": shift_type.name, - "company": "_Test Company", - "employee": employee, - "start_date": date, - } - ) - self.assertRaises(OverlappingShiftError, shift2.insert) - - def test_multiple_shift_assignments_for_same_day(self): - employee = make_employee("test_shift_assignment@example.com", company="_Test Company") - - # shift setup for 8-12 - shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00") - date = getdate() - make_shift_assignment(shift_type.name, employee, date) - - # shift setup for 13-15 - shift_type = setup_shift_type(shift_type="Shift 2", start_time="13:00:00", end_time="15:00:00") - date = getdate() - make_shift_assignment(shift_type.name, employee, date) - - def test_shift_assignment_calendar(self): - employee1 = make_employee("test_shift_assignment1@example.com", company="_Test Company") - employee2 = make_employee("test_shift_assignment2@example.com", company="_Test Company") - - shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00") - date = getdate() - shift1 = make_shift_assignment(shift_type.name, employee1, date) - make_shift_assignment(shift_type.name, employee2, date) - - events = get_events( - start=date, end=date, filters=[["Shift Assignment", "employee", "=", employee1, False]] - ) - self.assertEqual(len(events), 1) - self.assertEqual(events[0]["name"], shift1.name) diff --git a/erpnext/hr/doctype/shift_request/__init__.py b/erpnext/hr/doctype/shift_request/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/shift_request/shift_request.js b/erpnext/hr/doctype/shift_request/shift_request.js deleted file mode 100644 index b17a6f3845..0000000000 --- a/erpnext/hr/doctype/shift_request/shift_request.js +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Shift Request', { - setup: function(frm) { - frm.set_query("approver", function() { - return { - query: "erpnext.hr.doctype.department_approver.department_approver.get_approvers", - filters: { - employee: frm.doc.employee, - doctype: frm.doc.doctype - } - }; - }); - frm.set_query("employee", erpnext.queries.employee); - }, -}); diff --git a/erpnext/hr/doctype/shift_request/shift_request.json b/erpnext/hr/doctype/shift_request/shift_request.json deleted file mode 100644 index 64cbdfff7d..0000000000 --- a/erpnext/hr/doctype/shift_request/shift_request.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "HR-SHR-.YY.-.MM.-.#####", - "creation": "2018-04-13 16:32:27.974273", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "shift_type", - "employee", - "employee_name", - "department", - "status", - "column_break_4", - "company", - "approver", - "from_date", - "to_date", - "amended_from" - ], - "fields": [ - { - "fieldname": "shift_type", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Shift Type", - "options": "Shift Type", - "reqd": 1 - }, - { - "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 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "from_date", - "fieldtype": "Date", - "label": "From Date", - "reqd": 1 - }, - { - "fieldname": "to_date", - "fieldtype": "Date", - "label": "To Date" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Shift Request", - "print_hide": 1, - "read_only": 1 - }, - { - "default": "Draft", - "fieldname": "status", - "fieldtype": "Select", - "label": "Status", - "options": "Draft\nApproved\nRejected", - "reqd": 1 - }, - { - "fetch_from": "employee.shift_request_approver", - "fetch_if_empty": 1, - "fieldname": "approver", - "fieldtype": "Link", - "label": "Approver", - "options": "User", - "reqd": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2020-08-10 17:59:31.550558", - "modified_by": "Administrator", - "module": "HR", - "name": "Shift Request", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/shift_request/shift_request.py b/erpnext/hr/doctype/shift_request/shift_request.py deleted file mode 100644 index 2bee2404aa..0000000000 --- a/erpnext/hr/doctype/shift_request/shift_request.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.query_builder import Criterion -from frappe.utils import get_link_to_form, getdate - -from erpnext.hr.doctype.shift_assignment.shift_assignment import has_overlapping_timings -from erpnext.hr.utils import share_doc_with_approver, validate_active_employee - - -class OverlappingShiftRequestError(frappe.ValidationError): - pass - - -class ShiftRequest(Document): - def validate(self): - validate_active_employee(self.employee) - self.validate_dates() - self.validate_overlapping_shift_requests() - self.validate_approver() - self.validate_default_shift() - - def on_update(self): - share_doc_with_approver(self, self.approver) - - def on_submit(self): - if self.status not in ["Approved", "Rejected"]: - frappe.throw(_("Only Shift Request with status 'Approved' and 'Rejected' can be submitted")) - if self.status == "Approved": - assignment_doc = frappe.new_doc("Shift Assignment") - assignment_doc.company = self.company - assignment_doc.shift_type = self.shift_type - assignment_doc.employee = self.employee - assignment_doc.start_date = self.from_date - if self.to_date: - assignment_doc.end_date = self.to_date - assignment_doc.shift_request = self.name - assignment_doc.flags.ignore_permissions = 1 - assignment_doc.insert() - assignment_doc.submit() - - frappe.msgprint( - _("Shift Assignment: {0} created for Employee: {1}").format( - frappe.bold(assignment_doc.name), frappe.bold(self.employee) - ) - ) - - def on_cancel(self): - shift_assignment_list = frappe.get_list( - "Shift Assignment", {"employee": self.employee, "shift_request": self.name} - ) - if shift_assignment_list: - for shift in shift_assignment_list: - shift_assignment_doc = frappe.get_doc("Shift Assignment", shift["name"]) - shift_assignment_doc.cancel() - - def validate_default_shift(self): - default_shift = frappe.get_value("Employee", self.employee, "default_shift") - if self.shift_type == default_shift: - frappe.throw( - _("You can not request for your Default Shift: {0}").format(frappe.bold(self.shift_type)) - ) - - def validate_approver(self): - department = frappe.get_value("Employee", self.employee, "department") - shift_approver = frappe.get_value("Employee", self.employee, "shift_request_approver") - approvers = frappe.db.sql( - """select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", - (department), - ) - approvers = [approver[0] for approver in approvers] - approvers.append(shift_approver) - if self.approver not in approvers: - frappe.throw(_("Only Approvers can Approve this Request.")) - - def validate_dates(self): - if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)): - frappe.throw(_("To date cannot be before from date")) - - def validate_overlapping_shift_requests(self): - overlapping_dates = self.get_overlapping_dates() - if len(overlapping_dates): - # if dates are overlapping, check if timings are overlapping, else allow - overlapping_timings = has_overlapping_timings(self.shift_type, overlapping_dates[0].shift_type) - if overlapping_timings: - self.throw_overlap_error(overlapping_dates[0]) - - def get_overlapping_dates(self): - if not self.name: - self.name = "New Shift Request" - - shift = frappe.qb.DocType("Shift Request") - query = ( - frappe.qb.from_(shift) - .select(shift.name, shift.shift_type) - .where((shift.employee == self.employee) & (shift.docstatus < 2) & (shift.name != self.name)) - ) - - if self.to_date: - query = query.where( - Criterion.any( - [ - Criterion.any( - [ - shift.to_date.isnull(), - ((self.from_date >= shift.from_date) & (self.from_date <= shift.to_date)), - ] - ), - Criterion.any( - [ - ((self.to_date >= shift.from_date) & (self.to_date <= shift.to_date)), - shift.from_date.between(self.from_date, self.to_date), - ] - ), - ] - ) - ) - else: - query = query.where( - shift.to_date.isnull() - | ((self.from_date >= shift.from_date) & (self.from_date <= shift.to_date)) - ) - - return query.run(as_dict=True) - - def throw_overlap_error(self, shift_details): - shift_details = frappe._dict(shift_details) - msg = _( - "Employee {0} has already applied for Shift {1}: {2} that overlaps within this period" - ).format( - frappe.bold(self.employee), - frappe.bold(shift_details.shift_type), - get_link_to_form("Shift Request", shift_details.name), - ) - - frappe.throw(msg, title=_("Overlapping Shift Requests"), exc=OverlappingShiftRequestError) diff --git a/erpnext/hr/doctype/shift_request/shift_request_dashboard.py b/erpnext/hr/doctype/shift_request/shift_request_dashboard.py deleted file mode 100644 index 2859b8f771..0000000000 --- a/erpnext/hr/doctype/shift_request/shift_request_dashboard.py +++ /dev/null @@ -1,7 +0,0 @@ -def get_data(): - return { - "fieldname": "shift_request", - "transactions": [ - {"items": ["Shift Assignment"]}, - ], - } diff --git a/erpnext/hr/doctype/shift_request/test_shift_request.py b/erpnext/hr/doctype/shift_request/test_shift_request.py deleted file mode 100644 index c47418cfa8..0000000000 --- a/erpnext/hr/doctype/shift_request/test_shift_request.py +++ /dev/null @@ -1,258 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days, nowdate - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.shift_request.shift_request import OverlappingShiftRequestError -from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type - -test_dependencies = ["Shift Type"] - - -class TestShiftRequest(FrappeTestCase): - def setUp(self): - for doctype in ["Shift Request", "Shift Assignment", "Shift Type"]: - frappe.db.delete(doctype) - - def test_make_shift_request(self): - "Test creation/updation of Shift Assignment from Shift Request." - setup_shift_type(shift_type="Day Shift") - department = frappe.get_value("Employee", "_T-Employee-00001", "department") - set_shift_approver(department) - approver = frappe.db.sql( - """select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", - (department), - )[0][0] - - shift_request = make_shift_request(approver) - - # Only one shift assignment is created against a shift request - shift_assignment = frappe.db.get_value( - "Shift Assignment", - filters={"shift_request": shift_request.name}, - fieldname=["employee", "docstatus"], - as_dict=True, - ) - self.assertEqual(shift_request.employee, shift_assignment.employee) - self.assertEqual(shift_assignment.docstatus, 1) - - shift_request.cancel() - - shift_assignment_docstatus = frappe.db.get_value( - "Shift Assignment", filters={"shift_request": shift_request.name}, fieldname="docstatus" - ) - self.assertEqual(shift_assignment_docstatus, 2) - - def test_shift_request_approver_perms(self): - setup_shift_type(shift_type="Day Shift") - employee = frappe.get_doc("Employee", "_T-Employee-00001") - user = "test_approver_perm_emp@example.com" - make_employee(user, "_Test Company") - - # set approver for employee - employee.reload() - employee.shift_request_approver = user - employee.save() - - shift_request = make_shift_request(user, do_not_submit=True) - self.assertTrue(shift_request.name in frappe.share.get_shared("Shift Request", user)) - - # check shared doc revoked - shift_request.reload() - department = frappe.get_value("Employee", "_T-Employee-00001", "department") - set_shift_approver(department) - department_approver = frappe.db.sql( - """select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", - (department), - )[0][0] - shift_request.approver = department_approver - shift_request.save() - self.assertTrue(shift_request.name not in frappe.share.get_shared("Shift Request", user)) - - shift_request.reload() - shift_request.approver = user - shift_request.save() - - frappe.set_user(user) - shift_request.reload() - shift_request.status = "Approved" - shift_request.submit() - - # unset approver - frappe.set_user("Administrator") - employee.reload() - employee.shift_request_approver = "" - employee.save() - - def test_overlap_for_request_without_to_date(self): - # shift should be Ongoing if Only from_date is present - user = "test_shift_request@example.com" - employee = make_employee(user, company="_Test Company", shift_request_approver=user) - setup_shift_type(shift_type="Day Shift") - - shift_request = frappe.get_doc( - { - "doctype": "Shift Request", - "shift_type": "Day Shift", - "company": "_Test Company", - "employee": employee, - "from_date": nowdate(), - "approver": user, - "status": "Approved", - } - ).submit() - - shift_request = frappe.get_doc( - { - "doctype": "Shift Request", - "shift_type": "Day Shift", - "company": "_Test Company", - "employee": employee, - "from_date": add_days(nowdate(), 2), - "approver": user, - "status": "Approved", - } - ) - - self.assertRaises(OverlappingShiftRequestError, shift_request.save) - - def test_overlap_for_request_with_from_and_to_dates(self): - user = "test_shift_request@example.com" - employee = make_employee(user, company="_Test Company", shift_request_approver=user) - setup_shift_type(shift_type="Day Shift") - - shift_request = frappe.get_doc( - { - "doctype": "Shift Request", - "shift_type": "Day Shift", - "company": "_Test Company", - "employee": employee, - "from_date": nowdate(), - "to_date": add_days(nowdate(), 30), - "approver": user, - "status": "Approved", - } - ).submit() - - shift_request = frappe.get_doc( - { - "doctype": "Shift Request", - "shift_type": "Day Shift", - "company": "_Test Company", - "employee": employee, - "from_date": add_days(nowdate(), 10), - "to_date": add_days(nowdate(), 35), - "approver": user, - "status": "Approved", - } - ) - - self.assertRaises(OverlappingShiftRequestError, shift_request.save) - - def test_overlapping_for_a_fixed_period_shift_and_ongoing_shift(self): - user = "test_shift_request@example.com" - employee = make_employee(user, company="_Test Company", shift_request_approver=user) - - # shift setup for 8-12 - shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00") - date = nowdate() - - # shift with end date - frappe.get_doc( - { - "doctype": "Shift Request", - "shift_type": shift_type.name, - "company": "_Test Company", - "employee": employee, - "from_date": date, - "to_date": add_days(date, 30), - "approver": user, - "status": "Approved", - } - ).submit() - - # shift setup for 11-15 - shift_type = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="15:00:00") - shift2 = frappe.get_doc( - { - "doctype": "Shift Request", - "shift_type": shift_type.name, - "company": "_Test Company", - "employee": employee, - "from_date": date, - "approver": user, - "status": "Approved", - } - ) - - self.assertRaises(OverlappingShiftRequestError, shift2.insert) - - def test_allow_non_overlapping_shift_requests_for_same_day(self): - user = "test_shift_request@example.com" - employee = make_employee(user, company="_Test Company", shift_request_approver=user) - - # shift setup for 8-12 - shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00") - date = nowdate() - - # shift with end date - frappe.get_doc( - { - "doctype": "Shift Request", - "shift_type": shift_type.name, - "company": "_Test Company", - "employee": employee, - "from_date": date, - "to_date": add_days(date, 30), - "approver": user, - "status": "Approved", - } - ).submit() - - # shift setup for 13-15 - shift_type = setup_shift_type(shift_type="Shift 2", start_time="13:00:00", end_time="15:00:00") - frappe.get_doc( - { - "doctype": "Shift Request", - "shift_type": shift_type.name, - "company": "_Test Company", - "employee": employee, - "from_date": date, - "approver": user, - "status": "Approved", - } - ).submit() - - -def set_shift_approver(department): - department_doc = frappe.get_doc("Department", department) - department_doc.append("shift_request_approver", {"approver": "test1@example.com"}) - department_doc.save() - department_doc.reload() - - -def make_shift_request(approver, do_not_submit=0): - shift_request = frappe.get_doc( - { - "doctype": "Shift Request", - "shift_type": "Day Shift", - "company": "_Test Company", - "employee": "_T-Employee-00001", - "employee_name": "_Test Employee", - "from_date": nowdate(), - "to_date": add_days(nowdate(), 10), - "approver": approver, - "status": "Approved", - } - ).insert() - - if do_not_submit: - return shift_request - - shift_request.submit() - return shift_request diff --git a/erpnext/hr/doctype/shift_type/__init__.py b/erpnext/hr/doctype/shift_type/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/shift_type/shift_type.js b/erpnext/hr/doctype/shift_type/shift_type.js deleted file mode 100644 index 7138e3bcf3..0000000000 --- a/erpnext/hr/doctype/shift_type/shift_type.js +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Shift Type', { - refresh: function(frm) { - frm.add_custom_button( - __('Mark Attendance'), - () => { - if (!frm.doc.enable_auto_attendance) { - frm.scroll_to_field('enable_auto_attendance'); - frappe.throw(__('Please Enable Auto Attendance and complete the setup first.')); - } - - if (!frm.doc.process_attendance_after) { - frm.scroll_to_field('process_attendance_after'); - frappe.throw(__('Please set {0}.', [__('Process Attendance After').bold()])); - } - - if (!frm.doc.last_sync_of_checkin) { - frm.scroll_to_field('last_sync_of_checkin'); - frappe.throw(__('Please set {0}.', [__('Last Sync of Checkin').bold()])); - } - - frm.call({ - doc: frm.doc, - method: 'process_auto_attendance', - freeze: true, - callback: () => { - frappe.msgprint(__('Attendance has been marked as per employee check-ins')); - } - }); - } - ); - } -}); diff --git a/erpnext/hr/doctype/shift_type/shift_type.json b/erpnext/hr/doctype/shift_type/shift_type.json deleted file mode 100644 index 61f3d2c279..0000000000 --- a/erpnext/hr/doctype/shift_type/shift_type.json +++ /dev/null @@ -1,204 +0,0 @@ -{ - "autoname": "prompt", - "creation": "2018-04-13 16:22:52.954783", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "start_time", - "end_time", - "column_break_3", - "holiday_list", - "enable_auto_attendance", - "auto_attendance_settings_section", - "determine_check_in_and_check_out", - "working_hours_calculation_based_on", - "begin_check_in_before_shift_start_time", - "allow_check_out_after_shift_end_time", - "column_break_10", - "working_hours_threshold_for_half_day", - "working_hours_threshold_for_absent", - "process_attendance_after", - "last_sync_of_checkin", - "grace_period_settings_auto_attendance_section", - "enable_entry_grace_period", - "late_entry_grace_period", - "column_break_18", - "enable_exit_grace_period", - "early_exit_grace_period" - ], - "fields": [ - { - "fieldname": "start_time", - "fieldtype": "Time", - "in_list_view": 1, - "label": "Start Time", - "reqd": 1 - }, - { - "fieldname": "end_time", - "fieldtype": "Time", - "in_list_view": 1, - "label": "End Time", - "reqd": 1 - }, - { - "fieldname": "holiday_list", - "fieldtype": "Link", - "label": "Holiday List", - "options": "Holiday List" - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "column_break_10", - "fieldtype": "Column Break" - }, - { - "fieldname": "determine_check_in_and_check_out", - "fieldtype": "Select", - "label": "Determine Check-in and Check-out", - "options": "Alternating entries as IN and OUT during the same shift\nStrictly based on Log Type in Employee Checkin" - }, - { - "fieldname": "working_hours_calculation_based_on", - "fieldtype": "Select", - "label": "Working Hours Calculation Based On", - "options": "First Check-in and Last Check-out\nEvery Valid Check-in and Check-out" - }, - { - "description": "Working hours below which Half Day is marked. (Zero to disable)", - "fieldname": "working_hours_threshold_for_half_day", - "fieldtype": "Float", - "label": "Working Hours Threshold for Half Day", - "precision": "1" - }, - { - "description": "Working hours below which Absent is marked. (Zero to disable)", - "fieldname": "working_hours_threshold_for_absent", - "fieldtype": "Float", - "label": "Working Hours Threshold for Absent", - "precision": "1" - }, - { - "default": "60", - "description": "The time before the shift start time during which Employee Check-in is considered for attendance.", - "fieldname": "begin_check_in_before_shift_start_time", - "fieldtype": "Int", - "label": "Begin check-in before shift start time (in minutes)" - }, - { - "default": "0", - "fieldname": "enable_entry_grace_period", - "fieldtype": "Check", - "label": "Enable Entry Grace Period" - }, - { - "depends_on": "enable_entry_grace_period", - "description": "The time after the shift start time when check-in is considered as late (in minutes).", - "fieldname": "late_entry_grace_period", - "fieldtype": "Int", - "label": "Late Entry Grace Period" - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "enable_exit_grace_period", - "fieldtype": "Check", - "label": "Enable Exit Grace Period" - }, - { - "depends_on": "eval:doc.enable_exit_grace_period", - "description": "The time before the shift end time when check-out is considered as early (in minutes).", - "fieldname": "early_exit_grace_period", - "fieldtype": "Int", - "label": "Early Exit Grace Period" - }, - { - "default": "60", - "description": "Time after the end of shift during which check-out is considered for attendance.", - "fieldname": "allow_check_out_after_shift_end_time", - "fieldtype": "Int", - "label": "Allow check-out after shift end time (in minutes)" - }, - { - "depends_on": "enable_auto_attendance", - "fieldname": "auto_attendance_settings_section", - "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.", - "fieldname": "enable_auto_attendance", - "fieldtype": "Check", - "label": "Enable Auto Attendance" - }, - { - "description": "Attendance will be marked automatically only after this date.", - "fieldname": "process_attendance_after", - "fieldtype": "Date", - "label": "Process Attendance After" - }, - { - "description": "Last Known Successful Sync of Employee Checkin. Reset this only if you are sure that all Logs are synced from all the locations. Please don't modify this if you are unsure.", - "fieldname": "last_sync_of_checkin", - "fieldtype": "Datetime", - "label": "Last Sync of Checkin" - } - ], - "modified": "2019-07-30 01:05:24.660666", - "modified_by": "Administrator", - "module": "HR", - "name": "Shift Type", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1 - }, - { - "create": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py deleted file mode 100644 index a61bb9ee5f..0000000000 --- a/erpnext/hr/doctype/shift_type/shift_type.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import itertools -from datetime import datetime, timedelta - -import frappe -from frappe.model.document import Document -from frappe.utils import cint, get_datetime, get_time, getdate - -from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange -from erpnext.hr.doctype.attendance.attendance import mark_attendance -from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee -from erpnext.hr.doctype.employee_checkin.employee_checkin import ( - calculate_working_hours, - mark_attendance_and_link_log, -) -from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday -from erpnext.hr.doctype.shift_assignment.shift_assignment import ( - get_employee_shift, - get_shift_details, -) - - -class ShiftType(Document): - @frappe.whitelist() - def process_auto_attendance(self): - if ( - not cint(self.enable_auto_attendance) - or not self.process_attendance_after - or not self.last_sync_of_checkin - ): - return - - filters = { - "skip_auto_attendance": 0, - "attendance": ("is", "not set"), - "time": (">=", self.process_attendance_after), - "shift_actual_end": ("<", self.last_sync_of_checkin), - "shift": self.name, - } - logs = frappe.db.get_list( - "Employee Checkin", fields="*", filters=filters, order_by="employee,time" - ) - - for key, group in itertools.groupby( - logs, key=lambda x: (x["employee"], x["shift_actual_start"]) - ): - single_shift_logs = list(group) - ( - attendance_status, - working_hours, - late_entry, - early_exit, - in_time, - out_time, - ) = self.get_attendance(single_shift_logs) - - 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, - ) - - for employee in self.get_assigned_employee(self.process_attendance_after, True): - self.mark_absent_for_dates_with_no_attendance(employee) - - def get_attendance(self, logs): - """Return attendance_status, working_hours, late_entry, early_exit, in_time, out_time - for a set of logs belonging to a single shift. - Assumptions: - 1. These logs belongs to a single shift, single employee and it's not in a holiday date. - 2. Logs are in chronological order - """ - 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 - ) - 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 - - if ( - cint(self.enable_exit_grace_period) - and out_time - and out_time < logs[0].shift_end - timedelta(minutes=cint(self.early_exit_grace_period)) - ): - early_exit = True - - if ( - self.working_hours_threshold_for_half_day - and total_working_hours < self.working_hours_threshold_for_half_day - ): - return "Half Day", total_working_hours, late_entry, early_exit, in_time, out_time - if ( - self.working_hours_threshold_for_absent - and total_working_hours < self.working_hours_threshold_for_absent - ): - return "Absent", total_working_hours, late_entry, early_exit, in_time, out_time - return "Present", total_working_hours, late_entry, early_exit, in_time, out_time - - def mark_absent_for_dates_with_no_attendance(self, employee): - """Marks Absents for the given employee on working days in this shift which have no attendance marked. - The Absent is marked starting from 'process_attendance_after' or employee creation date. - """ - start_date, end_date = self.get_start_and_end_dates(employee) - - # no shift assignment found, no need to process absent attendance records - if start_date is None: - return - - holiday_list_name = self.holiday_list - if not holiday_list_name: - holiday_list_name = get_holiday_list_for_employee(employee, False) - - start_time = get_time(self.start_time) - - for date in daterange(getdate(start_date), getdate(end_date)): - if is_holiday(holiday_list_name, date): - # skip marking absent on a holiday - continue - - timestamp = datetime.combine(date, start_time) - shift_details = get_employee_shift(employee, timestamp, True) - - if shift_details and shift_details.shift_type.name == self.name: - attendance = mark_attendance(employee, date, "Absent", self.name) - if attendance: - frappe.get_doc( - { - "doctype": "Comment", - "comment_type": "Comment", - "reference_doctype": "Attendance", - "reference_name": attendance, - "content": frappe._("Employee was marked Absent due to missing Employee Checkins."), - } - ).insert(ignore_permissions=True) - - def get_start_and_end_dates(self, employee): - """Returns start and end dates for checking attendance and marking absent - return: start date = max of `process_attendance_after` and DOJ - return: end date = min of shift before `last_sync_of_checkin` and Relieving Date - """ - date_of_joining, relieving_date, employee_creation = frappe.db.get_value( - "Employee", employee, ["date_of_joining", "relieving_date", "creation"] - ) - - if not date_of_joining: - date_of_joining = employee_creation.date() - - start_date = max(getdate(self.process_attendance_after), date_of_joining) - end_date = None - - shift_details = get_shift_details(self.name, get_datetime(self.last_sync_of_checkin)) - last_shift_time = ( - shift_details.actual_start if shift_details else get_datetime(self.last_sync_of_checkin) - ) - - # check if shift is found for 1 day before the last sync of checkin - # absentees are auto-marked 1 day after the shift to wait for any manual attendance records - prev_shift = get_employee_shift(employee, last_shift_time - timedelta(days=1), True, "reverse") - if prev_shift: - end_date = ( - min(prev_shift.start_datetime.date(), relieving_date) - if relieving_date - else prev_shift.start_datetime.date() - ) - else: - # no shift found - return None, None - return start_date, end_date - - def get_assigned_employee(self, from_date=None, consider_default_shift=False): - filters = {"shift_type": self.name, "docstatus": "1"} - if from_date: - filters["start_date"] = (">", from_date) - - assigned_employees = frappe.get_all("Shift Assignment", filters=filters, pluck="employee") - - if consider_default_shift: - filters = {"default_shift": self.name, "status": ["!=", "Inactive"]} - default_shift_employees = frappe.get_all("Employee", filters=filters, pluck="name") - - return list(set(assigned_employees + default_shift_employees)) - return assigned_employees - - -def process_auto_attendance_for_all_shifts(): - shift_list = frappe.get_all("Shift Type", "name", {"enable_auto_attendance": "1"}, as_list=True) - for shift in shift_list: - doc = frappe.get_doc("Shift Type", shift[0]) - doc.process_auto_attendance() diff --git a/erpnext/hr/doctype/shift_type/shift_type_dashboard.py b/erpnext/hr/doctype/shift_type/shift_type_dashboard.py deleted file mode 100644 index 920d8fd547..0000000000 --- a/erpnext/hr/doctype/shift_type/shift_type_dashboard.py +++ /dev/null @@ -1,8 +0,0 @@ -def get_data(): - return { - "fieldname": "shift", - "non_standard_fieldnames": {"Shift Request": "shift_type", "Shift Assignment": "shift_type"}, - "transactions": [ - {"items": ["Attendance", "Employee Checkin", "Shift Request", "Shift Assignment"]} - ], - } diff --git a/erpnext/hr/doctype/shift_type/test_records.json b/erpnext/hr/doctype/shift_type/test_records.json deleted file mode 100644 index 9040b915a1..0000000000 --- a/erpnext/hr/doctype/shift_type/test_records.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "doctype": "Shift Type", - "name": "Day Shift", - "start_time": "9:00:00", - "end_time": "18:00:00" - } -] diff --git a/erpnext/hr/doctype/shift_type/test_shift_type.py b/erpnext/hr/doctype/shift_type/test_shift_type.py deleted file mode 100644 index 0d75292a1e..0000000000 --- a/erpnext/hr/doctype/shift_type/test_shift_type.py +++ /dev/null @@ -1,382 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest -from datetime import datetime, timedelta - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days, get_time, get_year_ending, get_year_start, getdate, now_datetime - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list -from erpnext.hr.doctype.leave_application.test_leave_application import get_first_sunday -from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list - - -class TestShiftType(FrappeTestCase): - def setUp(self): - frappe.db.delete("Shift Type") - frappe.db.delete("Shift Assignment") - frappe.db.delete("Employee Checkin") - frappe.db.delete("Attendance") - - from_date = get_year_start(getdate()) - to_date = get_year_ending(getdate()) - self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date) - - def test_mark_attendance(self): - from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin - - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - - shift_type = setup_shift_type() - date = getdate() - make_shift_assignment(shift_type.name, employee, date) - - timestamp = datetime.combine(date, get_time("08:00:00")) - log_in = make_checkin(employee, timestamp) - self.assertEqual(log_in.shift, shift_type.name) - - timestamp = datetime.combine(date, get_time("12:00:00")) - log_out = make_checkin(employee, timestamp) - self.assertEqual(log_out.shift, shift_type.name) - - shift_type.process_auto_attendance() - - attendance = frappe.db.get_value( - "Attendance", {"shift": shift_type.name}, ["status", "name"], as_dict=True - ) - self.assertEqual(attendance.status, "Present") - - def test_entry_and_exit_grace(self): - from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin - - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - - # doesn't mark late entry until 60 mins after shift start i.e. till 9 - # doesn't mark late entry until 60 mins before shift end i.e. 11 - shift_type = setup_shift_type( - enable_entry_grace_period=1, - enable_exit_grace_period=1, - late_entry_grace_period=60, - early_exit_grace_period=60, - ) - date = getdate() - make_shift_assignment(shift_type.name, employee, date) - - timestamp = datetime.combine(date, get_time("09:30:00")) - log_in = make_checkin(employee, timestamp) - self.assertEqual(log_in.shift, shift_type.name) - - timestamp = datetime.combine(date, get_time("10:30:00")) - log_out = make_checkin(employee, timestamp) - self.assertEqual(log_out.shift, shift_type.name) - - shift_type.process_auto_attendance() - - attendance = frappe.db.get_value( - "Attendance", - {"shift": shift_type.name}, - ["status", "name", "late_entry", "early_exit"], - as_dict=True, - ) - self.assertEqual(attendance.status, "Present") - self.assertEqual(attendance.late_entry, 1) - self.assertEqual(attendance.early_exit, 1) - - def test_working_hours_threshold_for_half_day(self): - from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin - - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - shift_type = setup_shift_type(shift_type="Half Day Test", working_hours_threshold_for_half_day=2) - date = getdate() - make_shift_assignment(shift_type.name, employee, date) - - timestamp = datetime.combine(date, get_time("08:00:00")) - log_in = make_checkin(employee, timestamp) - self.assertEqual(log_in.shift, shift_type.name) - - timestamp = datetime.combine(date, get_time("09:30:00")) - log_out = make_checkin(employee, timestamp) - self.assertEqual(log_out.shift, shift_type.name) - - shift_type.process_auto_attendance() - - attendance = frappe.db.get_value( - "Attendance", {"shift": shift_type.name}, ["status", "working_hours"], as_dict=True - ) - self.assertEqual(attendance.status, "Half Day") - self.assertEqual(attendance.working_hours, 1.5) - - def test_working_hours_threshold_for_absent(self): - from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin - - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - shift_type = setup_shift_type(shift_type="Absent Test", working_hours_threshold_for_absent=2) - date = getdate() - make_shift_assignment(shift_type.name, employee, date) - - timestamp = datetime.combine(date, get_time("08:00:00")) - log_in = make_checkin(employee, timestamp) - self.assertEqual(log_in.shift, shift_type.name) - - timestamp = datetime.combine(date, get_time("09:30:00")) - log_out = make_checkin(employee, timestamp) - self.assertEqual(log_out.shift, shift_type.name) - - shift_type.process_auto_attendance() - - attendance = frappe.db.get_value( - "Attendance", {"shift": shift_type.name}, ["status", "working_hours"], as_dict=True - ) - self.assertEqual(attendance.status, "Absent") - self.assertEqual(attendance.working_hours, 1.5) - - def test_working_hours_threshold_for_absent_and_half_day_1(self): - # considers half day over absent - from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin - - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - shift_type = setup_shift_type( - shift_type="Half Day + Absent Test", - working_hours_threshold_for_half_day=1, - working_hours_threshold_for_absent=2, - ) - date = getdate() - make_shift_assignment(shift_type.name, employee, date) - - timestamp = datetime.combine(date, get_time("08:00:00")) - log_in = make_checkin(employee, timestamp) - self.assertEqual(log_in.shift, shift_type.name) - - timestamp = datetime.combine(date, get_time("08:45:00")) - log_out = make_checkin(employee, timestamp) - self.assertEqual(log_out.shift, shift_type.name) - - shift_type.process_auto_attendance() - - attendance = frappe.db.get_value( - "Attendance", {"shift": shift_type.name}, ["status", "working_hours"], as_dict=True - ) - self.assertEqual(attendance.status, "Half Day") - self.assertEqual(attendance.working_hours, 0.75) - - def test_working_hours_threshold_for_absent_and_half_day_2(self): - # considers absent over half day - from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin - - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - shift_type = setup_shift_type( - shift_type="Half Day + Absent Test", - working_hours_threshold_for_half_day=1, - working_hours_threshold_for_absent=2, - ) - date = getdate() - make_shift_assignment(shift_type.name, employee, date) - - timestamp = datetime.combine(date, get_time("08:00:00")) - log_in = make_checkin(employee, timestamp) - self.assertEqual(log_in.shift, shift_type.name) - - timestamp = datetime.combine(date, get_time("09:30:00")) - log_out = make_checkin(employee, timestamp) - self.assertEqual(log_out.shift, shift_type.name) - - shift_type.process_auto_attendance() - - attendance = frappe.db.get_value("Attendance", {"shift": shift_type.name}, "status") - self.assertEqual(attendance, "Absent") - - def test_mark_absent_for_dates_with_no_attendance(self): - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - shift_type = setup_shift_type(shift_type="Test Absent with no Attendance") - - # absentees are auto-marked one day after to wait for any manual attendance records - date = add_days(getdate(), -1) - make_shift_assignment(shift_type.name, employee, date) - - shift_type.process_auto_attendance() - - attendance = frappe.db.get_value( - "Attendance", {"attendance_date": date, "employee": employee}, "status" - ) - self.assertEqual(attendance, "Absent") - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_skip_marking_absent_on_a_holiday(self): - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - shift_type = setup_shift_type(shift_type="Test Absent with no Attendance") - shift_type.holiday_list = None - shift_type.save() - - # should not mark any attendance if no shift assignment is created - shift_type.process_auto_attendance() - attendance = frappe.db.get_value("Attendance", {"employee": employee}, "status") - self.assertIsNone(attendance) - - first_sunday = get_first_sunday(self.holiday_list, for_date=getdate()) - make_shift_assignment(shift_type.name, employee, first_sunday) - - shift_type.process_auto_attendance() - - attendance = frappe.db.get_value( - "Attendance", {"attendance_date": first_sunday, "employee": employee}, "status" - ) - self.assertIsNone(attendance) - - def test_get_start_and_end_dates(self): - date = getdate() - - doj = add_days(date, -30) - relieving_date = add_days(date, -5) - employee = make_employee( - "test_employee_dates@example.com", - company="_Test Company", - date_of_joining=doj, - relieving_date=relieving_date, - ) - shift_type = setup_shift_type( - shift_type="Test Absent with no Attendance", process_attendance_after=add_days(doj, 2) - ) - - make_shift_assignment(shift_type.name, employee, add_days(date, -25)) - - shift_type.process_auto_attendance() - - # should not mark absent before shift assignment/process attendance after date - attendance = frappe.db.get_value( - "Attendance", {"attendance_date": doj, "employee": employee}, "name" - ) - self.assertIsNone(attendance) - - # mark absent on Relieving Date - attendance = frappe.db.get_value( - "Attendance", {"attendance_date": relieving_date, "employee": employee}, "status" - ) - self.assertEquals(attendance, "Absent") - - # should not mark absent after Relieving Date - attendance = frappe.db.get_value( - "Attendance", {"attendance_date": add_days(relieving_date, 1), "employee": employee}, "name" - ) - self.assertIsNone(attendance) - - def test_skip_auto_attendance_for_duplicate_record(self): - # Skip auto attendance in case of duplicate attendance record - from erpnext.hr.doctype.attendance.attendance import mark_attendance - from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin - - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - - shift_type = setup_shift_type() - date = getdate() - - # mark attendance - mark_attendance(employee, date, "Present") - make_shift_assignment(shift_type.name, employee, date) - - timestamp = datetime.combine(date, get_time("08:00:00")) - log_in = make_checkin(employee, timestamp) - self.assertEqual(log_in.shift, shift_type.name) - - timestamp = datetime.combine(date, get_time("12:00:00")) - log_out = make_checkin(employee, timestamp) - self.assertEqual(log_out.shift, shift_type.name) - - # auto attendance should skip marking - shift_type.process_auto_attendance() - - log_in.reload() - log_out.reload() - self.assertEqual(log_in.skip_auto_attendance, 1) - self.assertEqual(log_out.skip_auto_attendance, 1) - - def test_skip_auto_attendance_for_overlapping_shift(self): - # Skip auto attendance in case of overlapping shift attendance record - # this case won't occur in case of shift assignment, since it will not allow overlapping shifts to be assigned - # can happen if manual attendance records are created - from erpnext.hr.doctype.attendance.attendance import mark_attendance - from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin - - employee = make_employee("test_employee_checkin@example.com", company="_Test Company") - shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00") - shift_2 = setup_shift_type(shift_type="Shift 2", start_time="09:30:00", end_time="11:00:00") - - date = getdate() - - # mark attendance - mark_attendance(employee, date, "Present", shift=shift_1.name) - make_shift_assignment(shift_2.name, employee, date) - - timestamp = datetime.combine(date, get_time("09:30:00")) - log_in = make_checkin(employee, timestamp) - self.assertEqual(log_in.shift, shift_2.name) - - timestamp = datetime.combine(date, get_time("11:00:00")) - log_out = make_checkin(employee, timestamp) - self.assertEqual(log_out.shift, shift_2.name) - - # auto attendance should be skipped for shift 2 - # since it is already marked for overlapping shift 1 - shift_2.process_auto_attendance() - - log_in.reload() - log_out.reload() - self.assertEqual(log_in.skip_auto_attendance, 1) - self.assertEqual(log_out.skip_auto_attendance, 1) - - -def setup_shift_type(**args): - args = frappe._dict(args) - date = getdate() - - shift_type = frappe.get_doc( - { - "doctype": "Shift Type", - "__newname": args.shift_type or "_Test Shift", - "start_time": "08:00:00", - "end_time": "12:00:00", - "enable_auto_attendance": 1, - "determine_check_in_and_check_out": "Alternating entries as IN and OUT during the same shift", - "working_hours_calculation_based_on": "First Check-in and Last Check-out", - "begin_check_in_before_shift_start_time": 60, - "allow_check_out_after_shift_end_time": 60, - "process_attendance_after": add_days(date, -2), - "last_sync_of_checkin": now_datetime() + timedelta(days=1), - } - ) - - holiday_list = "Employee Checkin Test Holiday List" - if not frappe.db.exists("Holiday List", "Employee Checkin Test Holiday List"): - holiday_list = frappe.get_doc( - { - "doctype": "Holiday List", - "holiday_list_name": "Employee Checkin Test Holiday List", - "from_date": get_year_start(date), - "to_date": get_year_ending(date), - } - ).insert() - holiday_list = holiday_list.name - - shift_type.holiday_list = holiday_list - shift_type.update(args) - shift_type.save() - - return shift_type - - -def make_shift_assignment(shift_type, employee, start_date, end_date=None): - shift_assignment = frappe.get_doc( - { - "doctype": "Shift Assignment", - "shift_type": shift_type, - "company": "_Test Company", - "employee": employee, - "start_date": start_date, - "end_date": end_date, - } - ).insert() - shift_assignment.submit() - - return shift_assignment diff --git a/erpnext/hr/doctype/skill/__init__.py b/erpnext/hr/doctype/skill/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/skill/skill.js b/erpnext/hr/doctype/skill/skill.js deleted file mode 100644 index a939ff0dab..0000000000 --- a/erpnext/hr/doctype/skill/skill.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Skill', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/hr/doctype/skill/skill.json b/erpnext/hr/doctype/skill/skill.json deleted file mode 100644 index 4c8a8c92c1..0000000000 --- a/erpnext/hr/doctype/skill/skill.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "autoname": "field:skill_name", - "beta": 0, - "creation": "2019-04-16 09:54:39.486915", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 0, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 1, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "skill_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Skill Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 1 - }, - { - "allow_in_quick_entry": 1, - "fieldname": "description", - "fieldtype": "Text", - "label": "Description" - } - ], - "has_web_view": 0, - "hide_toolbar": 0, - "idx": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2021-02-26 10:55:00.536328", - "modified_by": "Administrator", - "module": "HR", - "name": "Skill", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} diff --git a/erpnext/hr/doctype/skill/skill.py b/erpnext/hr/doctype/skill/skill.py deleted file mode 100644 index d26e7ca490..0000000000 --- a/erpnext/hr/doctype/skill/skill.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class Skill(Document): - pass diff --git a/erpnext/hr/doctype/skill_assessment/__init__.py b/erpnext/hr/doctype/skill_assessment/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/skill_assessment/skill_assessment.json b/erpnext/hr/doctype/skill_assessment/skill_assessment.json deleted file mode 100644 index 8b935c4073..0000000000 --- a/erpnext/hr/doctype/skill_assessment/skill_assessment.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "actions": [], - "creation": "2021-04-12 17:07:39.656289", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "skill", - "rating" - ], - "fields": [ - { - "fieldname": "skill", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Skill", - "options": "Skill", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "rating", - "fieldtype": "Rating", - "in_list_view": 1, - "label": "Rating", - "reqd": 1 - } - ], - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2021-04-12 17:18:14.032298", - "modified_by": "Administrator", - "module": "HR", - "name": "Skill Assessment", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/skill_assessment/skill_assessment.py b/erpnext/hr/doctype/skill_assessment/skill_assessment.py deleted file mode 100644 index 13775be6bd..0000000000 --- a/erpnext/hr/doctype/skill_assessment/skill_assessment.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 SkillAssessment(Document): - pass diff --git a/erpnext/hr/doctype/staffing_plan/__init__.py b/erpnext/hr/doctype/staffing_plan/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.js b/erpnext/hr/doctype/staffing_plan/staffing_plan.js deleted file mode 100644 index 228391ba00..0000000000 --- a/erpnext/hr/doctype/staffing_plan/staffing_plan.js +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Staffing Plan', { - setup: function(frm) { - frm.set_query("designation", "staffing_details", function() { - let designations = []; - (frm.doc.staffing_details || []).forEach(function(staff_detail) { - if(staff_detail.designation){ - designations.push(staff_detail.designation) - } - }) - // Filter out designations already selected in Staffing Plan Detail - return { - filters: [ - ['Designation', 'name', 'not in', designations], - ] - } - }); - - frm.set_query("department", function() { - return { - "filters": { - "company": frm.doc.company, - } - }; - }); - }, -}); - -frappe.ui.form.on('Staffing Plan Detail', { - designation: function(frm, cdt, cdn) { - let child = locals[cdt][cdn]; - if(frm.doc.company && child.designation) { - set_number_of_positions(frm, cdt, cdn); - } - }, - - vacancies: function(frm, cdt, cdn) { - let child = locals[cdt][cdn]; - if(child.vacancies < child.current_openings) { - frappe.throw(__("Vacancies cannot be lower than the current openings")); - } - set_number_of_positions(frm, cdt, cdn); - }, - - current_count: function(frm, cdt, cdn) { - set_number_of_positions(frm, cdt, cdn); - }, - - estimated_cost_per_position: function(frm, cdt, cdn) { - set_total_estimated_cost(frm, cdt, cdn); - } -}); - -var set_number_of_positions = function(frm, cdt, cdn) { - let child = locals[cdt][cdn]; - if (!child.designation) frappe.throw(__("Please enter the designation")); - frappe.call({ - "method": "erpnext.hr.doctype.staffing_plan.staffing_plan.get_designation_counts", - args: { - designation: child.designation, - company: frm.doc.company - }, - callback: function (data) { - if(data.message){ - frappe.model.set_value(cdt, cdn, 'current_count', data.message.employee_count); - frappe.model.set_value(cdt, cdn, 'current_openings', data.message.job_openings); - let total_positions = cint(data.message.employee_count) + cint(child.vacancies); - if (cint(child.number_of_positions) < total_positions){ - frappe.model.set_value(cdt, cdn, 'number_of_positions', total_positions); - } - } - else{ // No employees for this designation - frappe.model.set_value(cdt, cdn, 'current_count', 0); - frappe.model.set_value(cdt, cdn, 'current_openings', 0); - } - } - }); - refresh_field("staffing_details"); - set_total_estimated_cost(frm, cdt, cdn); -} - -// Note: Estimated Cost is calculated on number of Vacancies -var set_total_estimated_cost = function(frm, cdt, cdn) { - let child = locals[cdt][cdn] - if(child.vacancies > 0 && child.estimated_cost_per_position) { - frappe.model.set_value(cdt, cdn, 'total_estimated_cost', child.vacancies * child.estimated_cost_per_position); - } - else { - frappe.model.set_value(cdt, cdn, 'total_estimated_cost', 0); - } - set_total_estimated_budget(frm); -}; - -var set_total_estimated_budget = function(frm) { - let estimated_budget = 0.0 - if(frm.doc.staffing_details) { - (frm.doc.staffing_details || []).forEach(function(staff_detail) { - if(staff_detail.total_estimated_cost){ - estimated_budget += staff_detail.total_estimated_cost - } - }) - frm.set_value('total_estimated_budget', estimated_budget); - } -}; diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.json b/erpnext/hr/doctype/staffing_plan/staffing_plan.json deleted file mode 100644 index 9576bc306a..0000000000 --- a/erpnext/hr/doctype/staffing_plan/staffing_plan.json +++ /dev/null @@ -1,403 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "prompt", - "beta": 0, - "creation": "2018-04-13 18:07:21.582747", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "from_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "From Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "to_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "To Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "staffing_plan_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Staffing Plan Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "staffing_details", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Staffing Plan Detail", - "length": 0, - "no_copy": 0, - "options": "Staffing Plan Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0.00", - "fieldname": "total_estimated_budget", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Estimated Budget", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Staffing Plan", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-05-28 18:30:27.041395", - "modified_by": "Administrator", - "module": "HR", - "name": "Staffing Plan", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py deleted file mode 100644 index 82472dec41..0000000000 --- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py +++ /dev/null @@ -1,222 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import cint, flt, getdate, nowdate -from frappe.utils.nestedset import get_descendants_of - - -class SubsidiaryCompanyError(frappe.ValidationError): - pass - - -class ParentCompanyError(frappe.ValidationError): - pass - - -class StaffingPlan(Document): - def validate(self): - self.validate_period() - self.validate_details() - self.set_total_estimated_budget() - - def validate_period(self): - # Validate Dates - if self.from_date and self.to_date and self.from_date > self.to_date: - frappe.throw(_("From Date cannot be greater than To Date")) - - def validate_details(self): - for detail in self.get("staffing_details"): - self.validate_overlap(detail) - self.validate_with_subsidiary_plans(detail) - self.validate_with_parent_plan(detail) - - def set_total_estimated_budget(self): - self.total_estimated_budget = 0 - - for detail in self.get("staffing_details"): - # Set readonly fields - self.set_number_of_positions(detail) - designation_counts = get_designation_counts(detail.designation, self.company) - detail.current_count = designation_counts["employee_count"] - detail.current_openings = designation_counts["job_openings"] - - detail.total_estimated_cost = 0 - if detail.number_of_positions > 0: - if detail.vacancies and detail.estimated_cost_per_position: - detail.total_estimated_cost = cint(detail.vacancies) * flt(detail.estimated_cost_per_position) - - self.total_estimated_budget += detail.total_estimated_cost - - def set_number_of_positions(self, detail): - detail.number_of_positions = cint(detail.vacancies) + cint(detail.current_count) - - def validate_overlap(self, staffing_plan_detail): - # Validate if any submitted Staffing Plan exist for any Designations in this plan - # and spd.vacancies>0 ? - overlap = frappe.db.sql( - """select spd.parent - from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name - where spd.designation=%s and sp.docstatus=1 - and sp.to_date >= %s and sp.from_date <= %s and sp.company = %s - """, - (staffing_plan_detail.designation, self.from_date, self.to_date, self.company), - ) - if overlap and overlap[0][0]: - frappe.throw( - _("Staffing Plan {0} already exist for designation {1}").format( - overlap[0][0], staffing_plan_detail.designation - ) - ) - - def validate_with_parent_plan(self, staffing_plan_detail): - if not frappe.get_cached_value("Company", self.company, "parent_company"): - return # No parent, nothing to validate - - # Get staffing plan applicable for the company (Parent Company) - parent_plan_details = get_active_staffing_plan_details( - self.company, staffing_plan_detail.designation, self.from_date, self.to_date - ) - if not parent_plan_details: - return # no staffing plan for any parent Company in hierarchy - - # Fetch parent company which owns the staffing plan. NOTE: Parent could be higher up in the hierarchy - parent_company = frappe.db.get_value("Staffing Plan", parent_plan_details[0].name, "company") - # Parent plan available, validate with parent, siblings as well as children of staffing plan Company - if cint(staffing_plan_detail.vacancies) > cint(parent_plan_details[0].vacancies) or flt( - staffing_plan_detail.total_estimated_cost - ) > flt(parent_plan_details[0].total_estimated_cost): - frappe.throw( - _( - "You can only plan for upto {0} vacancies and budget {1} for {2} as per staffing plan {3} for parent company {4}." - ).format( - cint(parent_plan_details[0].vacancies), - parent_plan_details[0].total_estimated_cost, - frappe.bold(staffing_plan_detail.designation), - parent_plan_details[0].name, - parent_company, - ), - ParentCompanyError, - ) - - # Get vacanices already planned for all companies down the hierarchy of Parent Company - lft, rgt = frappe.get_cached_value("Company", parent_company, ["lft", "rgt"]) - all_sibling_details = frappe.db.sql( - """select sum(spd.vacancies) as vacancies, - sum(spd.total_estimated_cost) as total_estimated_cost - from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name - where spd.designation=%s and sp.docstatus=1 - and sp.to_date >= %s and sp.from_date <=%s - and sp.company in (select name from tabCompany where lft > %s and rgt < %s) - """, - (staffing_plan_detail.designation, self.from_date, self.to_date, lft, rgt), - as_dict=1, - )[0] - - if ( - cint(parent_plan_details[0].vacancies) - < (cint(staffing_plan_detail.vacancies) + cint(all_sibling_details.vacancies)) - ) or ( - flt(parent_plan_details[0].total_estimated_cost) - < ( - flt(staffing_plan_detail.total_estimated_cost) + flt(all_sibling_details.total_estimated_cost) - ) - ): - frappe.throw( - _( - "{0} vacancies and {1} budget for {2} already planned for subsidiary companies of {3}. You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}." - ).format( - cint(all_sibling_details.vacancies), - all_sibling_details.total_estimated_cost, - frappe.bold(staffing_plan_detail.designation), - parent_company, - cint(parent_plan_details[0].vacancies), - parent_plan_details[0].total_estimated_cost, - parent_plan_details[0].name, - ) - ) - - def validate_with_subsidiary_plans(self, staffing_plan_detail): - # Valdate this plan with all child company plan - children_details = frappe.db.sql( - """select sum(spd.vacancies) as vacancies, - sum(spd.total_estimated_cost) as total_estimated_cost - from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name - where spd.designation=%s and sp.docstatus=1 - and sp.to_date >= %s and sp.from_date <=%s - and sp.company in (select name from tabCompany where parent_company = %s) - """, - (staffing_plan_detail.designation, self.from_date, self.to_date, self.company), - as_dict=1, - )[0] - - if ( - children_details - and cint(staffing_plan_detail.vacancies) < cint(children_details.vacancies) - or flt(staffing_plan_detail.total_estimated_cost) < flt(children_details.total_estimated_cost) - ): - frappe.throw( - _( - "Subsidiary companies have already planned for {1} vacancies at a budget of {2}. Staffing Plan for {0} should allocate more vacancies and budget for {3} than planned for its subsidiary companies" - ).format( - self.company, - cint(children_details.vacancies), - children_details.total_estimated_cost, - frappe.bold(staffing_plan_detail.designation), - ), - SubsidiaryCompanyError, - ) - - -@frappe.whitelist() -def get_designation_counts(designation, company, job_opening=None): - if not designation: - return False - - company_set = get_descendants_of("Company", company) - company_set.append(company) - - employee_count = frappe.db.count( - "Employee", {"designation": designation, "status": "Active", "company": ("in", company_set)} - ) - - filters = {"designation": designation, "status": "Open", "company": ("in", company_set)} - if job_opening: - filters["name"] = ("!=", job_opening) - - job_openings = frappe.db.count("Job Opening", filters) - - return {"employee_count": employee_count, "job_openings": job_openings} - - -@frappe.whitelist() -def get_active_staffing_plan_details(company, designation, from_date=None, to_date=None): - if from_date is None: - from_date = getdate(nowdate()) - if to_date is None: - to_date = getdate(nowdate()) - if not company or not designation: - frappe.throw(_("Please select Company and Designation")) - - staffing_plan = frappe.db.sql( - """ - select sp.name, spd.vacancies, spd.total_estimated_cost - from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name - where company=%s and spd.designation=%s and sp.docstatus=1 - and to_date >= %s and from_date <= %s """, - (company, designation, from_date, to_date), - as_dict=1, - ) - - if not staffing_plan: - parent_company = frappe.get_cached_value("Company", company, "parent_company") - if parent_company: - staffing_plan = get_active_staffing_plan_details( - parent_company, designation, from_date, to_date - ) - - # Only a single staffing plan can be active for a designation on given date - return staffing_plan if staffing_plan else None diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py b/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py deleted file mode 100644 index 0f555d9db2..0000000000 --- a/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py +++ /dev/null @@ -1,5 +0,0 @@ -def get_data(): - return { - "fieldname": "staffing_plan", - "transactions": [{"items": ["Job Opening"]}], - } diff --git a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py deleted file mode 100644 index ac69c21979..0000000000 --- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import add_days, nowdate - -from erpnext.hr.doctype.staffing_plan.staffing_plan import ( - ParentCompanyError, - SubsidiaryCompanyError, -) - -test_dependencies = ["Designation"] - - -class TestStaffingPlan(unittest.TestCase): - def test_staffing_plan(self): - _set_up() - frappe.db.set_value("Company", "_Test Company 3", "is_group", 1) - if frappe.db.exists("Staffing Plan", "Test"): - return - staffing_plan = frappe.new_doc("Staffing Plan") - staffing_plan.company = "_Test Company 10" - staffing_plan.name = "Test" - staffing_plan.from_date = nowdate() - staffing_plan.to_date = add_days(nowdate(), 10) - staffing_plan.append( - "staffing_details", - {"designation": "Designer", "vacancies": 6, "estimated_cost_per_position": 50000}, - ) - staffing_plan.insert() - staffing_plan.submit() - self.assertEqual(staffing_plan.total_estimated_budget, 300000.00) - - def test_staffing_plan_subsidiary_company(self): - self.test_staffing_plan() - if frappe.db.exists("Staffing Plan", "Test 1"): - return - staffing_plan = frappe.new_doc("Staffing Plan") - staffing_plan.company = "_Test Company 3" - staffing_plan.name = "Test 1" - staffing_plan.from_date = nowdate() - staffing_plan.to_date = add_days(nowdate(), 10) - staffing_plan.append( - "staffing_details", - {"designation": "Designer", "vacancies": 3, "estimated_cost_per_position": 45000}, - ) - self.assertRaises(SubsidiaryCompanyError, staffing_plan.insert) - - def test_staffing_plan_parent_company(self): - _set_up() - if frappe.db.exists("Staffing Plan", "Test"): - return - staffing_plan = frappe.new_doc("Staffing Plan") - staffing_plan.company = "_Test Company 3" - staffing_plan.name = "Test" - staffing_plan.from_date = nowdate() - staffing_plan.to_date = add_days(nowdate(), 10) - staffing_plan.append( - "staffing_details", - {"designation": "Designer", "vacancies": 7, "estimated_cost_per_position": 50000}, - ) - staffing_plan.insert() - staffing_plan.submit() - self.assertEqual(staffing_plan.total_estimated_budget, 350000.00) - if frappe.db.exists("Staffing Plan", "Test 1"): - return - staffing_plan = frappe.new_doc("Staffing Plan") - staffing_plan.company = "_Test Company 10" - staffing_plan.name = "Test 1" - staffing_plan.from_date = nowdate() - staffing_plan.to_date = add_days(nowdate(), 10) - staffing_plan.append( - "staffing_details", - {"designation": "Designer", "vacancies": 7, "estimated_cost_per_position": 60000}, - ) - staffing_plan.insert() - self.assertRaises(ParentCompanyError, staffing_plan.submit) - - -def _set_up(): - for doctype in ["Staffing Plan", "Staffing Plan Detail"]: - frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype)) - make_company() - - -def make_company(name=None, abbr=None): - if not name: - name = "_Test Company 10" - - if frappe.db.exists("Company", name): - return - - company = frappe.new_doc("Company") - company.company_name = name - company.abbr = abbr or "_TC10" - company.parent_company = "_Test Company 3" - company.default_currency = "INR" - company.country = "Pakistan" - company.insert() diff --git a/erpnext/hr/doctype/staffing_plan_detail/__init__.py b/erpnext/hr/doctype/staffing_plan_detail/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json b/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json deleted file mode 100644 index 77164c4e67..0000000000 --- a/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "creation": "2018-04-13 18:04:20.978931", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "designation", - "vacancies", - "estimated_cost_per_position", - "total_estimated_cost", - "column_break_5", - "current_count", - "current_openings", - "number_of_positions" - ], - "fields": [ - { - "fieldname": "designation", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Designation", - "options": "Designation", - "reqd": 1 - }, - { - "fieldname": "number_of_positions", - "fieldtype": "Int", - "in_list_view": 1, - "label": "Number Of Positions", - "read_only": 1 - }, - { - "fieldname": "estimated_cost_per_position", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Estimated Cost Per Position" - }, - { - "fieldname": "column_break_5", - "fieldtype": "Column Break" - }, - { - "fieldname": "current_count", - "fieldtype": "Int", - "label": "Current Count", - "read_only": 1 - }, - { - "fieldname": "current_openings", - "fieldtype": "Int", - "label": "Current Openings", - "read_only": 1 - }, - { - "fieldname": "vacancies", - "fieldtype": "Int", - "in_list_view": 1, - "label": "Vacancies" - }, - { - "fieldname": "total_estimated_cost", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Total Estimated Cost", - "read_only": 1 - } - ], - "istable": 1, - "modified": "2019-06-24 18:40:37.140178", - "modified_by": "Administrator", - "module": "HR", - "name": "Staffing Plan Detail", - "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/hr/doctype/staffing_plan_detail/staffing_plan_detail.py b/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.py deleted file mode 100644 index 6749690934..0000000000 --- a/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class StaffingPlanDetail(Document): - pass diff --git a/erpnext/hr/doctype/training_event/__init__.py b/erpnext/hr/doctype/training_event/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/training_event/test_training_event.py b/erpnext/hr/doctype/training_event/test_training_event.py deleted file mode 100644 index ec7eb74da9..0000000000 --- a/erpnext/hr/doctype/training_event/test_training_event.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import add_days, today - -from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee - - -class TestTrainingEvent(unittest.TestCase): - def setUp(self): - create_training_program("Basic Training") - employee = make_employee("robert_loan@trainig.com") - employee2 = make_employee("suzie.tan@trainig.com") - self.attendees = [{"employee": employee}, {"employee": employee2}] - - def test_training_event_status_update(self): - training_event = create_training_event(self.attendees) - training_event.submit() - - training_event.event_status = "Completed" - training_event.save() - training_event.reload() - - for entry in training_event.employees: - self.assertEqual(entry.status, "Completed") - - training_event.event_status = "Scheduled" - training_event.save() - training_event.reload() - - for entry in training_event.employees: - self.assertEqual(entry.status, "Open") - - def tearDown(self): - frappe.db.rollback() - - -def create_training_program(training_program): - if not frappe.db.get_value("Training Program", training_program): - frappe.get_doc( - { - "doctype": "Training Program", - "training_program": training_program, - "description": training_program, - } - ).insert() - - -def create_training_event(attendees): - return frappe.get_doc( - { - "doctype": "Training Event", - "event_name": "Basic Training Event", - "training_program": "Basic Training", - "location": "Union Square", - "start_time": add_days(today(), 5), - "end_time": add_days(today(), 6), - "introduction": "Welcome to the Basic Training Event", - "employees": attendees, - } - ).insert() diff --git a/erpnext/hr/doctype/training_event/training_event.js b/erpnext/hr/doctype/training_event/training_event.js deleted file mode 100644 index 642e6a1fd7..0000000000 --- a/erpnext/hr/doctype/training_event/training_event.js +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Training Event', { - onload_post_render: function (frm) { - frm.get_field("employees").grid.set_multiple_add("employee"); - }, - refresh: function (frm) { - if (!frm.doc.__islocal) { - frm.add_custom_button(__("Training Result"), function () { - frappe.route_options = { - training_event: frm.doc.name - }; - frappe.set_route("List", "Training Result"); - }); - frm.add_custom_button(__("Training Feedback"), function () { - frappe.route_options = { - training_event: frm.doc.name - }; - frappe.set_route("List", "Training Feedback"); - }); - } - frm.events.set_employee_query(frm); - }, - - set_employee_query: function(frm) { - let emp = []; - for (let d in frm.doc.employees) { - if (frm.doc.employees[d].employee) { - emp.push(frm.doc.employees[d].employee); - } - } - frm.set_query("employee", "employees", function () { - return { - filters: { - name: ["NOT IN", emp], - status: "Active" - } - }; - }); - } -}); - -frappe.ui.form.on("Training Event Employee", { - employee: function(frm) { - frm.events.set_employee_query(frm); - } -}); diff --git a/erpnext/hr/doctype/training_event/training_event.json b/erpnext/hr/doctype/training_event/training_event.json deleted file mode 100644 index 42e02ca3bf..0000000000 --- a/erpnext/hr/doctype/training_event/training_event.json +++ /dev/null @@ -1,231 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:event_name", - "creation": "2016-08-08 04:53:58.355206", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "event_name", - "training_program", - "event_status", - "has_certificate", - "column_break_2", - "type", - "level", - "company", - "section_break_4", - "trainer_name", - "trainer_email", - "column_break_7", - "supplier", - "contact_number", - "section_break_9", - "course", - "location", - "column_break_12", - "start_time", - "end_time", - "section_break_15", - "introduction", - "section_break_18", - "employees", - "amended_from", - "employee_emails" - ], - "fields": [ - { - "fieldname": "event_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Event Name", - "no_copy": 1, - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "training_program", - "fieldtype": "Link", - "label": "Training Program", - "options": "Training Program" - }, - { - "allow_on_submit": 1, - "fieldname": "event_status", - "fieldtype": "Select", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Event Status", - "options": "Scheduled\nCompleted\nCancelled", - "reqd": 1 - }, - { - "default": "0", - "depends_on": "eval:doc.type == 'Seminar' || doc.type == 'Workshop' || doc.type == 'Conference' || doc.type == 'Exam'", - "fieldname": "has_certificate", - "fieldtype": "Check", - "label": "Has Certificate" - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fieldname": "type", - "fieldtype": "Select", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Type", - "options": "Seminar\nTheory\nWorkshop\nConference\nExam\nInternet\nSelf-Study", - "reqd": 1 - }, - { - "depends_on": "eval:doc.type == 'Seminar' || doc.type == 'Workshop' || doc.type == 'Exam'", - "fieldname": "level", - "fieldtype": "Select", - "label": "Level", - "options": "\nBeginner\nIntermediate\nAdvance" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company" - }, - { - "fieldname": "section_break_4", - "fieldtype": "Section Break" - }, - { - "fieldname": "trainer_name", - "fieldtype": "Data", - "label": "Trainer Name" - }, - { - "fieldname": "trainer_email", - "fieldtype": "Data", - "label": "Trainer Email" - }, - { - "fieldname": "column_break_7", - "fieldtype": "Column Break" - }, - { - "fieldname": "supplier", - "fieldtype": "Link", - "label": "Supplier", - "options": "Supplier" - }, - { - "fieldname": "contact_number", - "fieldtype": "Data", - "label": "Contact Number" - }, - { - "fieldname": "section_break_9", - "fieldtype": "Section Break" - }, - { - "fieldname": "course", - "fieldtype": "Data", - "in_standard_filter": 1, - "label": "Course" - }, - { - "fieldname": "location", - "fieldtype": "Data", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Location", - "reqd": 1 - }, - { - "fieldname": "column_break_12", - "fieldtype": "Column Break" - }, - { - "fieldname": "start_time", - "fieldtype": "Datetime", - "label": "Start Time", - "reqd": 1 - }, - { - "fieldname": "end_time", - "fieldtype": "Datetime", - "label": "End Time", - "reqd": 1 - }, - { - "fieldname": "section_break_15", - "fieldtype": "Section Break" - }, - { - "fieldname": "introduction", - "fieldtype": "Text Editor", - "label": "Introduction", - "reqd": 1 - }, - { - "fieldname": "section_break_18", - "fieldtype": "Section Break", - "label": "Attendees" - }, - { - "allow_on_submit": 1, - "fieldname": "employees", - "fieldtype": "Table", - "label": "Employees", - "options": "Training Event Employee" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Training Event", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "employee_emails", - "fieldtype": "Small Text", - "hidden": 1, - "label": "Employee Emails", - "options": "Email" - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-04-28 13:29:35.139497", - "modified_by": "Administrator", - "module": "HR", - "name": "Training Event", - "naming_rule": "By fieldname", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "import": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "event_name", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "event_name" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/training_event/training_event.py b/erpnext/hr/doctype/training_event/training_event.py deleted file mode 100644 index 59972bb2f3..0000000000 --- a/erpnext/hr/doctype/training_event/training_event.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import time_diff_in_seconds - -from erpnext.hr.doctype.employee.employee import get_employee_emails - - -class TrainingEvent(Document): - def validate(self): - self.set_employee_emails() - self.validate_period() - - def on_update_after_submit(self): - self.set_status_for_attendees() - - def set_employee_emails(self): - self.employee_emails = ", ".join(get_employee_emails([d.employee for d in self.employees])) - - def validate_period(self): - if time_diff_in_seconds(self.end_time, self.start_time) <= 0: - frappe.throw(_("End time cannot be before start time")) - - def set_status_for_attendees(self): - if self.event_status == "Completed": - for employee in self.employees: - if employee.attendance == "Present" and employee.status != "Feedback Submitted": - employee.status = "Completed" - - elif self.event_status == "Scheduled": - for employee in self.employees: - employee.status = "Open" - - self.db_update_all() diff --git a/erpnext/hr/doctype/training_event/training_event_calendar.js b/erpnext/hr/doctype/training_event/training_event_calendar.js deleted file mode 100644 index cb168c01d5..0000000000 --- a/erpnext/hr/doctype/training_event/training_event_calendar.js +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.views.calendar["Training Event"] = { - field_map: { - "start": "start_time", - "end": "end_time", - "id": "name", - "title": "event_name", - "allDay": "allDay" - }, - gantt: true, - get_events_method: "frappe.desk.calendar.get_events", -} diff --git a/erpnext/hr/doctype/training_event/training_event_dashboard.py b/erpnext/hr/doctype/training_event/training_event_dashboard.py deleted file mode 100644 index ca13938e58..0000000000 --- a/erpnext/hr/doctype/training_event/training_event_dashboard.py +++ /dev/null @@ -1,7 +0,0 @@ -def get_data(): - return { - "fieldname": "training_event", - "transactions": [ - {"items": ["Training Result", "Training Feedback"]}, - ], - } diff --git a/erpnext/hr/doctype/training_event_employee/__init__.py b/erpnext/hr/doctype/training_event_employee/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/training_event_employee/training_event_employee.json b/erpnext/hr/doctype/training_event_employee/training_event_employee.json deleted file mode 100644 index bcb7d5e5bc..0000000000 --- a/erpnext/hr/doctype/training_event_employee/training_event_employee.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "actions": [], - "creation": "2016-08-08 05:33:39.965305", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "department", - "column_break_3", - "status", - "attendance", - "is_mandatory" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Employee", - "no_copy": 1, - "options": "Employee" - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Read Only", - "label": "Employee Name" - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "allow_on_submit": 1, - "default": "Open", - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Status", - "no_copy": 1, - "options": "Open\nInvited\nCompleted\nFeedback Submitted" - }, - { - "fieldname": "attendance", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Attendance", - "options": "Present\nAbsent" - }, - { - "columns": 2, - "default": "1", - "fieldname": "is_mandatory", - "fieldtype": "Check", - "in_list_view": 1, - "label": "Is Mandatory" - } - ], - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2021-07-02 17:20:27.630176", - "modified_by": "Administrator", - "module": "HR", - "name": "Training Event Employee", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/training_event_employee/training_event_employee.py b/erpnext/hr/doctype/training_event_employee/training_event_employee.py deleted file mode 100644 index 5dce6e17c0..0000000000 --- a/erpnext/hr/doctype/training_event_employee/training_event_employee.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class TrainingEventEmployee(Document): - pass diff --git a/erpnext/hr/doctype/training_feedback/__init__.py b/erpnext/hr/doctype/training_feedback/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/training_feedback/test_training_feedback.py b/erpnext/hr/doctype/training_feedback/test_training_feedback.py deleted file mode 100644 index c787b7038f..0000000000 --- a/erpnext/hr/doctype/training_feedback/test_training_feedback.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe - -from erpnext.hr.doctype.training_event.test_training_event import ( - create_training_event, - create_training_program, -) -from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee - - -class TestTrainingFeedback(unittest.TestCase): - def setUp(self): - create_training_program("Basic Training") - self.employee = make_employee("robert_loan@trainig.com") - self.employee2 = make_employee("suzie.tan@trainig.com") - self.attendees = [{"employee": self.employee}] - - def test_employee_validations_for_feedback(self): - training_event = create_training_event(self.attendees) - training_event.submit() - - training_event.event_status = "Completed" - training_event.save() - training_event.reload() - - # should not allow creating feedback since employee2 was not part of the event - feedback = create_training_feedback(training_event.name, self.employee2) - self.assertRaises(frappe.ValidationError, feedback.save) - - # cannot record feedback for absent employee - employee = frappe.db.get_value( - "Training Event Employee", {"parent": training_event.name, "employee": self.employee}, "name" - ) - - frappe.db.set_value("Training Event Employee", employee, "attendance", "Absent") - feedback = create_training_feedback(training_event.name, self.employee) - self.assertRaises(frappe.ValidationError, feedback.save) - - def test_training_feedback_status(self): - training_event = create_training_event(self.attendees) - training_event.submit() - - training_event.event_status = "Completed" - training_event.save() - training_event.reload() - - feedback = create_training_feedback(training_event.name, self.employee) - feedback.submit() - - status = frappe.db.get_value( - "Training Event Employee", {"parent": training_event.name, "employee": self.employee}, "status" - ) - - self.assertEqual(status, "Feedback Submitted") - - def tearDown(self): - frappe.db.rollback() - - -def create_training_feedback(event, employee): - return frappe.get_doc( - { - "doctype": "Training Feedback", - "training_event": event, - "employee": employee, - "feedback": "Test", - } - ) diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.js b/erpnext/hr/doctype/training_feedback/training_feedback.js deleted file mode 100644 index 5e875c1b43..0000000000 --- a/erpnext/hr/doctype/training_feedback/training_feedback.js +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Training Feedback', { - onload: function(frm) { - frm.add_fetch("training_event", "course", "course"); - frm.add_fetch("training_event", "event_name", "event_name"); - frm.add_fetch("training_event", "trainer_name", "trainer_name"); - } -}); diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.json b/erpnext/hr/doctype/training_feedback/training_feedback.json deleted file mode 100644 index e968911776..0000000000 --- a/erpnext/hr/doctype/training_feedback/training_feedback.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "actions": [], - "autoname": "HR-TRF-.YYYY.-.#####", - "creation": "2022-01-27 13:14:35.935580", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "department", - "course", - "column_break_3", - "training_event", - "event_name", - "trainer_name", - "section_break_6", - "feedback", - "amended_from" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "in_global_search": 1, - "in_standard_filter": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Read Only", - "in_global_search": 1, - "label": "Employee Name" - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fetch_from": "training_event.course", - "fieldname": "course", - "fieldtype": "Data", - "label": "Course", - "read_only": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "training_event", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Training Event", - "options": "Training Event", - "reqd": 1 - }, - { - "fetch_from": "training_event.event_name", - "fieldname": "event_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Event Name", - "read_only": 1 - }, - { - "fetch_from": "training_event.trainer_name", - "fieldname": "trainer_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Trainer Name", - "read_only": 1 - }, - { - "fieldname": "section_break_6", - "fieldtype": "Section Break" - }, - { - "fieldname": "feedback", - "fieldtype": "Text", - "label": "Feedback", - "reqd": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Training Feedback", - "print_hide": 1, - "read_only": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-04-28 13:32:29.261421", - "modified_by": "Administrator", - "module": "HR", - "name": "Training Feedback", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "employee_name, training_event, event_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.py b/erpnext/hr/doctype/training_feedback/training_feedback.py deleted file mode 100644 index d5de28ed2d..0000000000 --- a/erpnext/hr/doctype/training_feedback/training_feedback.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document - - -class TrainingFeedback(Document): - def validate(self): - training_event = frappe.get_doc("Training Event", self.training_event) - if training_event.docstatus != 1: - frappe.throw(_("{0} must be submitted").format(_("Training Event"))) - - emp_event_details = frappe.db.get_value( - "Training Event Employee", - {"parent": self.training_event, "employee": self.employee}, - ["name", "attendance"], - as_dict=True, - ) - - if not emp_event_details: - frappe.throw( - _("Employee {0} not found in Training Event Participants.").format( - frappe.bold(self.employee_name) - ) - ) - - if emp_event_details.attendance == "Absent": - frappe.throw(_("Feedback cannot be recorded for an absent Employee.")) - - def on_submit(self): - employee = frappe.db.get_value( - "Training Event Employee", {"parent": self.training_event, "employee": self.employee} - ) - - if employee: - frappe.db.set_value("Training Event Employee", employee, "status", "Feedback Submitted") - - def on_cancel(self): - employee = frappe.db.get_value( - "Training Event Employee", {"parent": self.training_event, "employee": self.employee} - ) - - if employee: - frappe.db.set_value("Training Event Employee", employee, "status", "Completed") diff --git a/erpnext/hr/doctype/training_program/__init__.py b/erpnext/hr/doctype/training_program/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/training_program/test_training_program.py b/erpnext/hr/doctype/training_program/test_training_program.py deleted file mode 100644 index 5000705ab2..0000000000 --- a/erpnext/hr/doctype/training_program/test_training_program.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestTrainingProgram(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/training_program/training_program.js b/erpnext/hr/doctype/training_program/training_program.js deleted file mode 100644 index a4ccf54063..0000000000 --- a/erpnext/hr/doctype/training_program/training_program.js +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Training Program', { -}); diff --git a/erpnext/hr/doctype/training_program/training_program.json b/erpnext/hr/doctype/training_program/training_program.json deleted file mode 100644 index 522d5e9e4f..0000000000 --- a/erpnext/hr/doctype/training_program/training_program.json +++ /dev/null @@ -1,454 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "autoname": "field:training_program", - "beta": 0, - "creation": "2017-10-11 04:43:17.230065", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "training_program", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Training Program", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 1, - "collapsible": 0, - "columns": 0, - "default": "Scheduled", - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Scheduled\nCompleted\nCancelled", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "trainer_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Trainer Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "trainer_email", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Trainer Email", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "supplier", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Supplier", - "length": 0, - "no_copy": 0, - "options": "Supplier", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_number", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contact Number", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_11", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Training Program", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-11-27 08:12:03.649247", - "modified_by": "Administrator", - "module": "HR", - "name": "Training Program", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "training_program", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/training_program/training_program.py b/erpnext/hr/doctype/training_program/training_program.py deleted file mode 100644 index 96b2fd7002..0000000000 --- a/erpnext/hr/doctype/training_program/training_program.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class TrainingProgram(Document): - pass diff --git a/erpnext/hr/doctype/training_program/training_program_dashboard.py b/erpnext/hr/doctype/training_program/training_program_dashboard.py deleted file mode 100644 index 1735db18e1..0000000000 --- a/erpnext/hr/doctype/training_program/training_program_dashboard.py +++ /dev/null @@ -1,10 +0,0 @@ -from frappe import _ - - -def get_data(): - return { - "fieldname": "training_program", - "transactions": [ - {"label": _("Training Events"), "items": ["Training Event"]}, - ], - } diff --git a/erpnext/hr/doctype/training_result/__init__.py b/erpnext/hr/doctype/training_result/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/training_result/test_training_result.py b/erpnext/hr/doctype/training_result/test_training_result.py deleted file mode 100644 index 136543cbe1..0000000000 --- a/erpnext/hr/doctype/training_result/test_training_result.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -# test_records = frappe.get_test_records('Training Result') - - -class TestTrainingResult(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/training_result/training_result.js b/erpnext/hr/doctype/training_result/training_result.js deleted file mode 100644 index 718b383e72..0000000000 --- a/erpnext/hr/doctype/training_result/training_result.js +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Training Result', { - onload: function(frm) { - frm.trigger("training_event"); - }, - - training_event: function(frm) { - frm.trigger("training_event"); - }, - - training_event: function(frm) { - if (frm.doc.training_event && !frm.doc.docstatus && !frm.doc.employees) { - frappe.call({ - method: "erpnext.hr.doctype.training_result.training_result.get_employees", - args: { - "training_event": frm.doc.training_event - }, - callback: function(r) { - frm.set_value("employees" ,""); - if (r.message) { - $.each(r.message, function(i, d) { - var row = frappe.model.add_child(frm.doc, "Training Result Employee", "employees"); - row.employee = d.employee; - row.employee_name = d.employee_name; - }); - } - refresh_field("employees"); - } - }); - } - } -}); diff --git a/erpnext/hr/doctype/training_result/training_result.json b/erpnext/hr/doctype/training_result/training_result.json deleted file mode 100644 index f28669e3c2..0000000000 --- a/erpnext/hr/doctype/training_result/training_result.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "actions": [], - "allow_rename": 1, - "autoname": "HR-TRR-.YYYY.-.#####", - "creation": "2016-11-04 02:13:48.407576", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "training_event", - "section_break_3", - "employees", - "amended_from", - "employee_emails" - ], - "fields": [ - { - "fieldname": "training_event", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Training Event", - "options": "Training Event", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "section_break_3", - "fieldtype": "Section Break" - }, - { - "fieldname": "employees", - "fieldtype": "Table", - "label": "Employees", - "options": "Training Result Employee" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Training Result", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "employee_emails", - "fieldtype": "Small Text", - "hidden": 1, - "label": "Employee Emails", - "options": "Email" - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-18 19:31:44.900034", - "modified_by": "Administrator", - "module": "HR", - "name": "Training Result", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "training_event", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "training_event" -} \ No newline at end of file diff --git a/erpnext/hr/doctype/training_result/training_result.py b/erpnext/hr/doctype/training_result/training_result.py deleted file mode 100644 index 48a5b2c2e9..0000000000 --- a/erpnext/hr/doctype/training_result/training_result.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document - -from erpnext.hr.doctype.employee.employee import get_employee_emails - - -class TrainingResult(Document): - def validate(self): - training_event = frappe.get_doc("Training Event", self.training_event) - if training_event.docstatus != 1: - frappe.throw(_("{0} must be submitted").format(_("Training Event"))) - - self.employee_emails = ", ".join(get_employee_emails([d.employee for d in self.employees])) - - def on_submit(self): - training_event = frappe.get_doc("Training Event", self.training_event) - training_event.status = "Completed" - for e in self.employees: - for e1 in training_event.employees: - if e1.employee == e.employee: - e1.status = "Completed" - break - - training_event.save() - - -@frappe.whitelist() -def get_employees(training_event): - return frappe.get_doc("Training Event", training_event).employees diff --git a/erpnext/hr/doctype/training_result_employee/__init__.py b/erpnext/hr/doctype/training_result_employee/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/training_result_employee/training_result_employee.json b/erpnext/hr/doctype/training_result_employee/training_result_employee.json deleted file mode 100644 index c4747545d0..0000000000 --- a/erpnext/hr/doctype/training_result_employee/training_result_employee.json +++ /dev/null @@ -1,334 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-11-04 02:39:12.825569", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "hours", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Hours", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "grade", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Grade", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "comments", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Comments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-01-30 11:28:14.337778", - "modified_by": "Administrator", - "module": "HR", - "name": "Training Result Employee", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/training_result_employee/training_result_employee.py b/erpnext/hr/doctype/training_result_employee/training_result_employee.py deleted file mode 100644 index e048ff5341..0000000000 --- a/erpnext/hr/doctype/training_result_employee/training_result_employee.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class TrainingResultEmployee(Document): - pass diff --git a/erpnext/hr/doctype/travel_itinerary/__init__.py b/erpnext/hr/doctype/travel_itinerary/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/travel_itinerary/travel_itinerary.json b/erpnext/hr/doctype/travel_itinerary/travel_itinerary.json deleted file mode 100644 index f887027b28..0000000000 --- a/erpnext/hr/doctype/travel_itinerary/travel_itinerary.json +++ /dev/null @@ -1,512 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-05-15 07:40:59.181192", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "travel_from", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Travel From", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "travel_to", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Travel To", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mode_of_travel", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Mode of Travel", - "length": 0, - "no_copy": 0, - "options": "\nFlight\nTrain\nTaxi\nRented Car", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "meal_preference", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Meal Preference", - "length": 0, - "no_copy": 0, - "options": "\nVegetarian\nNon-Vegetarian\nGluten Free\nNon Diary", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "travel_advance_required", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Travel Advance Required", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "travel_advance_required", - "fieldname": "advance_amount", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Advance Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "departure_date", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Departure Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "arrival_date", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Arrival Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lodging_required", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Lodging Required", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "lodging_required", - "fieldname": "preferred_area_for_lodging", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Preferred Area for Lodging", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "lodging_required", - "fieldname": "check_in_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Check-in Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "lodging_required", - "fieldname": "check_out_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Check-out Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_14", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "other_details", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Other Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-05-15 09:55:20.138108", - "modified_by": "Administrator", - "module": "HR", - "name": "Travel Itinerary", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/travel_itinerary/travel_itinerary.py b/erpnext/hr/doctype/travel_itinerary/travel_itinerary.py deleted file mode 100644 index 529909b0e0..0000000000 --- a/erpnext/hr/doctype/travel_itinerary/travel_itinerary.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class TravelItinerary(Document): - pass diff --git a/erpnext/hr/doctype/travel_request/__init__.py b/erpnext/hr/doctype/travel_request/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/travel_request/test_travel_request.py b/erpnext/hr/doctype/travel_request/test_travel_request.py deleted file mode 100644 index e29a1ca648..0000000000 --- a/erpnext/hr/doctype/travel_request/test_travel_request.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestTravelRequest(unittest.TestCase): - pass diff --git a/erpnext/hr/doctype/travel_request/travel_request.js b/erpnext/hr/doctype/travel_request/travel_request.js deleted file mode 100644 index 9dd48eb38e..0000000000 --- a/erpnext/hr/doctype/travel_request/travel_request.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Travel Request', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/travel_request/travel_request.json b/erpnext/hr/doctype/travel_request/travel_request.json deleted file mode 100644 index 7908e1a8ea..0000000000 --- a/erpnext/hr/doctype/travel_request/travel_request.json +++ /dev/null @@ -1,245 +0,0 @@ -{ - "actions": [], - "autoname": "HR-TRQ-.YYYY.-.#####", - "creation": "2018-05-15 06:32:33.950356", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "travel_type", - "travel_funding", - "travel_proof", - "column_break_2", - "purpose_of_travel", - "details_of_sponsor", - "employee_details", - "employee", - "employee_name", - "cell_number", - "prefered_email", - "column_break_7", - "date_of_birth", - "personal_id_type", - "personal_id_number", - "passport_number", - "section_break_4", - "description", - "travel_itinerary", - "itinerary", - "costing_details", - "costings", - "accounting_dimensions_section", - "cost_center", - "dimension_col_break", - "event_details", - "name_of_organizer", - "address_of_organizer", - "other_details", - "amended_from" - ], - "fields": [ - { - "fieldname": "travel_type", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Travel Type", - "options": "\nDomestic\nInternational", - "reqd": 1 - }, - { - "fieldname": "travel_funding", - "fieldtype": "Select", - "label": "Travel Funding", - "options": "\nRequire Full Funding\nFully Sponsored\nPartially Sponsored, Require Partial Funding" - }, - { - "fieldname": "travel_proof", - "fieldtype": "Attach", - "label": "Copy of Invitation/Announcement" - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fieldname": "purpose_of_travel", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Purpose of Travel", - "options": "Purpose of Travel", - "reqd": 1 - }, - { - "fieldname": "details_of_sponsor", - "fieldtype": "Data", - "label": "Details of Sponsor (Name, Location)" - }, - { - "collapsible": 1, - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "label": "Description" - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "label": "Any other details" - }, - { - "collapsible": 1, - "fieldname": "employee_details", - "fieldtype": "Section Break", - "label": "Employee Details" - }, - { - "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.cell_number", - "fieldname": "cell_number", - "fieldtype": "Data", - "label": "Contact Number" - }, - { - "fetch_from": "employee.prefered_email", - "fieldname": "prefered_email", - "fieldtype": "Data", - "label": "Contact Email" - }, - { - "fieldname": "column_break_7", - "fieldtype": "Column Break" - }, - { - "fetch_from": "employee.date_of_birth", - "fieldname": "date_of_birth", - "fieldtype": "Date", - "label": "Date of Birth", - "read_only": 1 - }, - { - "fieldname": "personal_id_type", - "fieldtype": "Link", - "label": "Identification Document Type", - "options": "Identification Document Type" - }, - { - "fieldname": "personal_id_number", - "fieldtype": "Data", - "label": "Identification Document Number" - }, - { - "fetch_from": "employee.passport_number", - "fieldname": "passport_number", - "fieldtype": "Data", - "label": "Passport Number" - }, - { - "fieldname": "travel_itinerary", - "fieldtype": "Section Break", - "label": "Travel Itinerary" - }, - { - "fieldname": "itinerary", - "fieldtype": "Table", - "options": "Travel Itinerary" - }, - { - "fieldname": "costing_details", - "fieldtype": "Section Break", - "label": "Costing Details" - }, - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center" - }, - { - "fieldname": "costings", - "fieldtype": "Table", - "label": "Costing", - "options": "Travel Request Costing" - }, - { - "collapsible": 1, - "fieldname": "event_details", - "fieldtype": "Section Break", - "label": "Event Details" - }, - { - "fieldname": "name_of_organizer", - "fieldtype": "Data", - "label": "Name of Organizer" - }, - { - "fieldname": "address_of_organizer", - "fieldtype": "Data", - "label": "Address of Organizer" - }, - { - "fieldname": "other_details", - "fieldtype": "Text", - "label": "Other Details" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Travel Request", - "print_hide": 1, - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "accounting_dimensions_section", - "fieldtype": "Section Break", - "label": "Accounting Dimensions" - }, - { - "fieldname": "dimension_col_break", - "fieldtype": "Column Break" - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-18 19:19:33.678664", - "modified_by": "Administrator", - "module": "HR", - "name": "Travel Request", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "search_fields": "employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/travel_request/travel_request.py b/erpnext/hr/doctype/travel_request/travel_request.py deleted file mode 100644 index 02379c5334..0000000000 --- a/erpnext/hr/doctype/travel_request/travel_request.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - -from erpnext.hr.utils import validate_active_employee - - -class TravelRequest(Document): - def validate(self): - validate_active_employee(self.employee) diff --git a/erpnext/hr/doctype/travel_request_costing/__init__.py b/erpnext/hr/doctype/travel_request_costing/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/travel_request_costing/travel_request_costing.json b/erpnext/hr/doctype/travel_request_costing/travel_request_costing.json deleted file mode 100644 index b64b1a9343..0000000000 --- a/erpnext/hr/doctype/travel_request_costing/travel_request_costing.json +++ /dev/null @@ -1,257 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-05-15 10:28:37.429581", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expense_type", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Expense Type", - "length": 0, - "no_copy": 0, - "options": "Expense Claim Type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sponsored_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Sponsored Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "funded_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Funded Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Total Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "comments", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Comments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-05-15 10:42:07.960530", - "modified_by": "Administrator", - "module": "HR", - "name": "Travel Request Costing", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/travel_request_costing/travel_request_costing.py b/erpnext/hr/doctype/travel_request_costing/travel_request_costing.py deleted file mode 100644 index 0d1a592c80..0000000000 --- a/erpnext/hr/doctype/travel_request_costing/travel_request_costing.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class TravelRequestCosting(Document): - pass diff --git a/erpnext/hr/doctype/upload_attendance/README.md b/erpnext/hr/doctype/upload_attendance/README.md deleted file mode 100644 index d0b3721016..0000000000 --- a/erpnext/hr/doctype/upload_attendance/README.md +++ /dev/null @@ -1 +0,0 @@ -Tool to upload attendance via csv file. \ No newline at end of file diff --git a/erpnext/hr/doctype/upload_attendance/__init__.py b/erpnext/hr/doctype/upload_attendance/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py b/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py deleted file mode 100644 index 537c20633b..0000000000 --- a/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import getdate - -import erpnext -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.upload_attendance.upload_attendance import get_data - -test_dependencies = ["Holiday List"] - - -class TestUploadAttendance(unittest.TestCase): - @classmethod - def setUpClass(cls): - frappe.db.set_value( - "Company", erpnext.get_default_company(), "default_holiday_list", "_Test Holiday List" - ) - - def test_date_range(self): - employee = make_employee("test_employee@company.com") - employee_doc = frappe.get_doc("Employee", employee) - date_of_joining = "2018-01-02" - relieving_date = "2018-01-03" - from_date = "2018-01-01" - to_date = "2018-01-04" - employee_doc.date_of_joining = date_of_joining - employee_doc.relieving_date = relieving_date - employee_doc.save() - args = {"from_date": from_date, "to_date": to_date} - data = get_data(args) - filtered_data = [] - for row in data: - if row[1] == employee: - filtered_data.append(row) - for row in filtered_data: - self.assertTrue( - getdate(row[3]) >= getdate(date_of_joining) and getdate(row[3]) <= getdate(relieving_date) - ) diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.js b/erpnext/hr/doctype/upload_attendance/upload_attendance.js deleted file mode 100644 index bbafc82076..0000000000 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - - - -frappe.provide("erpnext.hr"); - -erpnext.hr.AttendanceControlPanel = class AttendanceControlPanel extends frappe.ui.form.Controller { - onload() { - this.frm.set_value("att_fr_date", frappe.datetime.get_today()); - this.frm.set_value("att_to_date", frappe.datetime.get_today()); - } - - refresh() { - this.frm.disable_save(); - this.show_upload(); - this.setup_import_progress(); - } - - get_template() { - if(!this.frm.doc.att_fr_date || !this.frm.doc.att_to_date) { - frappe.msgprint(__("Attendance From Date and Attendance To Date is mandatory")); - return; - } - window.location.href = repl(frappe.request.url + - '?cmd=%(cmd)s&from_date=%(from_date)s&to_date=%(to_date)s', { - cmd: "erpnext.hr.doctype.upload_attendance.upload_attendance.get_template", - from_date: this.frm.doc.att_fr_date, - to_date: this.frm.doc.att_to_date, - }); - } - - show_upload() { - var $wrapper = $(cur_frm.fields_dict.upload_html.wrapper).empty(); - new frappe.ui.FileUploader({ - wrapper: $wrapper, - method: 'erpnext.hr.doctype.upload_attendance.upload_attendance.upload' - }); - } - - setup_import_progress() { - var $log_wrapper = $(this.frm.fields_dict.import_log.wrapper).empty(); - - frappe.realtime.on('import_attendance', (data) => { - if (data.progress) { - this.frm.dashboard.show_progress('Import Attendance', data.progress / data.total * 100, - __('Importing {0} of {1}', [data.progress, data.total])); - if (data.progress === data.total) { - this.frm.dashboard.hide_progress('Import Attendance'); - } - } else if (data.error) { - this.frm.dashboard.hide(); - let messages = [`${__('Error in some rows')}`].concat(data.messages - .filter(message => message.includes('Error')) - .map(message => `${message}`)) - .join(''); - $log_wrapper.append('' + messages); - } else if (data.messages) { - this.frm.dashboard.hide(); - let messages = [``].concat(data.messages - .map(message => ``)) - .join(''); - $log_wrapper.append('
${__('Import Successful')}
${message}
' + messages); - } - }); - } -} - -cur_frm.cscript = new erpnext.hr.AttendanceControlPanel({frm: cur_frm}); diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.json b/erpnext/hr/doctype/upload_attendance/upload_attendance.json deleted file mode 100644 index a1451fde1d..0000000000 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.json +++ /dev/null @@ -1,285 +0,0 @@ -{ - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-01-25 11:34:53", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 0, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "download_template", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Download Template", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "att_fr_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Attendance From Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "attenadnce_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "att_to_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Attendance To Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "get_template", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Get Template", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Button", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "upload_attendance_data", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Import Attendance", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "upload_html", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Upload HTML", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "import_log", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Import Log", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 1, - "icon": "fa fa-upload-alt", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 1, - "modified": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "module": "HR", - "name": "Upload Attendance", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "HR User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "track_changes": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py deleted file mode 100644 index a66a48124d..0000000000 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py +++ /dev/null @@ -1,222 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import add_days, cstr, date_diff, getdate -from frappe.utils.csvutils import UnicodeWriter - -from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee -from erpnext.hr.utils import get_holiday_dates_for_employee - - -class UploadAttendance(Document): - pass - - -@frappe.whitelist() -def get_template(): - if not frappe.has_permission("Attendance", "create"): - raise frappe.PermissionError - - args = frappe.local.form_dict - - if getdate(args.from_date) > getdate(args.to_date): - frappe.throw(_("To Date should be greater than From Date")) - - w = UnicodeWriter() - w = add_header(w) - - try: - w = add_data(w, args) - except Exception as e: - frappe.clear_messages() - frappe.respond_as_web_page("Holiday List Missing", html=e) - return - - # write out response as a type csv - frappe.response["result"] = cstr(w.getvalue()) - frappe.response["type"] = "csv" - frappe.response["doctype"] = "Attendance" - - -def add_header(w): - status = ", ".join( - (frappe.get_meta("Attendance").get_field("status").options or "").strip().split("\n") - ) - w.writerow(["Notes:"]) - w.writerow(["Please do not change the template headings"]) - w.writerow(["Status should be one of these values: " + status]) - w.writerow(["If you are overwriting existing attendance records, 'ID' column mandatory"]) - w.writerow( - ["ID", "Employee", "Employee Name", "Date", "Status", "Leave Type", "Company", "Naming Series"] - ) - return w - - -def add_data(w, args): - data = get_data(args) - writedata(w, data) - return w - - -def get_data(args): - dates = get_dates(args) - employees = get_active_employees() - holidays = get_holidays_for_employees( - [employee.name for employee in employees], args["from_date"], args["to_date"] - ) - existing_attendance_records = get_existing_attendance_records(args) - data = [] - for date in dates: - for employee in employees: - if getdate(date) < getdate(employee.date_of_joining): - continue - if employee.relieving_date: - if getdate(date) > getdate(employee.relieving_date): - continue - existing_attendance = {} - if ( - existing_attendance_records - and tuple([getdate(date), employee.name]) in existing_attendance_records - and getdate(employee.date_of_joining) <= getdate(date) - and getdate(employee.relieving_date) >= getdate(date) - ): - existing_attendance = existing_attendance_records[tuple([getdate(date), employee.name])] - - employee_holiday_list = get_holiday_list_for_employee(employee.name) - - row = [ - existing_attendance and existing_attendance.name or "", - employee.name, - employee.employee_name, - date, - existing_attendance and existing_attendance.status or "", - existing_attendance and existing_attendance.leave_type or "", - employee.company, - existing_attendance and existing_attendance.naming_series or get_naming_series(), - ] - if date in holidays[employee_holiday_list]: - row[4] = "Holiday" - data.append(row) - - return data - - -def get_holidays_for_employees(employees, from_date, to_date): - holidays = {} - for employee in employees: - holiday_list = get_holiday_list_for_employee(employee) - holiday = get_holiday_dates_for_employee(employee, getdate(from_date), getdate(to_date)) - if holiday_list not in holidays: - holidays[holiday_list] = holiday - - return holidays - - -def writedata(w, data): - for row in data: - w.writerow(row) - - -def get_dates(args): - """get list of dates in between from date and to date""" - no_of_days = date_diff(add_days(args["to_date"], 1), args["from_date"]) - dates = [add_days(args["from_date"], i) for i in range(0, no_of_days)] - return dates - - -def get_active_employees(): - employees = frappe.db.get_all( - "Employee", - fields=["name", "employee_name", "date_of_joining", "company", "relieving_date"], - filters={"docstatus": ["<", 2], "status": "Active"}, - ) - return employees - - -def get_existing_attendance_records(args): - attendance = frappe.db.sql( - """select name, attendance_date, employee, status, leave_type, naming_series - from `tabAttendance` where attendance_date between %s and %s and docstatus < 2""", - (args["from_date"], args["to_date"]), - as_dict=1, - ) - - existing_attendance = {} - for att in attendance: - existing_attendance[tuple([att.attendance_date, att.employee])] = att - - return existing_attendance - - -def get_naming_series(): - series = frappe.get_meta("Attendance").get_field("naming_series").options.strip().split("\n") - if not series: - frappe.throw(_("Please setup numbering series for Attendance via Setup > Numbering Series")) - return series[0] - - -@frappe.whitelist() -def upload(): - if not frappe.has_permission("Attendance", "create"): - raise frappe.PermissionError - - from frappe.utils.csvutils import read_csv_content - - rows = read_csv_content(frappe.local.uploaded_file) - if not rows: - frappe.throw(_("Please select a csv file")) - frappe.enqueue(import_attendances, rows=rows, now=True if len(rows) < 200 else False) - - -def import_attendances(rows): - def remove_holidays(rows): - rows = [row for row in rows if row[4] != "Holiday"] - return rows - - from frappe.modules import scrub - - rows = list(filter(lambda x: x and any(x), rows)) - columns = [scrub(f) for f in rows[4]] - columns[0] = "name" - columns[3] = "attendance_date" - rows = rows[5:] - ret = [] - error = False - - rows = remove_holidays(rows) - - from frappe.utils.csvutils import check_record, import_doc - - for i, row in enumerate(rows): - if not row: - continue - row_idx = i + 5 - d = frappe._dict(zip(columns, row)) - - d["doctype"] = "Attendance" - if d.name: - d["docstatus"] = frappe.db.get_value("Attendance", d.name, "docstatus") - - try: - check_record(d) - ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True)) - frappe.publish_realtime("import_attendance", dict(progress=i, total=len(rows))) - except AttributeError: - pass - except Exception as e: - error = True - ret.append("Error for row (#%d) %s : %s" % (row_idx, len(row) > 1 and row[1] or "", cstr(e))) - frappe.errprint(frappe.get_traceback()) - - if error: - frappe.db.rollback() - else: - frappe.db.commit() - - frappe.publish_realtime("import_attendance", dict(messages=ret, error=error)) diff --git a/erpnext/hr/doctype/vehicle/__init__.py b/erpnext/hr/doctype/vehicle/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/vehicle/test_vehicle.py b/erpnext/hr/doctype/vehicle/test_vehicle.py deleted file mode 100644 index 97fe651122..0000000000 --- a/erpnext/hr/doctype/vehicle/test_vehicle.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import random_string - -# test_records = frappe.get_test_records('Vehicle') - - -class TestVehicle(unittest.TestCase): - def test_make_vehicle(self): - vehicle = frappe.get_doc( - { - "doctype": "Vehicle", - "license_plate": random_string(10).upper(), - "make": "Maruti", - "model": "PCM", - "last_odometer": 5000, - "acquisition_date": frappe.utils.nowdate(), - "location": "Mumbai", - "chassis_no": "1234ABCD", - "uom": "Litre", - "vehicle_value": frappe.utils.flt(500000), - } - ) - vehicle.insert() diff --git a/erpnext/hr/doctype/vehicle/vehicle.js b/erpnext/hr/doctype/vehicle/vehicle.js deleted file mode 100644 index f12c434685..0000000000 --- a/erpnext/hr/doctype/vehicle/vehicle.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Vehicle', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hr/doctype/vehicle/vehicle.json b/erpnext/hr/doctype/vehicle/vehicle.json deleted file mode 100644 index 6f395b34be..0000000000 --- a/erpnext/hr/doctype/vehicle/vehicle.json +++ /dev/null @@ -1,875 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:license_plate", - "beta": 0, - "creation": "2016-09-03 03:33:27.680331", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "license_plate", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "License Plate", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 1 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "make", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Make", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "model", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Model", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "vehicle_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "last_odometer", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Odometer Value (Last)", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "acquisition_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Acquisition Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "location", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Location", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "chassis_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Chassis No", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "vehicle_value", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Vehicle Value", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 1, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "insurance_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Insurance Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "insurance_company", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Insurance Company", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "policy_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Policy No", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_15", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "start_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "end_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "End Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "additional_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "fuel_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Fuel Type", - "length": 0, - "no_copy": 0, - "options": "Petrol\nDiesel\nNatural Gas\nElectric", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Fuel UOM", - "length": 0, - "no_copy": 0, - "options": "UOM", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "carbon_check_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Last Carbon Check", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_21", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "color", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Color", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "wheels", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Wheels", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "doors", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Doors", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Vehicle", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-11-29 14:48:30.813359", - "modified_by": "Administrator", - "module": "HR", - "name": "Vehicle", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Fleet Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "license_plate,location,model", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/vehicle/vehicle.py b/erpnext/hr/doctype/vehicle/vehicle.py deleted file mode 100644 index 22c14c3727..0000000000 --- a/erpnext/hr/doctype/vehicle/vehicle.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import getdate - - -class Vehicle(Document): - def validate(self): - if getdate(self.start_date) > getdate(self.end_date): - frappe.throw(_("Insurance Start date should be less than Insurance End date")) - if getdate(self.carbon_check_date) > getdate(): - frappe.throw(_("Last carbon check date cannot be a future date")) - - -def get_timeline_data(doctype, name): - """Return timeline for vehicle log""" - return dict( - frappe.db.sql( - """select unix_timestamp(date), count(*) - from `tabVehicle Log` where license_plate=%s - and date > date_sub(curdate(), interval 1 year) - group by date""", - name, - ) - ) diff --git a/erpnext/hr/doctype/vehicle/vehicle_dashboard.py b/erpnext/hr/doctype/vehicle/vehicle_dashboard.py deleted file mode 100644 index 758dfbd60a..0000000000 --- a/erpnext/hr/doctype/vehicle/vehicle_dashboard.py +++ /dev/null @@ -1,13 +0,0 @@ -from frappe import _ - - -def get_data(): - return { - "heatmap": True, - "heatmap_message": _( - "This is based on logs against this Vehicle. See timeline below for details" - ), - "fieldname": "license_plate", - "non_standard_fieldnames": {"Delivery Trip": "vehicle"}, - "transactions": [{"items": ["Vehicle Log"]}, {"items": ["Delivery Trip"]}], - } diff --git a/erpnext/hr/doctype/vehicle_log/__init__.py b/erpnext/hr/doctype/vehicle_log/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py deleted file mode 100644 index bb29670d39..0000000000 --- a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import cstr, flt, nowdate, random_string - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.vehicle_log.vehicle_log import make_expense_claim - - -class TestVehicleLog(unittest.TestCase): - def setUp(self): - employee_id = frappe.db.sql( - """select name from `tabEmployee` where name='testdriver@example.com'""" - ) - self.employee_id = employee_id[0][0] if employee_id else None - - if not self.employee_id: - self.employee_id = make_employee("testdriver@example.com", company="_Test Company") - - self.license_plate = get_vehicle(self.employee_id) - - def tearDown(self): - frappe.delete_doc("Vehicle", self.license_plate, force=1) - frappe.delete_doc("Employee", self.employee_id, force=1) - - def test_make_vehicle_log_and_syncing_of_odometer_value(self): - vehicle_log = make_vehicle_log(self.license_plate, self.employee_id) - - # checking value of vehicle odometer value on submit. - vehicle = frappe.get_doc("Vehicle", self.license_plate) - self.assertEqual(vehicle.last_odometer, vehicle_log.odometer) - - # checking value vehicle odometer on vehicle log cancellation. - last_odometer = vehicle_log.last_odometer - current_odometer = vehicle_log.odometer - distance_travelled = current_odometer - last_odometer - - vehicle_log.cancel() - vehicle.reload() - - self.assertEqual(vehicle.last_odometer, current_odometer - distance_travelled) - - vehicle_log.delete() - - def test_vehicle_log_fuel_expense(self): - vehicle_log = make_vehicle_log(self.license_plate, self.employee_id) - - expense_claim = make_expense_claim(vehicle_log.name) - fuel_expense = expense_claim.expenses[0].amount - self.assertEqual(fuel_expense, 50 * 500) - - vehicle_log.cancel() - frappe.delete_doc("Expense Claim", expense_claim.name) - frappe.delete_doc("Vehicle Log", vehicle_log.name) - - def test_vehicle_log_with_service_expenses(self): - vehicle_log = make_vehicle_log(self.license_plate, self.employee_id, with_services=True) - - expense_claim = make_expense_claim(vehicle_log.name) - expenses = expense_claim.expenses[0].amount - self.assertEqual(expenses, 27000) - - vehicle_log.cancel() - frappe.delete_doc("Expense Claim", expense_claim.name) - frappe.delete_doc("Vehicle Log", vehicle_log.name) - - -def get_vehicle(employee_id): - license_plate = random_string(10).upper() - vehicle = frappe.get_doc( - { - "doctype": "Vehicle", - "license_plate": cstr(license_plate), - "make": "Maruti", - "model": "PCM", - "employee": employee_id, - "last_odometer": 5000, - "acquisition_date": nowdate(), - "location": "Mumbai", - "chassis_no": "1234ABCD", - "uom": "Litre", - "vehicle_value": flt(500000), - } - ) - try: - vehicle.insert(ignore_if_duplicate=True) - except frappe.DuplicateEntryError: - pass - return license_plate - - -def make_vehicle_log(license_plate, employee_id, with_services=False): - vehicle_log = frappe.get_doc( - { - "doctype": "Vehicle Log", - "license_plate": cstr(license_plate), - "employee": employee_id, - "date": nowdate(), - "odometer": 5010, - "fuel_qty": flt(50), - "price": flt(500), - } - ) - - if with_services: - vehicle_log.append( - "service_detail", - { - "service_item": "Oil Change", - "type": "Inspection", - "frequency": "Mileage", - "expense_amount": flt(500), - }, - ) - vehicle_log.append( - "service_detail", - { - "service_item": "Wheels", - "type": "Change", - "frequency": "Half Yearly", - "expense_amount": flt(1500), - }, - ) - - vehicle_log.save() - vehicle_log.submit() - - return vehicle_log diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.js b/erpnext/hr/doctype/vehicle_log/vehicle_log.js deleted file mode 100644 index 14fe9a02da..0000000000 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.js +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on("Vehicle Log", { - refresh: function(frm) { - if(frm.doc.docstatus == 1) { - frm.add_custom_button(__('Expense Claim'), function() { - frm.events.expense_claim(frm); - }, __('Create')); - frm.page.set_inner_btn_group_as_primary(__('Create')); - } - }, - - expense_claim: function(frm){ - frappe.call({ - method: "erpnext.hr.doctype.vehicle_log.vehicle_log.make_expense_claim", - args:{ - docname: frm.doc.name - }, - callback: function(r){ - var doc = frappe.model.sync(r.message); - frappe.set_route('Form', 'Expense Claim', r.message.name); - } - }); - } -}); diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.json b/erpnext/hr/doctype/vehicle_log/vehicle_log.json deleted file mode 100644 index 4ea904542d..0000000000 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.json +++ /dev/null @@ -1,191 +0,0 @@ -{ - "actions": [], - "autoname": "naming_series:", - "creation": "2016-09-03 14:14:51.788550", - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "vehicle_section", - "naming_series", - "license_plate", - "employee", - "column_break_7", - "model", - "make", - "odometer_reading", - "date", - "odometer", - "column_break_12", - "last_odometer", - "refuelling_details", - "fuel_qty", - "price", - "column_break_15", - "supplier", - "invoice", - "service_details", - "service_detail", - "amended_from" - ], - "fields": [ - { - "fieldname": "vehicle_section", - "fieldtype": "Section Break", - "options": "fa fa-user" - }, - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "no_copy": 1, - "options": "HR-VLOG-.YYYY.-", - "print_hide": 1, - "reqd": 1, - "set_only_once": 1 - }, - { - "fieldname": "license_plate", - "fieldtype": "Link", - "in_global_search": 1, - "in_list_view": 1, - "label": "License Plate", - "options": "Vehicle", - "reqd": 1 - }, - { - "fetch_from": "license_plate.employee", - "fetch_if_empty": 1, - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1 - }, - { - "fieldname": "column_break_7", - "fieldtype": "Column Break" - }, - { - "fetch_from": "license_plate.model", - "fieldname": "model", - "fieldtype": "Read Only", - "label": "Model" - }, - { - "fetch_from": "license_plate.make", - "fieldname": "make", - "fieldtype": "Read Only", - "label": "Make" - }, - { - "fieldname": "odometer_reading", - "fieldtype": "Section Break", - "label": "Odometer Reading" - }, - { - "fieldname": "date", - "fieldtype": "Date", - "label": "Date", - "reqd": 1 - }, - { - "fieldname": "odometer", - "fieldtype": "Int", - "label": "Current Odometer value ", - "reqd": 1 - }, - { - "collapsible": 1, - "fieldname": "refuelling_details", - "fieldtype": "Section Break", - "label": "Refuelling Details" - }, - { - "fieldname": "fuel_qty", - "fieldtype": "Float", - "label": "Fuel Qty" - }, - { - "fieldname": "price", - "fieldtype": "Currency", - "label": "Fuel Price" - }, - { - "fieldname": "column_break_15", - "fieldtype": "Column Break" - }, - { - "fieldname": "supplier", - "fieldtype": "Link", - "label": "Supplier", - "options": "Supplier" - }, - { - "fieldname": "invoice", - "fieldtype": "Data", - "label": "Invoice Ref" - }, - { - "collapsible": 1, - "fieldname": "service_details", - "fieldtype": "Section Break", - "label": "Service Details" - }, - { - "fieldname": "service_detail", - "fieldtype": "Table", - "options": "Vehicle Service" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Vehicle Log", - "print_hide": 1, - "read_only": 1 - }, - { - "fetch_from": "license_plate.last_odometer", - "fieldname": "last_odometer", - "fieldtype": "Int", - "label": "Last Odometer Value ", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "column_break_12", - "fieldtype": "Column Break" - } - ], - "is_submittable": 1, - "links": [], - "modified": "2021-05-17 00:10:21.188352", - "modified_by": "Administrator", - "module": "HR", - "name": "Vehicle Log", - "owner": "Administrator", - "permissions": [ - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Fleet Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.py b/erpnext/hr/doctype/vehicle_log/vehicle_log.py deleted file mode 100644 index 2c1d9a4efe..0000000000 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import flt - - -class VehicleLog(Document): - def validate(self): - if flt(self.odometer) < flt(self.last_odometer): - frappe.throw( - _("Current Odometer Value should be greater than Last Odometer Value {0}").format( - self.last_odometer - ) - ) - - def on_submit(self): - frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", self.odometer) - - def on_cancel(self): - distance_travelled = self.odometer - self.last_odometer - if distance_travelled > 0: - updated_odometer_value = ( - int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled - ) - frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", updated_odometer_value) - - -@frappe.whitelist() -def make_expense_claim(docname): - expense_claim = frappe.db.exists("Expense Claim", {"vehicle_log": docname}) - if expense_claim: - frappe.throw(_("Expense Claim {0} already exists for the Vehicle Log").format(expense_claim)) - - vehicle_log = frappe.get_doc("Vehicle Log", docname) - service_expense = sum([flt(d.expense_amount) for d in vehicle_log.service_detail]) - - claim_amount = service_expense + (flt(vehicle_log.price) * flt(vehicle_log.fuel_qty) or 1) - if not claim_amount: - frappe.throw(_("No additional expenses has been added")) - - exp_claim = frappe.new_doc("Expense Claim") - exp_claim.employee = vehicle_log.employee - exp_claim.vehicle_log = vehicle_log.name - exp_claim.remark = _("Expense Claim for Vehicle Log {0}").format(vehicle_log.name) - exp_claim.append( - "expenses", - {"expense_date": vehicle_log.date, "description": _("Vehicle Expenses"), "amount": claim_amount}, - ) - return exp_claim.as_dict() diff --git a/erpnext/hr/doctype/vehicle_service/__init__.py b/erpnext/hr/doctype/vehicle_service/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/doctype/vehicle_service/vehicle_service.json b/erpnext/hr/doctype/vehicle_service/vehicle_service.json deleted file mode 100644 index e0bce2b6ee..0000000000 --- a/erpnext/hr/doctype/vehicle_service/vehicle_service.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "creation": "2016-09-03 19:20:14.561962", - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "service_item", - "type", - "frequency", - "expense_amount" - ], - "fields": [ - { - "fieldname": "service_item", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Service Item", - "options": "\nBrake Oil\nBrake Pad\nClutch Plate\nEngine Oil\nOil Change\nWheels", - "reqd": 1 - }, - { - "fieldname": "type", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Type", - "options": "\nInspection\nService\nChange", - "reqd": 1 - }, - { - "fieldname": "frequency", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Frequency", - "options": "\nMileage\nMonthly\nQuarterly\nHalf Yearly\nYearly", - "reqd": 1 - }, - { - "fieldname": "expense_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Expense", - "reqd": 1 - } - ], - "istable": 1, - "modified": "2020-03-18 16:49:46.645004", - "modified_by": "Administrator", - "module": "HR", - "name": "Vehicle Service", - "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/hr/doctype/vehicle_service/vehicle_service.py b/erpnext/hr/doctype/vehicle_service/vehicle_service.py deleted file mode 100644 index fdd4e39dd8..0000000000 --- a/erpnext/hr/doctype/vehicle_service/vehicle_service.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class VehicleService(Document): - pass diff --git a/erpnext/hr/employee_property_update.js b/erpnext/hr/employee_property_update.js deleted file mode 100644 index 86130bf793..0000000000 --- a/erpnext/hr/employee_property_update.js +++ /dev/null @@ -1,174 +0,0 @@ -frappe.ui.form.on(cur_frm.doctype, { - setup: function(frm) { - frm.set_query("employee", function() { - return { - filters: { - "status": "Active" - } - }; - }); - }, - - onload: function(frm) { - if (frm.doc.__islocal) - frm.trigger("clear_property_table"); - }, - - employee: function(frm) { - frm.trigger("clear_property_table"); - }, - - clear_property_table: function(frm) { - let table = (frm.doctype == "Employee Promotion") ? "promotion_details" : "transfer_details"; - frm.clear_table(table); - frm.refresh_field(table); - - frm.fields_dict[table].grid.wrapper.find(".grid-add-row").hide(); - }, - - refresh: function(frm) { - let table; - if (frm.doctype == "Employee Promotion") { - table = "promotion_details"; - } else if (frm.doctype == "Employee Transfer") { - table = "transfer_details"; - } - - if (!table) - return; - - frm.fields_dict[table].grid.wrapper.find(".grid-add-row").hide(); - frm.events.setup_employee_property_button(frm, table); - }, - - setup_employee_property_button: function(frm, table) { - frm.fields_dict[table].grid.add_custom_button(__("Add Employee Property"), () => { - if (!frm.doc.employee) { - frappe.msgprint(__("Please select Employee first.")); - return; - } - - const allowed_fields = []; - const exclude_fields = ["naming_series", "employee", "first_name", "middle_name", "last_name", "marital_status", "ctc", - "employee_name", "status", "image", "gender", "date_of_birth", "date_of_joining", "lft", "rgt", "old_parent"]; - - const exclude_field_types = ["HTML", "Section Break", "Column Break", "Button", "Read Only", "Tab Break", "Table"]; - - frappe.model.with_doctype("Employee", () => { - const field_label_map = {}; - frappe.get_meta("Employee").fields.forEach(d => { - field_label_map[d.fieldname] = __(d.label) + ` (${d.fieldname})`; - if (!in_list(exclude_field_types, d.fieldtype) && !in_list(exclude_fields, d.fieldname)) { - allowed_fields.push({ - label: field_label_map[d.fieldname], - value: d.fieldname, - }); - } - }); - - show_dialog(frm, table, allowed_fields); - }); - }); - } -}); - -var show_dialog = function(frm, table, field_labels) { - var d = new frappe.ui.Dialog({ - title: "Update Property", - fields: [ - {fieldname: "property", label: __("Select Property"), fieldtype: "Autocomplete", options: field_labels}, - {fieldname: "current", fieldtype: "Data", label: __("Current"), read_only: true}, - {fieldname: "new_value", fieldtype: "Data", label: __("New")} - ], - primary_action_label: __("Add to Details"), - primary_action: () => { - d.get_primary_btn().attr("disabled", true); - if (d.data) { - d.data.new = d.get_values().new_value; - add_to_details(frm, d, table); - } - } - }); - - d.fields_dict["property"].df.onchange = () => { - let property = d.get_values().property; - d.data.fieldname = property; - if(!property){return;} - frappe.call({ - method: 'erpnext.hr.utils.get_employee_field_property', - args: {employee: frm.doc.employee, fieldname: property}, - callback: function(r) { - if (r.message) { - d.data.current = r.message.value; - d.data.property = r.message.label; - - d.set_value('current', r.message.value); - render_dynamic_field(d, r.message.datatype, r.message.options, property); - d.get_primary_btn().attr('disabled', false); - } - } - }); - }; - d.get_primary_btn().attr('disabled', true); - d.data = {}; - d.show(); -}; - -var render_dynamic_field = function(d, fieldtype, options, fieldname) { - d.data.new = null; - var dynamic_field = frappe.ui.form.make_control({ - df: { - "fieldtype": fieldtype, - "fieldname": fieldname, - "options": options || '', - "label": __("New") - }, - parent: d.fields_dict.new_value.wrapper, - only_input: false - }); - dynamic_field.make_input(); - d.replace_field("new_value", dynamic_field.df); -}; - -var add_to_details = function(frm, d, table) { - let data = d.data; - if (data.fieldname) { - if (validate_duplicate(frm, table, data.fieldname)) { - frappe.show_alert({message: __("Property already added"), indicator: "orange"}); - return false; - } - if (data.current == data.new) { - frappe.show_alert({message: __("Nothing to change"), indicator: "orange"}); - d.get_primary_btn().attr("disabled", false); - return false; - } - frm.add_child(table, { - fieldname: data.fieldname, - property: data.property, - current: data.current, - new: data.new - }); - frm.refresh_field(table); - - frm.fields_dict[table].grid.wrapper.find(".grid-add-row").hide(); - - d.fields_dict.new_value.$wrapper.html(""); - d.set_value("property", ""); - d.set_value("current", ""); - frappe.show_alert({message: __("Added to details"), indicator: "green"}); - d.data = {}; - } else { - frappe.show_alert({message: __("Value missing"), indicator: "red"}); - } -}; - -var validate_duplicate = function(frm, table, fieldname){ - let duplicate = false; - $.each(frm.doc[table], function(i, detail) { - if(detail.fieldname === fieldname){ - duplicate = true; - return; - } - }); - return duplicate; -}; diff --git a/erpnext/hr/hr_dashboard/human_resource/human_resource.json b/erpnext/hr/hr_dashboard/human_resource/human_resource.json deleted file mode 100644 index f74d9a3c57..0000000000 --- a/erpnext/hr/hr_dashboard/human_resource/human_resource.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "cards": [ - { - "card": "Total Employees" - }, - { - "card": "New Joinees (Last year)" - }, - { - "card": "Employees Left (Last year)" - }, - { - "card": "Total Applicants (Last month)" - } - ], - "charts": [ - { - "chart": "Attendance Count", - "width": "Full" - }, - { - "chart": "Gender Diversity Ratio", - "width": "Half" - }, - { - "chart": "Job Application Status", - "width": "Half" - }, - { - "chart": "Designation Wise Employee Count", - "width": "Half" - }, - { - "chart": "Department Wise Employee Count", - "width": "Half" - }, - { - "chart": "Designation Wise Openings", - "width": "Half" - }, - { - "chart": "Department Wise Openings", - "width": "Half" - } - ], - "creation": "2020-07-22 11:56:33.015888", - "dashboard_name": "Human Resource", - "docstatus": 0, - "doctype": "Dashboard", - "idx": 0, - "is_default": 0, - "is_standard": 1, - "modified": "2020-07-22 14:42:12.789249", - "modified_by": "Administrator", - "module": "HR", - "name": "Human Resource", - "owner": "Administrator" -} \ No newline at end of file diff --git a/erpnext/hr/module_onboarding/human_resource/human_resource.json b/erpnext/hr/module_onboarding/human_resource/human_resource.json deleted file mode 100644 index cd11bd1102..0000000000 --- a/erpnext/hr/module_onboarding/human_resource/human_resource.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "allow_roles": [ - { - "role": "HR Manager" - }, - { - "role": "HR User" - } - ], - "creation": "2020-05-14 11:51:45.050242", - "docstatus": 0, - "doctype": "Module Onboarding", - "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources", - "idx": 0, - "is_complete": 0, - "modified": "2021-05-19 05:32:01.794628", - "modified_by": "Administrator", - "module": "HR", - "name": "Human Resource", - "owner": "Administrator", - "steps": [ - { - "step": "HR Settings" - }, - { - "step": "Create Holiday list" - }, - { - "step": "Create Employee" - }, - { - "step": "Data import" - }, - { - "step": "Create Leave Type" - }, - { - "step": "Create Leave Allocation" - }, - { - "step": "Create Leave Application" - } - ], - "subtitle": "Employee, Leaves, and more.", - "success_message": "The Human Resource Module is all set up!", - "title": "Let's Set Up the Human Resource Module. " -} \ No newline at end of file diff --git a/erpnext/hr/notification/__init__.py b/erpnext/hr/notification/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/notification/exit_interview_scheduled/__init__.py b/erpnext/hr/notification/exit_interview_scheduled/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.json b/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.json deleted file mode 100644 index 8323ef0694..0000000000 --- a/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "attach_print": 0, - "channel": "Email", - "condition": "doc.date and doc.email and doc.docstatus != 2 and doc.status == 'Scheduled'", - "creation": "2021-12-05 22:11:47.263933", - "date_changed": "date", - "days_in_advance": 1, - "docstatus": 0, - "doctype": "Notification", - "document_type": "Exit Interview", - "enabled": 1, - "event": "Days Before", - "idx": 0, - "is_standard": 1, - "message": "
\n\t\n\t\n\t\t\n\t\t\n\t\t\n\t\n\t\n
\n\t\t\t
\n\t\t\t\t{{_(\"Exit Interview Scheduled:\")}} {{ doc.name }}\n\t\t\t
\n\t\t
\n\n\n\t\n\t\n\t\t\n\t\t\n\t\t\n\t\n\t\n
\n\t\t\t
\n\t\t\t\t
    \n\t\t\t\t\t
  • {{_(\"Employee\")}}: {{ doc.employee }} - {{ doc.employee_name }}
  • \n\t\t\t\t\t
  • {{_(\"Date\")}}: {{ doc.date }}
  • \n\t\t\t\t\t
  • {{_(\"Interviewers\")}}:
  • \n\t\t\t\t\t{% for entry in doc.interviewers %}\n\t\t\t\t\t\t
      \n\t\t\t\t\t\t\t
    • {{ entry.user }}
    • \n\t\t\t\t\t\t
    \n\t\t\t\t\t{% endfor %}\n\t\t\t\t\t
  • {{ _(\"Interview Document\") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • \n\t\t\t\t
\n\t\t\t
\n\t\t
\n", - "modified": "2021-12-05 22:26:57.096159", - "modified_by": "Administrator", - "module": "HR", - "name": "Exit Interview Scheduled", - "owner": "Administrator", - "recipients": [ - { - "receiver_by_document_field": "email" - } - ], - "send_system_notification": 0, - "send_to_all_assignees": 1, - "subject": "Exit Interview Scheduled: {{ doc.name }}" -} \ No newline at end of file diff --git a/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.md b/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.md deleted file mode 100644 index 6d6db4014b..0000000000 --- a/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.md +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - -
-
-

{{_("Exit Interview Scheduled:")}} {{ doc.name }}

-
-
- - - - - - - - - -
-
-
    -
  • {{_("Employee")}}: {{ doc.employee }} - {{ doc.employee_name }}
  • -
  • {{_("Date")}}: {{ frappe.utils.formatdate(doc.date) }}
  • -
  • {{_("Interviewers")}}:
  • - {% for entry in doc.interviewers %} -
      -
    • {{ entry.user }}
    • -
    - {% endfor %} -
  • {{ _("Interview Document") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • -
-
-
diff --git a/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.py b/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.py deleted file mode 100644 index 5f697c9613..0000000000 --- a/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.py +++ /dev/null @@ -1,6 +0,0 @@ -# import frappe - - -def get_context(context): - # do your magic here - pass diff --git a/erpnext/hr/notification/training_feedback/__init__.py b/erpnext/hr/notification/training_feedback/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/notification/training_feedback/training_feedback.html b/erpnext/hr/notification/training_feedback/training_feedback.html deleted file mode 100644 index b49662a6eb..0000000000 --- a/erpnext/hr/notification/training_feedback/training_feedback.html +++ /dev/null @@ -1,6 +0,0 @@ -

{{ _("Hello") }},

- -

You attended training {{ frappe.utils.get_link_to_form( - "Training Event", doc.training_event) }}

- -

{{ _("Please share your feedback to the training by clicking on 'Training Feedback' and then 'New'") }}

diff --git a/erpnext/hr/notification/training_feedback/training_feedback.json b/erpnext/hr/notification/training_feedback/training_feedback.json deleted file mode 100644 index 92b68a98a9..0000000000 --- a/erpnext/hr/notification/training_feedback/training_feedback.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "attach_print": 0, - "channel": "Email", - "creation": "2017-08-11 03:17:11.769210", - "days_in_advance": 0, - "docstatus": 0, - "doctype": "Notification", - "document_type": "Training Result", - "enabled": 1, - "event": "Submit", - "idx": 0, - "is_standard": 1, - "message": "

{{_(\"Training Event\")}}

\n

{{ message }}

\n\n

{{_(\"Details\")}}

\n{{_(\"Event Name\")}}: {{ name }}\n
{{_(\"Event Location\")}}: {{ location }}\n
{{_(\"Start Time\")}}: {{ start_time }}\n
{{_(\"End Time\")}}: {{ end_time }}\n
{{_(\"Attendance\")}}: {{ attendance }}\n", - "modified": "2017-08-11 04:26:58.194793", - "modified_by": "Administrator", - "module": "HR", - "name": "Training Feedback", - "owner": "Administrator", - "recipients": [ - { - "email_by_document_field": "employee_emails" - } - ], - "subject": "Please Share your Feedback For {{ doc.training_event }}" -} \ No newline at end of file diff --git a/erpnext/hr/notification/training_feedback/training_feedback.md b/erpnext/hr/notification/training_feedback/training_feedback.md deleted file mode 100644 index bcadf7df59..0000000000 --- a/erpnext/hr/notification/training_feedback/training_feedback.md +++ /dev/null @@ -1,9 +0,0 @@ -

{{_("Training Event")}}

-

{{ message }}

- -

{{_("Details")}}

-{{_("Event Name")}}: {{ name }} -
{{_("Event Location")}}: {{ location }} -
{{_("Start Time")}}: {{ start_time }} -
{{_("End Time")}}: {{ end_time }} -
{{_("Attendance")}}: {{ attendance }} diff --git a/erpnext/hr/notification/training_feedback/training_feedback.py b/erpnext/hr/notification/training_feedback/training_feedback.py deleted file mode 100644 index 02e3e93333..0000000000 --- a/erpnext/hr/notification/training_feedback/training_feedback.py +++ /dev/null @@ -1,3 +0,0 @@ -def get_context(context): - # do your magic here - pass diff --git a/erpnext/hr/notification/training_scheduled/__init__.py b/erpnext/hr/notification/training_scheduled/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.html b/erpnext/hr/notification/training_scheduled/training_scheduled.html deleted file mode 100644 index 50f6d07a47..0000000000 --- a/erpnext/hr/notification/training_scheduled/training_scheduled.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - -
-
- {{_("Training Event:")}} {{ doc.event_name }} -
-
- - - - - - - - - -
-
- {{ doc.introduction }} -
    -
  • {{_("Event Location")}}: {{ doc.location }}
  • - {% set start = frappe.utils.get_datetime(doc.start_time) %} - {% set end = frappe.utils.get_datetime(doc.end_time) %} - {% if start.date() == end.date() %} -
  • {{_("Date")}}: {{ start.strftime("%A, %d %b %Y") }}
  • -
  • - {{_("Timing")}}: {{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }} -
  • - {% else %} -
  • {{_("Start Time")}}: {{ start.strftime("%A, %d %b %Y at %I:%M %p") }} -
  • -
  • {{_("End Time")}}: {{ end.strftime("%A, %d %b %Y at %I:%M %p") }} -
  • - {% endif %} -
  • {{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • -
-
-
diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.json b/erpnext/hr/notification/training_scheduled/training_scheduled.json deleted file mode 100644 index f3650038fd..0000000000 --- a/erpnext/hr/notification/training_scheduled/training_scheduled.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "attach_print": 0, - "channel": "Email", - "condition": "", - "creation": "2017-08-11 03:13:40.519614", - "days_in_advance": 0, - "docstatus": 0, - "doctype": "Notification", - "document_type": "Training Event", - "enabled": 1, - "event": "Submit", - "idx": 0, - "is_standard": 1, - "message": "\n \n \n \n \n \n \n \n
\n
\n {{_(\"Training Event:\")}} {{ doc.event_name }}\n
\n
\n\n\n \n \n \n \n \n \n \n
\n
\n {{ doc.introduction }}\n
    \n
  • {{_(\"Event Location\")}}: {{ doc.location }}
  • \n {% set start = frappe.utils.get_datetime(doc.start_time) %}\n {% set end = frappe.utils.get_datetime(doc.end_time) %}\n {% if start.date() == end.date() %}\n
  • {{_(\"Date\")}}: {{ start.strftime(\"%A, %d %b %Y\") }}
  • \n
  • \n {{_(\"Timing\")}}: {{ start.strftime(\"%I:%M %p\") + ' to ' + end.strftime(\"%I:%M %p\") }}\n
  • \n {% else %}\n
  • \n {{_(\"Start Time\")}}: {{ start.strftime(\"%A, %d %b %Y at %I:%M %p\") }}\n
  • \n
  • {{_(\"End Time\")}}: {{ end.strftime(\"%A, %d %b %Y at %I:%M %p\") }}
  • \n {% endif %}\n
  • {{ _(\"Event Link\") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • \n {% if doc.is_mandatory %}\n
  • {{ _(\"Note: This Training Event is mandatory\") }}
  • \n {% endif %}\n
\n
\n
", - "modified": "2021-06-16 14:08:12.933367", - "modified_by": "Administrator", - "module": "HR", - "name": "Training Scheduled", - "owner": "Administrator", - "recipients": [ - { - "receiver_by_document_field": "employee_emails" - } - ], - "send_system_notification": 0, - "send_to_all_assignees": 0, - "subject": "Training Scheduled: {{ doc.name }}" -} \ No newline at end of file diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.md b/erpnext/hr/notification/training_scheduled/training_scheduled.md deleted file mode 100644 index b9ba846be5..0000000000 --- a/erpnext/hr/notification/training_scheduled/training_scheduled.md +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - -
-
- {{_("Training Event:")}} {{ doc.event_name }} -
-
- - - - - - - - - -
-
- {{ doc.introduction }} -
    -
  • {{_("Event Location")}}: {{ doc.location }}
  • - {% set start = frappe.utils.get_datetime(doc.start_time) %} - {% set end = frappe.utils.get_datetime(doc.end_time) %} - {% if start.date() == end.date() %} -
  • {{_("Date")}}: {{ start.strftime("%A, %d %b %Y") }}
  • -
  • - {{_("Timing")}}: {{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }} -
  • - {% else %} -
  • - {{_("Start Time")}}: {{ start.strftime("%A, %d %b %Y at %I:%M %p") }} -
  • -
  • {{_("End Time")}}: {{ end.strftime("%A, %d %b %Y at %I:%M %p") }}
  • - {% endif %} -
  • {{ _("Event Link") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • - {% if doc.is_mandatory %} -
  • {{ _("Note: This Training Event is mandatory") }}
  • - {% endif %} -
-
-
diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.py b/erpnext/hr/notification/training_scheduled/training_scheduled.py deleted file mode 100644 index 02e3e93333..0000000000 --- a/erpnext/hr/notification/training_scheduled/training_scheduled.py +++ /dev/null @@ -1,3 +0,0 @@ -def get_context(context): - # do your magic here - pass diff --git a/erpnext/hr/number_card/employees_left_(last_year)/employees_left_(last_year).json b/erpnext/hr/number_card/employees_left_(last_year)/employees_left_(last_year).json deleted file mode 100644 index 6a91912eff..0000000000 --- a/erpnext/hr/number_card/employees_left_(last_year)/employees_left_(last_year).json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "creation": "2020-07-22 11:56:32.947790", - "docstatus": 0, - "doctype": "Number Card", - "document_type": "Employee", - "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", - "filters_json": "[[\"Employee\",\"relieving_date\",\"Timespan\",\"last year\",false]]", - "function": "Count", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "label": "Employees Left (Last year)", - "modified": "2020-07-23 12:03:26.747447", - "modified_by": "Administrator", - "module": "HR", - "name": "Employees Left (Last year)", - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Monthly", - "type": "Document Type" -} \ No newline at end of file diff --git a/erpnext/hr/number_card/new_joinees_(last_year)/new_joinees_(last_year).json b/erpnext/hr/number_card/new_joinees_(last_year)/new_joinees_(last_year).json deleted file mode 100644 index 8f5ad9ce31..0000000000 --- a/erpnext/hr/number_card/new_joinees_(last_year)/new_joinees_(last_year).json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "creation": "2020-07-22 11:56:32.914057", - "docstatus": 0, - "doctype": "Number Card", - "document_type": "Employee", - "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", - "filters_json": "[[\"Employee\",\"date_of_joining\",\"Timespan\",\"last year\",false],[\"Employee\",\"status\",\"=\",\"Active\",false]]", - "function": "Count", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "label": "New Joinees (Last year)", - "modified": "2020-07-22 14:32:09.352301", - "modified_by": "Administrator", - "module": "HR", - "name": "New Joinees (Last year)", - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Monthly", - "type": "Document Type" -} \ No newline at end of file diff --git a/erpnext/hr/number_card/total_applicants_(last_month)/total_applicants_(last_month).json b/erpnext/hr/number_card/total_applicants_(last_month)/total_applicants_(last_month).json deleted file mode 100644 index 1af42cabf6..0000000000 --- a/erpnext/hr/number_card/total_applicants_(last_month)/total_applicants_(last_month).json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "creation": "2020-07-22 11:56:32.977716", - "docstatus": 0, - "doctype": "Number Card", - "document_type": "Job Applicant", - "dynamic_filters_json": "", - "filters_json": "[[\"Job Applicant\",\"creation\",\"Timespan\",\"last month\"]]", - "function": "Count", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "label": "Total Applicants (Last month)", - "modified": "2020-07-22 14:32:27.656855", - "modified_by": "Administrator", - "module": "HR", - "name": "Total Applicants (Last month)", - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Monthly", - "type": "Document Type" -} \ No newline at end of file diff --git a/erpnext/hr/number_card/total_employees/total_employees.json b/erpnext/hr/number_card/total_employees/total_employees.json deleted file mode 100644 index 932e255c9c..0000000000 --- a/erpnext/hr/number_card/total_employees/total_employees.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "creation": "2020-07-22 11:56:32.874849", - "docstatus": 0, - "doctype": "Number Card", - "document_type": "Employee", - "dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", - "filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]", - "function": "Count", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "label": "Total Employees", - "modified": "2020-07-22 14:31:59.118650", - "modified_by": "Administrator", - "module": "HR", - "name": "Total Employees", - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Monthly", - "type": "Document Type" -} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_department/create_department.json b/erpnext/hr/onboarding_step/create_department/create_department.json deleted file mode 100644 index 66a54cfc26..0000000000 --- a/erpnext/hr/onboarding_step/create_department/create_department.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-05-14 11:44:34.682115", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-05-14 12:22:26.448420", - "modified_by": "Administrator", - "name": "Create Department", - "owner": "Administrator", - "reference_document": "Department", - "show_full_form": 0, - "title": "Create Department", - "validate_action": 0 -} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_designation/create_designation.json b/erpnext/hr/onboarding_step/create_designation/create_designation.json deleted file mode 100644 index c4e9cc7798..0000000000 --- a/erpnext/hr/onboarding_step/create_designation/create_designation.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-05-14 11:45:07.514193", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-05-14 12:22:41.500795", - "modified_by": "Administrator", - "name": "Create Designation", - "owner": "Administrator", - "reference_document": "Designation", - "show_full_form": 0, - "title": "Create Designation", - "validate_action": 0 -} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_employee/create_employee.json b/erpnext/hr/onboarding_step/create_employee/create_employee.json deleted file mode 100644 index 47828186bf..0000000000 --- a/erpnext/hr/onboarding_step/create_employee/create_employee.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "action": "Show Form Tour", - "action_label": "Show Tour", - "creation": "2020-05-14 11:43:25.561152", - "description": "

Employee

\n\nAn individual who works and is recognized for his rights and duties in your company is your Employee. You can manage the Employee master. It captures the demographic, personal and professional details, joining and leave details, etc.", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2021-05-19 04:50:02.240321", - "modified_by": "Administrator", - "name": "Create Employee", - "owner": "Administrator", - "reference_document": "Employee", - "show_form_tour": 0, - "show_full_form": 0, - "title": "Create Employee", - "validate_action": 0 -} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json deleted file mode 100644 index a08e85fff0..0000000000 --- a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "action": "Show Form Tour", - "action_label": "Show Tour", - "creation": "2020-05-28 11:47:34.700174", - "description": "

Holiday List.

\n\nHoliday List is a list which contains the dates of holidays. Most organizations have a standard Holiday List for their employees. However, some of them may have different holiday lists based on different Locations or Departments. In ERPNext, you can configure multiple Holiday Lists.", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2021-05-19 04:19:52.305199", - "modified_by": "Administrator", - "name": "Create Holiday list", - "owner": "Administrator", - "reference_document": "Holiday List", - "show_form_tour": 0, - "show_full_form": 1, - "title": "Create Holiday List", - "validate_action": 0 -} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json b/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json deleted file mode 100644 index 0b0ce3fc8b..0000000000 --- a/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "action": "Show Form Tour", - "action_label": "Show Tour", - "creation": "2020-05-14 11:48:56.123718", - "description": "

Leave Allocation

\n\nLeave Allocation enables you to allocate a specific number of leaves of a particular type to an Employee so that, an employee will be able to create a Leave Application only if Leaves are allocated. ", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2021-05-19 04:22:34.220238", - "modified_by": "Administrator", - "name": "Create Leave Allocation", - "owner": "Administrator", - "reference_document": "Leave Allocation", - "show_form_tour": 0, - "show_full_form": 0, - "title": "Create Leave Allocation", - "validate_action": 0 -} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json b/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json deleted file mode 100644 index af63aa59ed..0000000000 --- a/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "action": "Show Form Tour", - "action_label": "Show Tour", - "creation": "2020-05-14 11:49:45.400764", - "description": "

Leave Application

\n\nLeave Application is a formal document created by an Employee to apply for Leaves for a particular time period based on there leave allocation and leave type according to there need.", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2021-05-19 04:39:09.893474", - "modified_by": "Administrator", - "name": "Create Leave Application", - "owner": "Administrator", - "reference_document": "Leave Application", - "show_form_tour": 0, - "show_full_form": 0, - "title": "Create Leave Application", - "validate_action": 0 -} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json b/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json deleted file mode 100644 index 397f5cde49..0000000000 --- a/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "action": "Show Form Tour", - "action_label": "Show Tour", - "creation": "2020-05-27 11:17:31.119312", - "description": "

Leave Type

\n\nLeave type is defined based on many factors and features like encashment, earned leaves, partially paid, without pay and, a lot more. To check other options and to define your leave type click on Show Tour.", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2021-05-19 04:32:48.135406", - "modified_by": "Administrator", - "name": "Create Leave Type", - "owner": "Administrator", - "reference_document": "Leave Type", - "show_form_tour": 0, - "show_full_form": 1, - "title": "Create Leave Type", - "validate_action": 0 -} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/data_import/data_import.json b/erpnext/hr/onboarding_step/data_import/data_import.json deleted file mode 100644 index ac343c6775..0000000000 --- a/erpnext/hr/onboarding_step/data_import/data_import.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "action": "Watch Video", - "action_label": "", - "creation": "2021-05-19 05:29:16.809610", - "description": "

Data Import

\n\nData import is the tool to migrate your existing data like Employee, Customer, Supplier, and a lot more to our ERPNext system.\nGo through the video for a detailed explanation of this tool.", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2021-05-19 05:29:16.809610", - "modified_by": "Administrator", - "name": "Data import", - "owner": "Administrator", - "show_form_tour": 0, - "show_full_form": 0, - "title": "Data Import", - "validate_action": 1, - "video_url": "https://www.youtube.com/watch?v=DQyqeurPI64" -} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json deleted file mode 100644 index 355664fbc5..0000000000 --- a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "action": "Show Form Tour", - "action_label": "Explore", - "creation": "2020-05-28 13:13:52.427711", - "description": "

HR Settings

\n\nHr Settings consists of major settings related to Employee Lifecycle, Leave Management, etc. Click on Explore, to explore Hr Settings.", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_single": 1, - "is_skipped": 0, - "modified": "2021-05-18 07:02:05.747548", - "modified_by": "Administrator", - "name": "HR Settings", - "owner": "Administrator", - "reference_document": "HR Settings", - "show_form_tour": 0, - "show_full_form": 0, - "title": "HR Settings", - "validate_action": 0 -} \ No newline at end of file diff --git a/erpnext/hr/page/__init__.py b/erpnext/hr/page/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/page/organizational_chart/__init__.py b/erpnext/hr/page/organizational_chart/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js deleted file mode 100644 index b0e41e0090..0000000000 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ /dev/null @@ -1,23 +0,0 @@ -frappe.pages['organizational-chart'].on_page_load = function(wrapper) { - frappe.ui.make_app_page({ - parent: wrapper, - title: __('Organizational Chart'), - single_column: true - }); - - $(wrapper).bind('show', () => { - frappe.require('hierarchy-chart.bundle.js', () => { - let organizational_chart = undefined; - let method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; - - if (frappe.is_mobile()) { - organizational_chart = new erpnext.HierarchyChartMobile('Employee', wrapper, method); - } else { - organizational_chart = new erpnext.HierarchyChart('Employee', wrapper, method); - } - - frappe.breadcrumbs.add('HR'); - organizational_chart.show(); - }); - }); -}; diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.json b/erpnext/hr/page/organizational_chart/organizational_chart.json deleted file mode 100644 index d802781320..0000000000 --- a/erpnext/hr/page/organizational_chart/organizational_chart.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "content": null, - "creation": "2021-05-25 10:53:10.107241", - "docstatus": 0, - "doctype": "Page", - "idx": 0, - "modified": "2021-05-25 10:53:18.201931", - "modified_by": "Administrator", - "module": "HR", - "name": "organizational-chart", - "owner": "Administrator", - "page_name": "Organizational Chart", - "roles": [ - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ], - "script": null, - "standard": "Yes", - "style": null, - "system_page": 0, - "title": "Organizational Chart" -} \ No newline at end of file diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py deleted file mode 100644 index 3674912dc0..0000000000 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ /dev/null @@ -1,45 +0,0 @@ -import frappe - - -@frappe.whitelist() -def get_children(parent=None, company=None, exclude_node=None): - filters = [["status", "!=", "Left"]] - if company and company != "All Companies": - filters.append(["company", "=", company]) - - if parent and company and parent != company: - filters.append(["reports_to", "=", parent]) - else: - filters.append(["reports_to", "=", ""]) - - if exclude_node: - filters.append(["name", "!=", exclude_node]) - - employees = frappe.get_list( - "Employee", - fields=["employee_name as name", "name as id", "reports_to", "image", "designation as title"], - filters=filters, - order_by="name", - ) - - for employee in employees: - is_expandable = frappe.db.count("Employee", filters={"reports_to": employee.get("id")}) - employee.connections = get_connections(employee.id) - employee.expandable = 1 if is_expandable else 0 - - return employees - - -def get_connections(employee): - num_connections = 0 - - nodes_to_expand = frappe.get_list("Employee", filters=[["reports_to", "=", employee]]) - num_connections += len(nodes_to_expand) - - while nodes_to_expand: - parent = nodes_to_expand.pop(0) - descendants = frappe.get_list("Employee", filters=[["reports_to", "=", parent.name]]) - num_connections += len(descendants) - nodes_to_expand.extend(descendants) - - return num_connections diff --git a/erpnext/hr/page/team_updates/__init__.py b/erpnext/hr/page/team_updates/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/page/team_updates/team_update_row.html b/erpnext/hr/page/team_updates/team_update_row.html deleted file mode 100644 index 4f367541b4..0000000000 --- a/erpnext/hr/page/team_updates/team_update_row.html +++ /dev/null @@ -1,15 +0,0 @@ -
-
- {%= date_sep || "" %}
-
-
-
- {{ avatar }} -
-
- {{ content }} -
-
-
-
diff --git a/erpnext/hr/page/team_updates/team_updates.css b/erpnext/hr/page/team_updates/team_updates.css deleted file mode 100644 index d37e7826b1..0000000000 --- a/erpnext/hr/page/team_updates/team_updates.css +++ /dev/null @@ -1,57 +0,0 @@ -.date-indicator { - background:none; - font-size:12px; - vertical-align:middle; - font-weight:bold; - color:#6c7680; -} -.date-indicator::after { - margin:0 -4px 0 12px; - content:''; - display:inline-block; - height:8px; - width:8px; - border-radius:8px; - background: #d1d8dd; -} - -.date-indicator.blue { - color: #5e64ff; -} - -.date-indicator.blue::after { - background: #5e64ff; -} - -.activity-row { -} - -.activity-message { - border-left: 1px solid #d1d8dd; -} - -.activity-message .row { - padding: 15px; - margin-right: 0px; - border-bottom: 1px solid #d1d8dd; -} - -.activity-row:last-child .activity-message .row { - border-bottom: none; -} - -.activity-row .content { - padding-left: 0px; - padding-right: 30px; -} - -.activity-date { - padding: 15px; - padding-right: 0px; - z-index: 1; -} - -.for-more { - border-top: 1px solid #d1d8dd; - padding: 10px; -} diff --git a/erpnext/hr/page/team_updates/team_updates.js b/erpnext/hr/page/team_updates/team_updates.js deleted file mode 100644 index 358329748e..0000000000 --- a/erpnext/hr/page/team_updates/team_updates.js +++ /dev/null @@ -1,80 +0,0 @@ -frappe.pages['team-updates'].on_page_load = function(wrapper) { - var page = frappe.ui.make_app_page({ - parent: wrapper, - title: __('Team Updates'), - single_column: true - }); - - frappe.team_updates.make(page); - frappe.team_updates.run(); - - if(frappe.model.can_read('Daily Work Summary Group')) { - page.add_menu_item(__('Daily Work Summary Group'), function() { - frappe.set_route('Form', 'Daily Work Summary Group'); - }); - } -} - -frappe.team_updates = { - start: 0, - make: function(page) { - var me = frappe.team_updates; - me.page = page; - me.body = $('
').appendTo(me.page.main); - me.more = $('
').appendTo(me.page.main) - .find('.btn-more').on('click', function() { - me.start += 40; - me.run(); - }); - }, - run: function() { - var me = frappe.team_updates; - frappe.call({ - method: 'erpnext.hr.page.team_updates.team_updates.get_data', - args: { - start: me.start - }, - callback: function(r) { - if (r.message && r.message.length > 0) { - r.message.forEach(function(d) { - me.add_row(d); - }); - } else { - frappe.show_alert({message: __('No more updates'), indicator: 'gray'}); - me.more.parent().addClass('hidden'); - } - } - }); - }, - add_row: function(data) { - var me = frappe.team_updates; - - data.by = frappe.user.full_name(data.sender); - data.avatar = frappe.avatar(data.sender); - data.when = comment_when(data.creation); - - var date = frappe.datetime.str_to_obj(data.creation); - var last = me.last_feed_date; - - if((last && frappe.datetime.obj_to_str(last) != frappe.datetime.obj_to_str(date)) || (!last)) { - var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); - var pdate; - if(diff < 1) { - pdate = 'Today'; - } else if(diff < 2) { - pdate = 'Yesterday'; - } else { - pdate = frappe.datetime.global_date_format(date); - } - data.date_sep = pdate; - data.date_class = pdate=='Today' ? "date-indicator blue" : "date-indicator"; - } else { - data.date_sep = null; - data.date_class = ""; - } - me.last_feed_date = date; - - $(frappe.render_template('team_update_row', data)).appendTo(me.body); - } -} diff --git a/erpnext/hr/page/team_updates/team_updates.json b/erpnext/hr/page/team_updates/team_updates.json deleted file mode 100644 index 167c67fb4e..0000000000 --- a/erpnext/hr/page/team_updates/team_updates.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "content": null, - "creation": "2017-01-31 11:02:31.614045", - "docstatus": 0, - "doctype": "Page", - "idx": 0, - "modified": "2017-01-31 11:25:01.983200", - "modified_by": "Administrator", - "module": "HR", - "name": "team-updates", - "owner": "Administrator", - "page_name": "team-updates", - "roles": [ - { - "role": "Employee" - }, - { - "role": "System Manager" - } - ], - "script": null, - "standard": "Yes", - "style": null, - "title": "Team Updates" -} \ No newline at end of file diff --git a/erpnext/hr/page/team_updates/team_updates.py b/erpnext/hr/page/team_updates/team_updates.py deleted file mode 100644 index c1fcb73585..0000000000 --- a/erpnext/hr/page/team_updates/team_updates.py +++ /dev/null @@ -1,24 +0,0 @@ -import frappe -from email_reply_parser import EmailReplyParser - - -@frappe.whitelist() -def get_data(start=0): - # frappe.only_for('Employee', 'System Manager') - data = frappe.get_all( - "Communication", - fields=("content", "text_content", "sender", "creation"), - filters=dict(reference_doctype="Daily Work Summary"), - order_by="creation desc", - limit=40, - start=start, - ) - - for d in data: - d.sender_name = ( - frappe.db.get_value("Employee", {"user_id": d.sender}, "employee_name") or d.sender - ) - if d.text_content: - d.content = frappe.utils.md_to_html(EmailReplyParser.parse_reply(d.text_content)) - - return data diff --git a/erpnext/hr/print_format/__init__.py b/erpnext/hr/print_format/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/print_format/job_offer/__init__.py b/erpnext/hr/print_format/job_offer/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/print_format/job_offer/job_offer.json b/erpnext/hr/print_format/job_offer/job_offer.json deleted file mode 100644 index 0a922306b4..0000000000 --- a/erpnext/hr/print_format/job_offer/job_offer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "align_labels_right": 0, - "creation": "2015-03-05 14:34:26.751210", - "custom_format": 1, - "disabled": 0, - "doc_type": "Job Offer", - "docstatus": 0, - "doctype": "Print Format", - "html": "{% set terms_exist = doc.terms and (doc.terms|striptags).strip() or \"\" %}\n\n{% if letter_head and not no_letterhead -%}\n
{{ letter_head }}
\n
\n{%- endif %}\n\n
\n

\n\n\nDate: {{ doc.offer_date }}\n

\n\nDear {{ doc.applicant_name }}, \n\n

\n\nWe are pleased to appoint you in the services of {{ doc.company }} on the terms and conditions detailed in this letter.\n\n

\n\nYour designation shall be {{ doc.designation }}.\n\n

\n\n\n\n{%- if doc.offer_terms -%}\n {%- for row in doc.offer_terms -%}\n {{ row.offer_term }}: {{ row.value }}\n\n
\n {%- endfor -%}\n{%- endif -%}\n\n
\n\n\n\n\nPlease read the detailed terms as below. If you have any queries, feel free to get in touch with us.\nWe look forward to your long and fruitful career association with our organisation.\nIf you decide to join us, 'Welcome to {{ doc.company }} !'\n\n

\n\n

\n\nYours truly,\n\n



\n\nAuthorized Signatory\n\n
\n\n{{ doc.company }}\n\n\n\n

\n
\n\n\n{% if terms_exist %}\n
{{ doc.terms }}
\n{% endif %}", - "idx": 0, - "line_breaks": 0, - "modified": "2018-02-15 03:03:55.844085", - "modified_by": "Administrator", - "module": "HR", - "name": "Job Offer", - "owner": "Administrator", - "print_format_builder": 0, - "print_format_type": "Jinja", - "show_section_headings": 0, - "standard": "Yes" -} \ No newline at end of file diff --git a/erpnext/hr/print_format/standard_appointment_letter/__init__.py b/erpnext/hr/print_format/standard_appointment_letter/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html b/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html deleted file mode 100644 index 87daafcaae..0000000000 --- a/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html +++ /dev/null @@ -1,38 +0,0 @@ -{%- from "templates/print_formats/standard_macros.html" import add_header -%} - -

Appointment Letter

-
-
- {{ doc.applicant_name }}, -
-
- Date: {{ doc.appointment_date }} -
-
-
- {{ doc.introduction }} -
-
-
    - {% for content in doc.terms %} -
  • - - {{ content.title }}: {{ content.description }} - -
  • - {% endfor %} -
-
-
-Your sincerely,
-For {{ doc.company }} -
- -
- {{ doc.closing_notes }} -
- -
- ________________
- {{ doc.applicant_name }} -
diff --git a/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.json b/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.json deleted file mode 100644 index 1813e711f5..0000000000 --- a/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "align_labels_right": 0, - "creation": "2019-12-26 15:22:44.200332", - "custom_format": 0, - "default_print_language": "en", - "disabled": 0, - "doc_type": "Appointment Letter", - "docstatus": 0, - "doctype": "Print Format", - "font": "Default", - "idx": 0, - "line_breaks": 0, - "modified": "2020-01-21 17:24:16.705082", - "modified_by": "Administrator", - "module": "HR", - "name": "Standard Appointment Letter", - "owner": "Administrator", - "print_format_builder": 0, - "print_format_type": "Jinja", - "raw_printing": 0, - "show_section_headings": 0, - "standard": "Yes" -} \ No newline at end of file diff --git a/erpnext/hr/report/__init__.py b/erpnext/hr/report/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/daily_work_summary_replies/__init__.py b/erpnext/hr/report/daily_work_summary_replies/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.js b/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.js deleted file mode 100644 index 0980c6957b..0000000000 --- a/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.js +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -/* eslint-disable */ -frappe.query_reports["Daily Work Summary Replies"] = { - "filters": [ - { - "fieldname":"group", - "label": __("Group"), - "fieldtype": "Link", - "options": "Daily Work Summary Group", - "reqd": 1 - }, - { - "fieldname": "range", - "label": __("Date Range"), - "fieldtype": "DateRange", - "reqd": 1 - } - ] -} diff --git a/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.json b/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.json deleted file mode 100644 index 04c88506d7..0000000000 --- a/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2018-06-04 10:30:25.673452", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2018-06-04 10:44:04.694509", - "modified_by": "Administrator", - "module": "HR", - "name": "Daily Work Summary Replies", - "owner": "Administrator", - "ref_doctype": "Daily Work Summary", - "report_name": "Daily Work Summary Replies", - "report_type": "Script Report", - "roles": [ - { - "role": "Employee" - }, - { - "role": "HR User" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.py b/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.py deleted file mode 100644 index d93688a492..0000000000 --- a/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ - -from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_user_emails_from_group - - -def execute(filters=None): - if not filters.group: - return [], [] - columns, data = get_columns(), get_data(filters) - return columns, data - - -def get_columns(filters=None): - columns = [ - {"label": _("User"), "fieldname": "user", "fieldtype": "Data", "width": 300}, - { - "label": _("Replies"), - "fieldname": "count", - "fieldtype": "data", - "width": 100, - "align": "right", - }, - { - "label": _("Total"), - "fieldname": "total", - "fieldtype": "data", - "width": 100, - "align": "right", - }, - ] - return columns - - -def get_data(filters): - daily_summary_emails = frappe.get_all( - "Daily Work Summary", fields=["name"], filters=[["creation", "Between", filters.range]] - ) - daily_summary_emails = [d.get("name") for d in daily_summary_emails] - replies = frappe.get_all( - "Communication", - fields=["content", "text_content", "sender"], - filters=[ - ["reference_doctype", "=", "Daily Work Summary"], - ["reference_name", "in", daily_summary_emails], - ["communication_type", "=", "Communication"], - ["sent_or_received", "=", "Received"], - ], - order_by="creation asc", - ) - data = [] - total = len(daily_summary_emails) - for user in get_user_emails_from_group(filters.group): - user_name = frappe.get_value("User", user, "full_name") - count = len([d for d in replies if d.sender == user]) - data.append([user_name, count, total]) - return data diff --git a/erpnext/hr/report/employee_advance_summary/__init__.py b/erpnext/hr/report/employee_advance_summary/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.js b/erpnext/hr/report/employee_advance_summary/employee_advance_summary.js deleted file mode 100644 index 8de4af5d4f..0000000000 --- a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.js +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Employee Advance Summary"] = { - "filters": [ - { - "fieldname":"employee", - "label": __("Employee"), - "fieldtype": "Link", - "options": "Employee", - "width": "80" - }, - { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_start_date"), - "width": "80" - }, - { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today() - }, - { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company" - }, - { - "fieldname":"status", - "label": __("Status"), - "fieldtype": "Select", - "options": "\nDraft\nPaid\nUnpaid\nClaimed\nCancelled" - } - ] -}; diff --git a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.json b/erpnext/hr/report/employee_advance_summary/employee_advance_summary.json deleted file mode 100644 index 60afd59d6b..0000000000 --- a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "add_total_row": 1, - "apply_user_permissions": 1, - "creation": "2018-02-21 07:12:37.299923", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2018-02-22 13:33:41.532005", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Advance Summary", - "owner": "Administrator", - "ref_doctype": "Employee Advance", - "report_name": "Employee Advance Summary", - "report_type": "Script Report", - "roles": [ - { - "role": "Employee" - }, - { - "role": "Expense Approver" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.py b/erpnext/hr/report/employee_advance_summary/employee_advance_summary.py deleted file mode 100644 index 29532f7680..0000000000 --- a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _, msgprint - - -def execute(filters=None): - if not filters: - filters = {} - - advances_list = get_advances(filters) - columns = get_columns() - - if not advances_list: - msgprint(_("No record found")) - return columns, advances_list - - data = [] - for advance in advances_list: - row = [ - advance.name, - advance.employee, - advance.company, - advance.posting_date, - advance.advance_amount, - advance.paid_amount, - advance.claimed_amount, - advance.status, - ] - data.append(row) - - return columns, data - - -def get_columns(): - return [ - { - "label": _("Title"), - "fieldname": "title", - "fieldtype": "Link", - "options": "Employee Advance", - "width": 120, - }, - { - "label": _("Employee"), - "fieldname": "employee", - "fieldtype": "Link", - "options": "Employee", - "width": 120, - }, - { - "label": _("Company"), - "fieldname": "company", - "fieldtype": "Link", - "options": "Company", - "width": 120, - }, - {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 120}, - { - "label": _("Advance Amount"), - "fieldname": "advance_amount", - "fieldtype": "Currency", - "width": 120, - }, - {"label": _("Paid Amount"), "fieldname": "paid_amount", "fieldtype": "Currency", "width": 120}, - { - "label": _("Claimed Amount"), - "fieldname": "claimed_amount", - "fieldtype": "Currency", - "width": 120, - }, - {"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 120}, - ] - - -def get_conditions(filters): - conditions = "" - - if filters.get("employee"): - conditions += "and employee = %(employee)s" - if filters.get("company"): - conditions += " and company = %(company)s" - if filters.get("status"): - conditions += " and status = %(status)s" - if filters.get("from_date"): - conditions += " and posting_date>=%(from_date)s" - if filters.get("to_date"): - conditions += " and posting_date<=%(to_date)s" - - return conditions - - -def get_advances(filters): - conditions = get_conditions(filters) - return frappe.db.sql( - """select name, employee, paid_amount, status, advance_amount, claimed_amount, company, - posting_date, purpose - from `tabEmployee Advance` - where docstatus<2 %s order by posting_date, name desc""" - % conditions, - filters, - as_dict=1, - ) diff --git a/erpnext/hr/report/employee_analytics/__init__.py b/erpnext/hr/report/employee_analytics/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.js b/erpnext/hr/report/employee_analytics/employee_analytics.js deleted file mode 100644 index 8620a65a90..0000000000 --- a/erpnext/hr/report/employee_analytics/employee_analytics.js +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Employee Analytics"] = { - "filters": [ - { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 - }, - { - "fieldname":"parameter", - "label": __("Parameter"), - "fieldtype": "Select", - "options": ["Branch","Grade","Department","Designation", "Employment Type"], - "reqd": 1 - } - ] -}; diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.json b/erpnext/hr/report/employee_analytics/employee_analytics.json deleted file mode 100644 index 5a7ab9a251..0000000000 --- a/erpnext/hr/report/employee_analytics/employee_analytics.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2020-05-12 13:52:50.631086", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2020-05-12 13:52:50.631086", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Analytics", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Employee", - "report_name": "Employee Analytics", - "report_type": "Script Report", - "roles": [ - { - "role": "Employee" - }, - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py deleted file mode 100644 index 12be156ab9..0000000000 --- a/erpnext/hr/report/employee_analytics/employee_analytics.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ - - -def execute(filters=None): - if not filters: - filters = {} - - if not filters["company"]: - frappe.throw(_("{0} is mandatory").format(_("Company"))) - - columns = get_columns() - employees = get_employees(filters) - parameters_result = get_parameters(filters) - parameters = [] - if parameters_result: - for department in parameters_result: - parameters.append(department) - - chart = get_chart_data(parameters, employees, filters) - return columns, employees, None, chart - - -def get_columns(): - return [ - _("Employee") + ":Link/Employee:120", - _("Name") + ":Data:200", - _("Date of Birth") + ":Date:100", - _("Branch") + ":Link/Branch:120", - _("Department") + ":Link/Department:120", - _("Designation") + ":Link/Designation:120", - _("Gender") + "::100", - _("Company") + ":Link/Company:120", - ] - - -def get_conditions(filters): - conditions = " and " + filters.get("parameter").lower().replace(" ", "_") + " IS NOT NULL " - - if filters.get("company"): - conditions += " and company = '%s'" % filters["company"].replace("'", "\\'") - return conditions - - -def get_employees(filters): - conditions = get_conditions(filters) - return frappe.db.sql( - """select name, employee_name, date_of_birth, - branch, department, designation, - gender, company from `tabEmployee` where status = 'Active' %s""" - % conditions, - as_list=1, - ) - - -def get_parameters(filters): - if filters.get("parameter") == "Grade": - parameter = "Employee Grade" - else: - parameter = filters.get("parameter") - - return frappe.db.sql("""select name from `tab""" + parameter + """` """, as_list=1) - - -def get_chart_data(parameters, employees, filters): - if not parameters: - parameters = [] - datasets = [] - parameter_field_name = filters.get("parameter").lower().replace(" ", "_") - label = [] - for parameter in parameters: - if parameter: - total_employee = frappe.db.sql( - """select count(*) from - `tabEmployee` where """ - + parameter_field_name - + """ = %s and company = %s""", - (parameter[0], filters.get("company")), - as_list=1, - ) - if total_employee[0][0]: - label.append(parameter) - datasets.append(total_employee[0][0]) - - values = [value for value in datasets if value != 0] - - total_employee = frappe.db.count("Employee", {"status": "Active"}) - others = total_employee - sum(values) - - label.append(["Not Set"]) - values.append(others) - - chart = {"data": {"labels": label, "datasets": [{"name": "Employees", "values": values}]}} - chart["type"] = "donut" - return chart diff --git a/erpnext/hr/report/employee_birthday/__init__.py b/erpnext/hr/report/employee_birthday/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/employee_birthday/employee_birthday.js b/erpnext/hr/report/employee_birthday/employee_birthday.js deleted file mode 100644 index bbe4a8d179..0000000000 --- a/erpnext/hr/report/employee_birthday/employee_birthday.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.query_reports["Employee Birthday"] = { - "filters": [ - { - "fieldname":"month", - "label": __("Month"), - "fieldtype": "Select", - "options": "Jan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug\nSep\nOct\nNov\nDec", - "default": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", - "Dec"][frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth()], - }, - { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") - } - ] -} diff --git a/erpnext/hr/report/employee_birthday/employee_birthday.json b/erpnext/hr/report/employee_birthday/employee_birthday.json deleted file mode 100644 index 7946a6a55d..0000000000 --- a/erpnext/hr/report/employee_birthday/employee_birthday.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-05-06 17:56:03", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2017-02-24 20:18:13.011024", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Birthday", - "owner": "Administrator", - "ref_doctype": "Employee", - "report_name": "Employee Birthday", - "report_type": "Script Report", - "roles": [ - { - "role": "Employee" - }, - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/employee_birthday/employee_birthday.py b/erpnext/hr/report/employee_birthday/employee_birthday.py deleted file mode 100644 index a6a13d8a4d..0000000000 --- a/erpnext/hr/report/employee_birthday/employee_birthday.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe import _ - - -def execute(filters=None): - if not filters: - filters = {} - - columns = get_columns() - data = get_employees(filters) - - return columns, data - - -def get_columns(): - return [ - _("Employee") + ":Link/Employee:120", - _("Name") + ":Data:200", - _("Date of Birth") + ":Date:100", - _("Branch") + ":Link/Branch:120", - _("Department") + ":Link/Department:120", - _("Designation") + ":Link/Designation:120", - _("Gender") + "::60", - _("Company") + ":Link/Company:120", - ] - - -def get_employees(filters): - conditions = get_conditions(filters) - return frappe.db.sql( - """select name, employee_name, date_of_birth, - branch, department, designation, - gender, company from tabEmployee where status = 'Active' %s""" - % conditions, - as_list=1, - ) - - -def get_conditions(filters): - conditions = "" - if filters.get("month"): - month = [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ].index(filters["month"]) + 1 - conditions += " and month(date_of_birth) = '%s'" % month - - if filters.get("company"): - conditions += " and company = '%s'" % filters["company"].replace("'", "\\'") - - return conditions diff --git a/erpnext/hr/report/employee_exits/__init__.py b/erpnext/hr/report/employee_exits/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/employee_exits/employee_exits.js b/erpnext/hr/report/employee_exits/employee_exits.js deleted file mode 100644 index ac677d87e7..0000000000 --- a/erpnext/hr/report/employee_exits/employee_exits.js +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Employee Exits"] = { - filters: [ - { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12) - }, - { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.nowdate() - }, - { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company" - }, - { - "fieldname": "department", - "label": __("Department"), - "fieldtype": "Link", - "options": "Department" - }, - { - "fieldname": "designation", - "label": __("Designation"), - "fieldtype": "Link", - "options": "Designation" - }, - { - "fieldname": "employee", - "label": __("Employee"), - "fieldtype": "Link", - "options": "Employee" - }, - { - "fieldname": "reports_to", - "label": __("Reports To"), - "fieldtype": "Link", - "options": "Employee" - }, - { - "fieldname": "interview_status", - "label": __("Interview Status"), - "fieldtype": "Select", - "options": ["", "Pending", "Scheduled", "Completed"] - }, - { - "fieldname": "final_decision", - "label": __("Final Decision"), - "fieldtype": "Select", - "options": ["", "Employee Retained", "Exit Confirmed"] - }, - { - "fieldname": "exit_interview_pending", - "label": __("Exit Interview Pending"), - "fieldtype": "Check" - }, - { - "fieldname": "questionnaire_pending", - "label": __("Exit Questionnaire Pending"), - "fieldtype": "Check" - }, - { - "fieldname": "fnf_pending", - "label": __("FnF Pending"), - "fieldtype": "Check" - } - ] -}; diff --git a/erpnext/hr/report/employee_exits/employee_exits.json b/erpnext/hr/report/employee_exits/employee_exits.json deleted file mode 100644 index 4fe9a853c0..0000000000 --- a/erpnext/hr/report/employee_exits/employee_exits.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "add_total_row": 0, - "columns": [], - "creation": "2021-12-05 19:47:18.332319", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "filters": [], - "idx": 0, - "is_standard": "Yes", - "letter_head": "Test", - "modified": "2021-12-05 19:47:18.332319", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Exits", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Exit Interview", - "report_name": "Employee Exits", - "report_type": "Script Report", - "roles": [ - { - "role": "System Manager" - }, - { - "role": "HR Manager" - }, - { - "role": "HR User" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/employee_exits/employee_exits.py b/erpnext/hr/report/employee_exits/employee_exits.py deleted file mode 100644 index 9cd9ff0a6b..0000000000 --- a/erpnext/hr/report/employee_exits/employee_exits.py +++ /dev/null @@ -1,235 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# License: MIT. See LICENSE - -import frappe -from frappe import _ -from frappe.query_builder import Order -from frappe.utils import getdate - - -def execute(filters=None): - columns = get_columns() - data = get_data(filters) - chart = get_chart_data(data) - report_summary = get_report_summary(data) - - return columns, data, None, chart, report_summary - - -def get_columns(): - return [ - { - "label": _("Employee"), - "fieldname": "employee", - "fieldtype": "Link", - "options": "Employee", - "width": 150, - }, - {"label": _("Employee Name"), "fieldname": "employee_name", "fieldtype": "Data", "width": 150}, - { - "label": _("Date of Joining"), - "fieldname": "date_of_joining", - "fieldtype": "Date", - "width": 120, - }, - {"label": _("Relieving Date"), "fieldname": "relieving_date", "fieldtype": "Date", "width": 120}, - { - "label": _("Exit Interview"), - "fieldname": "exit_interview", - "fieldtype": "Link", - "options": "Exit Interview", - "width": 150, - }, - { - "label": _("Interview Status"), - "fieldname": "interview_status", - "fieldtype": "Data", - "width": 130, - }, - { - "label": _("Final Decision"), - "fieldname": "employee_status", - "fieldtype": "Data", - "width": 150, - }, - { - "label": _("Full and Final Statement"), - "fieldname": "full_and_final_statement", - "fieldtype": "Link", - "options": "Full and Final Statement", - "width": 180, - }, - { - "label": _("Department"), - "fieldname": "department", - "fieldtype": "Link", - "options": "Department", - "width": 120, - }, - { - "label": _("Designation"), - "fieldname": "designation", - "fieldtype": "Link", - "options": "Designation", - "width": 120, - }, - { - "label": _("Reports To"), - "fieldname": "reports_to", - "fieldtype": "Link", - "options": "Employee", - "width": 120, - }, - ] - - -def get_data(filters): - employee = frappe.qb.DocType("Employee") - interview = frappe.qb.DocType("Exit Interview") - fnf = frappe.qb.DocType("Full and Final Statement") - - query = ( - frappe.qb.from_(employee) - .left_join(interview) - .on(interview.employee == employee.name) - .left_join(fnf) - .on(fnf.employee == employee.name) - .select( - employee.name.as_("employee"), - employee.employee_name.as_("employee_name"), - employee.date_of_joining.as_("date_of_joining"), - employee.relieving_date.as_("relieving_date"), - employee.department.as_("department"), - employee.designation.as_("designation"), - employee.reports_to.as_("reports_to"), - interview.name.as_("exit_interview"), - interview.status.as_("interview_status"), - interview.employee_status.as_("employee_status"), - interview.reference_document_name.as_("questionnaire"), - fnf.name.as_("full_and_final_statement"), - ) - .distinct() - .where( - ((employee.relieving_date.isnotnull()) | (employee.relieving_date != "")) - & ((interview.name.isnull()) | ((interview.name.isnotnull()) & (interview.docstatus != 2))) - & ((fnf.name.isnull()) | ((fnf.name.isnotnull()) & (fnf.docstatus != 2))) - ) - .orderby(employee.relieving_date, order=Order.asc) - ) - - query = get_conditions(filters, query, employee, interview, fnf) - result = query.run(as_dict=True) - - return result - - -def get_conditions(filters, query, employee, interview, fnf): - if filters.get("from_date") and filters.get("to_date"): - query = query.where( - employee.relieving_date[getdate(filters.get("from_date")) : getdate(filters.get("to_date"))] - ) - - elif filters.get("from_date"): - query = query.where(employee.relieving_date >= filters.get("from_date")) - - elif filters.get("to_date"): - query = query.where(employee.relieving_date <= filters.get("to_date")) - - if filters.get("company"): - query = query.where(employee.company == filters.get("company")) - - if filters.get("department"): - query = query.where(employee.department == filters.get("department")) - - if filters.get("designation"): - query = query.where(employee.designation == filters.get("designation")) - - if filters.get("employee"): - query = query.where(employee.name == filters.get("employee")) - - if filters.get("reports_to"): - query = query.where(employee.reports_to == filters.get("reports_to")) - - if filters.get("interview_status"): - query = query.where(interview.status == filters.get("interview_status")) - - if filters.get("final_decision"): - query = query.where(interview.employee_status == filters.get("final_decision")) - - if filters.get("exit_interview_pending"): - query = query.where((interview.name == "") | (interview.name.isnull())) - - if filters.get("questionnaire_pending"): - query = query.where( - (interview.reference_document_name == "") | (interview.reference_document_name.isnull()) - ) - - if filters.get("fnf_pending"): - query = query.where((fnf.name == "") | (fnf.name.isnull())) - - return query - - -def get_chart_data(data): - if not data: - return None - - retained = 0 - exit_confirmed = 0 - pending = 0 - - for entry in data: - if entry.employee_status == "Employee Retained": - retained += 1 - elif entry.employee_status == "Exit Confirmed": - exit_confirmed += 1 - else: - pending += 1 - - chart = { - "data": { - "labels": [_("Retained"), _("Exit Confirmed"), _("Decision Pending")], - "datasets": [{"name": _("Employee Status"), "values": [retained, exit_confirmed, pending]}], - }, - "type": "donut", - "colors": ["green", "red", "blue"], - } - - return chart - - -def get_report_summary(data): - if not data: - return None - - total_resignations = len(data) - interviews_pending = len([entry.name for entry in data if not entry.exit_interview]) - fnf_pending = len([entry.name for entry in data if not entry.full_and_final_statement]) - questionnaires_pending = len([entry.name for entry in data if not entry.questionnaire]) - - return [ - { - "value": total_resignations, - "label": _("Total Resignations"), - "indicator": "Red" if total_resignations > 0 else "Green", - "datatype": "Int", - }, - { - "value": interviews_pending, - "label": _("Pending Interviews"), - "indicator": "Blue" if interviews_pending > 0 else "Green", - "datatype": "Int", - }, - { - "value": fnf_pending, - "label": _("Pending FnF"), - "indicator": "Blue" if fnf_pending > 0 else "Green", - "datatype": "Int", - }, - { - "value": questionnaires_pending, - "label": _("Pending Questionnaires"), - "indicator": "Blue" if questionnaires_pending > 0 else "Green", - "datatype": "Int", - }, - ] diff --git a/erpnext/hr/report/employee_exits/test_employee_exits.py b/erpnext/hr/report/employee_exits/test_employee_exits.py deleted file mode 100644 index a9a30de554..0000000000 --- a/erpnext/hr/report/employee_exits/test_employee_exits.py +++ /dev/null @@ -1,245 +0,0 @@ -import unittest - -import frappe -from frappe.utils import add_days, getdate - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.exit_interview.test_exit_interview import create_exit_interview -from erpnext.hr.doctype.full_and_final_statement.test_full_and_final_statement import ( - create_full_and_final_statement, -) -from erpnext.hr.report.employee_exits.employee_exits import execute - - -class TestEmployeeExits(unittest.TestCase): - @classmethod - def setUpClass(cls): - create_company() - frappe.db.sql("delete from `tabEmployee` where company='Test Company'") - frappe.db.sql("delete from `tabFull and Final Statement` where company='Test Company'") - frappe.db.sql("delete from `tabExit Interview` where company='Test Company'") - - cls.create_records() - - @classmethod - def tearDownClass(cls): - frappe.db.rollback() - - @classmethod - def create_records(cls): - cls.emp1 = make_employee( - "employeeexit1@example.com", - company="Test Company", - date_of_joining=getdate("01-10-2021"), - relieving_date=add_days(getdate(), 14), - designation="Accountant", - ) - cls.emp2 = make_employee( - "employeeexit2@example.com", - company="Test Company", - date_of_joining=getdate("01-12-2021"), - relieving_date=add_days(getdate(), 15), - designation="Accountant", - ) - - cls.emp3 = make_employee( - "employeeexit3@example.com", - company="Test Company", - date_of_joining=getdate("02-12-2021"), - relieving_date=add_days(getdate(), 29), - designation="Engineer", - ) - cls.emp4 = make_employee( - "employeeexit4@example.com", - company="Test Company", - date_of_joining=getdate("01-12-2021"), - relieving_date=add_days(getdate(), 30), - designation="Engineer", - ) - - # exit interview for 3 employees only - cls.interview1 = create_exit_interview(cls.emp1) - cls.interview2 = create_exit_interview(cls.emp2) - cls.interview3 = create_exit_interview(cls.emp3) - - # create fnf for some records - cls.fnf1 = create_full_and_final_statement(cls.emp1) - cls.fnf2 = create_full_and_final_statement(cls.emp2) - - # link questionnaire for a few records - # setting employee doctype as reference instead of creating a questionnaire - # since this is just for a test - frappe.db.set_value( - "Exit Interview", - cls.interview1.name, - {"ref_doctype": "Employee", "reference_document_name": cls.emp1}, - ) - - frappe.db.set_value( - "Exit Interview", - cls.interview2.name, - {"ref_doctype": "Employee", "reference_document_name": cls.emp2}, - ) - - frappe.db.set_value( - "Exit Interview", - cls.interview3.name, - {"ref_doctype": "Employee", "reference_document_name": cls.emp3}, - ) - - def test_employee_exits_summary(self): - filters = { - "company": "Test Company", - "from_date": getdate(), - "to_date": add_days(getdate(), 15), - "designation": "Accountant", - } - - report = execute(filters) - - employee1 = frappe.get_doc("Employee", self.emp1) - employee2 = frappe.get_doc("Employee", self.emp2) - expected_data = [ - { - "employee": employee1.name, - "employee_name": employee1.employee_name, - "date_of_joining": employee1.date_of_joining, - "relieving_date": employee1.relieving_date, - "department": employee1.department, - "designation": employee1.designation, - "reports_to": None, - "exit_interview": self.interview1.name, - "interview_status": self.interview1.status, - "employee_status": "", - "questionnaire": employee1.name, - "full_and_final_statement": self.fnf1.name, - }, - { - "employee": employee2.name, - "employee_name": employee2.employee_name, - "date_of_joining": employee2.date_of_joining, - "relieving_date": employee2.relieving_date, - "department": employee2.department, - "designation": employee2.designation, - "reports_to": None, - "exit_interview": self.interview2.name, - "interview_status": self.interview2.status, - "employee_status": "", - "questionnaire": employee2.name, - "full_and_final_statement": self.fnf2.name, - }, - ] - - self.assertEqual(expected_data, report[1]) # rows - - def test_pending_exit_interviews_summary(self): - filters = { - "company": "Test Company", - "from_date": getdate(), - "to_date": add_days(getdate(), 30), - "exit_interview_pending": 1, - } - - report = execute(filters) - - employee4 = frappe.get_doc("Employee", self.emp4) - expected_data = [ - { - "employee": employee4.name, - "employee_name": employee4.employee_name, - "date_of_joining": employee4.date_of_joining, - "relieving_date": employee4.relieving_date, - "department": employee4.department, - "designation": employee4.designation, - "reports_to": None, - "exit_interview": None, - "interview_status": None, - "employee_status": None, - "questionnaire": None, - "full_and_final_statement": None, - } - ] - - self.assertEqual(expected_data, report[1]) # rows - - def test_pending_exit_questionnaire_summary(self): - filters = { - "company": "Test Company", - "from_date": getdate(), - "to_date": add_days(getdate(), 30), - "questionnaire_pending": 1, - } - - report = execute(filters) - - employee4 = frappe.get_doc("Employee", self.emp4) - expected_data = [ - { - "employee": employee4.name, - "employee_name": employee4.employee_name, - "date_of_joining": employee4.date_of_joining, - "relieving_date": employee4.relieving_date, - "department": employee4.department, - "designation": employee4.designation, - "reports_to": None, - "exit_interview": None, - "interview_status": None, - "employee_status": None, - "questionnaire": None, - "full_and_final_statement": None, - } - ] - - self.assertEqual(expected_data, report[1]) # rows - - def test_pending_fnf_summary(self): - filters = {"company": "Test Company", "fnf_pending": 1} - - report = execute(filters) - - employee3 = frappe.get_doc("Employee", self.emp3) - employee4 = frappe.get_doc("Employee", self.emp4) - expected_data = [ - { - "employee": employee3.name, - "employee_name": employee3.employee_name, - "date_of_joining": employee3.date_of_joining, - "relieving_date": employee3.relieving_date, - "department": employee3.department, - "designation": employee3.designation, - "reports_to": None, - "exit_interview": self.interview3.name, - "interview_status": self.interview3.status, - "employee_status": "", - "questionnaire": employee3.name, - "full_and_final_statement": None, - }, - { - "employee": employee4.name, - "employee_name": employee4.employee_name, - "date_of_joining": employee4.date_of_joining, - "relieving_date": employee4.relieving_date, - "department": employee4.department, - "designation": employee4.designation, - "reports_to": None, - "exit_interview": None, - "interview_status": None, - "employee_status": None, - "questionnaire": None, - "full_and_final_statement": None, - }, - ] - - self.assertEqual(expected_data, report[1]) # rows - - -def create_company(): - if not frappe.db.exists("Company", "Test Company"): - frappe.get_doc( - { - "doctype": "Company", - "company_name": "Test Company", - "default_currency": "INR", - "country": "India", - } - ).insert() diff --git a/erpnext/hr/report/employee_information/__init__.py b/erpnext/hr/report/employee_information/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/employee_information/employee_information.json b/erpnext/hr/report/employee_information/employee_information.json deleted file mode 100644 index ee68af3b63..0000000000 --- a/erpnext/hr/report/employee_information/employee_information.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-05-06 18:43:53", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "json": "{\"add_total_row\": 0, \"sort_by\": \"Employee.bank_ac_no\", \"sort_order\": \"desc\", \"sort_by_next\": \"\", \"filters\": [], \"sort_order_next\": \"desc\", \"columns\": [[\"name\", \"Employee\"], [\"employee_number\", \"Employee\"], [\"date_of_joining\", \"Employee\"], [\"branch\", \"Employee\"], [\"department\", \"Employee\"], [\"designation\", \"Employee\"], [\"gender\", \"Employee\"], [\"status\", \"Employee\"], [\"company\", \"Employee\"], [\"employment_type\", \"Employee\"], [\"reports_to\", \"Employee\"], [\"company_email\", \"Employee\"]]}", - "modified": "2017-02-24 20:01:38.681441", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Information", - "owner": "Administrator", - "ref_doctype": "Employee", - "report_name": "Employee Information", - "report_type": "Report Builder", - "roles": [ - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/employee_leave_balance/__init__.py b/erpnext/hr/report/employee_leave_balance/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js deleted file mode 100644 index 2339350d06..0000000000 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.query_reports["Employee Leave Balance"] = { - "filters": [ - { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.defaults.get_default("year_start_date") - }, - { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.defaults.get_default("year_end_date") - }, - { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") - }, - { - "fieldname": "department", - "label": __("Department"), - "fieldtype": "Link", - "options": "Department", - }, - { - "fieldname": "employee", - "label": __("Employee"), - "fieldtype": "Link", - "options": "Employee", - }, - { - "fieldname": "employee_status", - "label": __("Employee Status"), - "fieldtype": "Select", - "options": [ - "", - { "value": "Active", "label": __("Active") }, - { "value": "Inactive", "label": __("Inactive") }, - { "value": "Suspended", "label": __("Suspended") }, - { "value": "Left", "label": __("Left") }, - ], - "default": "Active", - } - ], - - onload: () => { - frappe.call({ - type: "GET", - method: "erpnext.hr.utils.get_leave_period", - args: { - "from_date": frappe.defaults.get_default("year_start_date"), - "to_date": frappe.defaults.get_default("year_end_date"), - "company": frappe.defaults.get_user_default("Company") - }, - freeze: true, - callback: (data) => { - frappe.query_report.set_filter_value("from_date", data.message[0].from_date); - frappe.query_report.set_filter_value("to_date", data.message[0].to_date); - } - }); - } -} diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.json b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.json deleted file mode 100644 index 8b47f7e842..0000000000 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-02-22 15:29:34", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2017-02-24 20:18:04.317397", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Leave Balance", - "owner": "Administrator", - "ref_doctype": "Employee", - "report_name": "Employee Leave Balance", - "report_type": "Script Report", - "roles": [ - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py deleted file mode 100644 index 1f7ade23f4..0000000000 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ /dev/null @@ -1,307 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from itertools import groupby -from typing import Dict, List, Optional, Tuple - -import frappe -from frappe import _ -from frappe.utils import add_days, getdate - -from erpnext.hr.doctype.leave_allocation.leave_allocation import get_previous_allocation -from erpnext.hr.doctype.leave_application.leave_application import ( - get_leave_balance_on, - get_leaves_for_period, -) - -Filters = frappe._dict - - -def execute(filters: Optional[Filters] = None) -> Tuple: - if filters.to_date <= filters.from_date: - frappe.throw(_('"From Date" can not be greater than or equal to "To Date"')) - - columns = get_columns() - data = get_data(filters) - charts = get_chart_data(data) - return columns, data, None, charts - - -def get_columns() -> List[Dict]: - return [ - { - "label": _("Leave Type"), - "fieldtype": "Link", - "fieldname": "leave_type", - "width": 200, - "options": "Leave Type", - }, - { - "label": _("Employee"), - "fieldtype": "Link", - "fieldname": "employee", - "width": 100, - "options": "Employee", - }, - { - "label": _("Employee Name"), - "fieldtype": "Dynamic Link", - "fieldname": "employee_name", - "width": 100, - "options": "employee", - }, - { - "label": _("Opening Balance"), - "fieldtype": "float", - "fieldname": "opening_balance", - "width": 150, - }, - { - "label": _("New Leave(s) Allocated"), - "fieldtype": "float", - "fieldname": "leaves_allocated", - "width": 200, - }, - { - "label": _("Leave(s) Taken"), - "fieldtype": "float", - "fieldname": "leaves_taken", - "width": 150, - }, - { - "label": _("Leave(s) Expired"), - "fieldtype": "float", - "fieldname": "leaves_expired", - "width": 150, - }, - { - "label": _("Closing Balance"), - "fieldtype": "float", - "fieldname": "closing_balance", - "width": 150, - }, - ] - - -def get_data(filters: Filters) -> List: - leave_types = frappe.db.get_list("Leave Type", pluck="name", order_by="name") - conditions = get_conditions(filters) - - user = frappe.session.user - department_approver_map = get_department_leave_approver_map(filters.get("department")) - - active_employees = frappe.get_list( - "Employee", - filters=conditions, - fields=["name", "employee_name", "department", "user_id", "leave_approver"], - ) - - data = [] - - for leave_type in leave_types: - if len(active_employees) > 1: - data.append({"leave_type": leave_type}) - else: - row = frappe._dict({"leave_type": leave_type}) - - for employee in active_employees: - - leave_approvers = department_approver_map.get(employee.department_name, []).append( - employee.leave_approver - ) - - if ( - (leave_approvers and len(leave_approvers) and user in leave_approvers) - or (user in ["Administrator", employee.user_id]) - or ("HR Manager" in frappe.get_roles(user)) - ): - if len(active_employees) > 1: - row = frappe._dict() - row.employee = employee.name - row.employee_name = employee.employee_name - - leaves_taken = ( - get_leaves_for_period(employee.name, leave_type, filters.from_date, filters.to_date) * -1 - ) - - new_allocation, expired_leaves, carry_forwarded_leaves = get_allocated_and_expired_leaves( - filters.from_date, filters.to_date, employee.name, leave_type - ) - opening = get_opening_balance(employee.name, leave_type, filters, carry_forwarded_leaves) - - row.leaves_allocated = new_allocation - row.leaves_expired = expired_leaves - row.opening_balance = opening - row.leaves_taken = leaves_taken - - # not be shown on the basis of days left it create in user mind for carry_forward leave - row.closing_balance = new_allocation + opening - (row.leaves_expired + leaves_taken) - row.indent = 1 - data.append(row) - - return data - - -def get_opening_balance( - employee: str, leave_type: str, filters: Filters, carry_forwarded_leaves: float -) -> float: - # allocation boundary condition - # opening balance is the closing leave balance 1 day before the filter start date - opening_balance_date = add_days(filters.from_date, -1) - allocation = get_previous_allocation(filters.from_date, leave_type, employee) - - if ( - allocation - and allocation.get("to_date") - and opening_balance_date - and getdate(allocation.get("to_date")) == getdate(opening_balance_date) - ): - # if opening balance date is same as the previous allocation's expiry - # then opening balance should only consider carry forwarded leaves - opening_balance = carry_forwarded_leaves - else: - # else directly get leave balance on the previous day - opening_balance = get_leave_balance_on(employee, leave_type, opening_balance_date) - - return opening_balance - - -def get_conditions(filters: Filters) -> Dict: - conditions = {} - - if filters.get("employee"): - conditions["name"] = filters.get("employee") - - if filters.get("company"): - conditions["company"] = filters.get("company") - - if filters.get("department"): - conditions["department"] = filters.get("department") - - if filters.get("employee_status"): - conditions["status"] = filters.get("employee_status") - - return conditions - - -def get_department_leave_approver_map(department: Optional[str] = None): - # get current department and all its child - department_list = frappe.get_list( - "Department", - filters={"disabled": 0}, - or_filters={"name": department, "parent_department": department}, - pluck="name", - ) - # retrieve approvers list from current department and from its subsequent child departments - approver_list = frappe.get_all( - "Department Approver", - filters={"parentfield": "leave_approvers", "parent": ("in", department_list)}, - fields=["parent", "approver"], - as_list=True, - ) - - approvers = {} - - for k, v in approver_list: - approvers.setdefault(k, []).append(v) - - return approvers - - -def get_allocated_and_expired_leaves( - from_date: str, to_date: str, employee: str, leave_type: str -) -> Tuple[float, float, float]: - new_allocation = 0 - expired_leaves = 0 - carry_forwarded_leaves = 0 - - records = get_leave_ledger_entries(from_date, to_date, employee, leave_type) - - for record in records: - # new allocation records with `is_expired=1` are created when leave expires - # these new records should not be considered, else it leads to negative leave balance - if record.is_expired: - continue - - if record.to_date < getdate(to_date): - # leave allocations ending before to_date, reduce leaves taken within that period - # since they are already used, they won't expire - expired_leaves += record.leaves - expired_leaves += get_leaves_for_period(employee, leave_type, record.from_date, record.to_date) - - if record.from_date >= getdate(from_date): - if record.is_carry_forward: - carry_forwarded_leaves += record.leaves - else: - new_allocation += record.leaves - - return new_allocation, expired_leaves, carry_forwarded_leaves - - -def get_leave_ledger_entries( - from_date: str, to_date: str, employee: str, leave_type: str -) -> List[Dict]: - ledger = frappe.qb.DocType("Leave Ledger Entry") - records = ( - frappe.qb.from_(ledger) - .select( - ledger.employee, - ledger.leave_type, - ledger.from_date, - ledger.to_date, - ledger.leaves, - ledger.transaction_name, - ledger.transaction_type, - ledger.is_carry_forward, - ledger.is_expired, - ) - .where( - (ledger.docstatus == 1) - & (ledger.transaction_type == "Leave Allocation") - & (ledger.employee == employee) - & (ledger.leave_type == leave_type) - & ( - (ledger.from_date[from_date:to_date]) - | (ledger.to_date[from_date:to_date]) - | ((ledger.from_date < from_date) & (ledger.to_date > to_date)) - ) - ) - ).run(as_dict=True) - - return records - - -def get_chart_data(data: List) -> Dict: - labels = [] - datasets = [] - employee_data = data - - if data and data[0].get("employee_name"): - get_dataset_for_chart(employee_data, datasets, labels) - - chart = { - "data": {"labels": labels, "datasets": datasets}, - "type": "bar", - "colors": ["#456789", "#EE8888", "#7E77BF"], - } - - return chart - - -def get_dataset_for_chart(employee_data: List, datasets: List, labels: List) -> List: - leaves = [] - employee_data = sorted(employee_data, key=lambda k: k["employee_name"]) - - for key, group in groupby(employee_data, lambda x: x["employee_name"]): - for grp in group: - if grp.closing_balance: - leaves.append( - frappe._dict({"leave_type": grp.leave_type, "closing_balance": grp.closing_balance}) - ) - - if leaves: - labels.append(key) - - for leave in leaves: - datasets.append({"name": leave.leave_type, "values": [leave.closing_balance]}) diff --git a/erpnext/hr/report/employee_leave_balance/test_employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/test_employee_leave_balance.py deleted file mode 100644 index 5354abf4f6..0000000000 --- a/erpnext/hr/report/employee_leave_balance/test_employee_leave_balance.py +++ /dev/null @@ -1,246 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import unittest - -import frappe -from frappe.utils import add_days, add_months, flt, get_year_ending, get_year_start, getdate - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list -from erpnext.hr.doctype.leave_application.test_leave_application import ( - get_first_sunday, - make_allocation_record, -) -from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation -from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type -from erpnext.hr.report.employee_leave_balance.employee_leave_balance import execute -from erpnext.payroll.doctype.salary_slip.test_salary_slip import ( - make_holiday_list, - make_leave_application, -) - -test_records = frappe.get_test_records("Leave Type") - - -class TestEmployeeLeaveBalance(unittest.TestCase): - def setUp(self): - for dt in [ - "Leave Application", - "Leave Allocation", - "Salary Slip", - "Leave Ledger Entry", - "Leave Type", - ]: - frappe.db.delete(dt) - - frappe.set_user("Administrator") - - self.employee_id = make_employee("test_emp_leave_balance@example.com", company="_Test Company") - - self.date = getdate() - self.year_start = getdate(get_year_start(self.date)) - self.mid_year = add_months(self.year_start, 6) - self.year_end = getdate(get_year_ending(self.date)) - - self.holiday_list = make_holiday_list( - "_Test Emp Balance Holiday List", self.year_start, self.year_end - ) - - def tearDown(self): - frappe.db.rollback() - - @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") - def test_employee_leave_balance(self): - frappe.get_doc(test_records[0]).insert() - - # 5 leaves - allocation1 = make_allocation_record( - employee=self.employee_id, - from_date=add_days(self.year_start, -11), - to_date=add_days(self.year_start, -1), - leaves=5, - ) - # 30 leaves - allocation2 = make_allocation_record( - employee=self.employee_id, from_date=self.year_start, to_date=self.year_end - ) - # expires 5 leaves - process_expired_allocation() - - # 4 days leave - first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start) - leave_application = make_leave_application( - self.employee_id, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type" - ) - leave_application.reload() - - filters = frappe._dict( - { - "from_date": allocation1.from_date, - "to_date": allocation2.to_date, - "employee": self.employee_id, - } - ) - - report = execute(filters) - - expected_data = [ - { - "leave_type": "_Test Leave Type", - "employee": self.employee_id, - "employee_name": "test_emp_leave_balance@example.com", - "leaves_allocated": flt(allocation1.new_leaves_allocated + allocation2.new_leaves_allocated), - "leaves_expired": flt(allocation1.new_leaves_allocated), - "opening_balance": flt(0), - "leaves_taken": flt(leave_application.total_leave_days), - "closing_balance": flt(allocation2.new_leaves_allocated - leave_application.total_leave_days), - "indent": 1, - } - ] - - self.assertEqual(report[1], expected_data) - - @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") - def test_opening_balance_on_alloc_boundary_dates(self): - frappe.get_doc(test_records[0]).insert() - - # 30 leaves allocated - allocation1 = make_allocation_record( - employee=self.employee_id, from_date=self.year_start, to_date=self.year_end - ) - # 4 days leave application in the first allocation - first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start) - leave_application = make_leave_application( - self.employee_id, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type" - ) - leave_application.reload() - - # Case 1: opening balance for first alloc boundary - filters = frappe._dict( - {"from_date": self.year_start, "to_date": self.year_end, "employee": self.employee_id} - ) - report = execute(filters) - self.assertEqual(report[1][0].opening_balance, 0) - - # Case 2: opening balance after leave application date - filters = frappe._dict( - { - "from_date": add_days(leave_application.to_date, 1), - "to_date": self.year_end, - "employee": self.employee_id, - } - ) - report = execute(filters) - self.assertEqual( - report[1][0].opening_balance, - (allocation1.new_leaves_allocated - leave_application.total_leave_days), - ) - - # Case 3: leave balance shows actual balance and not consumption balance as per remaining days near alloc end date - # eg: 3 days left for alloc to end, leave balance should still be 26 and not 3 - filters = frappe._dict( - { - "from_date": add_days(self.year_end, -3), - "to_date": self.year_end, - "employee": self.employee_id, - } - ) - report = execute(filters) - self.assertEqual( - report[1][0].opening_balance, - (allocation1.new_leaves_allocated - leave_application.total_leave_days), - ) - - @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") - def test_opening_balance_considers_carry_forwarded_leaves(self): - leave_type = create_leave_type(leave_type_name="_Test_CF_leave_expiry", is_carry_forward=1) - leave_type.insert() - - # 30 leaves allocated for first half of the year - allocation1 = make_allocation_record( - employee=self.employee_id, - from_date=self.year_start, - to_date=self.mid_year, - leave_type=leave_type.name, - ) - # 4 days leave application in the first allocation - first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start) - leave_application = make_leave_application( - self.employee_id, first_sunday, add_days(first_sunday, 3), leave_type.name - ) - leave_application.reload() - # 30 leaves allocated for second half of the year + carry forward leaves (26) from the previous allocation - allocation2 = make_allocation_record( - employee=self.employee_id, - from_date=add_days(self.mid_year, 1), - to_date=self.year_end, - carry_forward=True, - leave_type=leave_type.name, - ) - - # Case 1: carry forwarded leaves considered in opening balance for second alloc - filters = frappe._dict( - { - "from_date": add_days(self.mid_year, 1), - "to_date": self.year_end, - "employee": self.employee_id, - } - ) - report = execute(filters) - # available leaves from old alloc - opening_balance = allocation1.new_leaves_allocated - leave_application.total_leave_days - self.assertEqual(report[1][0].opening_balance, opening_balance) - - # Case 2: opening balance one day after alloc boundary = carry forwarded leaves + new leaves alloc - filters = frappe._dict( - { - "from_date": add_days(self.mid_year, 2), - "to_date": self.year_end, - "employee": self.employee_id, - } - ) - report = execute(filters) - # available leaves from old alloc - opening_balance = allocation2.new_leaves_allocated + ( - allocation1.new_leaves_allocated - leave_application.total_leave_days - ) - self.assertEqual(report[1][0].opening_balance, opening_balance) - - @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") - def test_employee_status_filter(self): - frappe.get_doc(test_records[0]).insert() - inactive_emp = make_employee("test_emp_status@example.com", company="_Test Company") - - allocation = make_allocation_record( - employee=inactive_emp, - from_date=self.year_start, - to_date=self.year_end, - leaves=5, - ) - - # set employee as inactive - frappe.db.set_value("Employee", inactive_emp, "status", "Inactive") - - filters = frappe._dict( - { - "from_date": allocation.from_date, - "to_date": allocation.to_date, - "employee": inactive_emp, - "employee_status": "Active", - } - ) - report = execute(filters) - self.assertEqual(len(report[1]), 0) - - filters = frappe._dict( - { - "from_date": allocation.from_date, - "to_date": allocation.to_date, - "employee": inactive_emp, - "employee_status": "Inactive", - } - ) - report = execute(filters) - self.assertEqual(len(report[1]), 1) diff --git a/erpnext/hr/report/employee_leave_balance_summary/__init__.py b/erpnext/hr/report/employee_leave_balance_summary/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.js b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.js deleted file mode 100644 index 26dd782533..0000000000 --- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.js +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports['Employee Leave Balance Summary'] = { - filters: [ - { - fieldname:'date', - label: __('Date'), - fieldtype: 'Date', - reqd: 1, - default: frappe.datetime.now_date() - }, - { - fieldname:'company', - label: __('Company'), - fieldtype: 'Link', - options: 'Company', - reqd: 1, - default: frappe.defaults.get_user_default('Company') - }, - { - fieldname:'employee', - label: __('Employee'), - fieldtype: 'Link', - options: 'Employee', - }, - { - fieldname:'department', - label: __('Department'), - fieldtype: 'Link', - options: 'Department', - }, - { - fieldname: "employee_status", - label: __("Employee Status"), - fieldtype: "Select", - options: [ - "", - { "value": "Active", "label": __("Active") }, - { "value": "Inactive", "label": __("Inactive") }, - { "value": "Suspended", "label": __("Suspended") }, - { "value": "Left", "label": __("Left") }, - ], - default: "Active", - } - ] -}; diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.json b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.json deleted file mode 100644 index 1c22ecef1a..0000000000 --- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2019-09-05 11:18:06.209397", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2019-09-06 11:18:06.209397", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Leave Balance Summary", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Employee", - "report_name": "Employee Leave Balance Summary", - "report_type": "Script Report", - "roles": [ - { - "role": "Employee" - }, - { - "role": "HR Manager" - }, - { - "role": "HR User" - }, - { - "role": "Leave Approver" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py deleted file mode 100644 index 82878dbebb..0000000000 --- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe import _ - -from erpnext.hr.doctype.leave_application.leave_application import get_leave_details -from erpnext.hr.report.employee_leave_balance.employee_leave_balance import ( - get_department_leave_approver_map, -) - - -def execute(filters=None): - leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc") - - columns = get_columns(leave_types) - data = get_data(filters, leave_types) - - return columns, data - - -def get_columns(leave_types): - columns = [ - _("Employee") + ":Link.Employee:150", - _("Employee Name") + "::200", - _("Department") + "::150", - ] - - for leave_type in leave_types: - columns.append(_(leave_type) + ":Float:160") - - return columns - - -def get_conditions(filters): - conditions = { - "company": filters.company, - } - if filters.get("employee_status"): - conditions.update({"status": filters.get("employee_status")}) - if filters.get("department"): - conditions.update({"department": filters.get("department")}) - if filters.get("employee"): - conditions.update({"employee": filters.get("employee")}) - - return conditions - - -def get_data(filters, leave_types): - user = frappe.session.user - conditions = get_conditions(filters) - - active_employees = frappe.get_list( - "Employee", - filters=conditions, - fields=["name", "employee_name", "department", "user_id", "leave_approver"], - ) - - department_approver_map = get_department_leave_approver_map(filters.get("department")) - - data = [] - for employee in active_employees: - leave_approvers = department_approver_map.get(employee.department_name, []) - if employee.leave_approver: - leave_approvers.append(employee.leave_approver) - - if ( - (len(leave_approvers) and user in leave_approvers) - or (user in ["Administrator", employee.user_id]) - or ("HR Manager" in frappe.get_roles(user)) - ): - row = [employee.name, employee.employee_name, employee.department] - available_leave = get_leave_details(employee.name, filters.date) - for leave_type in leave_types: - remaining = 0 - if leave_type in available_leave["leave_allocation"]: - # opening balance - remaining = available_leave["leave_allocation"][leave_type]["remaining_leaves"] - - row += [remaining] - - data.append(row) - - return data diff --git a/erpnext/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py deleted file mode 100644 index 2fd74b7983..0000000000 --- a/erpnext/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import unittest - -import frappe -from frappe.utils import add_days, flt, get_year_ending, get_year_start, getdate - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list -from erpnext.hr.doctype.leave_application.test_leave_application import ( - get_first_sunday, - make_allocation_record, -) -from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation -from erpnext.hr.report.employee_leave_balance_summary.employee_leave_balance_summary import execute -from erpnext.payroll.doctype.salary_slip.test_salary_slip import ( - make_holiday_list, - make_leave_application, -) - -test_records = frappe.get_test_records("Leave Type") - - -class TestEmployeeLeaveBalance(unittest.TestCase): - def setUp(self): - for dt in [ - "Leave Application", - "Leave Allocation", - "Salary Slip", - "Leave Ledger Entry", - "Leave Type", - ]: - frappe.db.delete(dt) - - frappe.set_user("Administrator") - - self.employee_id = make_employee("test_emp_leave_balance@example.com", company="_Test Company") - - self.date = getdate() - self.year_start = getdate(get_year_start(self.date)) - self.year_end = getdate(get_year_ending(self.date)) - - self.holiday_list = make_holiday_list( - "_Test Emp Balance Holiday List", self.year_start, self.year_end - ) - - def tearDown(self): - frappe.db.rollback() - - @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") - def test_employee_leave_balance_summary(self): - frappe.get_doc(test_records[0]).insert() - - # 5 leaves - allocation1 = make_allocation_record( - employee=self.employee_id, - from_date=add_days(self.year_start, -11), - to_date=add_days(self.year_start, -1), - leaves=5, - ) - # 30 leaves - allocation2 = make_allocation_record( - employee=self.employee_id, from_date=self.year_start, to_date=self.year_end - ) - - # 2 days leave within the first allocation - leave_application1 = make_leave_application( - self.employee_id, - add_days(self.year_start, -11), - add_days(self.year_start, -10), - "_Test Leave Type", - ) - leave_application1.reload() - - # expires 3 leaves - process_expired_allocation() - - # 4 days leave within the second allocation - first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start) - leave_application2 = make_leave_application( - self.employee_id, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type" - ) - leave_application2.reload() - - filters = frappe._dict( - { - "date": add_days(leave_application2.to_date, 1), - "company": "_Test Company", - "employee": self.employee_id, - } - ) - - report = execute(filters) - - expected_data = [ - [ - self.employee_id, - "test_emp_leave_balance@example.com", - frappe.db.get_value("Employee", self.employee_id, "department"), - flt( - allocation1.new_leaves_allocated # allocated = 5 - + allocation2.new_leaves_allocated # allocated = 30 - - leave_application1.total_leave_days # leaves taken in the 1st alloc = 2 - - ( - allocation1.new_leaves_allocated - leave_application1.total_leave_days - ) # leaves expired from 1st alloc = 3 - - leave_application2.total_leave_days # leaves taken in the 2nd alloc = 4 - ), - ] - ] - - self.assertEqual(report[1], expected_data) - - @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") - def test_get_leave_balance_near_alloc_expiry(self): - frappe.get_doc(test_records[0]).insert() - - # 30 leaves allocated - allocation = make_allocation_record( - employee=self.employee_id, from_date=self.year_start, to_date=self.year_end - ) - # 4 days leave application in the first allocation - first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start) - leave_application = make_leave_application( - self.employee_id, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type" - ) - leave_application.reload() - - # Leave balance should show actual balance, and not "consumption balance as per remaining days", near alloc end date - # eg: 3 days left for alloc to end, leave balance should still be 26 and not 3 - filters = frappe._dict( - {"date": add_days(self.year_end, -3), "company": "_Test Company", "employee": self.employee_id} - ) - report = execute(filters) - - expected_data = [ - [ - self.employee_id, - "test_emp_leave_balance@example.com", - frappe.db.get_value("Employee", self.employee_id, "department"), - flt(allocation.new_leaves_allocated - leave_application.total_leave_days), - ] - ] - - self.assertEqual(report[1], expected_data) - - @set_holiday_list("_Test Emp Balance Holiday List", "_Test Company") - def test_employee_status_filter(self): - frappe.get_doc(test_records[0]).insert() - - inactive_emp = make_employee("test_emp_status@example.com", company="_Test Company") - allocation = make_allocation_record( - employee=inactive_emp, from_date=self.year_start, to_date=self.year_end - ) - - # set employee as inactive - frappe.db.set_value("Employee", inactive_emp, "status", "Inactive") - - filters = frappe._dict( - { - "date": allocation.from_date, - "company": "_Test Company", - "employee": inactive_emp, - "employee_status": "Active", - } - ) - report = execute(filters) - self.assertEqual(len(report[1]), 0) - - filters = frappe._dict( - { - "date": allocation.from_date, - "company": "_Test Company", - "employee": inactive_emp, - "employee_status": "Inactive", - } - ) - report = execute(filters) - self.assertEqual(len(report[1]), 1) diff --git a/erpnext/hr/report/employees_working_on_a_holiday/__init__.py b/erpnext/hr/report/employees_working_on_a_holiday/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.js b/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.js deleted file mode 100644 index 97108e8575..0000000000 --- a/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.js +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.query_reports["Employees working on a holiday"] = { - "filters": [ - { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.year_start() - }, - { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.year_end() - }, - { - "fieldname":"holiday_list", - "label": __("Holiday List"), - "fieldtype": "Link", - "options": "Holiday List" - } - ] -} diff --git a/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.json b/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.json deleted file mode 100644 index 6c9f7ff7e6..0000000000 --- a/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2016-07-14 12:03:56.967739", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 2, - "is_standard": "Yes", - "modified": "2017-02-24 20:05:17.833885", - "modified_by": "Administrator", - "module": "HR", - "name": "Employees working on a holiday", - "owner": "Administrator", - "ref_doctype": "Attendance", - "report_name": "Employees working on a holiday", - "report_type": "Script Report", - "roles": [ - { - "role": "System Manager" - }, - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py b/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py deleted file mode 100644 index f13fabf06e..0000000000 --- a/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ - - -def execute(filters=None): - if not filters: - filters = {} - - columns = get_columns() - data = get_employees(filters) - return columns, data - - -def get_columns(): - return [ - _("Employee") + ":Link/Employee:120", - _("Name") + ":Data:200", - _("Date") + ":Date:100", - _("Status") + ":Data:70", - _("Holiday") + ":Data:200", - ] - - -def get_employees(filters): - holiday_filter = [ - ["holiday_date", ">=", filters.from_date], - ["holiday_date", "<=", filters.to_date], - ] - if filters.holiday_list: - holiday_filter.append(["parent", "=", filters.holiday_list]) - - holidays = frappe.get_all( - "Holiday", fields=["holiday_date", "description"], filters=holiday_filter - ) - - holiday_names = {} - holidays_list = [] - - for holiday in holidays: - holidays_list.append(holiday.holiday_date) - holiday_names[holiday.holiday_date] = holiday.description - - if holidays_list: - cond = " attendance_date in %(holidays_list)s" - - if filters.holiday_list: - cond += ( - """ and (employee in (select employee from tabEmployee where holiday_list = %(holidays)s))""" - ) - - employee_list = frappe.db.sql( - """select - employee, employee_name, attendance_date, status - from tabAttendance - where %s""" - % cond.format(", ".join(["%s"] * len(holidays_list))), - {"holidays_list": holidays_list, "holidays": filters.holiday_list}, - as_list=True, - ) - - for employee_data in employee_list: - employee_data.append(holiday_names[employee_data[2]]) - - return employee_list - else: - return [] diff --git a/erpnext/hr/report/monthly_attendance_sheet/__init__.py b/erpnext/hr/report/monthly_attendance_sheet/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js deleted file mode 100644 index 6f4bbd54fb..0000000000 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - - -frappe.query_reports["Monthly Attendance Sheet"] = { - "filters": [ - { - "fieldname": "month", - "label": __("Month"), - "fieldtype": "Select", - "reqd": 1 , - "options": [ - { "value": 1, "label": __("Jan") }, - { "value": 2, "label": __("Feb") }, - { "value": 3, "label": __("Mar") }, - { "value": 4, "label": __("Apr") }, - { "value": 5, "label": __("May") }, - { "value": 6, "label": __("June") }, - { "value": 7, "label": __("July") }, - { "value": 8, "label": __("Aug") }, - { "value": 9, "label": __("Sep") }, - { "value": 10, "label": __("Oct") }, - { "value": 11, "label": __("Nov") }, - { "value": 12, "label": __("Dec") }, - ], - "default": frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth() + 1 - }, - { - "fieldname":"year", - "label": __("Year"), - "fieldtype": "Select", - "reqd": 1 - }, - { - "fieldname":"employee", - "label": __("Employee"), - "fieldtype": "Link", - "options": "Employee", - get_query: () => { - var company = frappe.query_report.get_filter_value('company'); - return { - filters: { - 'company': company - } - }; - } - }, - { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 - }, - { - "fieldname":"group_by", - "label": __("Group By"), - "fieldtype": "Select", - "options": ["","Branch","Grade","Department","Designation"] - }, - { - "fieldname":"summarized_view", - "label": __("Summarized View"), - "fieldtype": "Check", - "Default": 0, - } - ], - onload: function() { - return frappe.call({ - method: "erpnext.hr.report.monthly_attendance_sheet.monthly_attendance_sheet.get_attendance_years", - callback: function(r) { - var year_filter = frappe.query_report.get_filter('year'); - year_filter.df.options = r.message; - year_filter.df.default = r.message.split("\n")[0]; - year_filter.refresh(); - year_filter.set_input(year_filter.df.default); - } - }); - }, - formatter: function(value, row, column, data, default_formatter) { - value = default_formatter(value, row, column, data); - const summarized_view = frappe.query_report.get_filter_value('summarized_view'); - const group_by = frappe.query_report.get_filter_value('group_by'); - - if (!summarized_view) { - if ((group_by && column.colIndex > 3) || (!group_by && column.colIndex > 2)) { - if (value == 'P' || value == 'WFH') - value = "" + value + ""; - else if (value == 'A') - value = "" + value + ""; - else if (value == 'HD') - value = "" + value + ""; - else if (value == 'L') - value = "" + value + ""; - } - } - - return value; - } -} diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.json b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.json deleted file mode 100644 index 4daab813fe..0000000000 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-05-13 14:04:03", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2017-02-24 20:16:50.550242", - "modified_by": "Administrator", - "module": "HR", - "name": "Monthly Attendance Sheet", - "owner": "Administrator", - "ref_doctype": "Attendance", - "report_name": "Monthly Attendance Sheet", - "report_type": "Script Report", - "roles": [ - { - "role": "System Manager" - }, - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py deleted file mode 100644 index efd2d382d5..0000000000 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ /dev/null @@ -1,620 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -from calendar import monthrange -from itertools import groupby -from typing import Dict, List, Optional, Tuple - -import frappe -from frappe import _ -from frappe.query_builder.functions import Count, Extract, Sum -from frappe.utils import cint, cstr, getdate - -Filters = frappe._dict - -status_map = { - "Present": "P", - "Absent": "A", - "Half Day": "HD", - "Work From Home": "WFH", - "On Leave": "L", - "Holiday": "H", - "Weekly Off": "WO", -} - -day_abbr = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] - - -def execute(filters: Optional[Filters] = None) -> Tuple: - filters = frappe._dict(filters or {}) - - if not (filters.month and filters.year): - frappe.throw(_("Please select month and year.")) - - attendance_map = get_attendance_map(filters) - if not attendance_map: - frappe.msgprint(_("No attendance records found."), alert=True, indicator="orange") - return [], [], None, None - - columns = get_columns(filters) - data = get_data(filters, attendance_map) - - if not data: - frappe.msgprint( - _("No attendance records found for this criteria."), alert=True, indicator="orange" - ) - return columns, [], None, None - - message = get_message() if not filters.summarized_view else "" - chart = get_chart_data(attendance_map, filters) - - return columns, data, message, chart - - -def get_message() -> str: - message = "" - colors = ["green", "red", "orange", "green", "#318AD8", "", ""] - - count = 0 - for status, abbr in status_map.items(): - message += f""" - - {status} - {abbr} - - """ - count += 1 - - return message - - -def get_columns(filters: Filters) -> List[Dict]: - columns = [] - - if filters.group_by: - columns.append( - { - "label": _(filters.group_by), - "fieldname": frappe.scrub(filters.group_by), - "fieldtype": "Link", - "options": "Branch", - "width": 120, - } - ) - - columns.extend( - [ - { - "label": _("Employee"), - "fieldname": "employee", - "fieldtype": "Link", - "options": "Employee", - "width": 135, - }, - {"label": _("Employee Name"), "fieldname": "employee_name", "fieldtype": "Data", "width": 120}, - ] - ) - - if filters.summarized_view: - columns.extend( - [ - { - "label": _("Total Present"), - "fieldname": "total_present", - "fieldtype": "Float", - "width": 110, - }, - {"label": _("Total Leaves"), "fieldname": "total_leaves", "fieldtype": "Float", "width": 110}, - {"label": _("Total Absent"), "fieldname": "total_absent", "fieldtype": "Float", "width": 110}, - { - "label": _("Total Holidays"), - "fieldname": "total_holidays", - "fieldtype": "Float", - "width": 120, - }, - { - "label": _("Unmarked Days"), - "fieldname": "unmarked_days", - "fieldtype": "Float", - "width": 130, - }, - ] - ) - columns.extend(get_columns_for_leave_types()) - columns.extend( - [ - { - "label": _("Total Late Entries"), - "fieldname": "total_late_entries", - "fieldtype": "Float", - "width": 140, - }, - { - "label": _("Total Early Exits"), - "fieldname": "total_early_exits", - "fieldtype": "Float", - "width": 140, - }, - ] - ) - else: - columns.append({"label": _("Shift"), "fieldname": "shift", "fieldtype": "Data", "width": 120}) - columns.extend(get_columns_for_days(filters)) - - return columns - - -def get_columns_for_leave_types() -> List[Dict]: - leave_types = frappe.db.get_all("Leave Type", pluck="name") - types = [] - for entry in leave_types: - types.append( - {"label": entry, "fieldname": frappe.scrub(entry), "fieldtype": "Float", "width": 120} - ) - - return types - - -def get_columns_for_days(filters: Filters) -> List[Dict]: - total_days = get_total_days_in_month(filters) - days = [] - - for day in range(1, total_days + 1): - # forms the dates from selected year and month from filters - date = "{}-{}-{}".format(cstr(filters.year), cstr(filters.month), cstr(day)) - # gets abbr from weekday number - weekday = day_abbr[getdate(date).weekday()] - # sets days as 1 Mon, 2 Tue, 3 Wed - label = "{} {}".format(cstr(day), weekday) - days.append({"label": label, "fieldtype": "Data", "fieldname": day, "width": 65}) - - return days - - -def get_total_days_in_month(filters: Filters) -> int: - return monthrange(cint(filters.year), cint(filters.month))[1] - - -def get_data(filters: Filters, attendance_map: Dict) -> List[Dict]: - employee_details, group_by_param_values = get_employee_related_details( - filters.group_by, filters.company - ) - holiday_map = get_holiday_map(filters) - data = [] - - if filters.group_by: - group_by_column = frappe.scrub(filters.group_by) - - for value in group_by_param_values: - if not value: - continue - - records = get_rows(employee_details[value], filters, holiday_map, attendance_map) - - if records: - data.append({group_by_column: frappe.bold(value)}) - data.extend(records) - else: - data = get_rows(employee_details, filters, holiday_map, attendance_map) - - return data - - -def get_attendance_map(filters: Filters) -> Dict: - """Returns a dictionary of employee wise attendance map as per shifts for all the days of the month like - { - 'employee1': { - 'Morning Shift': {1: 'Present', 2: 'Absent', ...} - 'Evening Shift': {1: 'Absent', 2: 'Present', ...} - }, - 'employee2': { - 'Afternoon Shift': {1: 'Present', 2: 'Absent', ...} - 'Night Shift': {1: 'Absent', 2: 'Absent', ...} - } - } - """ - Attendance = frappe.qb.DocType("Attendance") - query = ( - frappe.qb.from_(Attendance) - .select( - Attendance.employee, - Extract("day", Attendance.attendance_date).as_("day_of_month"), - Attendance.status, - Attendance.shift, - ) - .where( - (Attendance.docstatus == 1) - & (Attendance.company == filters.company) - & (Extract("month", Attendance.attendance_date) == filters.month) - & (Extract("year", Attendance.attendance_date) == filters.year) - ) - ) - if filters.employee: - query = query.where(Attendance.employee == filters.employee) - query = query.orderby(Attendance.employee, Attendance.attendance_date) - - attendance_list = query.run(as_dict=1) - attendance_map = {} - - for d in attendance_list: - attendance_map.setdefault(d.employee, frappe._dict()).setdefault(d.shift, frappe._dict()) - attendance_map[d.employee][d.shift][d.day_of_month] = d.status - - return attendance_map - - -def get_employee_related_details(group_by: str, company: str) -> Tuple[Dict, List]: - """Returns - 1. nested dict for employee details - 2. list of values for the group by filter - """ - Employee = frappe.qb.DocType("Employee") - query = ( - frappe.qb.from_(Employee) - .select( - Employee.name, - Employee.employee_name, - Employee.designation, - Employee.grade, - Employee.department, - Employee.branch, - Employee.company, - Employee.holiday_list, - ) - .where(Employee.company == company) - ) - - if group_by: - group_by = group_by.lower() - query = query.orderby(group_by) - - employee_details = query.run(as_dict=True) - - group_by_param_values = [] - emp_map = {} - - if group_by: - for parameter, employees in groupby(employee_details, key=lambda d: d[group_by]): - group_by_param_values.append(parameter) - emp_map.setdefault(parameter, frappe._dict()) - - for emp in employees: - emp_map[parameter][emp.name] = emp - else: - for emp in employee_details: - emp_map[emp.name] = emp - - return emp_map, group_by_param_values - - -def get_holiday_map(filters: Filters) -> Dict[str, List[Dict]]: - """ - Returns a dict of holidays falling in the filter month and year - with list name as key and list of holidays as values like - { - 'Holiday List 1': [ - {'day_of_month': '0' , 'weekly_off': 1}, - {'day_of_month': '1', 'weekly_off': 0} - ], - 'Holiday List 2': [ - {'day_of_month': '0' , 'weekly_off': 1}, - {'day_of_month': '1', 'weekly_off': 0} - ] - } - """ - # add default holiday list too - holiday_lists = frappe.db.get_all("Holiday List", pluck="name") - default_holiday_list = frappe.get_cached_value("Company", filters.company, "default_holiday_list") - holiday_lists.append(default_holiday_list) - - holiday_map = frappe._dict() - Holiday = frappe.qb.DocType("Holiday") - - for d in holiday_lists: - if not d: - continue - - holidays = ( - frappe.qb.from_(Holiday) - .select(Extract("day", Holiday.holiday_date).as_("day_of_month"), Holiday.weekly_off) - .where( - (Holiday.parent == d) - & (Extract("month", Holiday.holiday_date) == filters.month) - & (Extract("year", Holiday.holiday_date) == filters.year) - ) - ).run(as_dict=True) - - holiday_map.setdefault(d, holidays) - - return holiday_map - - -def get_rows( - employee_details: Dict, filters: Filters, holiday_map: Dict, attendance_map: Dict -) -> List[Dict]: - records = [] - default_holiday_list = frappe.get_cached_value("Company", filters.company, "default_holiday_list") - - for employee, details in employee_details.items(): - emp_holiday_list = details.holiday_list or default_holiday_list - holidays = holiday_map.get(emp_holiday_list) - - if filters.summarized_view: - attendance = get_attendance_status_for_summarized_view(employee, filters, holidays) - if not attendance: - continue - - leave_summary = get_leave_summary(employee, filters) - entry_exits_summary = get_entry_exits_summary(employee, filters) - - row = {"employee": employee, "employee_name": details.employee_name} - set_defaults_for_summarized_view(filters, row) - row.update(attendance) - row.update(leave_summary) - row.update(entry_exits_summary) - - records.append(row) - else: - employee_attendance = attendance_map.get(employee) - if not employee_attendance: - continue - - attendance_for_employee = get_attendance_status_for_detailed_view( - employee, filters, employee_attendance, holidays - ) - # set employee details in the first row - attendance_for_employee[0].update( - {"employee": employee, "employee_name": details.employee_name} - ) - - records.extend(attendance_for_employee) - - return records - - -def set_defaults_for_summarized_view(filters, row): - for entry in get_columns(filters): - if entry.get("fieldtype") == "Float": - row[entry.get("fieldname")] = 0.0 - - -def get_attendance_status_for_summarized_view( - employee: str, filters: Filters, holidays: List -) -> Dict: - """Returns dict of attendance status for employee like - {'total_present': 1.5, 'total_leaves': 0.5, 'total_absent': 13.5, 'total_holidays': 8, 'unmarked_days': 5} - """ - summary, attendance_days = get_attendance_summary_and_days(employee, filters) - if not any(summary.values()): - return {} - - total_days = get_total_days_in_month(filters) - total_holidays = total_unmarked_days = 0 - - for day in range(1, total_days + 1): - if day in attendance_days: - continue - - status = get_holiday_status(day, holidays) - if status in ["Weekly Off", "Holiday"]: - total_holidays += 1 - elif not status: - total_unmarked_days += 1 - - return { - "total_present": summary.total_present + summary.total_half_days, - "total_leaves": summary.total_leaves + summary.total_half_days, - "total_absent": summary.total_absent + summary.total_half_days, - "total_holidays": total_holidays, - "unmarked_days": total_unmarked_days, - } - - -def get_attendance_summary_and_days(employee: str, filters: Filters) -> Tuple[Dict, List]: - Attendance = frappe.qb.DocType("Attendance") - - present_case = ( - frappe.qb.terms.Case() - .when(((Attendance.status == "Present") | (Attendance.status == "Work From Home")), 1) - .else_(0) - ) - sum_present = Sum(present_case).as_("total_present") - - absent_case = frappe.qb.terms.Case().when(Attendance.status == "Absent", 1).else_(0) - sum_absent = Sum(absent_case).as_("total_absent") - - leave_case = frappe.qb.terms.Case().when(Attendance.status == "On Leave", 1).else_(0) - sum_leave = Sum(leave_case).as_("total_leaves") - - half_day_case = frappe.qb.terms.Case().when(Attendance.status == "Half Day", 0.5).else_(0) - sum_half_day = Sum(half_day_case).as_("total_half_days") - - summary = ( - frappe.qb.from_(Attendance) - .select( - sum_present, - sum_absent, - sum_leave, - sum_half_day, - ) - .where( - (Attendance.docstatus == 1) - & (Attendance.employee == employee) - & (Attendance.company == filters.company) - & (Extract("month", Attendance.attendance_date) == filters.month) - & (Extract("year", Attendance.attendance_date) == filters.year) - ) - ).run(as_dict=True) - - days = ( - frappe.qb.from_(Attendance) - .select(Extract("day", Attendance.attendance_date).as_("day_of_month")) - .distinct() - .where( - (Attendance.docstatus == 1) - & (Attendance.employee == employee) - & (Attendance.company == filters.company) - & (Extract("month", Attendance.attendance_date) == filters.month) - & (Extract("year", Attendance.attendance_date) == filters.year) - ) - ).run(pluck=True) - - return summary[0], days - - -def get_attendance_status_for_detailed_view( - employee: str, filters: Filters, employee_attendance: Dict, holidays: List -) -> List[Dict]: - """Returns list of shift-wise attendance status for employee - [ - {'shift': 'Morning Shift', 1: 'A', 2: 'P', 3: 'A'....}, - {'shift': 'Evening Shift', 1: 'P', 2: 'A', 3: 'P'....} - ] - """ - total_days = get_total_days_in_month(filters) - attendance_values = [] - - for shift, status_dict in employee_attendance.items(): - row = {"shift": shift} - - for day in range(1, total_days + 1): - status = status_dict.get(day) - if status is None and holidays: - status = get_holiday_status(day, holidays) - - abbr = status_map.get(status, "") - row[day] = abbr - - attendance_values.append(row) - - return attendance_values - - -def get_holiday_status(day: int, holidays: List) -> str: - status = None - for holiday in holidays: - if day == holiday.get("day_of_month"): - if holiday.get("weekly_off"): - status = "Weekly Off" - else: - status = "Holiday" - break - return status - - -def get_leave_summary(employee: str, filters: Filters) -> Dict[str, float]: - """Returns a dict of leave type and corresponding leaves taken by employee like: - {'leave_without_pay': 1.0, 'sick_leave': 2.0} - """ - Attendance = frappe.qb.DocType("Attendance") - day_case = frappe.qb.terms.Case().when(Attendance.status == "Half Day", 0.5).else_(1) - sum_leave_days = Sum(day_case).as_("leave_days") - - leave_details = ( - frappe.qb.from_(Attendance) - .select(Attendance.leave_type, sum_leave_days) - .where( - (Attendance.employee == employee) - & (Attendance.docstatus == 1) - & (Attendance.company == filters.company) - & ((Attendance.leave_type.isnotnull()) | (Attendance.leave_type != "")) - & (Extract("month", Attendance.attendance_date) == filters.month) - & (Extract("year", Attendance.attendance_date) == filters.year) - ) - .groupby(Attendance.leave_type) - ).run(as_dict=True) - - leaves = {} - for d in leave_details: - leave_type = frappe.scrub(d.leave_type) - leaves[leave_type] = d.leave_days - - return leaves - - -def get_entry_exits_summary(employee: str, filters: Filters) -> Dict[str, float]: - """Returns total late entries and total early exits for employee like: - {'total_late_entries': 5, 'total_early_exits': 2} - """ - Attendance = frappe.qb.DocType("Attendance") - - late_entry_case = frappe.qb.terms.Case().when(Attendance.late_entry == "1", "1") - count_late_entries = Count(late_entry_case).as_("total_late_entries") - - early_exit_case = frappe.qb.terms.Case().when(Attendance.early_exit == "1", "1") - count_early_exits = Count(early_exit_case).as_("total_early_exits") - - entry_exits = ( - frappe.qb.from_(Attendance) - .select(count_late_entries, count_early_exits) - .where( - (Attendance.docstatus == 1) - & (Attendance.employee == employee) - & (Attendance.company == filters.company) - & (Extract("month", Attendance.attendance_date) == filters.month) - & (Extract("year", Attendance.attendance_date) == filters.year) - ) - ).run(as_dict=True) - - return entry_exits[0] - - -@frappe.whitelist() -def get_attendance_years() -> str: - """Returns all the years for which attendance records exist""" - Attendance = frappe.qb.DocType("Attendance") - year_list = ( - frappe.qb.from_(Attendance) - .select(Extract("year", Attendance.attendance_date).as_("year")) - .distinct() - ).run(as_dict=True) - - if year_list: - year_list.sort(key=lambda d: d.year, reverse=True) - else: - year_list = [getdate().year] - - return "\n".join(cstr(entry.year) for entry in year_list) - - -def get_chart_data(attendance_map: Dict, filters: Filters) -> Dict: - days = get_columns_for_days(filters) - labels = [] - absent = [] - present = [] - leave = [] - - for day in days: - labels.append(day["label"]) - total_absent_on_day = total_leaves_on_day = total_present_on_day = 0 - - for employee, attendance_dict in attendance_map.items(): - for shift, attendance in attendance_dict.items(): - attendance_on_day = attendance.get(day["fieldname"]) - - if attendance_on_day == "Absent": - total_absent_on_day += 1 - elif attendance_on_day in ["Present", "Work From Home"]: - total_present_on_day += 1 - elif attendance_on_day == "Half Day": - total_present_on_day += 0.5 - total_leaves_on_day += 0.5 - elif attendance_on_day == "On Leave": - total_leaves_on_day += 1 - - absent.append(total_absent_on_day) - present.append(total_present_on_day) - leave.append(total_leaves_on_day) - - return { - "data": { - "labels": labels, - "datasets": [ - {"name": "Absent", "values": absent}, - {"name": "Present", "values": present}, - {"name": "Leave", "values": leave}, - ], - }, - "type": "line", - "colors": ["red", "green", "blue"], - } diff --git a/erpnext/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py deleted file mode 100644 index cde7dd3fff..0000000000 --- a/erpnext/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py +++ /dev/null @@ -1,249 +0,0 @@ -import frappe -from dateutil.relativedelta import relativedelta -from frappe.tests.utils import FrappeTestCase -from frappe.utils import get_year_ending, get_year_start, getdate, now_datetime - -from erpnext.hr.doctype.attendance.attendance import mark_attendance -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list -from erpnext.hr.doctype.leave_application.test_leave_application import make_allocation_record -from erpnext.hr.report.monthly_attendance_sheet.monthly_attendance_sheet import execute -from erpnext.payroll.doctype.salary_slip.test_salary_slip import ( - make_holiday_list, - make_leave_application, -) - -test_dependencies = ["Shift Type"] - - -class TestMonthlyAttendanceSheet(FrappeTestCase): - def setUp(self): - self.employee = make_employee("test_employee@example.com", company="_Test Company") - frappe.db.delete("Attendance") - - date = getdate() - from_date = get_year_start(date) - to_date = get_year_ending(date) - make_holiday_list(from_date=from_date, to_date=to_date) - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_monthly_attendance_sheet_report(self): - now = now_datetime() - previous_month = now.month - 1 - previous_month_first = now.replace(day=1).replace(month=previous_month).date() - - company = frappe.db.get_value("Employee", self.employee, "company") - - # mark different attendance status on first 3 days of previous month - mark_attendance(self.employee, previous_month_first, "Absent") - mark_attendance(self.employee, previous_month_first + relativedelta(days=1), "Present") - mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave") - - filters = frappe._dict( - { - "month": previous_month, - "year": now.year, - "company": company, - } - ) - report = execute(filters=filters) - - record = report[1][0] - datasets = report[3]["data"]["datasets"] - absent = datasets[0]["values"] - present = datasets[1]["values"] - leaves = datasets[2]["values"] - - # ensure correct attendance is reflected on the report - self.assertEqual(self.employee, record.get("employee")) - self.assertEqual(absent[0], 1) - self.assertEqual(present[1], 1) - self.assertEqual(leaves[2], 1) - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_monthly_attendance_sheet_with_detailed_view(self): - now = now_datetime() - previous_month = now.month - 1 - previous_month_first = now.replace(day=1).replace(month=previous_month).date() - - company = frappe.db.get_value("Employee", self.employee, "company") - - # attendance with shift - mark_attendance(self.employee, previous_month_first, "Absent", "Day Shift") - mark_attendance( - self.employee, previous_month_first + relativedelta(days=1), "Present", "Day Shift" - ) - - # attendance without shift - mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave") - mark_attendance(self.employee, previous_month_first + relativedelta(days=3), "Present") - - filters = frappe._dict( - { - "month": previous_month, - "year": now.year, - "company": company, - } - ) - report = execute(filters=filters) - - day_shift_row = report[1][0] - row_without_shift = report[1][1] - - self.assertEqual(day_shift_row["shift"], "Day Shift") - self.assertEqual(day_shift_row[1], "A") # absent on the 1st day of the month - self.assertEqual(day_shift_row[2], "P") # present on the 2nd day - - self.assertEqual(row_without_shift["shift"], None) - self.assertEqual(row_without_shift[3], "L") # on leave on the 3rd day - self.assertEqual(row_without_shift[4], "P") # present on the 4th day - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_monthly_attendance_sheet_with_summarized_view(self): - now = now_datetime() - previous_month = now.month - 1 - previous_month_first = now.replace(day=1).replace(month=previous_month).date() - - company = frappe.db.get_value("Employee", self.employee, "company") - - # attendance with shift - mark_attendance(self.employee, previous_month_first, "Absent", "Day Shift") - mark_attendance( - self.employee, previous_month_first + relativedelta(days=1), "Present", "Day Shift" - ) - mark_attendance( - self.employee, previous_month_first + relativedelta(days=2), "Half Day" - ) # half day - - mark_attendance( - self.employee, previous_month_first + relativedelta(days=3), "Present" - ) # attendance without shift - mark_attendance( - self.employee, previous_month_first + relativedelta(days=4), "Present", late_entry=1 - ) # late entry - mark_attendance( - self.employee, previous_month_first + relativedelta(days=5), "Present", early_exit=1 - ) # early exit - - leave_application = get_leave_application(self.employee) - - filters = frappe._dict( - {"month": previous_month, "year": now.year, "company": company, "summarized_view": 1} - ) - report = execute(filters=filters) - - row = report[1][0] - self.assertEqual(row["employee"], self.employee) - - # 4 present + half day absent 0.5 - self.assertEqual(row["total_present"], 4.5) - # 1 present + half day absent 0.5 - self.assertEqual(row["total_absent"], 1.5) - # leave days + half day leave 0.5 - self.assertEqual(row["total_leaves"], leave_application.total_leave_days + 0.5) - - self.assertEqual(row["_test_leave_type"], leave_application.total_leave_days) - self.assertEqual(row["total_late_entries"], 1) - self.assertEqual(row["total_early_exits"], 1) - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_attendance_with_group_by_filter(self): - now = now_datetime() - previous_month = now.month - 1 - previous_month_first = now.replace(day=1).replace(month=previous_month).date() - - company = frappe.db.get_value("Employee", self.employee, "company") - - # attendance with shift - mark_attendance(self.employee, previous_month_first, "Absent", "Day Shift") - mark_attendance( - self.employee, previous_month_first + relativedelta(days=1), "Present", "Day Shift" - ) - - # attendance without shift - mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave") - mark_attendance(self.employee, previous_month_first + relativedelta(days=3), "Present") - - filters = frappe._dict( - {"month": previous_month, "year": now.year, "company": company, "group_by": "Department"} - ) - report = execute(filters=filters) - - department = frappe.db.get_value("Employee", self.employee, "department") - department_row = report[1][0] - self.assertIn(department, department_row["department"]) - - day_shift_row = report[1][1] - row_without_shift = report[1][2] - - self.assertEqual(day_shift_row["shift"], "Day Shift") - self.assertEqual(day_shift_row[1], "A") # absent on the 1st day of the month - self.assertEqual(day_shift_row[2], "P") # present on the 2nd day - - self.assertEqual(row_without_shift["shift"], None) - self.assertEqual(row_without_shift[3], "L") # on leave on the 3rd day - self.assertEqual(row_without_shift[4], "P") # present on the 4th day - - def test_attendance_with_employee_filter(self): - now = now_datetime() - previous_month = now.month - 1 - previous_month_first = now.replace(day=1).replace(month=previous_month).date() - - company = frappe.db.get_value("Employee", self.employee, "company") - - # mark different attendance status on first 3 days of previous month - mark_attendance(self.employee, previous_month_first, "Absent") - mark_attendance(self.employee, previous_month_first + relativedelta(days=1), "Present") - mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave") - - filters = frappe._dict( - {"month": previous_month, "year": now.year, "company": company, "employee": self.employee} - ) - report = execute(filters=filters) - - record = report[1][0] - datasets = report[3]["data"]["datasets"] - absent = datasets[0]["values"] - present = datasets[1]["values"] - leaves = datasets[2]["values"] - - # ensure correct attendance is reflected on the report - self.assertEqual(self.employee, record.get("employee")) - self.assertEqual(absent[0], 1) - self.assertEqual(present[1], 1) - self.assertEqual(leaves[2], 1) - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_validations(self): - # validation error for filters without month and year - self.assertRaises(frappe.ValidationError, execute_report_with_invalid_filters) - - # execute report without attendance record - now = now_datetime() - previous_month = now.month - 1 - - company = frappe.db.get_value("Employee", self.employee, "company") - filters = frappe._dict( - {"month": previous_month, "year": now.year, "company": company, "group_by": "Department"} - ) - report = execute(filters=filters) - self.assertEqual(report, ([], [], None, None)) - - -def get_leave_application(employee): - now = now_datetime() - previous_month = now.month - 1 - - date = getdate() - year_start = getdate(get_year_start(date)) - year_end = getdate(get_year_ending(date)) - make_allocation_record(employee=employee, from_date=year_start, to_date=year_end) - - from_date = now.replace(day=7).replace(month=previous_month).date() - to_date = now.replace(day=8).replace(month=previous_month).date() - return make_leave_application(employee, from_date, to_date, "_Test Leave Type") - - -def execute_report_with_invalid_filters(): - filters = frappe._dict({"company": "_Test Company", "group_by": "Department"}) - execute(filters=filters) diff --git a/erpnext/hr/report/recruitment_analytics/__init__.py b/erpnext/hr/report/recruitment_analytics/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.js b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.js deleted file mode 100644 index 51dc7ff85b..0000000000 --- a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.js +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Recruitment Analytics"] = { - "filters": [ - { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 - }, - { - "fieldname":"on_date", - "label": __("On Date"), - "fieldtype": "Date", - "default": frappe.datetime.now_date(), - "reqd": 1, - }, - ] -}; diff --git a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.json b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.json deleted file mode 100644 index 30a8e17eb8..0000000000 --- a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2020-05-14 16:28:45.743869", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2020-05-14 16:28:45.743869", - "modified_by": "Administrator", - "module": "HR", - "name": "Recruitment Analytics", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Staffing Plan", - "report_name": "Recruitment Analytics", - "report_type": "Script Report", - "roles": [ - { - "role": "HR Manager" - }, - { - "role": "HR User" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py deleted file mode 100644 index b6caf400dd..0000000000 --- a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py +++ /dev/null @@ -1,198 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ - - -def execute(filters=None): - - if not filters: - filters = {} - filters = frappe._dict(filters) - - columns = get_columns() - - data = get_data(filters) - - return columns, data - - -def get_columns(): - return [ - { - "label": _("Staffing Plan"), - "fieldtype": "Link", - "fieldname": "staffing_plan", - "options": "Staffing Plan", - "width": 150, - }, - { - "label": _("Job Opening"), - "fieldtype": "Link", - "fieldname": "job_opening", - "options": "Job Opening", - "width": 105, - }, - { - "label": _("Job Applicant"), - "fieldtype": "Link", - "fieldname": "job_applicant", - "options": "Job Applicant", - "width": 150, - }, - {"label": _("Applicant name"), "fieldtype": "data", "fieldname": "applicant_name", "width": 130}, - { - "label": _("Application Status"), - "fieldtype": "Data", - "fieldname": "application_status", - "width": 150, - }, - { - "label": _("Job Offer"), - "fieldtype": "Link", - "fieldname": "job_offer", - "options": "job Offer", - "width": 150, - }, - {"label": _("Designation"), "fieldtype": "Data", "fieldname": "designation", "width": 100}, - {"label": _("Offer Date"), "fieldtype": "date", "fieldname": "offer_date", "width": 100}, - { - "label": _("Job Offer status"), - "fieldtype": "Data", - "fieldname": "job_offer_status", - "width": 150, - }, - ] - - -def get_data(filters): - data = [] - staffing_plan_details = get_staffing_plan(filters) - staffing_plan_list = list(set([details["name"] for details in staffing_plan_details])) - sp_jo_map, jo_list = get_job_opening(staffing_plan_list) - jo_ja_map, ja_list = get_job_applicant(jo_list) - ja_joff_map = get_job_offer(ja_list) - - for sp in sp_jo_map.keys(): - parent_row = get_parent_row(sp_jo_map, sp, jo_ja_map, ja_joff_map) - data += parent_row - - return data - - -def get_parent_row(sp_jo_map, sp, jo_ja_map, ja_joff_map): - data = [] - if sp in sp_jo_map.keys(): - for jo in sp_jo_map[sp]: - row = { - "staffing_plan": sp, - "job_opening": jo["name"], - } - data.append(row) - child_row = get_child_row(jo["name"], jo_ja_map, ja_joff_map) - data += child_row - return data - - -def get_child_row(jo, jo_ja_map, ja_joff_map): - data = [] - if jo in jo_ja_map.keys(): - for ja in jo_ja_map[jo]: - row = { - "indent": 1, - "job_applicant": ja.name, - "applicant_name": ja.applicant_name, - "application_status": ja.status, - } - if ja.name in ja_joff_map.keys(): - jo_detail = ja_joff_map[ja.name][0] - row["job_offer"] = jo_detail.name - row["job_offer_status"] = jo_detail.status - row["offer_date"] = jo_detail.offer_date.strftime("%d-%m-%Y") - row["designation"] = jo_detail.designation - - data.append(row) - return data - - -def get_staffing_plan(filters): - - staffing_plan = frappe.db.sql( - """ - select - sp.name, sp.department, spd.designation, spd.vacancies, spd.current_count, spd.parent, sp.to_date - from - `tabStaffing Plan Detail` spd , `tabStaffing Plan` sp - where - spd.parent = sp.name - And - sp.to_date > '{0}' - """.format( - filters.on_date - ), - as_dict=1, - ) - - return staffing_plan - - -def get_job_opening(sp_list): - - job_openings = frappe.get_all( - "Job Opening", filters=[["staffing_plan", "IN", sp_list]], fields=["name", "staffing_plan"] - ) - - sp_jo_map = {} - jo_list = [] - - for openings in job_openings: - if openings.staffing_plan not in sp_jo_map.keys(): - sp_jo_map[openings.staffing_plan] = [openings] - else: - sp_jo_map[openings.staffing_plan].append(openings) - - jo_list.append(openings.name) - - return sp_jo_map, jo_list - - -def get_job_applicant(jo_list): - - jo_ja_map = {} - ja_list = [] - - applicants = frappe.get_all( - "Job Applicant", - filters=[["job_title", "IN", jo_list]], - fields=["name", "job_title", "applicant_name", "status"], - ) - - for applicant in applicants: - if applicant.job_title not in jo_ja_map.keys(): - jo_ja_map[applicant.job_title] = [applicant] - else: - jo_ja_map[applicant.job_title].append(applicant) - - ja_list.append(applicant.name) - - return jo_ja_map, ja_list - - -def get_job_offer(ja_list): - ja_joff_map = {} - - offers = frappe.get_all( - "Job Offer", - filters=[["job_applicant", "IN", ja_list]], - fields=["name", "job_applicant", "status", "offer_date", "designation"], - ) - - for offer in offers: - if offer.job_applicant not in ja_joff_map.keys(): - ja_joff_map[offer.job_applicant] = [offer] - else: - ja_joff_map[offer.job_applicant].append(offer) - - return ja_joff_map diff --git a/erpnext/hr/report/vehicle_expenses/__init__.py b/erpnext/hr/report/vehicle_expenses/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py b/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py deleted file mode 100644 index da6dace72b..0000000000 --- a/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import unittest - -import frappe -from frappe.utils import getdate - -from erpnext.accounts.utils import get_fiscal_year -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.vehicle_log.test_vehicle_log import get_vehicle, make_vehicle_log -from erpnext.hr.doctype.vehicle_log.vehicle_log import make_expense_claim -from erpnext.hr.report.vehicle_expenses.vehicle_expenses import execute - - -class TestVehicleExpenses(unittest.TestCase): - @classmethod - def setUpClass(self): - frappe.db.sql("delete from `tabVehicle Log`") - - employee_id = frappe.db.sql( - '''select name from `tabEmployee` where name="testdriver@example.com"''' - ) - self.employee_id = employee_id[0][0] if employee_id else None - if not self.employee_id: - self.employee_id = make_employee("testdriver@example.com", company="_Test Company") - - self.license_plate = get_vehicle(self.employee_id) - - def test_vehicle_expenses_based_on_fiscal_year(self): - vehicle_log = make_vehicle_log(self.license_plate, self.employee_id, with_services=True) - expense_claim = make_expense_claim(vehicle_log.name) - - # Based on Fiscal Year - filters = {"filter_based_on": "Fiscal Year", "fiscal_year": get_fiscal_year(getdate())[0]} - - report = execute(filters) - - expected_data = [ - { - "vehicle": self.license_plate, - "make": "Maruti", - "model": "PCM", - "location": "Mumbai", - "log_name": vehicle_log.name, - "odometer": 5010, - "date": getdate(), - "fuel_qty": 50.0, - "fuel_price": 500.0, - "fuel_expense": 25000.0, - "service_expense": 2000.0, - "employee": self.employee_id, - } - ] - - self.assertEqual(report[1], expected_data) - - # Based on Date Range - fiscal_year = get_fiscal_year(getdate(), as_dict=True) - filters = { - "filter_based_on": "Date Range", - "from_date": fiscal_year.year_start_date, - "to_date": fiscal_year.year_end_date, - } - - report = execute(filters) - self.assertEqual(report[1], expected_data) - - # clean up - vehicle_log.cancel() - frappe.delete_doc("Expense Claim", expense_claim.name) - frappe.delete_doc("Vehicle Log", vehicle_log.name) - - def tearDown(self): - frappe.delete_doc("Vehicle", self.license_plate, force=1) - frappe.delete_doc("Employee", self.employee_id, force=1) diff --git a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.js b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.js deleted file mode 100644 index 2d0aa0f36d..0000000000 --- a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.js +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -frappe.query_reports["Vehicle Expenses"] = { - "filters": [ - { - "fieldname": "filter_based_on", - "label": __("Filter Based On"), - "fieldtype": "Select", - "options": ["Fiscal Year", "Date Range"], - "default": ["Fiscal Year"], - "reqd": 1 - }, - { - "fieldname": "fiscal_year", - "label": __("Fiscal Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), - "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", - "reqd": 1 - }, - { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "reqd": 1, - "depends_on": "eval: doc.filter_based_on == 'Date Range'", - "default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12) - }, - { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "reqd": 1, - "depends_on": "eval: doc.filter_based_on == 'Date Range'", - "default": frappe.datetime.nowdate() - }, - { - "fieldname": "vehicle", - "label": __("Vehicle"), - "fieldtype": "Link", - "options": "Vehicle" - }, - { - "fieldname": "employee", - "label": __("Employee"), - "fieldtype": "Link", - "options": "Employee" - } - ] -}; diff --git a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.json b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.json deleted file mode 100644 index 1a3e5a93bb..0000000000 --- a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "add_total_row": 1, - "columns": [], - "creation": "2016-09-09 03:33:40.605734", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "filters": [], - "idx": 2, - "is_standard": "Yes", - "modified": "2021-05-16 22:48:22.767535", - "modified_by": "Administrator", - "module": "HR", - "name": "Vehicle Expenses", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Vehicle", - "report_name": "Vehicle Expenses", - "report_type": "Script Report", - "roles": [ - { - "role": "Fleet Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py deleted file mode 100644 index fc5510ddad..0000000000 --- a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py +++ /dev/null @@ -1,177 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.utils import flt - -from erpnext.accounts.report.financial_statements import get_period_list - - -def execute(filters=None): - filters = frappe._dict(filters or {}) - - columns = get_columns() - data = get_vehicle_log_data(filters) - chart = get_chart_data(data, filters) - - return columns, data, None, chart - - -def get_columns(): - return [ - { - "fieldname": "vehicle", - "fieldtype": "Link", - "label": _("Vehicle"), - "options": "Vehicle", - "width": 150, - }, - {"fieldname": "make", "fieldtype": "Data", "label": _("Make"), "width": 100}, - {"fieldname": "model", "fieldtype": "Data", "label": _("Model"), "width": 80}, - {"fieldname": "location", "fieldtype": "Data", "label": _("Location"), "width": 100}, - { - "fieldname": "log_name", - "fieldtype": "Link", - "label": _("Vehicle Log"), - "options": "Vehicle Log", - "width": 100, - }, - {"fieldname": "odometer", "fieldtype": "Int", "label": _("Odometer Value"), "width": 120}, - {"fieldname": "date", "fieldtype": "Date", "label": _("Date"), "width": 100}, - {"fieldname": "fuel_qty", "fieldtype": "Float", "label": _("Fuel Qty"), "width": 80}, - {"fieldname": "fuel_price", "fieldtype": "Float", "label": _("Fuel Price"), "width": 100}, - {"fieldname": "fuel_expense", "fieldtype": "Currency", "label": _("Fuel Expense"), "width": 150}, - { - "fieldname": "service_expense", - "fieldtype": "Currency", - "label": _("Service Expense"), - "width": 150, - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "label": _("Employee"), - "options": "Employee", - "width": 150, - }, - ] - - -def get_vehicle_log_data(filters): - start_date, end_date = get_period_dates(filters) - conditions, values = get_conditions(filters) - - data = frappe.db.sql( - """ - SELECT - vhcl.license_plate as vehicle, vhcl.make, vhcl.model, - vhcl.location, log.name as log_name, log.odometer, - log.date, log.employee, log.fuel_qty, - log.price as fuel_price, - log.fuel_qty * log.price as fuel_expense - FROM - `tabVehicle` vhcl,`tabVehicle Log` log - WHERE - vhcl.license_plate = log.license_plate - and log.docstatus = 1 - and date between %(start_date)s and %(end_date)s - {0} - ORDER BY date""".format( - conditions - ), - values, - as_dict=1, - ) - - for row in data: - row["service_expense"] = get_service_expense(row.log_name) - - return data - - -def get_conditions(filters): - conditions = "" - - start_date, end_date = get_period_dates(filters) - values = {"start_date": start_date, "end_date": end_date} - - if filters.employee: - conditions += " and log.employee = %(employee)s" - values["employee"] = filters.employee - - if filters.vehicle: - conditions += " and vhcl.license_plate = %(vehicle)s" - values["vehicle"] = filters.vehicle - - return conditions, values - - -def get_period_dates(filters): - if filters.filter_based_on == "Fiscal Year" and filters.fiscal_year: - fy = frappe.db.get_value( - "Fiscal Year", filters.fiscal_year, ["year_start_date", "year_end_date"], as_dict=True - ) - return fy.year_start_date, fy.year_end_date - else: - return filters.from_date, filters.to_date - - -def get_service_expense(logname): - expense_amount = frappe.db.sql( - """ - SELECT sum(expense_amount) - FROM - `tabVehicle Log` log, `tabVehicle Service` service - WHERE - service.parent=log.name and log.name=%s - """, - logname, - ) - - return flt(expense_amount[0][0]) if expense_amount else 0.0 - - -def get_chart_data(data, filters): - period_list = get_period_list( - filters.fiscal_year, - filters.fiscal_year, - filters.from_date, - filters.to_date, - filters.filter_based_on, - "Monthly", - ) - - fuel_data, service_data = [], [] - - for period in period_list: - total_fuel_exp = 0 - total_service_exp = 0 - - for row in data: - if row.date <= period.to_date and row.date >= period.from_date: - total_fuel_exp += flt(row.fuel_expense) - total_service_exp += flt(row.service_expense) - - fuel_data.append([period.key, total_fuel_exp]) - service_data.append([period.key, total_service_exp]) - - labels = [period.label for period in period_list] - fuel_exp_data = [row[1] for row in fuel_data] - service_exp_data = [row[1] for row in service_data] - - datasets = [] - if fuel_exp_data: - datasets.append({"name": _("Fuel Expenses"), "values": fuel_exp_data}) - - if service_exp_data: - datasets.append({"name": _("Service Expenses"), "values": service_exp_data}) - - chart = { - "data": {"labels": labels, "datasets": datasets}, - "type": "line", - "fieldtype": "Currency", - } - - return chart diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py deleted file mode 100644 index c730b19924..0000000000 --- a/erpnext/hr/utils.py +++ /dev/null @@ -1,598 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe -from frappe import _ -from frappe.utils import ( - add_days, - cstr, - flt, - format_datetime, - formatdate, - get_datetime, - get_link_to_form, - getdate, - nowdate, - today, -) - -import erpnext -from erpnext.hr.doctype.employee.employee import ( - InactiveEmployeeStatusError, - get_holiday_list_for_employee, -) - - -class DuplicateDeclarationError(frappe.ValidationError): - pass - - -def set_employee_name(doc): - if doc.employee and not doc.employee_name: - doc.employee_name = frappe.db.get_value("Employee", doc.employee, "employee_name") - - -def update_employee_work_history(employee, details, date=None, cancel=False): - if not employee.internal_work_history and not cancel: - employee.append( - "internal_work_history", - { - "branch": employee.branch, - "designation": employee.designation, - "department": employee.department, - "from_date": employee.date_of_joining, - }, - ) - - internal_work_history = {} - for item in details: - field = frappe.get_meta("Employee").get_field(item.fieldname) - if not field: - continue - fieldtype = field.fieldtype - new_data = item.new if not cancel else item.current - if fieldtype == "Date" and new_data: - new_data = getdate(new_data) - elif fieldtype == "Datetime" and new_data: - new_data = get_datetime(new_data) - setattr(employee, item.fieldname, new_data) - if item.fieldname in ["department", "designation", "branch"]: - internal_work_history[item.fieldname] = item.new - - if internal_work_history and not cancel: - internal_work_history["from_date"] = date - employee.append("internal_work_history", internal_work_history) - - if cancel: - delete_employee_work_history(details, employee, date) - - return employee - - -def delete_employee_work_history(details, employee, date): - filters = {} - for d in details: - for history in employee.internal_work_history: - if d.property == "Department" and history.department == d.new: - department = d.new - filters["department"] = department - if d.property == "Designation" and history.designation == d.new: - designation = d.new - filters["designation"] = designation - if d.property == "Branch" and history.branch == d.new: - branch = d.new - filters["branch"] = branch - if date and date == history.from_date: - filters["from_date"] = date - if filters: - frappe.db.delete("Employee Internal Work History", filters) - - -@frappe.whitelist() -def get_employee_field_property(employee, fieldname): - if employee and fieldname: - field = frappe.get_meta("Employee").get_field(fieldname) - value = frappe.db.get_value("Employee", employee, fieldname) - options = field.options - if field.fieldtype == "Date": - value = formatdate(value) - elif field.fieldtype == "Datetime": - value = format_datetime(value) - return {"value": value, "datatype": field.fieldtype, "label": field.label, "options": options} - else: - return False - - -def validate_dates(doc, from_date, to_date): - date_of_joining, relieving_date = frappe.db.get_value( - "Employee", doc.employee, ["date_of_joining", "relieving_date"] - ) - if getdate(from_date) > getdate(to_date): - frappe.throw(_("To date can not be less than from date")) - elif getdate(from_date) > getdate(nowdate()): - frappe.throw(_("Future dates not allowed")) - elif date_of_joining and getdate(from_date) < getdate(date_of_joining): - frappe.throw(_("From date can not be less than employee's joining date")) - elif relieving_date and getdate(to_date) > getdate(relieving_date): - frappe.throw(_("To date can not greater than employee's relieving date")) - - -def validate_overlap(doc, from_date, to_date, company=None): - query = """ - select name - from `tab{0}` - where name != %(name)s - """ - query += get_doc_condition(doc.doctype) - - if not doc.name: - # hack! if name is null, it could cause problems with != - doc.name = "New " + doc.doctype - - overlap_doc = frappe.db.sql( - query.format(doc.doctype), - { - "employee": doc.get("employee"), - "from_date": from_date, - "to_date": to_date, - "name": doc.name, - "company": company, - }, - as_dict=1, - ) - - if overlap_doc: - if doc.get("employee"): - exists_for = doc.employee - if company: - exists_for = company - throw_overlap_error(doc, exists_for, overlap_doc[0].name, from_date, to_date) - - -def get_doc_condition(doctype): - if doctype == "Compensatory Leave Request": - return "and employee = %(employee)s and docstatus < 2 \ - and (work_from_date between %(from_date)s and %(to_date)s \ - or work_end_date between %(from_date)s and %(to_date)s \ - or (work_from_date < %(from_date)s and work_end_date > %(to_date)s))" - elif doctype == "Leave Period": - return "and company = %(company)s and (from_date between %(from_date)s and %(to_date)s \ - or to_date between %(from_date)s and %(to_date)s \ - or (from_date < %(from_date)s and to_date > %(to_date)s))" - - -def throw_overlap_error(doc, exists_for, overlap_doc, from_date, to_date): - msg = ( - _("A {0} exists between {1} and {2} (").format( - doc.doctype, formatdate(from_date), formatdate(to_date) - ) - + """ {1}""".format(doc.doctype, overlap_doc) - + _(") for {0}").format(exists_for) - ) - frappe.throw(msg) - - -def validate_duplicate_exemption_for_payroll_period(doctype, docname, payroll_period, employee): - existing_record = frappe.db.exists( - doctype, - { - "payroll_period": payroll_period, - "employee": employee, - "docstatus": ["<", 2], - "name": ["!=", docname], - }, - ) - if existing_record: - frappe.throw( - _("{0} already exists for employee {1} and period {2}").format( - doctype, employee, payroll_period - ), - DuplicateDeclarationError, - ) - - -def validate_tax_declaration(declarations): - subcategories = [] - for d in declarations: - if d.exemption_sub_category in subcategories: - frappe.throw(_("More than one selection for {0} not allowed").format(d.exemption_sub_category)) - subcategories.append(d.exemption_sub_category) - - -def get_total_exemption_amount(declarations): - exemptions = frappe._dict() - for d in declarations: - exemptions.setdefault(d.exemption_category, frappe._dict()) - category_max_amount = exemptions.get(d.exemption_category).max_amount - if not category_max_amount: - category_max_amount = frappe.db.get_value( - "Employee Tax Exemption Category", d.exemption_category, "max_amount" - ) - exemptions.get(d.exemption_category).max_amount = category_max_amount - sub_category_exemption_amount = ( - d.max_amount if (d.max_amount and flt(d.amount) > flt(d.max_amount)) else d.amount - ) - - exemptions.get(d.exemption_category).setdefault("total_exemption_amount", 0.0) - exemptions.get(d.exemption_category).total_exemption_amount += flt(sub_category_exemption_amount) - - if ( - category_max_amount - and exemptions.get(d.exemption_category).total_exemption_amount > category_max_amount - ): - exemptions.get(d.exemption_category).total_exemption_amount = category_max_amount - - total_exemption_amount = sum([flt(d.total_exemption_amount) for d in exemptions.values()]) - return total_exemption_amount - - -@frappe.whitelist() -def get_leave_period(from_date, to_date, company): - leave_period = frappe.db.sql( - """ - select name, from_date, to_date - from `tabLeave Period` - where company=%(company)s and is_active=1 - and (from_date between %(from_date)s and %(to_date)s - or to_date between %(from_date)s and %(to_date)s - or (from_date < %(from_date)s and to_date > %(to_date)s)) - """, - {"from_date": from_date, "to_date": to_date, "company": company}, - as_dict=1, - ) - - if leave_period: - return leave_period - - -def generate_leave_encashment(): - """Generates a draft leave encashment on allocation expiry""" - from erpnext.hr.doctype.leave_encashment.leave_encashment import create_leave_encashment - - if frappe.db.get_single_value("HR Settings", "auto_leave_encashment"): - leave_type = frappe.get_all("Leave Type", filters={"allow_encashment": 1}, fields=["name"]) - leave_type = [l["name"] for l in leave_type] - - leave_allocation = frappe.get_all( - "Leave Allocation", - filters={"to_date": add_days(today(), -1), "leave_type": ("in", leave_type)}, - fields=[ - "employee", - "leave_period", - "leave_type", - "to_date", - "total_leaves_allocated", - "new_leaves_allocated", - ], - ) - - create_leave_encashment(leave_allocation=leave_allocation) - - -def allocate_earned_leaves(): - """Allocate earned leaves to Employees""" - e_leave_types = get_earned_leaves() - today = getdate() - - for e_leave_type in e_leave_types: - - leave_allocations = get_leave_allocations(today, e_leave_type.name) - - for allocation in leave_allocations: - - if not allocation.leave_policy_assignment and not allocation.leave_policy: - continue - - leave_policy = ( - allocation.leave_policy - if allocation.leave_policy - else frappe.db.get_value( - "Leave Policy Assignment", allocation.leave_policy_assignment, ["leave_policy"] - ) - ) - - annual_allocation = frappe.db.get_value( - "Leave Policy Detail", - filters={"parent": leave_policy, "leave_type": e_leave_type.name}, - fieldname=["annual_allocation"], - ) - - from_date = allocation.from_date - - if e_leave_type.based_on_date_of_joining: - from_date = frappe.db.get_value("Employee", allocation.employee, "date_of_joining") - - if check_effective_date( - from_date, today, e_leave_type.earned_leave_frequency, e_leave_type.based_on_date_of_joining - ): - update_previous_leave_allocation(allocation, annual_allocation, e_leave_type) - - -def update_previous_leave_allocation(allocation, annual_allocation, e_leave_type): - earned_leaves = get_monthly_earned_leave( - annual_allocation, e_leave_type.earned_leave_frequency, e_leave_type.rounding - ) - - allocation = frappe.get_doc("Leave Allocation", allocation.name) - new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves) - - if new_allocation > e_leave_type.max_leaves_allowed and e_leave_type.max_leaves_allowed > 0: - new_allocation = e_leave_type.max_leaves_allowed - - if new_allocation != allocation.total_leaves_allocated: - today_date = today() - - allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False) - create_additional_leave_ledger_entry(allocation, earned_leaves, today_date) - - if e_leave_type.based_on_date_of_joining: - text = _("allocated {0} leave(s) via scheduler on {1} based on the date of joining").format( - frappe.bold(earned_leaves), frappe.bold(formatdate(today_date)) - ) - else: - text = _("allocated {0} leave(s) via scheduler on {1}").format( - frappe.bold(earned_leaves), frappe.bold(formatdate(today_date)) - ) - - allocation.add_comment(comment_type="Info", text=text) - - -def get_monthly_earned_leave(annual_leaves, frequency, rounding): - earned_leaves = 0.0 - divide_by_frequency = {"Yearly": 1, "Half-Yearly": 6, "Quarterly": 4, "Monthly": 12} - if annual_leaves: - earned_leaves = flt(annual_leaves) / divide_by_frequency[frequency] - if rounding: - if rounding == "0.25": - earned_leaves = round(earned_leaves * 4) / 4 - elif rounding == "0.5": - earned_leaves = round(earned_leaves * 2) / 2 - else: - earned_leaves = round(earned_leaves) - - return earned_leaves - - -def is_earned_leave_already_allocated(allocation, annual_allocation): - from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import ( - get_leave_type_details, - ) - - leave_type_details = get_leave_type_details() - date_of_joining = frappe.db.get_value("Employee", allocation.employee, "date_of_joining") - - assignment = frappe.get_doc("Leave Policy Assignment", allocation.leave_policy_assignment) - leaves_for_passed_months = assignment.get_leaves_for_passed_months( - allocation.leave_type, annual_allocation, leave_type_details, date_of_joining - ) - - # exclude carry-forwarded leaves while checking for leave allocation for passed months - num_allocations = allocation.total_leaves_allocated - if allocation.unused_leaves: - num_allocations -= allocation.unused_leaves - - if num_allocations >= leaves_for_passed_months: - return True - return False - - -def get_leave_allocations(date, leave_type): - return frappe.db.sql( - """select name, employee, from_date, to_date, leave_policy_assignment, leave_policy - from `tabLeave Allocation` - where - %s between from_date and to_date and docstatus=1 - and leave_type=%s""", - (date, leave_type), - as_dict=1, - ) - - -def get_earned_leaves(): - return frappe.get_all( - "Leave Type", - fields=[ - "name", - "max_leaves_allowed", - "earned_leave_frequency", - "rounding", - "based_on_date_of_joining", - ], - filters={"is_earned_leave": 1}, - ) - - -def create_additional_leave_ledger_entry(allocation, leaves, date): - """Create leave ledger entry for leave types""" - allocation.new_leaves_allocated = leaves - allocation.from_date = date - allocation.unused_leaves = 0 - allocation.create_leave_ledger_entry() - - -def check_effective_date(from_date, to_date, frequency, based_on_date_of_joining): - import calendar - - from dateutil import relativedelta - - from_date = get_datetime(from_date) - to_date = get_datetime(to_date) - rd = relativedelta.relativedelta(to_date, from_date) - # last day of month - last_day = calendar.monthrange(to_date.year, to_date.month)[1] - - if (from_date.day == to_date.day and based_on_date_of_joining) or ( - not based_on_date_of_joining and to_date.day == last_day - ): - if frequency == "Monthly": - return True - elif frequency == "Quarterly" and rd.months % 3: - return True - elif frequency == "Half-Yearly" and rd.months % 6: - return True - elif frequency == "Yearly" and rd.months % 12: - return True - - if frappe.flags.in_test: - return True - - return False - - -def get_salary_assignments(employee, payroll_period): - start_date, end_date = frappe.db.get_value( - "Payroll Period", payroll_period, ["start_date", "end_date"] - ) - assignments = frappe.db.get_all( - "Salary Structure Assignment", - filters={"employee": employee, "docstatus": 1, "from_date": ["between", (start_date, end_date)]}, - fields=["*"], - order_by="from_date", - ) - - return assignments - - -def get_sal_slip_total_benefit_given(employee, payroll_period, component=False): - total_given_benefit_amount = 0 - query = """ - select sum(sd.amount) as 'total_amount' - from `tabSalary Slip` ss, `tabSalary Detail` sd - where ss.employee=%(employee)s - and ss.docstatus = 1 and ss.name = sd.parent - and sd.is_flexible_benefit = 1 and sd.parentfield = "earnings" - and sd.parenttype = "Salary Slip" - and (ss.start_date between %(start_date)s and %(end_date)s - or ss.end_date between %(start_date)s and %(end_date)s - or (ss.start_date < %(start_date)s and ss.end_date > %(end_date)s)) - """ - - if component: - query += "and sd.salary_component = %(component)s" - - sum_of_given_benefit = frappe.db.sql( - query, - { - "employee": employee, - "start_date": payroll_period.start_date, - "end_date": payroll_period.end_date, - "component": component, - }, - as_dict=True, - ) - - if sum_of_given_benefit and flt(sum_of_given_benefit[0].total_amount) > 0: - total_given_benefit_amount = sum_of_given_benefit[0].total_amount - return total_given_benefit_amount - - -def get_holiday_dates_for_employee(employee, start_date, end_date): - """return a list of holiday dates for the given employee between start_date and end_date""" - # return only date - holidays = get_holidays_for_employee(employee, start_date, end_date) - - return [cstr(h.holiday_date) for h in holidays] - - -def get_holidays_for_employee( - employee, start_date, end_date, raise_exception=True, only_non_weekly=False -): - """Get Holidays for a given employee - - `employee` (str) - `start_date` (str or datetime) - `end_date` (str or datetime) - `raise_exception` (bool) - `only_non_weekly` (bool) - - return: list of dicts with `holiday_date` and `description` - """ - holiday_list = get_holiday_list_for_employee(employee, raise_exception=raise_exception) - - if not holiday_list: - return [] - - filters = {"parent": holiday_list, "holiday_date": ("between", [start_date, end_date])} - - if only_non_weekly: - filters["weekly_off"] = False - - holidays = frappe.get_all("Holiday", fields=["description", "holiday_date"], filters=filters) - - return holidays - - -@erpnext.allow_regional -def calculate_annual_eligible_hra_exemption(doc): - # Don't delete this method, used for localization - # Indian HRA Exemption Calculation - return {} - - -@erpnext.allow_regional -def calculate_hra_exemption_for_period(doc): - # Don't delete this method, used for localization - # Indian HRA Exemption Calculation - return {} - - -def get_previous_claimed_amount(employee, payroll_period, non_pro_rata=False, component=False): - total_claimed_amount = 0 - query = """ - select sum(claimed_amount) as 'total_amount' - from `tabEmployee Benefit Claim` - where employee=%(employee)s - and docstatus = 1 - and (claim_date between %(start_date)s and %(end_date)s) - """ - if non_pro_rata: - query += "and pay_against_benefit_claim = 1" - if component: - query += "and earning_component = %(component)s" - - sum_of_claimed_amount = frappe.db.sql( - query, - { - "employee": employee, - "start_date": payroll_period.start_date, - "end_date": payroll_period.end_date, - "component": component, - }, - as_dict=True, - ) - if sum_of_claimed_amount and flt(sum_of_claimed_amount[0].total_amount) > 0: - total_claimed_amount = sum_of_claimed_amount[0].total_amount - return total_claimed_amount - - -def share_doc_with_approver(doc, user): - # if approver does not have permissions, share - if not frappe.has_permission(doc=doc, ptype="submit", user=user): - frappe.share.add(doc.doctype, doc.name, user, submit=1, flags={"ignore_share_permission": True}) - - frappe.msgprint( - _("Shared with the user {0} with {1} access").format(user, frappe.bold("submit"), alert=True) - ) - - # remove shared doc if approver changes - doc_before_save = doc.get_doc_before_save() - if doc_before_save: - approvers = { - "Leave Application": "leave_approver", - "Expense Claim": "expense_approver", - "Shift Request": "approver", - } - - approver = approvers.get(doc.doctype) - if doc_before_save.get(approver) != doc.get(approver): - frappe.share.remove(doc.doctype, doc.name, doc_before_save.get(approver)) - - -def validate_active_employee(employee): - if frappe.db.get_value("Employee", employee, "status") == "Inactive": - frappe.throw( - _("Transactions cannot be created for an Inactive Employee {0}.").format( - get_link_to_form("Employee", employee) - ), - InactiveEmployeeStatusError, - ) diff --git a/erpnext/hr/web_form/__init__.py b/erpnext/hr/web_form/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/web_form/job_application/__init__.py b/erpnext/hr/web_form/job_application/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/hr/web_form/job_application/job_application.js b/erpnext/hr/web_form/job_application/job_application.js deleted file mode 100644 index ffc5e98425..0000000000 --- a/erpnext/hr/web_form/job_application/job_application.js +++ /dev/null @@ -1,3 +0,0 @@ -frappe.ready(function() { - // bind events here -}) diff --git a/erpnext/hr/web_form/job_application/job_application.json b/erpnext/hr/web_form/job_application/job_application.json deleted file mode 100644 index 512ba5c555..0000000000 --- a/erpnext/hr/web_form/job_application/job_application.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "accept_payment": 0, - "allow_comments": 1, - "allow_delete": 0, - "allow_edit": 1, - "allow_incomplete": 0, - "allow_multiple": 1, - "allow_print": 0, - "amount": 0.0, - "amount_based_on_field": 0, - "apply_document_permissions": 0, - "client_script": "frappe.web_form.on('resume_link', (field, value) => {\n if (!frappe.utils.is_url(value)) {\n frappe.msgprint(__('Resume link not valid'));\n }\n});\n", - "creation": "2016-09-10 02:53:16.598314", - "doc_type": "Job Applicant", - "docstatus": 0, - "doctype": "Web Form", - "idx": 0, - "introduction_text": "", - "is_standard": 1, - "login_required": 0, - "max_attachment_size": 0, - "modified": "2020-10-07 19:27:17.143355", - "modified_by": "Administrator", - "module": "HR", - "name": "job-application", - "owner": "Administrator", - "published": 1, - "route": "job_application", - "route_to_success_link": 0, - "show_attachments": 0, - "show_in_grid": 0, - "show_sidebar": 1, - "sidebar_items": [], - "success_message": "Thank you for applying.", - "success_url": "/jobs", - "title": "Job Application", - "web_form_fields": [ - { - "allow_read_on_all_link_options": 0, - "fieldname": "job_title", - "fieldtype": "Data", - "hidden": 0, - "label": "Job Opening", - "max_length": 0, - "max_value": 0, - "options": "", - "read_only": 1, - "reqd": 0, - "show_in_filter": 0 - }, - { - "allow_read_on_all_link_options": 0, - "fieldname": "applicant_name", - "fieldtype": "Data", - "hidden": 0, - "label": "Applicant Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 1, - "show_in_filter": 0 - }, - { - "allow_read_on_all_link_options": 0, - "fieldname": "email_id", - "fieldtype": "Data", - "hidden": 0, - "label": "Email Address", - "max_length": 0, - "max_value": 0, - "options": "Email", - "read_only": 0, - "reqd": 1, - "show_in_filter": 0 - }, - { - "allow_read_on_all_link_options": 0, - "fieldname": "phone_number", - "fieldtype": "Data", - "hidden": 0, - "label": "Phone Number", - "max_length": 0, - "max_value": 0, - "options": "Phone", - "read_only": 0, - "reqd": 0, - "show_in_filter": 0 - }, - { - "allow_read_on_all_link_options": 0, - "fieldname": "country", - "fieldtype": "Link", - "hidden": 0, - "label": "Country of Residence", - "max_length": 0, - "max_value": 0, - "options": "Country", - "read_only": 0, - "reqd": 0, - "show_in_filter": 0 - }, - { - "allow_read_on_all_link_options": 0, - "fieldname": "cover_letter", - "fieldtype": "Text", - "hidden": 0, - "label": "Cover Letter", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0, - "show_in_filter": 0 - }, - { - "allow_read_on_all_link_options": 0, - "fieldname": "resume_link", - "fieldtype": "Data", - "hidden": 0, - "label": "Resume Link", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0, - "show_in_filter": 0 - }, - { - "allow_read_on_all_link_options": 0, - "fieldname": "", - "fieldtype": "Section Break", - "hidden": 0, - "label": "Expected Salary Range per month", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 1, - "show_in_filter": 0 - }, - { - "allow_read_on_all_link_options": 0, - "fieldname": "currency", - "fieldtype": "Link", - "hidden": 0, - "label": "Currency", - "max_length": 0, - "max_value": 0, - "options": "Currency", - "read_only": 0, - "reqd": 0, - "show_in_filter": 0 - }, - { - "allow_read_on_all_link_options": 0, - "fieldname": "", - "fieldtype": "Column Break", - "hidden": 0, - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0, - "show_in_filter": 0 - }, - { - "allow_read_on_all_link_options": 0, - "fieldname": "lower_range", - "fieldtype": "Currency", - "hidden": 0, - "label": "Lower Range", - "max_length": 0, - "max_value": 0, - "options": "currency", - "read_only": 0, - "reqd": 0, - "show_in_filter": 0 - }, - { - "allow_read_on_all_link_options": 0, - "fieldname": "", - "fieldtype": "Column Break", - "hidden": 0, - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0, - "show_in_filter": 0 - }, - { - "allow_read_on_all_link_options": 0, - "fieldname": "upper_range", - "fieldtype": "Currency", - "hidden": 0, - "label": "Upper Range", - "max_length": 0, - "max_value": 0, - "options": "currency", - "read_only": 0, - "reqd": 0, - "show_in_filter": 0 - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/web_form/job_application/job_application.py b/erpnext/hr/web_form/job_application/job_application.py deleted file mode 100644 index 02e3e93333..0000000000 --- a/erpnext/hr/web_form/job_application/job_application.py +++ /dev/null @@ -1,3 +0,0 @@ -def get_context(context): - # do your magic here - pass diff --git a/erpnext/hr/workspace/hr/hr.json b/erpnext/hr/workspace/hr/hr.json deleted file mode 100644 index 30cec1b4a8..0000000000 --- a/erpnext/hr/workspace/hr/hr.json +++ /dev/null @@ -1,1697 +0,0 @@ -{ - "charts": [ - { - "chart_name": "Outgoing Salary", - "label": "Outgoing Salary" - } - ], - "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Human Resource\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Outgoing Salary\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Employee\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leave Application\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Attendance\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Applicant\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Monthly Attendance Sheet\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Employee\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Employee Lifecycle\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Employee Exit\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Shift Management\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Leaves\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Attendance\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Expense Claims\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Loans\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Recruitment\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Performance\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Fleet Management\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Training\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]", - "creation": "2020-03-02 15:48:58.322521", - "docstatus": 0, - "doctype": "Workspace", - "for_user": "", - "hide_custom": 0, - "icon": "hr", - "idx": 0, - "label": "HR", - "links": [ - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee", - "link_count": 0, - "link_to": "Employee", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employment Type", - "link_count": 0, - "link_to": "Employment Type", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Branch", - "link_count": 0, - "link_to": "Branch", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Department", - "link_count": 0, - "link_to": "Department", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Designation", - "link_count": 0, - "link_to": "Designation", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Grade", - "link_count": 0, - "link_to": "Employee Grade", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Group", - "link_count": 0, - "link_to": "Employee Group", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Health Insurance", - "link_count": 0, - "link_to": "Employee Health Insurance", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Job Applicant", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Onboarding", - "link_count": 0, - "link_to": "Employee Onboarding", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Skill Map", - "link_count": 0, - "link_to": "Employee Skill Map", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Promotion", - "link_count": 0, - "link_to": "Employee Promotion", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Transfer", - "link_count": 0, - "link_to": "Employee Transfer", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Grievance Type", - "link_count": 0, - "link_to": "Grievance Type", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Employee Grievance", - "link_count": 0, - "link_to": "Employee Grievance", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Separation", - "link_count": 0, - "link_to": "Employee Separation", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Onboarding Template", - "link_count": 0, - "link_to": "Employee Onboarding Template", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Separation Template", - "link_count": 0, - "link_to": "Employee Separation Template", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Full and Final Statement", - "link_count": 0, - "link_to": "Full and Final Statement", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Shift Type", - "link_count": 0, - "link_to": "Shift Type", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Shift Request", - "link_count": 0, - "link_to": "Shift Request", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Shift Assignment", - "link_count": 0, - "link_to": "Shift Assignment", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Holiday List", - "link_count": 0, - "link_to": "Holiday List", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Type", - "link_count": 0, - "link_to": "Leave Type", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Period", - "link_count": 0, - "link_to": "Leave Period", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Leave Type", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Policy", - "link_count": 0, - "link_to": "Leave Policy", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Leave Policy", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Policy Assignment", - "link_count": 0, - "link_to": "Leave Policy Assignment", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Application", - "link_count": 0, - "link_to": "Leave Application", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Allocation", - "link_count": 0, - "link_to": "Leave Allocation", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Encashment", - "link_count": 0, - "link_to": "Leave Encashment", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Block List", - "link_count": 0, - "link_to": "Leave Block List", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Compensatory Leave Request", - "link_count": 0, - "link_to": "Compensatory Leave Request", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Attendance Tool", - "link_count": 0, - "link_to": "Employee Attendance Tool", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Attendance", - "link_count": 0, - "link_to": "Attendance", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Attendance Request", - "link_count": 0, - "link_to": "Attendance Request", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Upload Attendance", - "link_count": 0, - "link_to": "Upload Attendance", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Checkin", - "link_count": 0, - "link_to": "Employee Checkin", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Expense Claim", - "link_count": 0, - "link_to": "Expense Claim", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Advance", - "link_count": 0, - "link_to": "Employee Advance", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Travel Request", - "link_count": 0, - "link_to": "Travel Request", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "HR Settings", - "link_count": 0, - "link_to": "HR Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Daily Work Summary Group", - "link_count": 0, - "link_to": "Daily Work Summary Group", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Team Updates", - "link_count": 0, - "link_to": "team-updates", - "link_type": "Page", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Driver", - "link_count": 0, - "link_to": "Driver", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Vehicle", - "link_count": 0, - "link_to": "Vehicle", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Vehicle Log", - "link_count": 0, - "link_to": "Vehicle Log", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Vehicle", - "hidden": 0, - "is_query_report": 1, - "label": "Vehicle Expenses", - "link_count": 0, - "link_to": "Vehicle Expenses", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Job Opening", - "link_count": 0, - "link_to": "Job Opening", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Employee Referral", - "link_count": 0, - "link_to": "Employee Referral", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Job Applicant", - "link_count": 0, - "link_to": "Job Applicant", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Job Offer", - "link_count": 0, - "link_to": "Job Offer", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Staffing Plan", - "link_count": 0, - "link_to": "Staffing Plan", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Appointment Letter", - "link_count": 0, - "link_to": "Appointment Letter", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Appointment Letter Template", - "link_count": 0, - "link_to": "Appointment Letter Template", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Loans", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Loan Application", - "link_count": 0, - "link_to": "Loan Application", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Loan", - "link_count": 0, - "link_to": "Loan", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Loan Type", - "link_count": 0, - "link_to": "Loan Type", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Training", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Training Program", - "link_count": 0, - "link_to": "Training Program", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Training Event", - "link_count": 0, - "link_to": "Training Event", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Training Result", - "link_count": 0, - "link_to": "Training Result", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Training Feedback", - "link_count": 0, - "link_to": "Training Feedback", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Performance", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Appraisal", - "link_count": 0, - "link_to": "Appraisal", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Appraisal Template", - "link_count": 0, - "link_to": "Appraisal Template", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Energy Point Rule", - "link_count": 0, - "link_to": "Energy Point Rule", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Energy Point Log", - "link_count": 0, - "link_to": "Energy Point Log", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Attendance", - "hidden": 0, - "is_query_report": 1, - "label": "Monthly Attendance Sheet", - "link_count": 0, - "link_to": "Monthly Attendance Sheet", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Staffing Plan", - "hidden": 0, - "is_query_report": 1, - "label": "Recruitment Analytics", - "link_count": 0, - "link_to": "Recruitment Analytics", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 1, - "label": "Employee Analytics", - "link_count": 0, - "link_to": "Employee Analytics", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 1, - "label": "Employee Leave Balance", - "link_count": 0, - "link_to": "Employee Leave Balance", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 1, - "label": "Employee Leave Balance Summary", - "link_count": 0, - "link_to": "Employee Leave Balance Summary", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee Advance", - "hidden": 0, - "is_query_report": 1, - "label": "Employee Advance Summary", - "link_count": 0, - "link_to": "Employee Advance Summary", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Other Reports", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Information", - "link_count": 0, - "link_to": "Employee Information", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 1, - "label": "Employee Birthday", - "link_count": 0, - "link_to": "Employee Birthday", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 1, - "label": "Employees Working on a Holiday", - "link_count": 0, - "link_to": "Employees working on a holiday", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Daily Work Summary", - "hidden": 0, - "is_query_report": 1, - "label": "Daily Work Summary Replies", - "link_count": 0, - "link_to": "Daily Work Summary Replies", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Employee Lifecycle", - "link_count": 7, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "Job Applicant", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Onboarding", - "link_count": 0, - "link_to": "Employee Onboarding", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Skill Map", - "link_count": 0, - "link_to": "Employee Skill Map", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Promotion", - "link_count": 0, - "link_to": "Employee Promotion", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Transfer", - "link_count": 0, - "link_to": "Employee Transfer", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Grievance Type", - "link_count": 0, - "link_to": "Grievance Type", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Employee Grievance", - "link_count": 0, - "link_to": "Employee Grievance", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Onboarding Template", - "link_count": 0, - "link_to": "Employee Onboarding Template", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Employee Exit", - "link_count": 4, - "onboard": 0, - "type": "Card Break" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Employee Separation Template", - "link_count": 0, - "link_to": "Employee Separation Template", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Employee Separation", - "link_count": 0, - "link_to": "Employee Separation", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Full and Final Statement", - "link_count": 0, - "link_to": "Full and Final Statement", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Exit Interview", - "link_count": 0, - "link_to": "Exit Interview", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Employee", - "link_count": 8, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee", - "link_count": 0, - "link_to": "Employee", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employment Type", - "link_count": 0, - "link_to": "Employment Type", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Branch", - "link_count": 0, - "link_to": "Branch", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Department", - "link_count": 0, - "link_to": "Department", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Designation", - "link_count": 0, - "link_to": "Designation", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Grade", - "link_count": 0, - "link_to": "Employee Grade", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Group", - "link_count": 0, - "link_to": "Employee Group", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Health Insurance", - "link_count": 0, - "link_to": "Employee Health Insurance", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Key Reports", - "link_count": 7, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "Attendance", - "hidden": 0, - "is_query_report": 1, - "label": "Monthly Attendance Sheet", - "link_count": 0, - "link_to": "Monthly Attendance Sheet", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Staffing Plan", - "hidden": 0, - "is_query_report": 1, - "label": "Recruitment Analytics", - "link_count": 0, - "link_to": "Recruitment Analytics", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 1, - "label": "Employee Analytics", - "link_count": 0, - "link_to": "Employee Analytics", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 1, - "label": "Employee Leave Balance", - "link_count": 0, - "link_to": "Employee Leave Balance", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 1, - "label": "Employee Leave Balance Summary", - "link_count": 0, - "link_to": "Employee Leave Balance Summary", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee Advance", - "hidden": 0, - "is_query_report": 1, - "label": "Employee Advance Summary", - "link_count": 0, - "link_to": "Employee Advance Summary", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Employee Exits", - "link_count": 0, - "link_to": "Employee Exits", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Recruitment", - "link_count": 11, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Job Opening", - "link_count": 0, - "link_to": "Job Opening", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Employee Referral", - "link_count": 0, - "link_to": "Employee Referral", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Job Applicant", - "link_count": 0, - "link_to": "Job Applicant", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Job Offer", - "link_count": 0, - "link_to": "Job Offer", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Staffing Plan", - "link_count": 0, - "link_to": "Staffing Plan", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Appointment Letter", - "link_count": 0, - "link_to": "Appointment Letter", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Appointment Letter Template", - "link_count": 0, - "link_to": "Appointment Letter Template", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Interview Type", - "link_count": 0, - "link_to": "Interview Type", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Interview Round", - "link_count": 0, - "link_to": "Interview Round", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Interview", - "link_count": 0, - "link_to": "Interview", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Interview Feedback", - "link_count": 0, - "link_to": "Interview Feedback", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Fleet Management", - "link_count": 4, - "onboard": 0, - "type": "Card Break" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Driver", - "link_count": 0, - "link_to": "Driver", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Vehicle", - "link_count": 0, - "link_to": "Vehicle", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Vehicle Log", - "link_count": 0, - "link_to": "Vehicle Log", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Vehicle", - "hidden": 0, - "is_query_report": 1, - "label": "Vehicle Expenses", - "link_count": 0, - "link_to": "Vehicle Expenses", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Settings", - "link_count": 3, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "HR Settings", - "link_count": 0, - "link_to": "HR Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Daily Work Summary Group", - "link_count": 0, - "link_to": "Daily Work Summary Group", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Team Updates", - "link_count": 0, - "link_to": "team-updates", - "link_type": "Page", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Expense Claims", - "link_count": 3, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Expense Claim", - "link_count": 0, - "link_to": "Expense Claim", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Advance", - "link_count": 0, - "link_to": "Employee Advance", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Travel Request", - "link_count": 0, - "link_to": "Travel Request", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Attendance", - "link_count": 5, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Attendance Tool", - "link_count": 0, - "link_to": "Employee Attendance Tool", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Attendance", - "link_count": 0, - "link_to": "Attendance", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Attendance Request", - "link_count": 0, - "link_to": "Attendance Request", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Upload Attendance", - "link_count": 0, - "link_to": "Upload Attendance", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Checkin", - "link_count": 0, - "link_to": "Employee Checkin", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Leaves", - "link_count": 10, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Holiday List", - "link_count": 0, - "link_to": "Holiday List", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Type", - "link_count": 0, - "link_to": "Leave Type", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Period", - "link_count": 0, - "link_to": "Leave Period", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Leave Type", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Policy", - "link_count": 0, - "link_to": "Leave Policy", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Leave Policy", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Policy Assignment", - "link_count": 0, - "link_to": "Leave Policy Assignment", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Application", - "link_count": 0, - "link_to": "Leave Application", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Allocation", - "link_count": 0, - "link_to": "Leave Allocation", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Encashment", - "link_count": 0, - "link_to": "Leave Encashment", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Leave Block List", - "link_count": 0, - "link_to": "Leave Block List", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Employee", - "hidden": 0, - "is_query_report": 0, - "label": "Compensatory Leave Request", - "link_count": 0, - "link_to": "Compensatory Leave Request", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Shift Management", - "link_count": 3, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Shift Type", - "link_count": 0, - "link_to": "Shift Type", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Shift Request", - "link_count": 0, - "link_to": "Shift Request", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Shift Assignment", - "link_count": 0, - "link_to": "Shift Assignment", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - } - ], - "modified": "2022-01-13 17:38:45.489128", - "modified_by": "Administrator", - "module": "HR", - "name": "HR", - "owner": "Administrator", - "parent_page": "", - "public": 1, - "restrict_to_domain": "", - "roles": [], - "sequence_id": 14.0, - "shortcuts": [ - { - "color": "Green", - "format": "{} Active", - "label": "Employee", - "link_to": "Employee", - "stats_filter": "{\"status\":\"Active\"}", - "type": "DocType" - }, - { - "color": "Yellow", - "format": "{} Open", - "label": "Leave Application", - "link_to": "Leave Application", - "stats_filter": "{\"status\":\"Open\"}", - "type": "DocType" - }, - { - "label": "Attendance", - "link_to": "Attendance", - "stats_filter": "", - "type": "DocType" - }, - { - "label": "Job Applicant", - "link_to": "Job Applicant", - "type": "DocType" - }, - { - "label": "Monthly Attendance Sheet", - "link_to": "Monthly Attendance Sheet", - "type": "Report" - }, - { - "format": "{} Open", - "label": "Dashboard", - "link_to": "Human Resource", - "stats_filter": "{\n \"status\": \"Open\"\n}", - "type": "Dashboard" - } - ], - "title": "HR" -} \ No newline at end of file diff --git a/erpnext/modules.txt b/erpnext/modules.txt index 869166b939..cd061f73ab 100644 --- a/erpnext/modules.txt +++ b/erpnext/modules.txt @@ -4,7 +4,6 @@ Buying Projects Selling Setup -HR Manufacturing Stock Support @@ -17,7 +16,6 @@ ERPNext Integrations Quality Management Communication Loan Management -Payroll Telephony Bulk Transaction E-commerce diff --git a/erpnext/payroll/__init__.py b/erpnext/payroll/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/dashboard_chart/department_wise_salary(last_month)/department_wise_salary(last_month).json b/erpnext/payroll/dashboard_chart/department_wise_salary(last_month)/department_wise_salary(last_month).json deleted file mode 100644 index 61ae86ff02..0000000000 --- a/erpnext/payroll/dashboard_chart/department_wise_salary(last_month)/department_wise_salary(last_month).json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "aggregate_function_based_on": "rounded_total", - "chart_name": "Department Wise Salary(Last Month)", - "chart_type": "Group By", - "creation": "2020-07-22 11:56:34.511940", - "custom_options": "", - "docstatus": 0, - "doctype": "Dashboard Chart", - "document_type": "Salary Slip", - "dynamic_filters_json": "[[\"Salary Slip\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", - "filters_json": "[[\"Salary Slip\",\"docstatus\",\"=\",\"1\",false],[\"Salary Slip\",\"start_date\",\"Timespan\",\"last month\",false]]", - "group_by_based_on": "department", - "group_by_type": "Sum", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "last_synced_on": "2020-07-22 12:46:05.272076", - "modified": "2020-07-22 12:48:12.080992", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Department Wise Salary(Last Month)", - "number_of_groups": 0, - "owner": "Administrator", - "time_interval": "Monthly", - "timeseries": 0, - "timespan": "Last Year", - "type": "Bar", - "use_report_chart": 0, - "y_axis": [] -} \ No newline at end of file diff --git a/erpnext/payroll/dashboard_chart/designation_wise_salary(last_month)/designation_wise_salary(last_month).json b/erpnext/payroll/dashboard_chart/designation_wise_salary(last_month)/designation_wise_salary(last_month).json deleted file mode 100644 index b3c4e59395..0000000000 --- a/erpnext/payroll/dashboard_chart/designation_wise_salary(last_month)/designation_wise_salary(last_month).json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "aggregate_function_based_on": "rounded_total", - "chart_name": "Designation Wise Salary(Last Month)", - "chart_type": "Group By", - "creation": "2020-07-22 11:56:34.550339", - "custom_options": "", - "docstatus": 0, - "doctype": "Dashboard Chart", - "document_type": "Salary Slip", - "dynamic_filters_json": "[[\"Salary Slip\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", - "filters_json": "[[\"Salary Slip\",\"docstatus\",\"=\",\"1\",false],[\"Salary Slip\",\"start_date\",\"Timespan\",\"last month\",false]]", - "group_by_based_on": "designation", - "group_by_type": "Sum", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "last_synced_on": "2020-07-22 12:22:18.412822", - "modified": "2020-07-22 12:39:07.923382", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Designation Wise Salary(Last Month)", - "number_of_groups": 0, - "owner": "Administrator", - "time_interval": "Monthly", - "timeseries": 0, - "timespan": "Last Year", - "type": "Bar", - "use_report_chart": 0, - "y_axis": [] -} \ No newline at end of file diff --git a/erpnext/payroll/dashboard_chart/outgoing_salary/outgoing_salary.json b/erpnext/payroll/dashboard_chart/outgoing_salary/outgoing_salary.json deleted file mode 100644 index c77c8a5a36..0000000000 --- a/erpnext/payroll/dashboard_chart/outgoing_salary/outgoing_salary.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "based_on": "end_date", - "chart_name": "Outgoing Salary", - "chart_type": "Sum", - "creation": "2020-07-22 11:56:34.478848", - "custom_options": "", - "docstatus": 0, - "doctype": "Dashboard Chart", - "document_type": "Salary Slip", - "dynamic_filters_json": "[[\"Salary Slip\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", - "filters_json": "[[\"Salary Slip\",\"docstatus\",\"=\",\"1\",false]]", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "last_synced_on": "2020-07-22 12:11:27.481231", - "modified": "2020-07-22 12:20:05.777715", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Outgoing Salary", - "number_of_groups": 0, - "owner": "Administrator", - "time_interval": "Monthly", - "timeseries": 1, - "timespan": "Last Year", - "type": "Line", - "use_report_chart": 0, - "value_based_on": "rounded_total", - "y_axis": [] -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/__init__.py b/erpnext/payroll/doctype/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/additional_salary/__init__.py b/erpnext/payroll/doctype/additional_salary/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.js b/erpnext/payroll/doctype/additional_salary/additional_salary.js deleted file mode 100644 index 24ffce537c..0000000000 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.js +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Additional Salary', { - setup: function(frm) { - frm.add_fetch("salary_component", "deduct_full_tax_on_selected_payroll_date", "deduct_full_tax_on_selected_payroll_date"); - - frm.set_query("employee", function() { - return { - filters: { - company: frm.doc.company - } - }; - }); - }, - - onload: function(frm) { - if (frm.doc.type) { - frm.trigger('set_component_query'); - } - }, - - employee: function(frm) { - if (frm.doc.employee) { - frappe.run_serially([ - () => frm.trigger('get_employee_currency'), - () => frm.trigger('set_company') - ]); - } else { - frm.set_value("company", null); - } - }, - - set_company: function(frm) { - frappe.call({ - method: "frappe.client.get_value", - args: { - doctype: "Employee", - fieldname: "company", - filters: { - name: frm.doc.employee - } - }, - callback: function(data) { - if (data.message) { - frm.set_value("company", data.message.company); - } - } - }); - }, - - company: function(frm) { - frm.set_value("type", ""); - frm.trigger('set_component_query'); - }, - - set_component_query: function(frm) { - if (!frm.doc.company) return; - let filters = {company: frm.doc.company}; - if (frm.doc.type) { - filters.type = frm.doc.type; - } - frm.set_query("salary_component", function() { - return { - filters: filters - }; - }); - }, - - get_employee_currency: function(frm) { - frappe.call({ - method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", - args: { - employee: frm.doc.employee, - }, - callback: function(r) { - if (r.message) { - frm.set_value('currency', r.message); - frm.refresh_fields(); - } - } - }); - }, -}); diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.json b/erpnext/payroll/doctype/additional_salary/additional_salary.json deleted file mode 100644 index 9c897a7c69..0000000000 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.json +++ /dev/null @@ -1,249 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "naming_series:", - "creation": "2018-05-10 12:04:08.396461", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee_details_section", - "naming_series", - "employee", - "employee_name", - "column_break_5", - "company", - "department", - "salary_details_section", - "salary_component", - "type", - "currency", - "amount", - "column_break_13", - "is_recurring", - "payroll_date", - "from_date", - "to_date", - "properties_and_references_section", - "deduct_full_tax_on_selected_payroll_date", - "ref_doctype", - "ref_docname", - "column_break_22", - "overwrite_salary_structure_amount", - "amended_from" - ], - "fields": [ - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "options": "HR-ADS-.YY.-.MM.-", - "reqd": 1 - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "salary_component", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Salary Component", - "options": "Salary Component", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Amount", - "options": "currency", - "reqd": 1 - }, - { - "default": "1", - "fieldname": "overwrite_salary_structure_amount", - "fieldtype": "Check", - "label": "Overwrite Salary Structure Amount" - }, - { - "default": "0", - "fieldname": "deduct_full_tax_on_selected_payroll_date", - "fieldtype": "Check", - "label": "Deduct Full Tax on Selected Payroll Date" - }, - { - "fieldname": "column_break_5", - "fieldtype": "Column Break" - }, - { - "depends_on": "eval:(doc.is_recurring==0)", - "description": "The date on which Salary Component with Amount will contribute for Earnings/Deduction in Salary Slip. ", - "fieldname": "payroll_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Payroll Date", - "mandatory_depends_on": "eval:(doc.is_recurring==0)", - "search_index": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Employee Name", - "read_only": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fetch_from": "salary_component.type", - "fieldname": "type", - "fieldtype": "Data", - "label": "Salary Component Type", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Additional Salary", - "print_hide": 1, - "read_only": 1 - }, - { - "default": "0", - "fieldname": "is_recurring", - "fieldtype": "Check", - "label": "Is Recurring" - }, - { - "depends_on": "eval:(doc.is_recurring==1)", - "fieldname": "from_date", - "fieldtype": "Date", - "label": "From Date", - "mandatory_depends_on": "eval:(doc.is_recurring==1)" - }, - { - "depends_on": "eval:(doc.is_recurring==1)", - "fieldname": "to_date", - "fieldtype": "Date", - "label": "To Date", - "mandatory_depends_on": "eval:(doc.is_recurring==1)" - }, - { - "fieldname": "ref_doctype", - "fieldtype": "Link", - "label": "Reference Document Type", - "options": "DocType", - "read_only": 1 - }, - { - "fieldname": "ref_docname", - "fieldtype": "Dynamic Link", - "label": "Reference Document", - "no_copy": 1, - "options": "ref_doctype", - "read_only": 1 - }, - { - "depends_on": "eval:(doc.docstatus==1 || doc.employee)", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "print_hide": 1, - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "employee_details_section", - "fieldtype": "Section Break", - "label": "Employee Details" - }, - { - "fieldname": "column_break_13", - "fieldtype": "Column Break" - }, - { - "fieldname": "column_break_22", - "fieldtype": "Column Break" - }, - { - "fieldname": "salary_details_section", - "fieldtype": "Section Break", - "label": "Salary Details" - }, - { - "fieldname": "properties_and_references_section", - "fieldtype": "Section Break", - "label": "Properties and References" - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-19 12:56:51.765353", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Additional Salary", - "naming_rule": "By \"Naming Series\" field", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py deleted file mode 100644 index 18bd3b7733..0000000000 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _, bold -from frappe.model.document import Document -from frappe.utils import comma_and, date_diff, formatdate, getdate - -from erpnext.hr.utils import validate_active_employee - - -class AdditionalSalary(Document): - def on_submit(self): - self.update_return_amount_in_employee_advance() - self.update_employee_referral() - - def on_cancel(self): - self.update_return_amount_in_employee_advance() - self.update_employee_referral(cancel=True) - - def validate(self): - validate_active_employee(self.employee) - self.validate_dates() - self.validate_salary_structure() - self.validate_recurring_additional_salary_overlap() - self.validate_employee_referral() - - if self.amount < 0: - frappe.throw(_("Amount should not be less than zero")) - - def validate_salary_structure(self): - if not frappe.db.exists("Salary Structure Assignment", {"employee": self.employee}): - frappe.throw( - _("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format( - self.employee - ) - ) - - def validate_recurring_additional_salary_overlap(self): - if self.is_recurring: - additional_salaries = frappe.db.sql( - """ - SELECT - name - FROM `tabAdditional Salary` - WHERE - employee=%s - AND name <> %s - AND docstatus=1 - AND is_recurring=1 - AND salary_component = %s - AND to_date >= %s - AND from_date <= %s""", - (self.employee, self.name, self.salary_component, self.from_date, self.to_date), - as_dict=1, - ) - - additional_salaries = [salary.name for salary in additional_salaries] - - if additional_salaries and len(additional_salaries): - frappe.throw( - _( - "Additional Salary: {0} already exist for Salary Component: {1} for period {2} and {3}" - ).format( - bold(comma_and(additional_salaries)), - bold(self.salary_component), - bold(formatdate(self.from_date)), - bold(formatdate(self.to_date)), - ) - ) - - def validate_dates(self): - date_of_joining, relieving_date = frappe.db.get_value( - "Employee", self.employee, ["date_of_joining", "relieving_date"] - ) - - if getdate(self.from_date) > getdate(self.to_date): - frappe.throw(_("From Date can not be greater than To Date.")) - - if date_of_joining: - if self.payroll_date and getdate(self.payroll_date) < getdate(date_of_joining): - frappe.throw(_("Payroll date can not be less than employee's joining date.")) - elif self.from_date and getdate(self.from_date) < getdate(date_of_joining): - frappe.throw(_("From date can not be less than employee's joining date.")) - - if relieving_date: - if self.to_date and getdate(self.to_date) > getdate(relieving_date): - frappe.throw(_("To date can not be greater than employee's relieving date.")) - if self.payroll_date and getdate(self.payroll_date) > getdate(relieving_date): - frappe.throw(_("Payroll date can not be greater than employee's relieving date.")) - - def validate_employee_referral(self): - if self.ref_doctype == "Employee Referral": - referral_details = frappe.db.get_value( - "Employee Referral", - self.ref_docname, - ["is_applicable_for_referral_bonus", "status"], - as_dict=1, - ) - - if not referral_details.is_applicable_for_referral_bonus: - frappe.throw( - _("Employee Referral {0} is not applicable for referral bonus.").format(self.ref_docname) - ) - - if self.type == "Deduction": - frappe.throw(_("Earning Salary Component is required for Employee Referral Bonus.")) - - if referral_details.status != "Accepted": - frappe.throw( - _( - "Additional Salary for referral bonus can only be created against Employee Referral with status {0}" - ).format(frappe.bold("Accepted")) - ) - - def update_return_amount_in_employee_advance(self): - if self.ref_doctype == "Employee Advance" and self.ref_docname: - return_amount = frappe.db.get_value("Employee Advance", self.ref_docname, "return_amount") - - if self.docstatus == 2: - return_amount -= self.amount - else: - return_amount += self.amount - - frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", return_amount) - advance = frappe.get_doc("Employee Advance", self.ref_docname) - advance.set_status(update=True) - - def update_employee_referral(self, cancel=False): - if self.ref_doctype == "Employee Referral": - status = "Unpaid" if cancel else "Paid" - frappe.db.set_value("Employee Referral", self.ref_docname, "referral_payment_status", status) - - def get_amount(self, sal_start_date, sal_end_date): - start_date = getdate(sal_start_date) - end_date = getdate(sal_end_date) - total_days = date_diff(getdate(self.to_date), getdate(self.from_date)) + 1 - amount_per_day = self.amount / total_days - if getdate(sal_start_date) <= getdate(self.from_date): - start_date = getdate(self.from_date) - if getdate(sal_end_date) > getdate(self.to_date): - end_date = getdate(self.to_date) - no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1 - return amount_per_day * no_of_days - - -@frappe.whitelist() -def get_additional_salaries(employee, start_date, end_date, component_type): - from frappe.query_builder import Criterion - - comp_type = "Earning" if component_type == "earnings" else "Deduction" - - additional_sal = frappe.qb.DocType("Additional Salary") - component_field = additional_sal.salary_component.as_("component") - overwrite_field = additional_sal.overwrite_salary_structure_amount.as_("overwrite") - - additional_salary_list = ( - frappe.qb.from_(additional_sal) - .select( - additional_sal.name, - component_field, - additional_sal.type, - additional_sal.amount, - additional_sal.is_recurring, - overwrite_field, - additional_sal.deduct_full_tax_on_selected_payroll_date, - ) - .where( - (additional_sal.employee == employee) - & (additional_sal.docstatus == 1) - & (additional_sal.type == comp_type) - ) - .where( - Criterion.any( - [ - Criterion.all( - [ # is recurring and additional salary dates fall within the payroll period - additional_sal.is_recurring == 1, - additional_sal.from_date <= end_date, - additional_sal.to_date >= end_date, - ] - ), - Criterion.all( - [ # is not recurring and additional salary's payroll date falls within the payroll period - additional_sal.is_recurring == 0, - additional_sal.payroll_date[start_date:end_date], - ] - ), - ] - ) - ) - .run(as_dict=True) - ) - - additional_salaries = [] - components_to_overwrite = [] - - for d in additional_salary_list: - if d.overwrite: - if d.component in components_to_overwrite: - frappe.throw( - _( - "Multiple Additional Salaries with overwrite property exist for Salary Component {0} between {1} and {2}." - ).format(frappe.bold(d.component), start_date, end_date), - title=_("Error"), - ) - - components_to_overwrite.append(d.component) - - additional_salaries.append(d) - - return additional_salaries diff --git a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py deleted file mode 100644 index bd739368a0..0000000000 --- a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days, add_months, nowdate - -import erpnext -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component -from erpnext.payroll.doctype.salary_slip.test_salary_slip import ( - make_employee_salary_slip, - setup_test, -) -from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - - -class TestAdditionalSalary(FrappeTestCase): - def setUp(self): - setup_test() - - def test_recurring_additional_salary(self): - amount = 0 - salary_component = None - emp_id = make_employee("test_additional@salary.com") - frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(nowdate(), 1800)) - salary_structure = make_salary_structure( - "Test Salary Structure Additional Salary", "Monthly", employee=emp_id - ) - add_sal = get_additional_salary(emp_id) - - ss = make_employee_salary_slip( - "test_additional@salary.com", "Monthly", salary_structure=salary_structure.name - ) - for earning in ss.earnings: - if earning.salary_component == "Recurring Salary Component": - amount = earning.amount - salary_component = earning.salary_component - break - - self.assertEqual(amount, add_sal.amount) - self.assertEqual(salary_component, add_sal.salary_component) - - def test_non_recurring_additional_salary(self): - amount = 0 - salary_component = None - date = nowdate() - - emp_id = make_employee("test_additional@salary.com") - frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(date, 1800)) - salary_structure = make_salary_structure( - "Test Salary Structure Additional Salary", "Monthly", employee=emp_id - ) - add_sal = get_additional_salary(emp_id, recurring=False, payroll_date=date) - - ss = make_employee_salary_slip( - "test_additional@salary.com", "Monthly", salary_structure=salary_structure.name - ) - - amount, salary_component = None, None - for earning in ss.earnings: - if earning.salary_component == "Recurring Salary Component": - amount = earning.amount - salary_component = earning.salary_component - break - - self.assertEqual(amount, add_sal.amount) - self.assertEqual(salary_component, add_sal.salary_component) - - # should not show up in next months - ss.posting_date = add_months(date, 1) - ss.start_date = ss.end_date = None - ss.earnings = [] - ss.deductions = [] - ss.save() - - amount, salary_component = None, None - for earning in ss.earnings: - if earning.salary_component == "Recurring Salary Component": - amount = earning.amount - salary_component = earning.salary_component - break - - self.assertIsNone(amount) - self.assertIsNone(salary_component) - - -def get_additional_salary(emp_id, recurring=True, payroll_date=None): - create_salary_component("Recurring Salary Component") - add_sal = frappe.new_doc("Additional Salary") - add_sal.employee = emp_id - add_sal.salary_component = "Recurring Salary Component" - - add_sal.is_recurring = 1 if recurring else 0 - add_sal.from_date = add_days(nowdate(), -50) - add_sal.to_date = add_days(nowdate(), 180) - add_sal.payroll_date = payroll_date - - add_sal.amount = 5000 - add_sal.currency = erpnext.get_default_currency() - add_sal.save() - add_sal.submit() - - return add_sal diff --git a/erpnext/payroll/doctype/employee_benefit_application/__init__.py b/erpnext/payroll/doctype/employee_benefit_application/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js deleted file mode 100644 index 6756cd93e7..0000000000 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Benefit Application', { - employee: function(frm) { - if (frm.doc.employee) { - frappe.run_serially([ - () => frm.trigger('get_employee_currency'), - () => frm.trigger('set_earning_component') - ]); - } - var method, args; - if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){ - method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; - args = { - employee: frm.doc.employee, - on_date: frm.doc.date, - payroll_period: frm.doc.payroll_period - }; - get_max_benefits(frm, method, args); - } - else if(frm.doc.employee && frm.doc.date){ - method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits"; - args = { - employee: frm.doc.employee, - on_date: frm.doc.date - }; - get_max_benefits(frm, method, args); - } - }, - - date: function(frm) { - frm.trigger('set_earning_component'); - }, - - set_earning_component: function(frm) { - if(!frm.doc.employee && !frm.doc.date) return; - frm.set_query("earning_component", "employee_benefits", function() { - return { - query : "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", - filters: {date: frm.doc.date, employee: frm.doc.employee} - }; - }); - }, - - get_employee_currency: function(frm) { - if (frm.doc.employee) { - frappe.call({ - method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", - args: { - employee: frm.doc.employee, - }, - callback: function(r) { - if (r.message) { - frm.set_value('currency', r.message); - frm.refresh_fields(); - } - } - }); - } - }, - - payroll_period: function(frm) { - var method, args; - if (frm.doc.employee && frm.doc.date && frm.doc.payroll_period) { - method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; - args = { - employee: frm.doc.employee, - on_date: frm.doc.date, - payroll_period: frm.doc.payroll_period - }; - get_max_benefits(frm, method, args); - } - }, - max_benefits: function(frm) { - calculate_all(frm.doc); - } -}); - -var get_max_benefits=function(frm, method, args) { - frappe.call({ - method: method, - args: args, - callback: function (data) { - if (!data.exc) { - if (data.message) { - frm.set_value("max_benefits", data.message); - } else { - frm.set_value("max_benefits", 0); - } - } - frm.refresh_fields(); - } - }); -}; - -frappe.ui.form.on("Employee Benefit Application Detail",{ - amount: function(frm) { - calculate_all(frm.doc); - }, - employee_benefits_remove: function(frm) { - calculate_all(frm.doc); - } -}); - -var calculate_all = function(doc) { - var tbl = doc.employee_benefits || []; - var pro_rata_dispensed_amount = 0; - var total_amount = 0; - if (doc.max_benefits === 0) { - doc.employee_benefits = []; - } else { - for (var i = 0; i < tbl.length; i++) { - if (cint(tbl[i].amount) > 0) { - total_amount += flt(tbl[i].amount); - } - if (tbl[i].pay_against_benefit_claim != 1) { - pro_rata_dispensed_amount += flt(tbl[i].amount); - } - } - } - - doc.total_amount = total_amount; - doc.remaining_benefit = doc.max_benefits - total_amount; - doc.pro_rata_dispensed_amount = pro_rata_dispensed_amount; - refresh_many(['pro_rata_dispensed_amount', 'total_amount','remaining_benefit']); -}; diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json deleted file mode 100644 index 2e4b64e9da..0000000000 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json +++ /dev/null @@ -1,222 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-BEN-APP-.YY.-.MM.-.#####", - "creation": "2018-04-13 16:31:39.190787", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "currency", - "max_benefits", - "remaining_benefit", - "column_break_2", - "date", - "payroll_period", - "department", - "company", - "amended_from", - "section_break_4", - "employee_benefits", - "totals", - "total_amount", - "column_break", - "pro_rata_dispensed_amount" - ], - "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 - }, - { - "fieldname": "max_benefits", - "fieldtype": "Currency", - "label": "Max Benefits (Yearly)", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "remaining_benefit", - "fieldtype": "Currency", - "label": "Remaining Benefits (Yearly)", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "default": "Today", - "fieldname": "date", - "fieldtype": "Date", - "label": "Date", - "reqd": 1 - }, - { - "fieldname": "payroll_period", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Payroll Period", - "options": "Payroll Period", - "reqd": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Employee Benefit Application", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "label": "Benefits Applied" - }, - { - "fieldname": "employee_benefits", - "fieldtype": "Table", - "label": "Employee Benefits", - "options": "Employee Benefit Application Detail", - "reqd": 1 - }, - { - "fieldname": "totals", - "fieldtype": "Section Break", - "label": "Totals" - }, - { - "fieldname": "total_amount", - "fieldtype": "Currency", - "label": "Total Amount", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "pro_rata_dispensed_amount", - "fieldtype": "Currency", - "label": "Dispensed Amount (Pro-rated)", - "options": "currency", - "read_only": 1 - }, - { - "depends_on": "eval:(doc.docstatus==1 || doc.employee)", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "read_only": 1, - "reqd": 1 - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "column_break", - "fieldtype": "Column Break" - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-19 12:58:31.664468", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Employee Benefit Application", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "search_fields": "employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py deleted file mode 100644 index 8df1bb6e87..0000000000 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py +++ /dev/null @@ -1,371 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import add_days, cstr, date_diff, flt, getdate, rounded - -from erpnext.hr.utils import ( - get_holiday_dates_for_employee, - get_previous_claimed_amount, - get_sal_slip_total_benefit_given, - validate_active_employee, -) -from erpnext.payroll.doctype.payroll_period.payroll_period import ( - get_payroll_period_days, - get_period_factor, -) -from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import ( - get_assigned_salary_structure, -) - - -class EmployeeBenefitApplication(Document): - def validate(self): - validate_active_employee(self.employee) - self.validate_duplicate_on_payroll_period() - if not self.max_benefits: - self.max_benefits = flt( - get_max_benefits_remaining(self.employee, self.date, self.payroll_period), - self.precision("max_benefits"), - ) - if self.max_benefits and self.max_benefits > 0: - self.validate_max_benefit_for_component() - self.validate_prev_benefit_claim() - if self.remaining_benefit and self.remaining_benefit > 0: - self.validate_remaining_benefit_amount() - else: - frappe.throw( - _("As per your assigned Salary Structure you cannot apply for benefits").format(self.employee) - ) - - def validate_prev_benefit_claim(self): - if self.employee_benefits: - for benefit in self.employee_benefits: - if benefit.pay_against_benefit_claim == 1: - payroll_period = frappe.get_doc("Payroll Period", self.payroll_period) - benefit_claimed = get_previous_claimed_amount( - self.employee, payroll_period, component=benefit.earning_component - ) - benefit_given = get_sal_slip_total_benefit_given( - self.employee, payroll_period, component=benefit.earning_component - ) - benefit_claim_remining = benefit_claimed - benefit_given - if benefit_claimed > 0 and benefit_claim_remining > benefit.amount: - frappe.throw( - _( - "An amount of {0} already claimed for the component {1}, set the amount equal or greater than {2}" - ).format( - benefit_claimed, benefit.earning_component, benefit_claim_remining - ) - ) - - def validate_remaining_benefit_amount(self): - # check salary structure earnings have flexi component (sum of max_benefit_amount) - # without pro-rata which satisfy the remaining_benefit - # else pro-rata component for the amount - # again comes the same validation and satisfy or throw - benefit_components = [] - if self.employee_benefits: - for employee_benefit in self.employee_benefits: - benefit_components.append(employee_benefit.earning_component) - salary_struct_name = get_assigned_salary_structure(self.employee, self.date) - if salary_struct_name: - non_pro_rata_amount = 0 - pro_rata_amount = 0 - salary_structure = frappe.get_doc("Salary Structure", salary_struct_name) - if salary_structure.earnings: - for earnings in salary_structure.earnings: - if earnings.is_flexible_benefit == 1 and earnings.salary_component not in benefit_components: - pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value( - "Salary Component", - earnings.salary_component, - ["pay_against_benefit_claim", "max_benefit_amount"], - ) - if pay_against_benefit_claim != 1: - pro_rata_amount += max_benefit_amount - else: - non_pro_rata_amount += max_benefit_amount - - if pro_rata_amount == 0 and non_pro_rata_amount == 0: - frappe.throw( - _("Please add the remaining benefits {0} to any of the existing component").format( - self.remaining_benefit - ) - ) - elif non_pro_rata_amount > 0 and non_pro_rata_amount < rounded(self.remaining_benefit): - frappe.throw( - _( - "You can claim only an amount of {0}, the rest amount {1} should be in the application as pro-rata component" - ).format(non_pro_rata_amount, self.remaining_benefit - non_pro_rata_amount) - ) - elif non_pro_rata_amount == 0: - frappe.throw( - _("Please add the remaining benefits {0} to the application as pro-rata component").format( - self.remaining_benefit - ) - ) - - def validate_max_benefit_for_component(self): - if self.employee_benefits: - max_benefit_amount = 0 - for employee_benefit in self.employee_benefits: - self.validate_max_benefit(employee_benefit.earning_component) - max_benefit_amount += flt(employee_benefit.amount) - if max_benefit_amount > self.max_benefits: - frappe.throw( - _("Maximum benefit amount of employee {0} exceeds {1}").format( - self.employee, self.max_benefits - ) - ) - - def validate_max_benefit(self, earning_component_name): - max_benefit_amount = frappe.db.get_value( - "Salary Component", earning_component_name, "max_benefit_amount" - ) - benefit_amount = 0 - for employee_benefit in self.employee_benefits: - if employee_benefit.earning_component == earning_component_name: - benefit_amount += flt(employee_benefit.amount) - - prev_sal_slip_flexi_amount = get_sal_slip_total_benefit_given( - self.employee, frappe.get_doc("Payroll Period", self.payroll_period), earning_component_name - ) - benefit_amount += prev_sal_slip_flexi_amount - if rounded(benefit_amount, 2) > max_benefit_amount: - frappe.throw( - _("Maximum benefit amount of component {0} exceeds {1}").format( - earning_component_name, max_benefit_amount - ) - ) - - def validate_duplicate_on_payroll_period(self): - application = frappe.db.exists( - "Employee Benefit Application", - {"employee": self.employee, "payroll_period": self.payroll_period, "docstatus": 1}, - ) - if application: - frappe.throw( - _("Employee {0} already submited an apllication {1} for the payroll period {2}").format( - self.employee, application, self.payroll_period - ) - ) - - -@frappe.whitelist() -def get_max_benefits(employee, on_date): - sal_struct = get_assigned_salary_structure(employee, on_date) - if sal_struct: - max_benefits = frappe.db.get_value("Salary Structure", sal_struct, "max_benefits") - if max_benefits > 0: - return max_benefits - return False - - -@frappe.whitelist() -def get_max_benefits_remaining(employee, on_date, payroll_period): - max_benefits = get_max_benefits(employee, on_date) - if max_benefits and max_benefits > 0: - have_depends_on_payment_days = False - per_day_amount_total = 0 - payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[1] - payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period) - - # Get all salary slip flexi amount in the payroll period - prev_sal_slip_flexi_total = get_sal_slip_total_benefit_given(employee, payroll_period_obj) - - if prev_sal_slip_flexi_total > 0: - # Check salary structure hold depends_on_payment_days component - # If yes then find the amount per day of each component and find the sum - sal_struct_name = get_assigned_salary_structure(employee, on_date) - if sal_struct_name: - sal_struct = frappe.get_doc("Salary Structure", sal_struct_name) - for sal_struct_row in sal_struct.get("earnings"): - salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component) - if ( - salary_component.depends_on_payment_days == 1 - and salary_component.pay_against_benefit_claim != 1 - ): - have_depends_on_payment_days = True - benefit_amount = get_benefit_amount_based_on_pro_rata( - sal_struct, salary_component.max_benefit_amount - ) - amount_per_day = benefit_amount / payroll_period_days - per_day_amount_total += amount_per_day - - # Then the sum multiply with the no of lwp in that period - # Include that amount to the prev_sal_slip_flexi_total to get the actual - if have_depends_on_payment_days and per_day_amount_total > 0: - holidays = get_holiday_dates_for_employee(employee, payroll_period_obj.start_date, on_date) - working_days = date_diff(on_date, payroll_period_obj.start_date) + 1 - leave_days = calculate_lwp(employee, payroll_period_obj.start_date, holidays, working_days) - leave_days_amount = leave_days * per_day_amount_total - prev_sal_slip_flexi_total += leave_days_amount - - return max_benefits - prev_sal_slip_flexi_total - return max_benefits - - -def calculate_lwp(employee, start_date, holidays, working_days): - lwp = 0 - holidays = "','".join(holidays) - - for d in range(working_days): - date = add_days(cstr(getdate(start_date)), d) - - LeaveApplication = frappe.qb.DocType("Leave Application") - LeaveType = frappe.qb.DocType("Leave Type") - - is_half_day = ( - frappe.qb.terms.Case() - .when( - ( - (LeaveApplication.half_day_date == date) - | (LeaveApplication.from_date == LeaveApplication.to_date) - ), - LeaveApplication.half_day, - ) - .else_(0) - ).as_("is_half_day") - - query = ( - frappe.qb.from_(LeaveApplication) - .inner_join(LeaveType) - .on((LeaveType.name == LeaveApplication.leave_type)) - .select(LeaveApplication.name, is_half_day) - .where( - (LeaveType.is_lwp == 1) - & (LeaveApplication.docstatus == 1) - & (LeaveApplication.status == "Approved") - & (LeaveApplication.employee == employee) - & ((LeaveApplication.from_date <= date) & (date <= LeaveApplication.to_date)) - ) - ) - - # if it's a holiday only include if leave type has "include holiday" enabled - if date in holidays: - query = query.where((LeaveType.include_holiday == "1")) - leaves = query.run(as_dict=True) - - if leaves: - lwp += 0.5 if leaves[0].is_half_day else 1 - - return lwp - - -def get_benefit_component_amount( - employee, start_date, end_date, salary_component, sal_struct, payroll_frequency, payroll_period -): - if not payroll_period: - frappe.msgprint( - _("Start and end dates not in a valid Payroll Period, cannot calculate {0}").format( - salary_component - ) - ) - return False - - # Considering there is only one application for a year - benefit_application = frappe.db.sql( - """ - select name - from `tabEmployee Benefit Application` - where - payroll_period=%(payroll_period)s - and employee=%(employee)s - and docstatus = 1 - """, - {"employee": employee, "payroll_period": payroll_period.name}, - ) - - current_benefit_amount = 0.0 - component_max_benefit, depends_on_payment_days = frappe.db.get_value( - "Salary Component", salary_component, ["max_benefit_amount", "depends_on_payment_days"] - ) - - benefit_amount = 0 - if benefit_application: - benefit_amount = frappe.db.get_value( - "Employee Benefit Application Detail", - {"parent": benefit_application[0][0], "earning_component": salary_component}, - "amount", - ) - elif component_max_benefit: - benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit) - - current_benefit_amount = 0 - if benefit_amount: - total_sub_periods = get_period_factor( - employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days - )[0] - - current_benefit_amount = benefit_amount / total_sub_periods - - return current_benefit_amount - - -def get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit): - max_benefits_total = 0 - benefit_amount = 0 - for d in sal_struct.get("earnings"): - if d.is_flexible_benefit == 1: - component = frappe.db.get_value( - "Salary Component", - d.salary_component, - ["max_benefit_amount", "pay_against_benefit_claim"], - as_dict=1, - ) - if not component.pay_against_benefit_claim: - max_benefits_total += component.max_benefit_amount - - if max_benefits_total > 0: - benefit_amount = sal_struct.max_benefits * component.max_benefit_amount / max_benefits_total - if benefit_amount > component_max_benefit: - benefit_amount = component_max_benefit - - return benefit_amount - - -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def get_earning_components(doctype, txt, searchfield, start, page_len, filters): - if len(filters) < 2: - return {} - - salary_structure = get_assigned_salary_structure(filters["employee"], filters["date"]) - - if salary_structure: - return frappe.db.sql( - """ - select salary_component - from `tabSalary Detail` - where parent = %s and is_flexible_benefit = 1 - order by name - """, - salary_structure, - ) - else: - frappe.throw( - _("Salary Structure not found for employee {0} and date {1}").format( - filters["employee"], filters["date"] - ) - ) - - -@frappe.whitelist() -def get_earning_components_max_benefits(employee, date, earning_component): - salary_structure = get_assigned_salary_structure(employee, date) - amount = frappe.db.sql( - """ - select amount - from `tabSalary Detail` - where parent = %s and is_flexible_benefit = 1 - and salary_component = %s - order by name - """, - salary_structure, - earning_component, - ) - - return amount if amount else 0 diff --git a/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py deleted file mode 100644 index de8f9b6a7a..0000000000 --- a/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days, date_diff, get_year_ending, get_year_start, getdate - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list -from erpnext.hr.doctype.leave_application.test_leave_application import get_first_sunday -from erpnext.hr.utils import get_holiday_dates_for_employee -from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import ( - calculate_lwp, -) -from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import ( - create_payroll_period, -) -from erpnext.payroll.doctype.salary_slip.test_salary_slip import ( - make_holiday_list, - make_leave_application, -) -from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip -from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - - -class TestEmployeeBenefitApplication(FrappeTestCase): - def setUp(self): - date = getdate() - make_holiday_list(from_date=get_year_start(date), to_date=get_year_ending(date)) - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_employee_benefit_application(self): - payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company") - employee = make_employee("test_employee_benefits@salary.com", company="_Test Company") - first_sunday = get_first_sunday("Salary Slip Test Holiday List") - - leave_application = make_leave_application( - employee, - add_days(first_sunday, 1), - add_days(first_sunday, 3), - "Leave Without Pay", - half_day=1, - half_day_date=add_days(first_sunday, 1), - submit=True, - ) - - frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0) - salary_structure = make_salary_structure( - "Test Employee Benefits", - "Monthly", - other_details={"max_benefits": 100000}, - include_flexi_benefits=True, - employee=employee, - payroll_period=payroll_period, - ) - salary_slip = make_salary_slip(salary_structure.name, employee=employee, posting_date=getdate()) - salary_slip.insert() - salary_slip.submit() - - application = make_employee_benefit_application( - employee, payroll_period.name, date=leave_application.to_date - ) - self.assertEqual(application.employee_benefits[0].max_benefit_amount, 15000) - - holidays = get_holiday_dates_for_employee(employee, payroll_period.start_date, application.date) - working_days = date_diff(application.date, payroll_period.start_date) + 1 - lwp = calculate_lwp(employee, payroll_period.start_date, holidays, working_days) - self.assertEqual(lwp, 2.5) - - -def make_employee_benefit_application(employee, payroll_period, date): - frappe.db.delete("Employee Benefit Application") - - return frappe.get_doc( - { - "doctype": "Employee Benefit Application", - "employee": employee, - "date": date, - "payroll_period": payroll_period, - "employee_benefits": [{"earning_component": "Medical Allowance", "amount": 1500}], - } - ).insert() diff --git a/erpnext/payroll/doctype/employee_benefit_application_detail/__init__.py b/erpnext/payroll/doctype/employee_benefit_application_detail/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json deleted file mode 100644 index c93d356c20..0000000000 --- a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "actions": [], - "creation": "2018-04-13 16:36:18.389786", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "earning_component", - "pay_against_benefit_claim", - "max_benefit_amount", - "amount" - ], - "fields": [ - { - "fieldname": "earning_component", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Earning Component", - "options": "Salary Component", - "reqd": 1 - }, - { - "default": "0", - "fetch_from": "earning_component.pay_against_benefit_claim", - "fieldname": "pay_against_benefit_claim", - "fieldtype": "Check", - "label": "Pay Against Benefit Claim", - "read_only": 1 - }, - { - "fetch_from": "earning_component.max_benefit_amount", - "fieldname": "max_benefit_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Max Benefit Amount", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Amount", - "options": "currency", - "reqd": 1 - } - ], - "istable": 1, - "links": [], - "modified": "2020-09-29 16:22:15.783854", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Employee Benefit Application Detail", - "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/employee_benefit_application_detail/employee_benefit_application_detail.py b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py deleted file mode 100644 index 51aa2c9dcf..0000000000 --- a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class EmployeeBenefitApplicationDetail(Document): - pass diff --git a/erpnext/payroll/doctype/employee_benefit_claim/__init__.py b/erpnext/payroll/doctype/employee_benefit_claim/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js deleted file mode 100644 index e1f8431ec5..0000000000 --- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Benefit Claim', { - setup: function(frm) { - frm.set_query("earning_component", function() { - return { - query : "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", - filters: {date: frm.doc.claim_date, employee: frm.doc.employee} - }; - }); - }, - employee: function(frm) { - frm.set_value("earning_component", null); - if (frm.doc.employee) { - frappe.call({ - method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", - args: { - employee: frm.doc.employee, - }, - callback: function(r) { - if (r.message) { - frm.set_value('currency', r.message); - } - } - }); - } - if (!frm.doc.earning_component) { - frm.doc.max_amount_eligible = null; - frm.doc.claimed_amount = null; - } - frm.refresh_fields(); - } -}); diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json deleted file mode 100644 index 5deb0a5eca..0000000000 --- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json +++ /dev/null @@ -1,218 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "HR-BEN-CLM-.YY.-.MM.-.#####", - "creation": "2018-04-13 16:43:10.386409", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "department", - "column_break_3", - "claim_date", - "currency", - "company", - "benefit_type_and_amount", - "earning_component", - "max_amount_eligible", - "pay_against_benefit_claim", - "claimed_amount", - "salary_slip", - "amended_from", - "section_break_9", - "attachments" - ], - "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 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "default": "Today", - "fieldname": "claim_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Claim Date", - "reqd": 1 - }, - { - "fieldname": "benefit_type_and_amount", - "fieldtype": "Section Break", - "label": "Benefit Type and Amount" - }, - { - "fieldname": "earning_component", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Claim Benefit For", - "options": "Salary Component", - "reqd": 1 - }, - { - "fetch_from": "earning_component.max_benefit_amount", - "fieldname": "max_amount_eligible", - "fieldtype": "Currency", - "label": "Max Amount Eligible", - "options": "currency", - "read_only": 1 - }, - { - "default": "0", - "fetch_from": "earning_component.pay_against_benefit_claim", - "fieldname": "pay_against_benefit_claim", - "fieldtype": "Check", - "hidden": 1, - "label": "Pay Against Benefit Claim", - "read_only": 1 - }, - { - "fieldname": "claimed_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Claimed Amount", - "options": "currency", - "reqd": 1 - }, - { - "fieldname": "salary_slip", - "fieldtype": "Link", - "label": "Salary Slip", - "options": "Salary Slip", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Employee Benefit Claim", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "section_break_9", - "fieldtype": "Section Break", - "label": "Expense Proof" - }, - { - "fieldname": "attachments", - "fieldtype": "Attach", - "label": "Attachments" - }, - { - "depends_on": "eval: doc.employee", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "read_only": 1, - "reqd": 1 - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-19 12:59:15.699118", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Employee Benefit Claim", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "write": 1 - } - ], - "search_fields": "employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py deleted file mode 100644 index 6ec34b9e71..0000000000 --- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py +++ /dev/null @@ -1,241 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import flt - -from erpnext.hr.utils import get_previous_claimed_amount, validate_active_employee -from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import ( - get_max_benefits, -) -from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period -from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import ( - get_assigned_salary_structure, -) - - -class EmployeeBenefitClaim(Document): - def validate(self): - validate_active_employee(self.employee) - max_benefits = get_max_benefits(self.employee, self.claim_date) - if not max_benefits or max_benefits <= 0: - frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee)) - payroll_period = get_payroll_period( - self.claim_date, self.claim_date, frappe.db.get_value("Employee", self.employee, "company") - ) - if not payroll_period: - frappe.throw( - _("{0} is not in a valid Payroll Period").format( - frappe.format(self.claim_date, dict(fieldtype="Date")) - ) - ) - self.validate_max_benefit_for_component(payroll_period) - self.validate_max_benefit_for_sal_struct(max_benefits) - self.validate_benefit_claim_amount(max_benefits, payroll_period) - if self.pay_against_benefit_claim: - self.validate_non_pro_rata_benefit_claim(max_benefits, payroll_period) - - def validate_benefit_claim_amount(self, max_benefits, payroll_period): - claimed_amount = self.claimed_amount - claimed_amount += get_previous_claimed_amount(self.employee, payroll_period) - if max_benefits < claimed_amount: - frappe.throw( - _( - "Maximum benefit of employee {0} exceeds {1} by the sum {2} of previous claimed amount" - ).format(self.employee, max_benefits, claimed_amount - max_benefits) - ) - - def validate_max_benefit_for_sal_struct(self, max_benefits): - if self.claimed_amount > max_benefits: - frappe.throw( - _("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, max_benefits) - ) - - def validate_max_benefit_for_component(self, payroll_period): - if self.max_amount_eligible: - claimed_amount = self.claimed_amount - claimed_amount += get_previous_claimed_amount( - self.employee, payroll_period, component=self.earning_component - ) - if claimed_amount > self.max_amount_eligible: - frappe.throw( - _("Maximum amount eligible for the component {0} exceeds {1}").format( - self.earning_component, self.max_amount_eligible - ) - ) - - def validate_non_pro_rata_benefit_claim(self, max_benefits, payroll_period): - claimed_amount = self.claimed_amount - pro_rata_amount = self.get_pro_rata_amount_in_application(payroll_period.name) - if not pro_rata_amount: - pro_rata_amount = 0 - # Get pro_rata_amount if there is no application, - # get salary structure for the date and calculate pro-rata amount - sal_struct_name = get_assigned_salary_structure(self.employee, self.claim_date) - if sal_struct_name: - sal_struct = frappe.get_doc("Salary Structure", sal_struct_name) - pro_rata_amount = get_benefit_pro_rata_ratio_amount(self.employee, self.claim_date, sal_struct) - - claimed_amount += get_previous_claimed_amount(self.employee, payroll_period, non_pro_rata=True) - if max_benefits < pro_rata_amount + claimed_amount: - frappe.throw( - _( - "Maximum benefit of employee {0} exceeds {1} by the sum {2} of benefit application pro-rata component amount and previous claimed amount" - ).format( - self.employee, max_benefits, pro_rata_amount + claimed_amount - max_benefits - ) - ) - - def get_pro_rata_amount_in_application(self, payroll_period): - application = frappe.db.exists( - "Employee Benefit Application", - {"employee": self.employee, "payroll_period": payroll_period, "docstatus": 1}, - ) - if application: - return frappe.db.get_value( - "Employee Benefit Application", application, "pro_rata_dispensed_amount" - ) - return False - - -def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct): - total_pro_rata_max = 0 - benefit_amount_total = 0 - for sal_struct_row in sal_struct.get("earnings"): - try: - pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value( - "Salary Component", - sal_struct_row.salary_component, - ["pay_against_benefit_claim", "max_benefit_amount"], - ) - except TypeError: - # show the error in tests? - frappe.throw(_("Unable to find Salary Component {0}").format(sal_struct_row.salary_component)) - if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: - total_pro_rata_max += max_benefit_amount - if total_pro_rata_max > 0: - for sal_struct_row in sal_struct.get("earnings"): - pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value( - "Salary Component", - sal_struct_row.salary_component, - ["pay_against_benefit_claim", "max_benefit_amount"], - ) - - if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: - component_max = max_benefit_amount - benefit_amount = component_max * sal_struct.max_benefits / total_pro_rata_max - if benefit_amount > component_max: - benefit_amount = component_max - benefit_amount_total += benefit_amount - return benefit_amount_total - - -def get_benefit_claim_amount(employee, start_date, end_date, salary_component=None): - query = """ - select sum(claimed_amount) - from `tabEmployee Benefit Claim` - where - employee=%(employee)s - and docstatus = 1 - and pay_against_benefit_claim = 1 - and claim_date between %(start_date)s and %(end_date)s - """ - - if salary_component: - query += " and earning_component = %(earning_component)s" - - claimed_amount = flt( - frappe.db.sql( - query, - { - "employee": employee, - "start_date": start_date, - "end_date": end_date, - "earning_component": salary_component, - }, - )[0][0] - ) - - return claimed_amount - - -def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period): - pro_rata_amount = 0 - claimed_amount = 0 - application = frappe.db.exists( - "Employee Benefit Application", - {"employee": employee, "payroll_period": payroll_period.name, "docstatus": 1}, - ) - if application: - application_obj = frappe.get_doc("Employee Benefit Application", application) - pro_rata_amount = ( - application_obj.pro_rata_dispensed_amount - + application_obj.max_benefits - - application_obj.remaining_benefit - ) - else: - pro_rata_amount = get_benefit_pro_rata_ratio_amount(employee, sal_slip_start_date, sal_struct) - - claimed_amount += get_benefit_claim_amount( - employee, payroll_period.start_date, payroll_period.end_date - ) - - return claimed_amount + pro_rata_amount - - -def get_last_payroll_period_benefits( - employee, sal_slip_start_date, sal_slip_end_date, payroll_period, sal_struct -): - max_benefits = get_max_benefits(employee, payroll_period.end_date) - if not max_benefits: - max_benefits = 0 - remaining_benefit = max_benefits - get_total_benefit_dispensed( - employee, sal_struct, sal_slip_start_date, payroll_period - ) - if remaining_benefit > 0: - have_remaining = True - # Set the remaining benefits to flexi non pro-rata component in the salary structure - salary_components_array = [] - for d in sal_struct.get("earnings"): - if d.is_flexible_benefit == 1: - salary_component = frappe.get_doc("Salary Component", d.salary_component) - if salary_component.pay_against_benefit_claim == 1: - claimed_amount = get_benefit_claim_amount( - employee, payroll_period.start_date, sal_slip_end_date, d.salary_component - ) - amount_fit_to_component = salary_component.max_benefit_amount - claimed_amount - if amount_fit_to_component > 0: - if remaining_benefit > amount_fit_to_component: - amount = amount_fit_to_component - remaining_benefit -= amount_fit_to_component - else: - amount = remaining_benefit - have_remaining = False - current_claimed_amount = get_benefit_claim_amount( - employee, sal_slip_start_date, sal_slip_end_date, d.salary_component - ) - amount += current_claimed_amount - struct_row = {} - salary_components_dict = {} - struct_row["depends_on_payment_days"] = salary_component.depends_on_payment_days - struct_row["salary_component"] = salary_component.name - struct_row["abbr"] = salary_component.salary_component_abbr - struct_row["do_not_include_in_total"] = salary_component.do_not_include_in_total - struct_row["is_tax_applicable"] = (salary_component.is_tax_applicable,) - struct_row["is_flexible_benefit"] = (salary_component.is_flexible_benefit,) - struct_row[ - "variable_based_on_taxable_salary" - ] = salary_component.variable_based_on_taxable_salary - salary_components_dict["amount"] = amount - salary_components_dict["struct_row"] = struct_row - salary_components_array.append(salary_components_dict) - if not have_remaining: - break - - if len(salary_components_array) > 0: - return salary_components_array - - return False diff --git a/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py deleted file mode 100644 index b1d3c66ca8..0000000000 --- a/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestEmployeeBenefitClaim(unittest.TestCase): - pass diff --git a/erpnext/payroll/doctype/employee_cost_center/__init__.py b/erpnext/payroll/doctype/employee_cost_center/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.json b/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.json deleted file mode 100644 index 8fed9f7752..0000000000 --- a/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "actions": [], - "creation": "2021-12-23 12:44:38.389283", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "cost_center", - "percentage" - ], - "fields": [ - { - "allow_on_submit": 1, - "fieldname": "cost_center", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Cost Center", - "options": "Cost Center", - "reqd": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "percentage", - "fieldtype": "Int", - "in_list_view": 1, - "label": "Percentage (%)", - "non_negative": 1, - "reqd": 1 - } - ], - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2021-12-23 17:39:03.410924", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Employee Cost Center", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC", - "states": [] -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py b/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py deleted file mode 100644 index 6c5be9744b..0000000000 --- a/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py +++ /dev/null @@ -1,9 +0,0 @@ -# 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 EmployeeCostCenter(Document): - pass diff --git a/erpnext/payroll/doctype/employee_incentive/__init__.py b/erpnext/payroll/doctype/employee_incentive/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js deleted file mode 100644 index b2809b164a..0000000000 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Incentive', { - setup: function(frm) { - frm.set_query("employee", function() { - return { - filters: { - "status": "Active" - } - }; - }); - frm.trigger('set_earning_component'); - }, - - employee: function(frm) { - if (frm.doc.employee) { - frappe.run_serially([ - () => frm.trigger('get_employee_currency'), - () => frm.trigger('set_company') - ]); - } else { - frm.set_value("company", null); - } - }, - - set_company: function(frm) { - frappe.call({ - method: "frappe.client.get_value", - args: { - doctype: "Employee", - fieldname: "company", - filters: { - name: frm.doc.employee - } - }, - callback: function(data) { - if (data.message) { - frm.set_value("company", data.message.company); - frm.trigger('set_earning_component'); - } - } - }); - }, - - set_earning_component: function(frm) { - if (!frm.doc.company) return; - frm.set_query("salary_component", function() { - return { - filters: {type: "earning", company: frm.doc.company} - }; - }); - }, - - get_employee_currency: function(frm) { - frappe.call({ - method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", - args: { - employee: frm.doc.employee, - }, - callback: function(r) { - if (r.message) { - frm.set_value('currency', r.message); - frm.refresh_fields(); - } - } - }); - }, -}); diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.json b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json deleted file mode 100644 index 64fb8c5c98..0000000000 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.json +++ /dev/null @@ -1,146 +0,0 @@ -{ - "actions": [], - "autoname": "HR-EINV-.YY.-.MM.-.#####", - "creation": "2018-04-13 16:13:43.404546", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "company", - "currency", - "incentive_amount", - "column_break_5", - "salary_component", - "payroll_date", - "department", - "amended_from" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1 - }, - { - "fieldname": "incentive_amount", - "fieldtype": "Currency", - "label": "Incentive Amount", - "options": "currency", - "reqd": 1 - }, - { - "fieldname": "payroll_date", - "fieldtype": "Date", - "label": "Payroll Date", - "reqd": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Employee Incentive", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "column_break_5", - "fieldtype": "Column Break" - }, - { - "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 - }, - { - "fieldname": "salary_component", - "fieldtype": "Link", - "label": "Salary Component", - "options": "Salary Component", - "reqd": 1 - }, - { - "depends_on": "eval:(doc.docstatus==1 || doc.employee)", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "print_hide": 1, - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-19 12:52:19.850710", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Employee Incentive", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1 - }, - { - "create": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - } - ], - "search_fields": "employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py deleted file mode 100644 index 7686185349..0000000000 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document - -from erpnext.hr.utils import validate_active_employee - - -class EmployeeIncentive(Document): - def validate(self): - validate_active_employee(self.employee) - self.validate_salary_structure() - - def validate_salary_structure(self): - if not frappe.db.exists("Salary Structure Assignment", {"employee": self.employee}): - frappe.throw( - _("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format( - self.employee - ) - ) - - def on_submit(self): - company = frappe.db.get_value("Employee", self.employee, "company") - - additional_salary = frappe.new_doc("Additional Salary") - additional_salary.employee = self.employee - additional_salary.currency = self.currency - additional_salary.salary_component = self.salary_component - additional_salary.overwrite_salary_structure_amount = 0 - additional_salary.amount = self.incentive_amount - additional_salary.payroll_date = self.payroll_date - additional_salary.company = company - additional_salary.ref_doctype = self.doctype - additional_salary.ref_docname = self.name - additional_salary.submit() diff --git a/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py deleted file mode 100644 index e296fdf864..0000000000 --- a/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestEmployeeIncentive(unittest.TestCase): - pass diff --git a/erpnext/payroll/doctype/employee_other_income/__init__.py b/erpnext/payroll/doctype/employee_other_income/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/employee_other_income/employee_other_income.js b/erpnext/payroll/doctype/employee_other_income/employee_other_income.js deleted file mode 100644 index c1a74e863b..0000000000 --- a/erpnext/payroll/doctype/employee_other_income/employee_other_income.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Other Income', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/payroll/doctype/employee_other_income/employee_other_income.json b/erpnext/payroll/doctype/employee_other_income/employee_other_income.json deleted file mode 100644 index 04ce9f79a1..0000000000 --- a/erpnext/payroll/doctype/employee_other_income/employee_other_income.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "actions": [], - "autoname": "HR-INCOME-.######", - "creation": "2020-03-18 15:04:40.767434", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "payroll_period", - "column_break_3", - "company", - "source", - "amount", - "amended_from" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1 - }, - { - "fieldname": "payroll_period", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Payroll Period", - "options": "Payroll Period", - "reqd": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "source", - "fieldtype": "Data", - "label": "Source" - }, - { - "fieldname": "amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Amount", - "options": "Company:company:default_currency", - "reqd": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "label": "Employee Name", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Employee Other Income", - "print_hide": 1, - "read_only": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-19 12:58:43.255900", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Employee Other Income", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "quick_entry": 1, - "search_fields": "employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_other_income/employee_other_income.py b/erpnext/payroll/doctype/employee_other_income/employee_other_income.py deleted file mode 100644 index 51059a1364..0000000000 --- a/erpnext/payroll/doctype/employee_other_income/employee_other_income.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class EmployeeOtherIncome(Document): - pass diff --git a/erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py b/erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py deleted file mode 100644 index 8f0f637650..0000000000 --- a/erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -import unittest - - -class TestEmployeeOtherIncome(unittest.TestCase): - pass diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_category/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js deleted file mode 100644 index 1df609f320..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Tax Exemption Category', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json deleted file mode 100644 index f2556d7d96..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "Prompt", - "creation": "2018-04-13 16:51:36.971140", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "max_amount", - "is_active" - ], - "fields": [ - { - "fieldname": "max_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Max Exemption Amount" - }, - { - "default": "1", - "fieldname": "is_active", - "fieldtype": "Check", - "label": "Is Active" - } - ], - "links": [], - "modified": "2020-06-22 23:16:47.472910", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Employee Tax Exemption Category", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py deleted file mode 100644 index 5c109dec96..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class EmployeeTaxExemptionCategory(Document): - pass diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py deleted file mode 100644 index 84e6183f3b..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestEmployeeTaxExemptionCategory(unittest.TestCase): - pass diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js deleted file mode 100644 index fb11875e96..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Tax Exemption Declaration', { - setup: function(frm) { - frm.set_query('employee', function() { - return { - filters: { - 'status': "Active" - } - } - }); - - frm.set_query('payroll_period', function() { - const fields = {'employee': 'Employee', 'company': 'Company'}; - - for (let [field, label] of Object.entries(fields)) { - if (!frm.doc[field]) { - frappe.msgprint(__("Please select {0}", [label])) - } - }; - - if (frm.doc.employee && frm.doc.company){ - return { - filters: { - 'company': frm.doc.company - } - } - } - }); - - frm.set_query('exemption_sub_category', 'declarations', function() { - return { - filters: { - 'is_active': 1 - } - } - }); - }, - - refresh: function(frm) { - if(frm.doc.docstatus==1) { - frm.add_custom_button(__('Submit Proof'), function() { - frappe.model.open_mapped_doc({ - method: "erpnext.payroll.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", - frm: frm - }); - }).addClass("btn-primary"); - } - }, - - employee: function(frm) { - if (frm.doc.employee) { - frm.trigger('get_employee_currency'); - } - }, - - get_employee_currency: function(frm) { - frappe.call({ - method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", - args: { - employee: frm.doc.employee, - }, - callback: function(r) { - if (r.message) { - frm.set_value('currency', r.message); - frm.refresh_fields(); - } - } - }); - } -}); diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json deleted file mode 100644 index 5ef373e887..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json +++ /dev/null @@ -1,196 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-TAX-DEC-.YYYY.-.#####", - "creation": "2018-04-13 16:53:36.175504", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "department", - "column_break_2", - "payroll_period", - "company", - "currency", - "amended_from", - "section_break_8", - "declarations", - "section_break_10", - "total_declared_amount", - "column_break_12", - "total_exemption_amount" - ], - "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 - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fieldname": "payroll_period", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Payroll Period", - "options": "Payroll Period", - "reqd": 1 - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Employee Tax Exemption Declaration", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "section_break_8", - "fieldtype": "Section Break" - }, - { - "fieldname": "declarations", - "fieldtype": "Table", - "label": "Declarations", - "options": "Employee Tax Exemption Declaration Category" - }, - { - "fieldname": "section_break_10", - "fieldtype": "Section Break" - }, - { - "fieldname": "total_declared_amount", - "fieldtype": "Currency", - "label": "Total Declared Amount", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "column_break_12", - "fieldtype": "Column Break" - }, - { - "fieldname": "total_exemption_amount", - "fieldtype": "Currency", - "label": "Total Exemption Amount", - "options": "currency", - "read_only": 1 - }, - { - "depends_on": "eval: doc.employee", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "print_hide": 1, - "reqd": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-19 12:58:54.707871", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Employee Tax Exemption Declaration", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py deleted file mode 100644 index 3d1d96598f..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe.model.document import Document -from frappe.model.mapper import get_mapped_doc -from frappe.utils import flt - -from erpnext.hr.utils import ( - calculate_annual_eligible_hra_exemption, - get_total_exemption_amount, - validate_active_employee, - validate_duplicate_exemption_for_payroll_period, - validate_tax_declaration, -) - - -class EmployeeTaxExemptionDeclaration(Document): - def validate(self): - validate_active_employee(self.employee) - validate_tax_declaration(self.declarations) - validate_duplicate_exemption_for_payroll_period( - self.doctype, self.name, self.payroll_period, self.employee - ) - self.set_total_declared_amount() - self.set_total_exemption_amount() - self.calculate_hra_exemption() - - def set_total_declared_amount(self): - self.total_declared_amount = 0.0 - for d in self.declarations: - self.total_declared_amount += flt(d.amount) - - def set_total_exemption_amount(self): - self.total_exemption_amount = flt( - get_total_exemption_amount(self.declarations), self.precision("total_exemption_amount") - ) - - def calculate_hra_exemption(self): - self.salary_structure_hra, self.annual_hra_exemption, self.monthly_hra_exemption = 0, 0, 0 - if self.get("monthly_house_rent"): - hra_exemption = calculate_annual_eligible_hra_exemption(self) - if hra_exemption: - self.total_exemption_amount += hra_exemption["annual_exemption"] - self.total_exemption_amount = flt( - self.total_exemption_amount, self.precision("total_exemption_amount") - ) - self.salary_structure_hra = flt( - hra_exemption["hra_amount"], self.precision("salary_structure_hra") - ) - self.annual_hra_exemption = flt( - hra_exemption["annual_exemption"], self.precision("annual_hra_exemption") - ) - self.monthly_hra_exemption = flt( - hra_exemption["monthly_exemption"], self.precision("monthly_hra_exemption") - ) - - -@frappe.whitelist() -def make_proof_submission(source_name, target_doc=None): - doclist = get_mapped_doc( - "Employee Tax Exemption Declaration", - source_name, - { - "Employee Tax Exemption Declaration": { - "doctype": "Employee Tax Exemption Proof Submission", - "field_no_map": ["monthly_house_rent", "monthly_hra_exemption"], - }, - "Employee Tax Exemption Declaration Category": { - "doctype": "Employee Tax Exemption Proof Submission Detail", - "add_if_empty": True, - }, - }, - target_doc, - ) - - return doclist diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py deleted file mode 100644 index 2d8df35011..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py +++ /dev/null @@ -1,490 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_months, getdate - -import erpnext -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.utils import DuplicateDeclarationError - - -class TestEmployeeTaxExemptionDeclaration(FrappeTestCase): - def setUp(self): - make_employee("employee@taxexemption.com", company="_Test Company") - make_employee("employee1@taxexemption.com", company="_Test Company") - create_payroll_period(company="_Test Company") - create_exemption_category() - frappe.db.delete("Employee Tax Exemption Declaration") - frappe.db.delete("Salary Structure Assignment") - - def test_duplicate_category_in_declaration(self): - declaration = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Declaration", - "employee": frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name"), - "company": erpnext.get_default_company(), - "payroll_period": "_Test Payroll Period", - "currency": erpnext.get_default_currency(), - "declarations": [ - dict( - exemption_sub_category="_Test Sub Category", - exemption_category="_Test Category", - amount=100000, - ), - dict( - exemption_sub_category="_Test Sub Category", - exemption_category="_Test Category", - amount=50000, - ), - ], - } - ) - self.assertRaises(frappe.ValidationError, declaration.save) - - def test_duplicate_entry_for_payroll_period(self): - declaration = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Declaration", - "employee": frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name"), - "company": erpnext.get_default_company(), - "payroll_period": "_Test Payroll Period", - "currency": erpnext.get_default_currency(), - "declarations": [ - dict( - exemption_sub_category="_Test Sub Category", - exemption_category="_Test Category", - amount=100000, - ), - dict( - exemption_sub_category="_Test1 Sub Category", - exemption_category="_Test Category", - amount=50000, - ), - ], - } - ).insert() - - duplicate_declaration = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Declaration", - "employee": frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name"), - "company": erpnext.get_default_company(), - "payroll_period": "_Test Payroll Period", - "currency": erpnext.get_default_currency(), - "declarations": [ - dict( - exemption_sub_category="_Test Sub Category", - exemption_category="_Test Category", - amount=100000, - ) - ], - } - ) - self.assertRaises(DuplicateDeclarationError, duplicate_declaration.insert) - duplicate_declaration.employee = frappe.get_value( - "Employee", {"user_id": "employee1@taxexemption.com"}, "name" - ) - self.assertTrue(duplicate_declaration.insert) - - def test_exemption_amount(self): - declaration = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Declaration", - "employee": frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name"), - "company": erpnext.get_default_company(), - "payroll_period": "_Test Payroll Period", - "currency": erpnext.get_default_currency(), - "declarations": [ - dict( - exemption_sub_category="_Test Sub Category", - exemption_category="_Test Category", - amount=80000, - ), - dict( - exemption_sub_category="_Test1 Sub Category", - exemption_category="_Test Category", - amount=60000, - ), - ], - } - ).insert() - - self.assertEqual(declaration.total_exemption_amount, 100000) - - def test_india_hra_exemption(self): - # set country - current_country = frappe.flags.country - frappe.flags.country = "India" - - setup_hra_exemption_prerequisites("Monthly") - employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") - - declaration = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Declaration", - "employee": employee, - "company": "_Test Company", - "payroll_period": "_Test Payroll Period", - "currency": "INR", - "monthly_house_rent": 50000, - "rented_in_metro_city": 1, - "declarations": [ - dict( - exemption_sub_category="_Test Sub Category", - exemption_category="_Test Category", - amount=80000, - ), - dict( - exemption_sub_category="_Test1 Sub Category", - exemption_category="_Test Category", - amount=60000, - ), - ], - } - ).insert() - - # Monthly HRA received = 3000 - # should set HRA exemption as per actual annual HRA because that's the minimum - self.assertEqual(declaration.monthly_hra_exemption, 3000) - self.assertEqual(declaration.annual_hra_exemption, 36000) - # 100000 Standard Exemption + 36000 HRA exemption - self.assertEqual(declaration.total_exemption_amount, 136000) - - # reset - frappe.flags.country = current_country - - def test_india_hra_exemption_with_daily_payroll_frequency(self): - # set country - current_country = frappe.flags.country - frappe.flags.country = "India" - - setup_hra_exemption_prerequisites("Daily") - employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") - - declaration = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Declaration", - "employee": employee, - "company": "_Test Company", - "payroll_period": "_Test Payroll Period", - "currency": "INR", - "monthly_house_rent": 170000, - "rented_in_metro_city": 1, - "declarations": [ - dict( - exemption_sub_category="_Test1 Sub Category", - exemption_category="_Test Category", - amount=60000, - ), - ], - } - ).insert() - - # Daily HRA received = 3000 - # should set HRA exemption as per (rent - 10% of Basic Salary), that's the minimum - self.assertEqual(declaration.monthly_hra_exemption, 17916.67) - self.assertEqual(declaration.annual_hra_exemption, 215000) - # 50000 Standard Exemption + 215000 HRA exemption - self.assertEqual(declaration.total_exemption_amount, 265000) - - # reset - frappe.flags.country = current_country - - def test_india_hra_exemption_with_weekly_payroll_frequency(self): - # set country - current_country = frappe.flags.country - frappe.flags.country = "India" - - setup_hra_exemption_prerequisites("Weekly") - employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") - - declaration = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Declaration", - "employee": employee, - "company": "_Test Company", - "payroll_period": "_Test Payroll Period", - "currency": "INR", - "monthly_house_rent": 170000, - "rented_in_metro_city": 1, - "declarations": [ - dict( - exemption_sub_category="_Test1 Sub Category", - exemption_category="_Test Category", - amount=60000, - ), - ], - } - ).insert() - - # Weekly HRA received = 3000 - # should set HRA exemption as per actual annual HRA because that's the minimum - self.assertEqual(declaration.monthly_hra_exemption, 13000) - self.assertEqual(declaration.annual_hra_exemption, 156000) - # 50000 Standard Exemption + 156000 HRA exemption - self.assertEqual(declaration.total_exemption_amount, 206000) - - # reset - frappe.flags.country = current_country - - def test_india_hra_exemption_with_fortnightly_payroll_frequency(self): - # set country - current_country = frappe.flags.country - frappe.flags.country = "India" - - setup_hra_exemption_prerequisites("Fortnightly") - employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") - - declaration = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Declaration", - "employee": employee, - "company": "_Test Company", - "payroll_period": "_Test Payroll Period", - "currency": "INR", - "monthly_house_rent": 170000, - "rented_in_metro_city": 1, - "declarations": [ - dict( - exemption_sub_category="_Test1 Sub Category", - exemption_category="_Test Category", - amount=60000, - ), - ], - } - ).insert() - - # Fortnightly HRA received = 3000 - # should set HRA exemption as per actual annual HRA because that's the minimum - self.assertEqual(declaration.monthly_hra_exemption, 6500) - self.assertEqual(declaration.annual_hra_exemption, 78000) - # 50000 Standard Exemption + 78000 HRA exemption - self.assertEqual(declaration.total_exemption_amount, 128000) - - # reset - frappe.flags.country = current_country - - def test_india_hra_exemption_with_bimonthly_payroll_frequency(self): - # set country - current_country = frappe.flags.country - frappe.flags.country = "India" - - setup_hra_exemption_prerequisites("Bimonthly") - employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") - - declaration = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Declaration", - "employee": employee, - "company": "_Test Company", - "payroll_period": "_Test Payroll Period", - "currency": "INR", - "monthly_house_rent": 50000, - "rented_in_metro_city": 1, - "declarations": [ - dict( - exemption_sub_category="_Test Sub Category", - exemption_category="_Test Category", - amount=80000, - ), - dict( - exemption_sub_category="_Test1 Sub Category", - exemption_category="_Test Category", - amount=60000, - ), - ], - } - ).insert() - - # Bimonthly HRA received = 3000 - # should set HRA exemption as per actual annual HRA because that's the minimum - self.assertEqual(declaration.monthly_hra_exemption, 1500) - self.assertEqual(declaration.annual_hra_exemption, 18000) - # 100000 Standard Exemption + 18000 HRA exemption - self.assertEqual(declaration.total_exemption_amount, 118000) - - # reset - frappe.flags.country = current_country - - def test_india_hra_exemption_with_multiple_salary_structure_assignments(self): - from erpnext.payroll.doctype.salary_slip.test_salary_slip import create_tax_slab - from erpnext.payroll.doctype.salary_structure.test_salary_structure import ( - create_salary_structure_assignment, - make_salary_structure, - ) - - # set country - current_country = frappe.flags.country - frappe.flags.country = "India" - - employee = make_employee("employee@taxexemption2.com", company="_Test Company") - payroll_period = create_payroll_period(name="_Test Payroll Period", company="_Test Company") - - create_tax_slab( - payroll_period, - allow_tax_exemption=True, - currency="INR", - effective_date=getdate("2019-04-01"), - company="_Test Company", - ) - - frappe.db.set_value( - "Company", "_Test Company", {"basic_component": "Basic Salary", "hra_component": "HRA"} - ) - - # salary structure with base 50000, HRA 3000 - make_salary_structure( - "Monthly Structure for HRA Exemption 1", - "Monthly", - employee=employee, - company="_Test Company", - currency="INR", - payroll_period=payroll_period.name, - from_date=payroll_period.start_date, - ) - - # salary structure with base 70000, HRA = base * 0.2 = 14000 - salary_structure = make_salary_structure( - "Monthly Structure for HRA Exemption 2", - "Monthly", - employee=employee, - company="_Test Company", - currency="INR", - payroll_period=payroll_period.name, - from_date=payroll_period.start_date, - dont_submit=True, - ) - for component_row in salary_structure.earnings: - if component_row.salary_component == "HRA": - component_row.amount = 0 - component_row.amount_based_on_formula = 1 - component_row.formula = "base * 0.2" - break - - salary_structure.submit() - - create_salary_structure_assignment( - employee, - salary_structure.name, - from_date=add_months(payroll_period.start_date, 6), - company="_Test Company", - currency="INR", - payroll_period=payroll_period.name, - base=70000, - allow_duplicate=True, - ) - - declaration = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Declaration", - "employee": employee, - "company": "_Test Company", - "payroll_period": payroll_period.name, - "currency": "INR", - "monthly_house_rent": 50000, - "rented_in_metro_city": 1, - "declarations": [ - dict( - exemption_sub_category="_Test1 Sub Category", - exemption_category="_Test Category", - amount=60000, - ), - ], - } - ).insert() - - # Monthly HRA received = 50000 * 6 months + 70000 * 6 months - # should set HRA exemption as per actual annual HRA because that's the minimum - self.assertEqual(declaration.monthly_hra_exemption, 8500) - self.assertEqual(declaration.annual_hra_exemption, 102000) - # 50000 Standard Exemption + 102000 HRA exemption - self.assertEqual(declaration.total_exemption_amount, 152000) - - # reset - frappe.flags.country = current_country - - -def create_payroll_period(**args): - args = frappe._dict(args) - name = args.name or "_Test Payroll Period" - if not frappe.db.exists("Payroll Period", name): - from datetime import date - - payroll_period = frappe.get_doc( - dict( - doctype="Payroll Period", - name=name, - company=args.company or erpnext.get_default_company(), - start_date=args.start_date or date(date.today().year, 1, 1), - end_date=args.end_date or date(date.today().year, 12, 31), - ) - ).insert() - return payroll_period - else: - return frappe.get_doc("Payroll Period", name) - - -def create_exemption_category(): - if not frappe.db.exists("Employee Tax Exemption Category", "_Test Category"): - category = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Category", - "name": "_Test Category", - "deduction_component": "Income Tax", - "max_amount": 100000, - } - ).insert() - if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test Sub Category"): - frappe.get_doc( - { - "doctype": "Employee Tax Exemption Sub Category", - "name": "_Test Sub Category", - "exemption_category": "_Test Category", - "max_amount": 100000, - "is_active": 1, - } - ).insert() - if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test1 Sub Category"): - frappe.get_doc( - { - "doctype": "Employee Tax Exemption Sub Category", - "name": "_Test1 Sub Category", - "exemption_category": "_Test Category", - "max_amount": 50000, - "is_active": 1, - } - ).insert() - - -def setup_hra_exemption_prerequisites(frequency, employee=None): - from erpnext.payroll.doctype.salary_slip.test_salary_slip import create_tax_slab - from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - - payroll_period = create_payroll_period(name="_Test Payroll Period", company="_Test Company") - if not employee: - employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") - - create_tax_slab( - payroll_period, - allow_tax_exemption=True, - currency="INR", - effective_date=getdate("2019-04-01"), - company="_Test Company", - ) - - make_salary_structure( - f"{frequency} Structure for HRA Exemption", - frequency, - employee=employee, - company="_Test Company", - currency="INR", - payroll_period=payroll_period, - ) - - frappe.db.set_value( - "Company", "_Test Company", {"basic_component": "Basic Salary", "hra_component": "HRA"} - ) diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json deleted file mode 100644 index 723a3df3c7..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "actions": [], - "creation": "2018-04-13 16:56:23.333041", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "exemption_sub_category", - "exemption_category", - "max_amount", - "amount" - ], - "fields": [ - { - "fieldname": "exemption_sub_category", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Exemption Sub Category", - "options": "Employee Tax Exemption Sub Category", - "reqd": 1 - }, - { - "fetch_from": "exemption_sub_category.exemption_category", - "fieldname": "exemption_category", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Exemption Category", - "options": "Employee Tax Exemption Category", - "read_only": 1, - "reqd": 1 - }, - { - "fetch_from": "exemption_sub_category.max_amount", - "fieldname": "max_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Maximum Exempted Amount", - "options": "currency", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Declared Amount", - "options": "currency", - "reqd": 1 - } - ], - "istable": 1, - "links": [], - "modified": "2020-10-20 16:43:09.606265", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Employee Tax Exemption Declaration Category", - "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/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py deleted file mode 100644 index 4322f31c01..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class EmployeeTaxExemptionDeclarationCategory(Document): - pass diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js deleted file mode 100644 index 4fb0a3771e..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Tax Exemption Proof Submission', { - setup: function(frm) { - frm.set_query('employee', function() { - return { - filters: { - 'status': "Active" - } - } - }); - - frm.set_query('payroll_period', function() { - if(frm.doc.employee && frm.doc.company){ - return { - filters: { - 'company': frm.doc.company - } - } - }else { - frappe.msgprint(__("Please select Employee")); - } - }); - - frm.set_query('exemption_sub_category', 'tax_exemption_proofs', function() { - return { - filters: { - 'is_active': 1 - } - } - }); - }, - - refresh: function(frm) { - if(frm.doc.docstatus === 0) { - let filters = { - docstatus: 1, - company: frm.doc.company - }; - if(frm.doc.employee) filters["employee"] = frm.doc.employee; - if(frm.doc.payroll_period) filters["payroll_period"] = frm.doc.payroll_period; - - frm.add_custom_button(__('Get Details From Declaration'), function() { - erpnext.utils.map_current_doc({ - method: "erpnext.payroll.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", - source_doctype: "Employee Tax Exemption Declaration", - target: frm, - date_field: "creation", - setters: { - employee: frm.doc.employee || undefined - }, - get_query_filters: filters - }); - }); - } - }, - - currency: function(frm) { - frm.refresh_fields(); - }, - - employee: function(frm) { - if (frm.doc.employee) { - frm.trigger('get_employee_currency'); - } - }, - - get_employee_currency: function(frm) { - frappe.call({ - method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", - args: { - employee: frm.doc.employee, - }, - callback: function(r) { - if (r.message) { - frm.set_value('currency', r.message); - frm.refresh_fields(); - } - } - }); - }, -}); diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json deleted file mode 100644 index bb90051e5d..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json +++ /dev/null @@ -1,219 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-TAX-PRF-.YYYY.-.#####", - "creation": "2018-04-13 17:24:11.456132", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "department", - "currency", - "column_break_2", - "submission_date", - "payroll_period", - "company", - "section_break_5", - "tax_exemption_proofs", - "section_break_10", - "total_actual_amount", - "column_break_12", - "exemption_amount", - "attachment_section", - "attachments", - "amended_from" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 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 - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "default": "Today", - "fieldname": "submission_date", - "fieldtype": "Date", - "label": "Submission Date", - "reqd": 1 - }, - { - "fieldname": "payroll_period", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Payroll Period", - "options": "Payroll Period", - "reqd": 1 - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "section_break_5", - "fieldtype": "Section Break" - }, - { - "fieldname": "tax_exemption_proofs", - "fieldtype": "Table", - "label": "Tax Exemption Proofs", - "options": "Employee Tax Exemption Proof Submission Detail" - }, - { - "fieldname": "section_break_10", - "fieldtype": "Section Break" - }, - { - "fieldname": "total_actual_amount", - "fieldtype": "Currency", - "label": "Total Actual Amount", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "column_break_12", - "fieldtype": "Column Break" - }, - { - "fieldname": "exemption_amount", - "fieldtype": "Currency", - "label": "Total Exemption Amount", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "attachment_section", - "fieldtype": "Section Break" - }, - { - "fieldname": "attachments", - "fieldtype": "Attach", - "label": "Attachments" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Employee Tax Exemption Proof Submission", - "print_hide": 1, - "read_only": 1 - }, - { - "depends_on": "eval: doc.employee", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "print_hide": 1, - "reqd": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-19 12:58:24.244546", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Employee Tax Exemption Proof Submission", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py deleted file mode 100644 index b3b66b9e7b..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document -from frappe.utils import flt - -from erpnext.hr.utils import ( - calculate_hra_exemption_for_period, - get_total_exemption_amount, - validate_active_employee, - validate_duplicate_exemption_for_payroll_period, - validate_tax_declaration, -) - - -class EmployeeTaxExemptionProofSubmission(Document): - def validate(self): - validate_active_employee(self.employee) - validate_tax_declaration(self.tax_exemption_proofs) - self.set_total_actual_amount() - self.set_total_exemption_amount() - self.calculate_hra_exemption() - validate_duplicate_exemption_for_payroll_period( - self.doctype, self.name, self.payroll_period, self.employee - ) - - def set_total_actual_amount(self): - self.total_actual_amount = flt(self.get("house_rent_payment_amount")) - for d in self.tax_exemption_proofs: - self.total_actual_amount += flt(d.amount) - - def set_total_exemption_amount(self): - self.exemption_amount = flt( - get_total_exemption_amount(self.tax_exemption_proofs), self.precision("exemption_amount") - ) - - def calculate_hra_exemption(self): - self.monthly_hra_exemption, self.monthly_house_rent, self.total_eligible_hra_exemption = 0, 0, 0 - if self.get("house_rent_payment_amount"): - hra_exemption = calculate_hra_exemption_for_period(self) - if hra_exemption: - self.exemption_amount += hra_exemption["total_eligible_hra_exemption"] - self.exemption_amount = flt(self.exemption_amount, self.precision("exemption_amount")) - self.monthly_hra_exemption = flt( - hra_exemption["monthly_exemption"], self.precision("monthly_hra_exemption") - ) - self.monthly_house_rent = flt( - hra_exemption["monthly_house_rent"], self.precision("monthly_house_rent") - ) - self.total_eligible_hra_exemption = flt( - hra_exemption["total_eligible_hra_exemption"], self.precision("total_eligible_hra_exemption") - ) diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py deleted file mode 100644 index 416cf316c9..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import ( - create_exemption_category, - create_payroll_period, - setup_hra_exemption_prerequisites, -) - - -class TestEmployeeTaxExemptionProofSubmission(FrappeTestCase): - def setUp(self): - make_employee("employee@proofsubmission.com", company="_Test Company") - create_payroll_period(company="_Test Company") - create_exemption_category() - frappe.db.delete("Employee Tax Exemption Proof Submission") - frappe.db.delete("Salary Structure Assignment") - - def test_exemption_amount_lesser_than_category_max(self): - proof = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Proof Submission", - "employee": frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name"), - "payroll_period": "Test Payroll Period", - "tax_exemption_proofs": [ - dict( - exemption_sub_category="_Test Sub Category", - type_of_proof="Test Proof", - exemption_category="_Test Category", - amount=150000, - ) - ], - } - ) - self.assertRaises(frappe.ValidationError, proof.save) - proof = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Proof Submission", - "payroll_period": "Test Payroll Period", - "employee": frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name"), - "tax_exemption_proofs": [ - dict( - exemption_sub_category="_Test Sub Category", - type_of_proof="Test Proof", - exemption_category="_Test Category", - amount=100000, - ) - ], - } - ) - self.assertTrue(proof.save) - self.assertTrue(proof.submit) - - def test_duplicate_category_in_proof_submission(self): - proof = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Proof Submission", - "employee": frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name"), - "payroll_period": "Test Payroll Period", - "tax_exemption_proofs": [ - dict( - exemption_sub_category="_Test Sub Category", - exemption_category="_Test Category", - type_of_proof="Test Proof", - amount=100000, - ), - dict( - exemption_sub_category="_Test Sub Category", - exemption_category="_Test Category", - amount=50000, - ), - ], - } - ) - self.assertRaises(frappe.ValidationError, proof.save) - - def test_india_hra_exemption(self): - # set country - current_country = frappe.flags.country - frappe.flags.country = "India" - - employee = frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name") - setup_hra_exemption_prerequisites("Monthly", employee) - payroll_period = frappe.db.get_value( - "Payroll Period", "_Test Payroll Period", ["start_date", "end_date"], as_dict=True - ) - - proof = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Proof Submission", - "employee": employee, - "company": "_Test Company", - "payroll_period": "_Test Payroll Period", - "currency": "INR", - "house_rent_payment_amount": 600000, - "rented_in_metro_city": 1, - "rented_from_date": payroll_period.start_date, - "rented_to_date": payroll_period.end_date, - "tax_exemption_proofs": [ - dict( - exemption_sub_category="_Test Sub Category", - exemption_category="_Test Category", - type_of_proof="Test Proof", - amount=100000, - ), - dict( - exemption_sub_category="_Test1 Sub Category", - exemption_category="_Test Category", - type_of_proof="Test Proof", - amount=50000, - ), - ], - } - ).insert() - - self.assertEqual(proof.monthly_house_rent, 50000) - - # Monthly HRA received = 3000 - # should set HRA exemption as per actual annual HRA because that's the minimum - self.assertEqual(proof.monthly_hra_exemption, 3000) - self.assertEqual(proof.total_eligible_hra_exemption, 36000) - - # total exemptions + house rent payment amount - self.assertEqual(proof.total_actual_amount, 750000) - - # 100000 Standard Exemption + 36000 HRA exemption - self.assertEqual(proof.exemption_amount, 136000) - - # reset - frappe.flags.country = current_country diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json deleted file mode 100644 index 2fd8b94efd..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "actions": [], - "creation": "2018-04-13 17:19:03.006149", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "exemption_sub_category", - "exemption_category", - "max_amount", - "type_of_proof", - "amount" - ], - "fields": [ - { - "fieldname": "exemption_sub_category", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Exemption Sub Category", - "options": "Employee Tax Exemption Sub Category", - "reqd": 1 - }, - { - "fetch_from": "exemption_sub_category.exemption_category", - "fieldname": "exemption_category", - "fieldtype": "Read Only", - "in_list_view": 1, - "label": "Exemption Category", - "reqd": 1 - }, - { - "fetch_from": "exemption_sub_category.max_amount", - "fieldname": "max_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Maximum Exemption Amount", - "options": "currency", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "type_of_proof", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Type of Proof", - "reqd": 1 - }, - { - "fieldname": "amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Actual Amount", - "options": "currency" - } - ], - "istable": 1, - "links": [], - "modified": "2020-10-20 16:47:31.480870", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Employee Tax Exemption Proof Submission Detail", - "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/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py deleted file mode 100644 index 37209e5840..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class EmployeeTaxExemptionProofSubmissionDetail(Document): - pass diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js deleted file mode 100644 index 8a83a2739c..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Employee Tax Exemption Sub Category', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json deleted file mode 100644 index f8c4b8bcb0..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "Prompt", - "creation": "2018-05-09 12:47:26.983095", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "exemption_category", - "max_amount", - "is_active" - ], - "fields": [ - { - "fieldname": "exemption_category", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Tax Exemption Category", - "options": "Employee Tax Exemption Category", - "reqd": 1 - }, - { - "fetch_from": "exemption_category.max_amount", - "fetch_if_empty": 1, - "fieldname": "max_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Max Exemption Amount" - }, - { - "default": "1", - "fieldname": "is_active", - "fieldtype": "Check", - "label": "Is Active" - } - ], - "links": [], - "modified": "2020-06-22 23:18:08.254645", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Employee Tax Exemption Sub Category", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py deleted file mode 100644 index fb75d6706c..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import flt - - -class EmployeeTaxExemptionSubCategory(Document): - def validate(self): - category_max_amount = frappe.db.get_value( - "Employee Tax Exemption Category", self.exemption_category, "max_amount" - ) - if flt(self.max_amount) > flt(category_max_amount): - frappe.throw( - _( - "Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1}" - ).format(category_max_amount, self.exemption_category) - ) diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py deleted file mode 100644 index 64d2e3a1e6..0000000000 --- a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestEmployeeTaxExemptionSubCategory(unittest.TestCase): - pass diff --git a/erpnext/payroll/doctype/gratuity/__init__.py b/erpnext/payroll/doctype/gratuity/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/gratuity/gratuity.js b/erpnext/payroll/doctype/gratuity/gratuity.js deleted file mode 100644 index 3d69c46e55..0000000000 --- a/erpnext/payroll/doctype/gratuity/gratuity.js +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Gratuity', { - setup: function (frm) { - frm.set_query("salary_component", function () { - return { - filters: { - type: "Earning" - } - }; - }); - - frm.set_query("expense_account", function () { - return { - filters: { - "root_type": "Expense", - "is_group": 0, - "company": frm.doc.company - } - }; - }); - - frm.set_query("payable_account", function () { - return { - filters: { - "root_type": "Liability", - "is_group": 0, - "company": frm.doc.company - } - }; - }); - }, - refresh: function (frm) { - if (frm.doc.docstatus == 1 && !frm.doc.pay_via_salary_slip && frm.doc.status == "Unpaid") { - frm.add_custom_button(__("Create Payment Entry"), function () { - return frappe.call({ - method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry', - args: { - "dt": frm.doc.doctype, - "dn": frm.doc.name - }, - callback: function (r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }); - }); - } - }, - employee: function (frm) { - frm.events.calculate_work_experience_and_amount(frm); - }, - gratuity_rule: function (frm) { - frm.events.calculate_work_experience_and_amount(frm); - }, - calculate_work_experience_and_amount: function (frm) { - - if (frm.doc.employee && frm.doc.gratuity_rule) { - frappe.call({ - method: "erpnext.payroll.doctype.gratuity.gratuity.calculate_work_experience_and_amount", - args: { - employee: frm.doc.employee, - gratuity_rule: frm.doc.gratuity_rule - } - }).then((r) => { - frm.set_value("current_work_experience", r.message['current_work_experience']); - frm.set_value("amount", r.message['amount']); - }); - } - } - -}); diff --git a/erpnext/payroll/doctype/gratuity/gratuity.json b/erpnext/payroll/doctype/gratuity/gratuity.json deleted file mode 100644 index c540baf7e6..0000000000 --- a/erpnext/payroll/doctype/gratuity/gratuity.json +++ /dev/null @@ -1,233 +0,0 @@ -{ - "actions": [], - "autoname": "HR-GRA-PAY-.#####", - "creation": "2022-01-27 16:24:28.200061", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "department", - "designation", - "column_break_3", - "posting_date", - "status", - "company", - "gratuity_rule", - "section_break_5", - "pay_via_salary_slip", - "payroll_date", - "salary_component", - "payable_account", - "expense_account", - "mode_of_payment", - "cost_center", - "column_break_15", - "current_work_experience", - "amount", - "paid_amount", - "amended_from" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "in_global_search": 1, - "in_list_view": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1, - "search_index": 1 - }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "posting_date", - "fieldtype": "Date", - "label": "Posting date", - "reqd": 1 - }, - { - "default": "0", - "fieldname": "current_work_experience", - "fieldtype": "Int", - "label": "Current Work Experience", - "read_only": 1 - }, - { - "default": "0", - "fieldname": "amount", - "fieldtype": "Currency", - "label": "Total Amount", - "read_only": 1, - "reqd": 1 - }, - { - "default": "Draft", - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Status", - "options": "Draft\nUnpaid\nPaid\nSubmitted\nCancelled", - "read_only": 1 - }, - { - "depends_on": "eval: !doc.pay_via_salary_slip", - "fieldname": "expense_account", - "fieldtype": "Link", - "label": "Expense Account", - "mandatory_depends_on": "eval: !doc.pay_via_salary_slip", - "options": "Account" - }, - { - "depends_on": "eval: !doc.pay_via_salary_slip", - "fieldname": "mode_of_payment", - "fieldtype": "Link", - "label": "Mode of Payment", - "mandatory_depends_on": "eval: !doc.pay_via_salary_slip", - "options": "Mode of Payment" - }, - { - "fieldname": "gratuity_rule", - "fieldtype": "Link", - "label": "Gratuity Rule", - "options": "Gratuity Rule", - "reqd": 1 - }, - { - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "label": "Payment Configuration" - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "label": "Employee Name", - "read_only": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fetch_from": "employee.designation", - "fieldname": "designation", - "fieldtype": "Data", - "label": "Designation", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Gratuity", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "column_break_15", - "fieldtype": "Column Break" - }, - { - "default": "0", - "depends_on": "eval:doc.pay_via_salary_slip == 0", - "fieldname": "paid_amount", - "fieldtype": "Currency", - "label": "Paid Amount", - "read_only": 1 - }, - { - "depends_on": "eval: !doc.pay_via_salary_slip", - "fieldname": "payable_account", - "fieldtype": "Link", - "label": "Payable Account", - "mandatory_depends_on": "eval: !doc.pay_via_salary_slip", - "options": "Account" - }, - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center" - }, - { - "default": "1", - "fieldname": "pay_via_salary_slip", - "fieldtype": "Check", - "label": "Pay via Salary Slip" - }, - { - "depends_on": "pay_via_salary_slip", - "fieldname": "payroll_date", - "fieldtype": "Date", - "label": "Payroll Date", - "mandatory_depends_on": "pay_via_salary_slip" - }, - { - "depends_on": "pay_via_salary_slip", - "fieldname": "salary_component", - "fieldtype": "Link", - "label": "Salary Component", - "mandatory_depends_on": "pay_via_salary_slip", - "options": "Salary Component" - } - ], - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2022-05-27 13:56:14.349183", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Gratuity", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - } - ], - "search_fields": "employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name" -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/gratuity/gratuity.py b/erpnext/payroll/doctype/gratuity/gratuity.py deleted file mode 100644 index 91740ae8c6..0000000000 --- a/erpnext/payroll/doctype/gratuity/gratuity.py +++ /dev/null @@ -1,334 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from math import floor - -import frappe -from frappe import _, bold -from frappe.utils import flt, get_datetime, get_link_to_form - -from erpnext.accounts.general_ledger import make_gl_entries -from erpnext.controllers.accounts_controller import AccountsController - - -class Gratuity(AccountsController): - def validate(self): - data = calculate_work_experience_and_amount(self.employee, self.gratuity_rule) - self.current_work_experience = data["current_work_experience"] - self.amount = data["amount"] - if self.docstatus == 1: - self.status = "Unpaid" - - def on_submit(self): - if self.pay_via_salary_slip: - self.create_additional_salary() - else: - self.create_gl_entries() - - def on_cancel(self): - self.ignore_linked_doctypes = ["GL Entry"] - self.create_gl_entries(cancel=True) - - def create_gl_entries(self, cancel=False): - gl_entries = self.get_gl_entries() - make_gl_entries(gl_entries, cancel) - - def get_gl_entries(self): - gl_entry = [] - # payable entry - if self.amount: - gl_entry.append( - self.get_gl_dict( - { - "account": self.payable_account, - "credit": self.amount, - "credit_in_account_currency": self.amount, - "against": self.expense_account, - "party_type": "Employee", - "party": self.employee, - "against_voucher_type": self.doctype, - "against_voucher": self.name, - "cost_center": self.cost_center, - }, - item=self, - ) - ) - - # expense entries - gl_entry.append( - self.get_gl_dict( - { - "account": self.expense_account, - "debit": self.amount, - "debit_in_account_currency": self.amount, - "against": self.payable_account, - "cost_center": self.cost_center, - }, - item=self, - ) - ) - else: - frappe.throw(_("Total Amount can not be zero")) - - return gl_entry - - def create_additional_salary(self): - if self.pay_via_salary_slip: - additional_salary = frappe.new_doc("Additional Salary") - additional_salary.employee = self.employee - additional_salary.salary_component = self.salary_component - additional_salary.overwrite_salary_structure_amount = 0 - additional_salary.amount = self.amount - additional_salary.payroll_date = self.payroll_date - additional_salary.company = self.company - additional_salary.ref_doctype = self.doctype - additional_salary.ref_docname = self.name - additional_salary.submit() - - def set_total_advance_paid(self): - paid_amount = frappe.db.sql( - """ - select ifnull(sum(debit_in_account_currency), 0) as paid_amount - from `tabGL Entry` - where against_voucher_type = 'Gratuity' - and against_voucher = %s - and party_type = 'Employee' - and party = %s - """, - (self.name, self.employee), - as_dict=1, - )[0].paid_amount - - if flt(paid_amount) > self.amount: - frappe.throw(_("Row {0}# Paid Amount cannot be greater than Total amount")) - - self.db_set("paid_amount", paid_amount) - if self.amount == self.paid_amount: - self.db_set("status", "Paid") - - -@frappe.whitelist() -def calculate_work_experience_and_amount(employee, gratuity_rule): - current_work_experience = calculate_work_experience(employee, gratuity_rule) or 0 - gratuity_amount = calculate_gratuity_amount(employee, gratuity_rule, current_work_experience) or 0 - - return {"current_work_experience": current_work_experience, "amount": gratuity_amount} - - -def calculate_work_experience(employee, gratuity_rule): - - total_working_days_per_year, minimum_year_for_gratuity = frappe.db.get_value( - "Gratuity Rule", gratuity_rule, ["total_working_days_per_year", "minimum_year_for_gratuity"] - ) - - date_of_joining, relieving_date = frappe.db.get_value( - "Employee", employee, ["date_of_joining", "relieving_date"] - ) - if not relieving_date: - frappe.throw( - _("Please set Relieving Date for employee: {0}").format( - bold(get_link_to_form("Employee", employee)) - ) - ) - - method = frappe.db.get_value( - "Gratuity Rule", gratuity_rule, "work_experience_calculation_function" - ) - employee_total_workings_days = calculate_employee_total_workings_days( - employee, date_of_joining, relieving_date - ) - - current_work_experience = employee_total_workings_days / total_working_days_per_year or 1 - current_work_experience = get_work_experience_using_method( - method, current_work_experience, minimum_year_for_gratuity, employee - ) - return current_work_experience - - -def calculate_employee_total_workings_days(employee, date_of_joining, relieving_date): - employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(date_of_joining)).days - - payroll_based_on = frappe.db.get_value("Payroll Settings", None, "payroll_based_on") or "Leave" - if payroll_based_on == "Leave": - total_lwp = get_non_working_days(employee, relieving_date, "On Leave") - employee_total_workings_days -= total_lwp - elif payroll_based_on == "Attendance": - total_absents = get_non_working_days(employee, relieving_date, "Absent") - employee_total_workings_days -= total_absents - - return employee_total_workings_days - - -def get_work_experience_using_method( - method, current_work_experience, minimum_year_for_gratuity, employee -): - if method == "Round off Work Experience": - current_work_experience = round(current_work_experience) - else: - current_work_experience = floor(current_work_experience) - - if current_work_experience < minimum_year_for_gratuity: - frappe.throw( - _("Employee: {0} have to complete minimum {1} years for gratuity").format( - bold(employee), minimum_year_for_gratuity - ) - ) - return current_work_experience - - -def get_non_working_days(employee, relieving_date, status): - - filters = { - "docstatus": 1, - "status": status, - "employee": employee, - "attendance_date": ("<=", get_datetime(relieving_date)), - } - - if status == "On Leave": - lwp_leave_types = frappe.get_list("Leave Type", filters={"is_lwp": 1}) - lwp_leave_types = [leave_type.name for leave_type in lwp_leave_types] - filters["leave_type"] = ("IN", lwp_leave_types) - - record = frappe.get_all("Attendance", filters=filters, fields=["COUNT(name) as total_lwp"]) - return record[0].total_lwp if len(record) else 0 - - -def calculate_gratuity_amount(employee, gratuity_rule, experience): - applicable_earnings_component = get_applicable_components(gratuity_rule) - total_applicable_components_amount = get_total_applicable_component_amount( - employee, applicable_earnings_component, gratuity_rule - ) - - calculate_gratuity_amount_based_on = frappe.db.get_value( - "Gratuity Rule", gratuity_rule, "calculate_gratuity_amount_based_on" - ) - gratuity_amount = 0 - slabs = get_gratuity_rule_slabs(gratuity_rule) - slab_found = False - year_left = experience - - for slab in slabs: - if calculate_gratuity_amount_based_on == "Current Slab": - slab_found, gratuity_amount = calculate_amount_based_on_current_slab( - slab.from_year, - slab.to_year, - experience, - total_applicable_components_amount, - slab.fraction_of_applicable_earnings, - ) - if slab_found: - break - - elif calculate_gratuity_amount_based_on == "Sum of all previous slabs": - if slab.to_year == 0 and slab.from_year == 0: - gratuity_amount += ( - year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings - ) - slab_found = True - break - - if experience > slab.to_year and experience > slab.from_year and slab.to_year != 0: - gratuity_amount += ( - (slab.to_year - slab.from_year) - * total_applicable_components_amount - * slab.fraction_of_applicable_earnings - ) - year_left -= slab.to_year - slab.from_year - slab_found = True - elif slab.from_year <= experience and (experience < slab.to_year or slab.to_year == 0): - gratuity_amount += ( - year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings - ) - slab_found = True - - if not slab_found: - frappe.throw( - _("No Suitable Slab found for Calculation of gratuity amount in Gratuity Rule: {0}").format( - bold(gratuity_rule) - ) - ) - return gratuity_amount - - -def get_applicable_components(gratuity_rule): - applicable_earnings_component = frappe.get_all( - "Gratuity Applicable Component", filters={"parent": gratuity_rule}, fields=["salary_component"] - ) - if len(applicable_earnings_component) == 0: - frappe.throw( - _("No Applicable Earnings Component found for Gratuity Rule: {0}").format( - bold(get_link_to_form("Gratuity Rule", gratuity_rule)) - ) - ) - applicable_earnings_component = [ - component.salary_component for component in applicable_earnings_component - ] - - return applicable_earnings_component - - -def get_total_applicable_component_amount(employee, applicable_earnings_component, gratuity_rule): - sal_slip = get_last_salary_slip(employee) - if not sal_slip: - frappe.throw(_("No Salary Slip is found for Employee: {0}").format(bold(employee))) - component_and_amounts = frappe.get_all( - "Salary Detail", - filters={ - "docstatus": 1, - "parent": sal_slip, - "parentfield": "earnings", - "salary_component": ("in", applicable_earnings_component), - }, - fields=["amount"], - ) - total_applicable_components_amount = 0 - if not len(component_and_amounts): - frappe.throw(_("No Applicable Component is present in last month salary slip")) - for data in component_and_amounts: - total_applicable_components_amount += data.amount - return total_applicable_components_amount - - -def calculate_amount_based_on_current_slab( - from_year, - to_year, - experience, - total_applicable_components_amount, - fraction_of_applicable_earnings, -): - slab_found = False - gratuity_amount = 0 - if experience >= from_year and (to_year == 0 or experience < to_year): - gratuity_amount = ( - total_applicable_components_amount * experience * fraction_of_applicable_earnings - ) - if fraction_of_applicable_earnings: - slab_found = True - - return slab_found, gratuity_amount - - -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): - return frappe.get_list( - "Salary Structure Assignment", - filters={"employee": employee, "docstatus": 1}, - fields=["from_date", "salary_structure"], - order_by="from_date desc", - )[0].salary_structure - - -def get_last_salary_slip(employee): - salary_slips = frappe.get_list( - "Salary Slip", filters={"employee": employee, "docstatus": 1}, order_by="start_date desc" - ) - if not salary_slips: - return - return salary_slips[0].name diff --git a/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py b/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py deleted file mode 100644 index 9396461f1d..0000000000 --- a/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py +++ /dev/null @@ -1,11 +0,0 @@ -from frappe import _ - - -def get_data(): - return { - "fieldname": "reference_name", - "non_standard_fieldnames": { - "Additional Salary": "ref_docname", - }, - "transactions": [{"label": _("Payment"), "items": ["Payment Entry", "Additional Salary"]}], - } diff --git a/erpnext/payroll/doctype/gratuity/gratuity_list.js b/erpnext/payroll/doctype/gratuity/gratuity_list.js deleted file mode 100644 index 20e3d5b4e5..0000000000 --- a/erpnext/payroll/doctype/gratuity/gratuity_list.js +++ /dev/null @@ -1,12 +0,0 @@ -frappe.listview_settings["Gratuity"] = { - get_indicator: function(doc) { - let status_color = { - "Draft": "red", - "Submitted": "blue", - "Cancelled": "red", - "Paid": "green", - "Unpaid": "orange", - }; - return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; - } -}; \ No newline at end of file diff --git a/erpnext/payroll/doctype/gratuity/test_gratuity.py b/erpnext/payroll/doctype/gratuity/test_gratuity.py deleted file mode 100644 index 1155a06edd..0000000000 --- a/erpnext/payroll/doctype/gratuity/test_gratuity.py +++ /dev/null @@ -1,231 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days, add_months, floor, flt, get_datetime, get_first_day, getdate - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.expense_claim.test_expense_claim import get_payable_account -from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list -from erpnext.payroll.doctype.gratuity.gratuity import get_last_salary_slip -from erpnext.payroll.doctype.salary_slip.test_salary_slip import ( - make_deduction_salary_component, - make_earning_salary_component, - make_employee_salary_slip, - make_holiday_list, -) -from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip -from erpnext.regional.united_arab_emirates.setup import create_gratuity_rule - -test_dependencies = ["Salary Component", "Salary Slip", "Account"] - - -class TestGratuity(FrappeTestCase): - def setUp(self): - frappe.db.delete("Gratuity") - frappe.db.delete("Salary Slip") - frappe.db.delete("Additional Salary", {"ref_doctype": "Gratuity"}) - - make_earning_salary_component( - setup=True, test_tax=True, company_list=["_Test Company"], include_flexi_benefits=True - ) - make_deduction_salary_component(setup=True, test_tax=True, company_list=["_Test Company"]) - make_holiday_list() - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_get_last_salary_slip_should_return_none_for_new_employee(self): - new_employee = make_employee("new_employee@salary.com", company="_Test Company") - salary_slip = get_last_salary_slip(new_employee) - self.assertIsNone(salary_slip) - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_gratuity_based_on_current_slab_via_additional_salary(self): - """ - Range | Fraction - 5-0 | 1 - """ - doj = add_days(getdate(), -(6 * 365)) - relieving_date = getdate() - - employee = make_employee( - "test_employee_gratuity@salary.com", - company="_Test Company", - date_of_joining=doj, - relieving_date=relieving_date, - ) - sal_slip = create_salary_slip("test_employee_gratuity@salary.com") - - rule = get_gratuity_rule("Rule Under Unlimited Contract on termination (UAE)") - gratuity = create_gratuity(pay_via_salary_slip=1, employee=employee, rule=rule.name) - - # work experience calculation - employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(doj)).days - experience = floor(employee_total_workings_days / rule.total_working_days_per_year) - self.assertEqual(gratuity.current_work_experience, experience) - - # amount calculation - component_amount = frappe.get_all( - "Salary Detail", - filters={ - "docstatus": 1, - "parent": sal_slip, - "parentfield": "earnings", - "salary_component": "Basic Salary", - }, - fields=["amount"], - limit=1, - ) - gratuity_amount = component_amount[0].amount * experience - self.assertEqual(flt(gratuity_amount, 2), flt(gratuity.amount, 2)) - - # additional salary creation (Pay via salary slip) - self.assertTrue(frappe.db.exists("Additional Salary", {"ref_docname": gratuity.name})) - - # gratuity should be marked "Paid" on the next salary slip submission - salary_slip = make_salary_slip("Test Gratuity", employee=employee) - salary_slip.posting_date = getdate() - salary_slip.insert() - salary_slip.submit() - - gratuity.reload() - self.assertEqual(gratuity.status, "Paid") - - @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") - def test_gratuity_based_on_all_previous_slabs_via_payment_entry(self): - """ - Range | Fraction - 0-1 | 0 - 1-5 | 0.7 - 5-0 | 1 - """ - from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry - - doj = add_days(getdate(), -(6 * 365)) - relieving_date = getdate() - - employee = make_employee( - "test_employee_gratuity@salary.com", - company="_Test Company", - date_of_joining=doj, - relieving_date=relieving_date, - ) - - sal_slip = create_salary_slip("test_employee_gratuity@salary.com") - rule = get_gratuity_rule("Rule Under Limited Contract (UAE)") - set_mode_of_payment_account() - - gratuity = create_gratuity( - expense_account="Payment Account - _TC", mode_of_payment="Cash", employee=employee - ) - - # work experience calculation - employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(doj)).days - experience = floor(employee_total_workings_days / rule.total_working_days_per_year) - self.assertEqual(gratuity.current_work_experience, experience) - - # amount calculation - component_amount = frappe.get_all( - "Salary Detail", - filters={ - "docstatus": 1, - "parent": sal_slip, - "parentfield": "earnings", - "salary_component": "Basic Salary", - }, - fields=["amount"], - limit=1, - ) - - gratuity_amount = ((0 * 1) + (4 * 0.7) + (1 * 1)) * component_amount[0].amount - self.assertEqual(flt(gratuity_amount, 2), flt(gratuity.amount, 2)) - self.assertEqual(gratuity.status, "Unpaid") - - pe = get_payment_entry("Gratuity", gratuity.name) - pe.reference_no = "123467" - pe.reference_date = getdate() - pe.submit() - - gratuity.reload() - self.assertEqual(gratuity.status, "Paid") - self.assertEqual(flt(gratuity.paid_amount, 2), flt(gratuity.amount, 2)) - - -def get_gratuity_rule(name): - rule = frappe.db.exists("Gratuity Rule", name) - if not rule: - create_gratuity_rule() - rule = frappe.get_doc("Gratuity Rule", name) - rule.applicable_earnings_component = [] - rule.append("applicable_earnings_component", {"salary_component": "Basic Salary"}) - rule.save() - - return rule - - -def create_gratuity(**args): - if args: - args = frappe._dict(args) - gratuity = frappe.new_doc("Gratuity") - gratuity.employee = args.employee - gratuity.posting_date = getdate() - gratuity.gratuity_rule = args.rule or "Rule Under Limited Contract (UAE)" - gratuity.pay_via_salary_slip = args.pay_via_salary_slip or 0 - if gratuity.pay_via_salary_slip: - gratuity.payroll_date = getdate() - gratuity.salary_component = "Performance Bonus" - else: - gratuity.expense_account = args.expense_account or "Payment Account - _TC" - gratuity.payable_account = args.payable_account or get_payable_account("_Test Company") - gratuity.mode_of_payment = args.mode_of_payment or "Cash" - - gratuity.save() - gratuity.submit() - - return gratuity - - -def set_mode_of_payment_account(): - if not frappe.db.exists("Account", "Payment Account - _TC"): - mode_of_payment = create_account() - - mode_of_payment = frappe.get_doc("Mode of Payment", "Cash") - - mode_of_payment.accounts = [] - mode_of_payment.append( - "accounts", {"company": "_Test Company", "default_account": "_Test Bank - _TC"} - ) - mode_of_payment.save() - - -def create_account(): - return frappe.get_doc( - { - "doctype": "Account", - "company": "_Test Company", - "account_name": "Payment Account", - "root_type": "Asset", - "report_type": "Balance Sheet", - "currency": "INR", - "parent_account": "Bank Accounts - _TC", - "account_type": "Bank", - } - ).insert(ignore_permissions=True) - - -def create_salary_slip(employee): - if not frappe.db.exists("Salary Slip", {"employee": employee}): - posting_date = get_first_day(add_months(getdate(), -1)) - salary_slip = make_employee_salary_slip( - employee, "Monthly", "Test Gratuity", posting_date=posting_date - ) - salary_slip.start_date = posting_date - salary_slip.end_date = None - salary_slip.submit() - salary_slip = salary_slip.name - else: - salary_slip = get_last_salary_slip(employee) - - return salary_slip diff --git a/erpnext/payroll/doctype/gratuity_applicable_component/__init__.py b/erpnext/payroll/doctype/gratuity_applicable_component/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.json b/erpnext/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.json deleted file mode 100644 index eea0e852b1..0000000000 --- a/erpnext/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "actions": [], - "creation": "2020-08-05 19:00:28.097265", - "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 - } - ], - "istable": 1, - "links": [], - "modified": "2020-08-05 20:17:13.855035", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Gratuity Applicable 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/gratuity_applicable_component/gratuity_applicable_component.py b/erpnext/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.py deleted file mode 100644 index 9c1657d21d..0000000000 --- a/erpnext/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class GratuityApplicableComponent(Document): - pass diff --git a/erpnext/payroll/doctype/gratuity_rule/__init__.py b/erpnext/payroll/doctype/gratuity_rule/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js deleted file mode 100644 index 7290a9eafa..0000000000 --- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Gratuity Rule', { - // refresh: function(frm) { - - // } -}); - -frappe.ui.form.on('Gratuity Rule Slab', { - - /* - Slabs should be in order like - - from | to | fraction - 0 | 4 | 0.5 - 4 | 6 | 0.7 - - So, on row addition setting current_row.from = previous row.to. - On to_year insert we have to check that it is not less than from_year - - Wrong order may lead to Wrong Calculation - */ - - gratuity_rule_slabs_add(frm, cdt, cdn) { - let row = locals[cdt][cdn]; - let array_idx = row.idx - 1; - if (array_idx > 0) { - row.from_year = cur_frm.doc.gratuity_rule_slabs[array_idx - 1].to_year; - frm.refresh(); - } - }, - - to_year(frm, cdt, cdn) { - let row = locals[cdt][cdn]; - if (row.to_year <= row.from_year && row.to_year === 0) { - frappe.throw(__("To(Year) year can not be less than From(year)")); - } - } -}); diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.json b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.json deleted file mode 100644 index 84cdcf5038..0000000000 --- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "actions": [], - "autoname": "Prompt", - "creation": "2020-08-05 19:00:36.103500", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "applicable_earnings_component", - "work_experience_calculation_function", - "total_working_days_per_year", - "column_break_3", - "disable", - "calculate_gratuity_amount_based_on", - "minimum_year_for_gratuity", - "gratuity_rules_section", - "gratuity_rule_slabs" - ], - "fields": [ - { - "default": "0", - "fieldname": "disable", - "fieldtype": "Check", - "label": "Disable" - }, - { - "fieldname": "calculate_gratuity_amount_based_on", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Calculate Gratuity Amount Based On", - "options": "Current Slab\nSum of all previous slabs", - "reqd": 1 - }, - { - "description": "Salary components should be part of the Salary Structure.", - "fieldname": "applicable_earnings_component", - "fieldtype": "Table MultiSelect", - "label": "Applicable Earnings Component", - "options": "Gratuity Applicable Component", - "reqd": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "gratuity_rules_section", - "fieldtype": "Section Break", - "label": "Gratuity Rules" - }, - { - "description": "Leave From and To 0 for no upper and lower limit.", - "fieldname": "gratuity_rule_slabs", - "fieldtype": "Table", - "label": "Current Work Experience", - "options": "Gratuity Rule Slab", - "reqd": 1 - }, - { - "default": "Round off Work Experience", - "fieldname": "work_experience_calculation_function", - "fieldtype": "Select", - "label": "Work Experience Calculation method", - "options": "Round off Work Experience\nTake Exact Completed Years" - }, - { - "default": "365", - "fieldname": "total_working_days_per_year", - "fieldtype": "Int", - "label": "Total working Days Per Year" - }, - { - "fieldname": "minimum_year_for_gratuity", - "fieldtype": "Int", - "label": "Minimum Year for Gratuity" - } - ], - "index_web_pages_for_search": 1, - "links": [], - "modified": "2020-12-03 17:08:27.891535", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Gratuity Rule", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "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/gratuity_rule/gratuity_rule.py b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py deleted file mode 100644 index 5cde79a162..0000000000 --- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document - - -class GratuityRule(Document): - def validate(self): - for current_slab in self.gratuity_rule_slabs: - if (current_slab.from_year > current_slab.to_year) and current_slab.to_year != 0: - frappe.throw( - _("Row {0}: From (Year) can not be greater than To (Year)").format(current_slab.idx) - ) - - if ( - current_slab.to_year == 0 and current_slab.from_year == 0 and len(self.gratuity_rule_slabs) > 1 - ): - frappe.throw( - _("You can not define multiple slabs if you have a slab with no lower and upper limits.") - ) - - -def get_gratuity_rule(name, slabs, **args): - args = frappe._dict(args) - - rule = frappe.new_doc("Gratuity Rule") - rule.name = name - rule.calculate_gratuity_amount_based_on = ( - args.calculate_gratuity_amount_based_on or "Current Slab" - ) - rule.work_experience_calculation_method = ( - args.work_experience_calculation_method or "Take Exact Completed Years" - ) - rule.minimum_year_for_gratuity = 1 - - for slab in slabs: - slab = frappe._dict(slab) - rule.append("gratuity_rule_slabs", slab) - return rule diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py deleted file mode 100644 index fa5a9dedd3..0000000000 --- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py +++ /dev/null @@ -1,8 +0,0 @@ -from frappe import _ - - -def get_data(): - return { - "fieldname": "gratuity_rule", - "transactions": [{"label": _("Gratuity"), "items": ["Gratuity"]}], - } diff --git a/erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py b/erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py deleted file mode 100644 index 8393050b4a..0000000000 --- a/erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -import unittest - - -class TestGratuityRule(unittest.TestCase): - pass diff --git a/erpnext/payroll/doctype/gratuity_rule_slab/__init__.py b/erpnext/payroll/doctype/gratuity_rule_slab/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.json b/erpnext/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.json deleted file mode 100644 index bc37b0f51e..0000000000 --- a/erpnext/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "actions": [], - "creation": "2020-08-05 19:12:49.423500", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "from_year", - "to_year", - "fraction_of_applicable_earnings" - ], - "fields": [ - { - "fieldname": "fraction_of_applicable_earnings", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Fraction of Applicable Earnings ", - "reqd": 1 - }, - { - "default": "0", - "fieldname": "from_year", - "fieldtype": "Int", - "in_list_view": 1, - "label": "From(Year)", - "read_only": 1, - "reqd": 1 - }, - { - "default": "0", - "fieldname": "to_year", - "fieldtype": "Int", - "in_list_view": 1, - "label": "To(Year)", - "reqd": 1 - } - ], - "istable": 1, - "links": [], - "modified": "2020-08-17 14:09:56.781712", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Gratuity Rule Slab", - "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/gratuity_rule_slab/gratuity_rule_slab.py b/erpnext/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.py deleted file mode 100644 index 2ae6b54798..0000000000 --- a/erpnext/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class GratuityRuleSlab(Document): - pass diff --git a/erpnext/payroll/doctype/income_tax_slab/__init__.py b/erpnext/payroll/doctype/income_tax_slab/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js deleted file mode 100644 index 7d780d3b04..0000000000 --- a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Income Tax Slab', { - currency: function(frm) { - frm.refresh_fields(); - } -}); diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json deleted file mode 100644 index 5a7de37bec..0000000000 --- a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "actions": [], - "autoname": "Prompt", - "creation": "2020-03-17 16:50:35.564915", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "effective_from", - "company", - "column_break_3", - "currency", - "standard_tax_exemption_amount", - "allow_tax_exemption", - "disabled", - "amended_from", - "taxable_salary_slabs_section", - "slabs", - "taxes_and_charges_on_income_tax_section", - "other_taxes_and_charges" - ], - "fields": [ - { - "fieldname": "effective_from", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Effective from", - "reqd": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "default": "0", - "description": "If enabled, Tax Exemption Declaration will be considered for income tax calculation.", - "fieldname": "allow_tax_exemption", - "fieldtype": "Check", - "label": "Allow Tax Exemption" - }, - { - "fieldname": "taxable_salary_slabs_section", - "fieldtype": "Section Break", - "label": "Taxable Salary Slabs" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Income Tax Slab", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "slabs", - "fieldtype": "Table", - "label": "Taxable Salary Slabs", - "options": "Taxable Salary Slab", - "reqd": 1 - }, - { - "allow_on_submit": 1, - "default": "0", - "fieldname": "disabled", - "fieldtype": "Check", - "label": "Disabled" - }, - { - "depends_on": "allow_tax_exemption", - "fieldname": "standard_tax_exemption_amount", - "fieldtype": "Currency", - "label": "Standard Tax Exemption Amount", - "options": "currency" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company" - }, - { - "collapsible": 1, - "collapsible_depends_on": "other_taxes_and_charges", - "fieldname": "taxes_and_charges_on_income_tax_section", - "fieldtype": "Section Break", - "label": "Taxes and Charges on Income Tax" - }, - { - "fieldname": "other_taxes_and_charges", - "fieldtype": "Table", - "label": "Other Taxes and Charges", - "options": "Income Tax Slab Other Charges" - }, - { - "fetch_from": "company.default_currency", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "print_hide": 1, - "reqd": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2021-03-31 22:42:08.139520", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Income Tax Slab", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py deleted file mode 100644 index e62d61f4c2..0000000000 --- a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - -# import frappe -import erpnext - - -class IncomeTaxSlab(Document): - def validate(self): - if self.company: - self.currency = erpnext.get_company_currency(self.company) diff --git a/erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py b/erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py deleted file mode 100644 index 680cb3bb00..0000000000 --- a/erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -import unittest - - -class TestIncomeTaxSlab(unittest.TestCase): - pass diff --git a/erpnext/payroll/doctype/income_tax_slab_other_charges/__init__.py b/erpnext/payroll/doctype/income_tax_slab_other_charges/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json deleted file mode 100644 index 0dba338250..0000000000 --- a/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "actions": [], - "creation": "2020-04-24 11:46:59.041180", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "description", - "column_break_2", - "percent", - "conditions_section", - "min_taxable_income", - "column_break_7", - "max_taxable_income" - ], - "fields": [ - { - "columns": 4, - "fieldname": "description", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Description", - "reqd": 1 - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "columns": 2, - "fieldname": "percent", - "fieldtype": "Percent", - "in_list_view": 1, - "label": "Percent", - "reqd": 1 - }, - { - "fieldname": "conditions_section", - "fieldtype": "Section Break", - "label": "Conditions" - }, - { - "columns": 2, - "fieldname": "min_taxable_income", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Min Taxable Income", - "options": "currency" - }, - { - "fieldname": "column_break_7", - "fieldtype": "Column Break" - }, - { - "columns": 2, - "fieldname": "max_taxable_income", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Max Taxable Income", - "options": "currency" - } - ], - "istable": 1, - "links": [], - "modified": "2020-10-19 13:45:12.850090", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Income Tax Slab Other Charges", - "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/income_tax_slab_other_charges/income_tax_slab_other_charges.py b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py deleted file mode 100644 index 53911a945b..0000000000 --- a/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class IncomeTaxSlabOtherCharges(Document): - pass diff --git a/erpnext/payroll/doctype/payroll_employee_detail/__init__.py b/erpnext/payroll/doctype/payroll_employee_detail/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json deleted file mode 100644 index 09c7eb9a45..0000000000 --- a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "actions": [], - "creation": "2017-11-30 06:07:33.477781", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "column_break_3", - "department", - "designation" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Employee", - "options": "Employee" - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Employee Name", - "read_only": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fetch_from": "employee.designation", - "fieldname": "designation", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Designation", - "read_only": 1 - } - ], - "istable": 1, - "links": [], - "modified": "2020-12-17 15:43:29.542977", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Payroll Employee Detail", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py deleted file mode 100644 index 8cc426b9db..0000000000 --- a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class PayrollEmployeeDetail(Document): - pass diff --git a/erpnext/payroll/doctype/payroll_entry/__init__.py b/erpnext/payroll/doctype/payroll_entry/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js deleted file mode 100644 index b06f3502e2..0000000000 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -var in_progress = false; - -frappe.provide("erpnext.accounts.dimensions"); - -frappe.ui.form.on('Payroll Entry', { - onload: function (frm) { - if (!frm.doc.posting_date) { - frm.doc.posting_date = frappe.datetime.nowdate(); - } - frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet); - - erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); - frm.events.department_filters(frm); - frm.events.payroll_payable_account_filters(frm); - }, - - department_filters: function (frm) { - frm.set_query("department", function () { - return { - "filters": { - "company": frm.doc.company, - } - }; - }); - }, - - payroll_payable_account_filters: function (frm) { - frm.set_query("payroll_payable_account", function () { - return { - filters: { - "company": frm.doc.company, - "root_type": "Liability", - "is_group": 0, - } - }; - }); - }, - - refresh: function (frm) { - if (frm.doc.docstatus === 0 && !frm.is_new()) { - frm.page.clear_primary_action(); - frm.add_custom_button(__("Get Employees"), - function () { - frm.events.get_employee_details(frm); - } - ).toggleClass("btn-primary", !(frm.doc.employees || []).length); - } - - if ( - (frm.doc.employees || []).length - && !frappe.model.has_workflow(frm.doctype) - && !cint(frm.doc.salary_slips_created) - && (frm.doc.docstatus != 2) - ) { - if (frm.doc.docstatus == 0) { - frm.page.clear_primary_action(); - frm.page.set_primary_action(__("Create Salary Slips"), () => { - frm.save("Submit").then(() => { - frm.page.clear_primary_action(); - frm.refresh(); - frm.events.refresh(frm); - }); - }); - } else if (frm.doc.docstatus == 1 && frm.doc.status == "Failed") { - frm.add_custom_button(__("Create Salary Slip"), function () { - frm.call("create_salary_slips", {}, () => { - frm.reload_doc(); - }); - }).addClass("btn-primary"); - } - } - - if (frm.doc.docstatus == 1 && frm.doc.status == "Submitted") { - if (frm.custom_buttons) frm.clear_custom_buttons(); - frm.events.add_context_buttons(frm); - } - - if (frm.doc.status == "Failed" && frm.doc.error_message) { - const issue = `issue`; - let process = (cint(frm.doc.salary_slips_created)) ? "submission" : "creation"; - - frm.dashboard.set_headline( - __("Salary Slip {0} failed. You can resolve the {1} and retry {0}.", [process, issue]) - ); - - $("#jump_to_error").on("click", (e) => { - e.preventDefault(); - frappe.utils.scroll_to( - frm.get_field("error_message").$wrapper, - true, - 30 - ); - }); - } - - frappe.realtime.on("completed_salary_slip_creation", function() { - frm.reload_doc(); - }); - - frappe.realtime.on("completed_salary_slip_submission", function() { - frm.reload_doc(); - }); - }, - - get_employee_details: function (frm) { - return frappe.call({ - doc: frm.doc, - method: 'fill_employee_details', - }).then(r => { - if (r.docs && r.docs[0].employees) { - frm.employees = r.docs[0].employees; - frm.dirty(); - frm.save(); - frm.refresh(); - if (r.docs[0].validate_attendance) { - render_employee_attendance(frm, r.message); - } - } - }); - }, - - create_salary_slips: function (frm) { - frm.call({ - doc: frm.doc, - method: "create_salary_slips", - callback: function () { - frm.reload_doc(); - frm.toolbar.refresh(); - } - }); - }, - - add_context_buttons: function (frm) { - if (frm.doc.salary_slips_submitted || (frm.doc.__onload && frm.doc.__onload.submitted_ss)) { - frm.events.add_bank_entry_button(frm); - } else if (frm.doc.salary_slips_created && frm.doc.status != 'Queued') { - frm.add_custom_button(__("Submit Salary Slip"), function () { - submit_salary_slip(frm); - }).addClass("btn-primary"); - } - }, - - add_bank_entry_button: function (frm) { - frappe.call({ - method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.payroll_entry_has_bank_entries', - args: { - 'name': frm.doc.name - }, - callback: function (r) { - if (r.message && !r.message.submitted) { - frm.add_custom_button(__("Make Bank Entry"), function () { - make_bank_entry(frm); - }).addClass("btn-primary"); - } - } - }); - }, - - setup: function (frm) { - frm.add_fetch('company', 'cost_center', 'cost_center'); - - frm.set_query("payment_account", function () { - var account_types = ["Bank", "Cash"]; - return { - filters: { - "account_type": ["in", account_types], - "is_group": 0, - "company": frm.doc.company - } - }; - }); - - frm.set_query('employee', 'employees', () => { - let error_fields = []; - let mandatory_fields = ['company', 'payroll_frequency', 'start_date', 'end_date']; - - let message = __('Mandatory fields required in {0}', [__(frm.doc.doctype)]); - - mandatory_fields.forEach(field => { - if (!frm.doc[field]) { - error_fields.push(frappe.unscrub(field)); - } - }); - - if (error_fields && error_fields.length) { - message = message + '

  • ' + error_fields.join('
  • ') + "
"; - frappe.throw({ - message: message, - indicator: 'red', - title: __('Missing Fields') - }); - } - - return { - query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.employee_query", - filters: frm.events.get_employee_filters(frm) - }; - }); - }, - - get_employee_filters: function (frm) { - let filters = {}; - filters['salary_slip_based_on_timesheet'] = frm.doc.salary_slip_based_on_timesheet; - - let fields = ['company', 'start_date', 'end_date', 'payroll_frequency', 'payroll_payable_account', - 'currency', 'department', 'branch', 'designation']; - - fields.forEach(field => { - if (frm.doc[field]) { - filters[field] = frm.doc[field]; - } - }); - - if (frm.doc.employees) { - let employees = frm.doc.employees.filter(d => d.employee).map(d => d.employee); - if (employees && employees.length) { - filters['employees'] = employees; - } - } - return filters; - }, - - payroll_frequency: function (frm) { - frm.trigger("set_start_end_dates").then( ()=> { - frm.events.clear_employee_table(frm); - }); - }, - - company: function (frm) { - frm.events.clear_employee_table(frm); - erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); - frm.trigger("set_payable_account_and_currency"); - }, - - set_payable_account_and_currency: function (frm) { - frappe.db.get_value("Company", {"name": frm.doc.company}, "default_currency", (r) => { - frm.set_value('currency', r.default_currency); - }); - frappe.db.get_value("Company", {"name": frm.doc.company}, "default_payroll_payable_account", (r) => { - frm.set_value('payroll_payable_account', r.default_payroll_payable_account); - }); - }, - - currency: function (frm) { - var company_currency; - if (!frm.doc.company) { - company_currency = erpnext.get_currency(frappe.defaults.get_default("Company")); - } else { - company_currency = erpnext.get_currency(frm.doc.company); - } - if (frm.doc.currency) { - if (company_currency != frm.doc.currency) { - frappe.call({ - method: "erpnext.setup.utils.get_exchange_rate", - args: { - from_currency: frm.doc.currency, - to_currency: company_currency, - }, - callback: function (r) { - frm.set_value("exchange_rate", flt(r.message)); - frm.set_df_property('exchange_rate', 'hidden', 0); - frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + - " = [?] " + company_currency); - } - }); - } else { - frm.set_value("exchange_rate", 1.0); - frm.set_df_property('exchange_rate', 'hidden', 1); - frm.set_df_property("exchange_rate", "description", ""); - } - } - }, - - department: function (frm) { - frm.events.clear_employee_table(frm); - }, - - designation: function (frm) { - frm.events.clear_employee_table(frm); - }, - - branch: function (frm) { - frm.events.clear_employee_table(frm); - }, - - start_date: function (frm) { - if (!in_progress && frm.doc.start_date) { - frm.trigger("set_end_date"); - } else { - // reset flag - in_progress = false; - } - frm.events.clear_employee_table(frm); - }, - - project: function (frm) { - frm.events.clear_employee_table(frm); - }, - - salary_slip_based_on_timesheet: function (frm) { - frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet); - }, - - set_start_end_dates: function (frm) { - if (!frm.doc.salary_slip_based_on_timesheet) { - frappe.call({ - method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_start_end_dates', - args: { - payroll_frequency: frm.doc.payroll_frequency, - start_date: frm.doc.posting_date - }, - callback: function (r) { - if (r.message) { - in_progress = true; - frm.set_value('start_date', r.message.start_date); - frm.set_value('end_date', r.message.end_date); - } - } - }); - } - }, - - set_end_date: function (frm) { - frappe.call({ - method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_end_date', - args: { - frequency: frm.doc.payroll_frequency, - start_date: frm.doc.start_date - }, - callback: function (r) { - if (r.message) { - frm.set_value('end_date', r.message.end_date); - } - } - }); - }, - - validate_attendance: function (frm) { - if (frm.doc.validate_attendance && frm.doc.employees) { - frappe.call({ - method: 'validate_employee_attendance', - args: {}, - callback: function (r) { - render_employee_attendance(frm, r.message); - }, - doc: frm.doc, - freeze: true, - freeze_message: __('Validating Employee Attendance...') - }); - } else { - frm.fields_dict.attendance_detail_html.html(""); - } - }, - - clear_employee_table: function (frm) { - frm.clear_table('employees'); - frm.refresh(); - }, -}); - -// Submit salary slips - -const submit_salary_slip = function (frm) { - frappe.confirm(__('This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?'), - function () { - frappe.call({ - method: 'submit_salary_slips', - args: {}, - callback: function () { - frm.reload_doc(); - frm.events.refresh(frm); - }, - doc: frm.doc, - freeze: true, - freeze_message: __('Submitting Salary Slips and creating Journal Entry...') - }); - }, - function () { - if (frappe.dom.freeze_count) { - frappe.dom.unfreeze(); - frm.events.refresh(frm); - } - } - ); -}; - -let make_bank_entry = function (frm) { - var doc = frm.doc; - if (doc.payment_account) { - return frappe.call({ - doc: cur_frm.doc, - method: "make_payment_entry", - callback: function () { - frappe.set_route( - 'List', 'Journal Entry', { - "Journal Entry Account.reference_name": frm.doc.name - } - ); - }, - freeze: true, - freeze_message: __("Creating Payment Entries......") - }); - } else { - frappe.msgprint(__("Payment Account is mandatory")); - frm.scroll_to_field('payment_account'); - } -}; - -let render_employee_attendance = function (frm, data) { - frm.fields_dict.attendance_detail_html.html( - frappe.render_template('employees_to_mark_attendance', { - data: data - }) - ); -}; diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json deleted file mode 100644 index 17882eb5d9..0000000000 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json +++ /dev/null @@ -1,342 +0,0 @@ -{ - "actions": [], - "allow_copy": 1, - "autoname": "HR-PRUN-.YYYY.-.#####", - "creation": "2017-10-23 15:22:29.291323", - "doctype": "DocType", - "document_type": "Other", - "engine": "InnoDB", - "field_order": [ - "section_break0", - "posting_date", - "payroll_frequency", - "company", - "column_break1", - "status", - "currency", - "exchange_rate", - "payroll_payable_account", - "section_break_8", - "branch", - "department", - "column_break_10", - "designation", - "number_of_employees", - "sec_break20", - "employees", - "section_break_13", - "validate_attendance", - "attendance_detail_html", - "section_break_12", - "salary_slip_based_on_timesheet", - "select_payroll_period", - "start_date", - "end_date", - "column_break_11", - "deduct_tax_for_unclaimed_employee_benefits", - "deduct_tax_for_unsubmitted_tax_exemption_proof", - "accounting_dimensions_section", - "project", - "dimension_col_break", - "cost_center", - "account", - "payment_account", - "column_break_33", - "bank_account", - "salary_slips_created", - "salary_slips_submitted", - "failure_details_section", - "error_message", - "section_break_41", - "amended_from" - ], - "fields": [ - { - "fieldname": "section_break0", - "fieldtype": "Section Break", - "label": "Select Employees" - }, - { - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "label": "Posting Date", - "reqd": 1 - }, - { - "depends_on": "eval:doc.salary_slip_based_on_timesheet == 0", - "fieldname": "payroll_frequency", - "fieldtype": "Select", - "label": "Payroll Frequency", - "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily", - "reqd": 1 - }, - { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "width": "50%" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Company", - "options": "Company", - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "label": "Employees" - }, - { - "fieldname": "branch", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Branch", - "options": "Branch" - }, - { - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department" - }, - { - "fieldname": "column_break_10", - "fieldtype": "Column Break" - }, - { - "fieldname": "designation", - "fieldtype": "Link", - "label": "Designation", - "options": "Designation" - }, - { - "fieldname": "number_of_employees", - "fieldtype": "Int", - "label": "Number Of Employees", - "read_only": 1 - }, - { - "fieldname": "sec_break20", - "fieldtype": "Section Break" - }, - { - "fieldname": "employees", - "fieldtype": "Table", - "label": "Employee Details", - "options": "Payroll Employee Detail" - }, - { - "fieldname": "section_break_13", - "fieldtype": "Section Break" - }, - { - "default": "0", - "fieldname": "validate_attendance", - "fieldtype": "Check", - "label": "Validate Attendance" - }, - { - "fieldname": "attendance_detail_html", - "fieldtype": "HTML" - }, - { - "fieldname": "section_break_12", - "fieldtype": "Section Break" - }, - { - "default": "0", - "fieldname": "salary_slip_based_on_timesheet", - "fieldtype": "Check", - "label": "Salary Slip Based on Timesheet" - }, - { - "fieldname": "select_payroll_period", - "fieldtype": "Section Break", - "label": "Select Payroll Period" - }, - { - "fieldname": "start_date", - "fieldtype": "Date", - "label": "Start Date", - "reqd": 1 - }, - { - "fieldname": "end_date", - "fieldtype": "Date", - "label": "End Date", - "reqd": 1 - }, - { - "fieldname": "column_break_11", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "deduct_tax_for_unclaimed_employee_benefits", - "fieldtype": "Check", - "label": "Deduct Tax For Unclaimed Employee Benefits" - }, - { - "default": "0", - "fieldname": "deduct_tax_for_unsubmitted_tax_exemption_proof", - "fieldtype": "Check", - "label": "Deduct Tax For Unsubmitted Tax Exemption Proof" - }, - { - "default": ":Company", - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center", - "reqd": 1 - }, - { - "fieldname": "project", - "fieldtype": "Link", - "label": "Project", - "options": "Project" - }, - { - "fieldname": "account", - "fieldtype": "Section Break", - "label": "Payment Entry" - }, - { - "allow_on_submit": 1, - "description": "Select Payment Account to make Bank Entry", - "fetch_from": "bank_account.account", - "fieldname": "payment_account", - "fieldtype": "Link", - "label": "Payment Account", - "options": "Account" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Payroll Entry", - "print_hide": 1, - "read_only": 1 - }, - { - "default": "0", - "fieldname": "salary_slips_created", - "fieldtype": "Check", - "hidden": 1, - "label": "Salary Slips Created", - "no_copy": 1, - "read_only": 1 - }, - { - "default": "0", - "fieldname": "salary_slips_submitted", - "fieldtype": "Check", - "hidden": 1, - "label": "Salary Slips Submitted", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "accounting_dimensions_section", - "fieldtype": "Section Break", - "label": "Accounting Dimensions" - }, - { - "fieldname": "dimension_col_break", - "fieldtype": "Column Break" - }, - { - "fieldname": "bank_account", - "fieldtype": "Link", - "label": "Bank Account", - "options": "Bank Account" - }, - { - "fieldname": "column_break_33", - "fieldtype": "Column Break" - }, - { - "depends_on": "company", - "fieldname": "currency", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Currency", - "options": "Currency", - "reqd": 1 - }, - { - "depends_on": "company", - "fieldname": "exchange_rate", - "fieldtype": "Float", - "label": "Exchange Rate", - "precision": "9", - "reqd": 1 - }, - { - "depends_on": "company", - "fieldname": "payroll_payable_account", - "fieldtype": "Link", - "label": "Payroll Payable Account", - "options": "Account", - "reqd": 1 - }, - { - "collapsible": 1, - "collapsible_depends_on": "error_message", - "depends_on": "eval:doc.status=='Failed';", - "fieldname": "failure_details_section", - "fieldtype": "Section Break", - "label": "Failure Details" - }, - { - "depends_on": "eval:doc.status=='Failed';", - "fieldname": "error_message", - "fieldtype": "Small Text", - "label": "Error Message", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "section_break_41", - "fieldtype": "Section Break" - }, - { - "fieldname": "status", - "fieldtype": "Select", - "label": "Status", - "options": "Draft\nSubmitted\nCancelled\nQueued\nFailed", - "print_hide": 1, - "read_only": 1 - } - ], - "icon": "fa fa-cog", - "is_submittable": 1, - "links": [], - "modified": "2022-03-16 12:45:21.662765", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Payroll Entry", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "cancel": 1, - "create": 1, - "delete": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "states": [] -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py deleted file mode 100644 index 620fcadceb..0000000000 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ /dev/null @@ -1,1057 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -import json - -import frappe -from dateutil.relativedelta import relativedelta -from frappe import _ -from frappe.desk.reportview import get_filters_cond, get_match_cond -from frappe.model.document import Document -from frappe.query_builder.functions import Coalesce -from frappe.utils import ( - DATE_FORMAT, - add_days, - add_to_date, - cint, - comma_and, - date_diff, - flt, - get_link_to_form, - getdate, -) - -import erpnext -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( - get_accounting_dimensions, -) -from erpnext.accounts.utils import get_fiscal_year -from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee - - -class PayrollEntry(Document): - def onload(self): - if not self.docstatus == 1 or self.salary_slips_submitted: - return - - # check if salary slips were manually submitted - entries = frappe.db.count("Salary Slip", {"payroll_entry": self.name, "docstatus": 1}, ["name"]) - if cint(entries) == len(self.employees): - self.set_onload("submitted_ss", True) - - def validate(self): - self.number_of_employees = len(self.employees) - self.set_status() - - def on_submit(self): - self.set_status(update=True, status="Submitted") - self.create_salary_slips() - - def before_submit(self): - self.validate_employee_details() - self.validate_payroll_payable_account() - if self.validate_attendance: - if self.validate_employee_attendance(): - frappe.throw(_("Cannot Submit, Employees left to mark attendance")) - - def set_status(self, status=None, update=False): - if not status: - status = {0: "Draft", 1: "Submitted", 2: "Cancelled"}[self.docstatus or 0] - - if update: - self.db_set("status", status) - else: - self.status = status - - def validate_employee_details(self): - emp_with_sal_slip = [] - for employee_details in self.employees: - if frappe.db.exists( - "Salary Slip", - { - "employee": employee_details.employee, - "start_date": self.start_date, - "end_date": self.end_date, - "docstatus": 1, - }, - ): - emp_with_sal_slip.append(employee_details.employee) - - if len(emp_with_sal_slip): - frappe.throw(_("Salary Slip already exists for {0}").format(comma_and(emp_with_sal_slip))) - - def validate_payroll_payable_account(self): - if frappe.db.get_value("Account", self.payroll_payable_account, "account_type"): - frappe.throw( - _( - "Account type cannot be set for payroll payable account {0}, please remove and try again" - ).format(frappe.bold(get_link_to_form("Account", self.payroll_payable_account))) - ) - - def on_cancel(self): - frappe.delete_doc( - "Salary Slip", - frappe.db.sql_list( - """select name from `tabSalary Slip` - where payroll_entry=%s """, - (self.name), - ), - ) - self.db_set("salary_slips_created", 0) - self.db_set("salary_slips_submitted", 0) - self.set_status(update=True, status="Cancelled") - self.db_set("error_message", "") - - def get_emp_list(self): - """ - Returns list of active employees based on selected criteria - and for which salary structure exists - """ - self.check_mandatory() - filters = self.make_filters() - cond = get_filter_condition(filters) - cond += get_joining_relieving_condition(self.start_date, self.end_date) - - condition = "" - if self.payroll_frequency: - condition = """and payroll_frequency = '%(payroll_frequency)s'""" % { - "payroll_frequency": self.payroll_frequency - } - - sal_struct = get_sal_struct( - self.company, self.currency, self.salary_slip_based_on_timesheet, condition - ) - if sal_struct: - cond += "and t2.salary_structure IN %(sal_struct)s " - cond += "and t2.payroll_payable_account = %(payroll_payable_account)s " - cond += "and %(from_date)s >= t2.from_date" - emp_list = get_emp_list(sal_struct, cond, self.end_date, self.payroll_payable_account) - emp_list = remove_payrolled_employees(emp_list, self.start_date, self.end_date) - return emp_list - - def make_filters(self): - filters = frappe._dict() - filters["company"] = self.company - filters["branch"] = self.branch - filters["department"] = self.department - filters["designation"] = self.designation - - return filters - - @frappe.whitelist() - def fill_employee_details(self): - self.set("employees", []) - employees = self.get_emp_list() - if not employees: - error_msg = _( - "No employees found for the mentioned criteria:
Company: {0}
Currency: {1}
Payroll Payable Account: {2}" - ).format( - frappe.bold(self.company), - frappe.bold(self.currency), - frappe.bold(self.payroll_payable_account), - ) - if self.branch: - error_msg += "
" + _("Branch: {0}").format(frappe.bold(self.branch)) - if self.department: - error_msg += "
" + _("Department: {0}").format(frappe.bold(self.department)) - if self.designation: - error_msg += "
" + _("Designation: {0}").format(frappe.bold(self.designation)) - if self.start_date: - error_msg += "
" + _("Start date: {0}").format(frappe.bold(self.start_date)) - if self.end_date: - error_msg += "
" + _("End date: {0}").format(frappe.bold(self.end_date)) - frappe.throw(error_msg, title=_("No employees found")) - - for d in employees: - self.append("employees", d) - - self.number_of_employees = len(self.employees) - if self.validate_attendance: - return self.validate_employee_attendance() - - def check_mandatory(self): - for fieldname in ["company", "start_date", "end_date"]: - if not self.get(fieldname): - frappe.throw(_("Please set {0}").format(self.meta.get_label(fieldname))) - - @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=600, - 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() - - def get_sal_slip_list(self, ss_status, as_dict=False): - """ - Returns list of salary slips based on selected criteria - """ - - ss = frappe.qb.DocType("Salary Slip") - ss_list = ( - frappe.qb.from_(ss) - .select(ss.name, ss.salary_structure) - .where( - (ss.docstatus == ss_status) - & (ss.start_date >= self.start_date) - & (ss.end_date <= self.end_date) - & (ss.payroll_entry == self.name) - & ((ss.journal_entry.isnull()) | (ss.journal_entry == "")) - & (Coalesce(ss.salary_slip_based_on_timesheet, 0) == self.salary_slip_based_on_timesheet) - ) - ).run(as_dict=as_dict) - - return ss_list - - @frappe.whitelist() - def submit_salary_slips(self): - self.check_permission("write") - salary_slips = self.get_sal_slip_list(ss_status=0) - if len(salary_slips) > 30 or frappe.flags.enqueue_payroll_entry: - self.db_set("status", "Queued") - frappe.enqueue( - submit_salary_slips_for_employees, - timeout=600, - payroll_entry=self, - salary_slips=salary_slips, - publish_progress=False, - ) - frappe.msgprint( - _("Salary Slip submission is queued. It may take a few minutes"), - alert=True, - indicator="blue", - ) - else: - submit_salary_slips_for_employees(self, salary_slips, publish_progress=False) - - def email_salary_slip(self, submitted_ss): - if frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee"): - for ss in submitted_ss: - ss.email_salary_slip() - - def get_salary_component_account(self, salary_component): - account = frappe.db.get_value( - "Salary Component Account", {"parent": salary_component, "company": self.company}, "account" - ) - - if not account: - frappe.throw( - _("Please set account in Salary Component {0}").format( - get_link_to_form("Salary Component", salary_component) - ) - ) - - return account - - def get_salary_components(self, component_type): - salary_slips = self.get_sal_slip_list(ss_status=1, as_dict=True) - - if salary_slips: - ss = frappe.qb.DocType("Salary Slip") - ssd = frappe.qb.DocType("Salary Detail") - salary_components = ( - frappe.qb.from_(ss) - .join(ssd) - .on(ss.name == ssd.parent) - .select(ssd.salary_component, ssd.amount, ssd.parentfield, ss.salary_structure, ss.employee) - .where( - (ssd.parentfield == component_type) & (ss.name.isin(tuple([d.name for d in salary_slips]))) - ) - ).run(as_dict=True) - - return salary_components - - def get_salary_component_total(self, component_type=None): - salary_components = self.get_salary_components(component_type) - if salary_components: - component_dict = {} - self.employee_cost_centers = {} - for item in salary_components: - employee_cost_centers = self.get_payroll_cost_centers_for_employee( - item.employee, item.salary_structure - ) - - add_component_to_accrual_jv_entry = True - if component_type == "earnings": - is_flexible_benefit, only_tax_impact = frappe.get_cached_value( - "Salary Component", item["salary_component"], ["is_flexible_benefit", "only_tax_impact"] - ) - if is_flexible_benefit == 1 and only_tax_impact == 1: - add_component_to_accrual_jv_entry = False - - if add_component_to_accrual_jv_entry: - for cost_center, percentage in employee_cost_centers.items(): - amount_against_cost_center = flt(item.amount) * percentage / 100 - component_dict[(item.salary_component, cost_center)] = ( - component_dict.get((item.salary_component, cost_center), 0) + amount_against_cost_center - ) - - account_details = self.get_account(component_dict=component_dict) - return account_details - - def get_payroll_cost_centers_for_employee(self, employee, salary_structure): - if not self.employee_cost_centers.get(employee): - ss_assignment_name = frappe.db.get_value( - "Salary Structure Assignment", - {"employee": employee, "salary_structure": salary_structure, "docstatus": 1}, - "name", - ) - - if ss_assignment_name: - cost_centers = dict( - frappe.get_all( - "Employee Cost Center", - {"parent": ss_assignment_name}, - ["cost_center", "percentage"], - as_list=1, - ) - ) - if not cost_centers: - default_cost_center, department = frappe.get_cached_value( - "Employee", employee, ["payroll_cost_center", "department"] - ) - if not default_cost_center and department: - default_cost_center = frappe.get_cached_value( - "Department", department, "payroll_cost_center" - ) - if not default_cost_center: - default_cost_center = self.cost_center - - cost_centers = {default_cost_center: 100} - - self.employee_cost_centers.setdefault(employee, cost_centers) - - return self.employee_cost_centers.get(employee, {}) - - def get_account(self, component_dict=None): - account_dict = {} - for key, amount in component_dict.items(): - account = self.get_salary_component_account(key[0]) - account_dict[(account, key[1])] = account_dict.get((account, key[1]), 0) + amount - return account_dict - - def make_accrual_jv_entry(self): - self.check_permission("write") - earnings = self.get_salary_component_total(component_type="earnings") or {} - deductions = self.get_salary_component_total(component_type="deductions") or {} - payroll_payable_account = self.payroll_payable_account - jv_name = "" - precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency") - - if earnings or deductions: - journal_entry = frappe.new_doc("Journal Entry") - journal_entry.voucher_type = "Journal Entry" - journal_entry.user_remark = _("Accrual Journal Entry for salaries from {0} to {1}").format( - self.start_date, self.end_date - ) - journal_entry.company = self.company - journal_entry.posting_date = self.posting_date - accounting_dimensions = get_accounting_dimensions() or [] - - accounts = [] - currencies = [] - payable_amount = 0 - multi_currency = 0 - company_currency = erpnext.get_company_currency(self.company) - - # Earnings - for acc_cc, amount in earnings.items(): - exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry( - acc_cc[0], amount, company_currency, currencies - ) - payable_amount += flt(amount, precision) - accounts.append( - self.update_accounting_dimensions( - { - "account": acc_cc[0], - "debit_in_account_currency": flt(amt, precision), - "exchange_rate": flt(exchange_rate), - "cost_center": acc_cc[1] or self.cost_center, - "project": self.project, - }, - accounting_dimensions, - ) - ) - - # Deductions - for acc_cc, amount in deductions.items(): - exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry( - acc_cc[0], amount, company_currency, currencies - ) - payable_amount -= flt(amount, precision) - accounts.append( - self.update_accounting_dimensions( - { - "account": acc_cc[0], - "credit_in_account_currency": flt(amt, precision), - "exchange_rate": flt(exchange_rate), - "cost_center": acc_cc[1] or self.cost_center, - "project": self.project, - }, - accounting_dimensions, - ) - ) - - # Payable amount - exchange_rate, payable_amt = self.get_amount_and_exchange_rate_for_journal_entry( - payroll_payable_account, payable_amount, company_currency, currencies - ) - accounts.append( - self.update_accounting_dimensions( - { - "account": payroll_payable_account, - "credit_in_account_currency": flt(payable_amt, precision), - "exchange_rate": flt(exchange_rate), - "cost_center": self.cost_center, - }, - accounting_dimensions, - ) - ) - - journal_entry.set("accounts", accounts) - if len(currencies) > 1: - multi_currency = 1 - journal_entry.multi_currency = multi_currency - journal_entry.title = payroll_payable_account - journal_entry.save() - - try: - journal_entry.submit() - jv_name = journal_entry.name - self.update_salary_slip_status(jv_name=jv_name) - except Exception as e: - if type(e) in (str, list, tuple): - frappe.msgprint(e) - raise - - return jv_name - - def update_accounting_dimensions(self, row, accounting_dimensions): - for dimension in accounting_dimensions: - row.update({dimension: self.get(dimension)}) - - return row - - def get_amount_and_exchange_rate_for_journal_entry( - self, account, amount, company_currency, currencies - ): - conversion_rate = 1 - exchange_rate = self.exchange_rate - account_currency = frappe.db.get_value("Account", account, "account_currency") - if account_currency not in currencies: - currencies.append(account_currency) - if account_currency == company_currency: - conversion_rate = self.exchange_rate - exchange_rate = 1 - amount = flt(amount) * flt(conversion_rate) - return exchange_rate, amount - - @frappe.whitelist() - def make_payment_entry(self): - self.check_permission("write") - - salary_slip_name_list = frappe.db.sql( - """ select t1.name from `tabSalary Slip` t1 - where t1.docstatus = 1 and start_date >= %s and end_date <= %s and t1.payroll_entry = %s - """, - (self.start_date, self.end_date, self.name), - as_list=True, - ) - - if salary_slip_name_list and len(salary_slip_name_list) > 0: - salary_slip_total = 0 - for salary_slip_name in salary_slip_name_list: - salary_slip = frappe.get_doc("Salary Slip", salary_slip_name[0]) - for sal_detail in salary_slip.earnings: - ( - is_flexible_benefit, - only_tax_impact, - creat_separate_je, - statistical_component, - ) = frappe.db.get_value( - "Salary Component", - sal_detail.salary_component, - [ - "is_flexible_benefit", - "only_tax_impact", - "create_separate_payment_entry_against_benefit_claim", - "statistical_component", - ], - ) - if only_tax_impact != 1 and statistical_component != 1: - if is_flexible_benefit == 1 and creat_separate_je == 1: - self.create_journal_entry(sal_detail.amount, sal_detail.salary_component) - else: - salary_slip_total += sal_detail.amount - for sal_detail in salary_slip.deductions: - statistical_component = frappe.db.get_value( - "Salary Component", sal_detail.salary_component, "statistical_component" - ) - if statistical_component != 1: - salary_slip_total -= sal_detail.amount - if salary_slip_total > 0: - self.create_journal_entry(salary_slip_total, "salary") - - def create_journal_entry(self, je_payment_amount, user_remark): - payroll_payable_account = self.payroll_payable_account - precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency") - - accounts = [] - currencies = [] - multi_currency = 0 - company_currency = erpnext.get_company_currency(self.company) - accounting_dimensions = get_accounting_dimensions() or [] - - exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry( - self.payment_account, je_payment_amount, company_currency, currencies - ) - accounts.append( - self.update_accounting_dimensions( - { - "account": self.payment_account, - "bank_account": self.bank_account, - "credit_in_account_currency": flt(amount, precision), - "exchange_rate": flt(exchange_rate), - }, - accounting_dimensions, - ) - ) - - exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry( - payroll_payable_account, je_payment_amount, company_currency, currencies - ) - accounts.append( - self.update_accounting_dimensions( - { - "account": payroll_payable_account, - "debit_in_account_currency": flt(amount, precision), - "exchange_rate": flt(exchange_rate), - "reference_type": self.doctype, - "reference_name": self.name, - }, - accounting_dimensions, - ) - ) - - if len(currencies) > 1: - multi_currency = 1 - - journal_entry = frappe.new_doc("Journal Entry") - journal_entry.voucher_type = "Bank Entry" - journal_entry.user_remark = _("Payment of {0} from {1} to {2}").format( - user_remark, self.start_date, self.end_date - ) - journal_entry.company = self.company - journal_entry.posting_date = self.posting_date - journal_entry.multi_currency = multi_currency - - journal_entry.set("accounts", accounts) - journal_entry.save(ignore_permissions=True) - - def update_salary_slip_status(self, jv_name=None): - ss_list = self.get_sal_slip_list(ss_status=1) - for ss in ss_list: - ss_obj = frappe.get_doc("Salary Slip", ss[0]) - frappe.db.set_value("Salary Slip", ss_obj.name, "journal_entry", jv_name) - - def set_start_end_dates(self): - self.update( - get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date, self.company) - ) - - @frappe.whitelist() - def validate_employee_attendance(self): - employees_to_mark_attendance = [] - days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0 - for employee_detail in self.employees: - employee_joining_date = frappe.db.get_value( - "Employee", employee_detail.employee, "date_of_joining" - ) - start_date = self.start_date - if employee_joining_date > getdate(self.start_date): - start_date = employee_joining_date - days_holiday = self.get_count_holidays_of_employee(employee_detail.employee, start_date) - days_attendance_marked = self.get_count_employee_attendance( - employee_detail.employee, start_date - ) - days_in_payroll = date_diff(self.end_date, start_date) + 1 - if days_in_payroll > days_holiday + days_attendance_marked: - employees_to_mark_attendance.append( - {"employee": employee_detail.employee, "employee_name": employee_detail.employee_name} - ) - return employees_to_mark_attendance - - def get_count_holidays_of_employee(self, employee, start_date): - holiday_list = get_holiday_list_for_employee(employee) - holidays = 0 - if holiday_list: - days = frappe.db.sql( - """select count(*) from tabHoliday where - parent=%s and holiday_date between %s and %s""", - (holiday_list, start_date, self.end_date), - ) - if days and days[0][0]: - holidays = days[0][0] - return holidays - - def get_count_employee_attendance(self, employee, start_date): - marked_days = 0 - attendances = frappe.get_all( - "Attendance", - fields=["count(*)"], - filters={"employee": employee, "attendance_date": ("between", [start_date, self.end_date])}, - as_list=1, - ) - if attendances and attendances[0][0]: - marked_days = attendances[0][0] - return marked_days - - -def get_sal_struct(company, currency, salary_slip_based_on_timesheet, condition): - return frappe.db.sql_list( - """ - select - name from `tabSalary Structure` - where - docstatus = 1 and - is_active = 'Yes' - and company = %(company)s - and currency = %(currency)s and - ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s - {condition}""".format( - condition=condition - ), - { - "company": company, - "currency": currency, - "salary_slip_based_on_timesheet": salary_slip_based_on_timesheet, - }, - ) - - -def get_filter_condition(filters): - cond = "" - for f in ["company", "branch", "department", "designation"]: - if filters.get(f): - cond += " and t1." + f + " = " + frappe.db.escape(filters.get(f)) - - return cond - - -def get_joining_relieving_condition(start_date, end_date): - cond = """ - and ifnull(t1.date_of_joining, '0000-00-00') <= '%(end_date)s' - and ifnull(t1.relieving_date, '2199-12-31') >= '%(start_date)s' - """ % { - "start_date": start_date, - "end_date": end_date, - } - return cond - - -def get_emp_list(sal_struct, cond, end_date, payroll_payable_account): - return frappe.db.sql( - """ - select - distinct t1.name as employee, t1.employee_name, t1.department, t1.designation - from - `tabEmployee` t1, `tabSalary Structure Assignment` t2 - where - t1.name = t2.employee - and t2.docstatus = 1 - and t1.status != 'Inactive' - %s order by t2.from_date desc - """ - % cond, - { - "sal_struct": tuple(sal_struct), - "from_date": end_date, - "payroll_payable_account": payroll_payable_account, - }, - as_dict=True, - ) - - -def remove_payrolled_employees(emp_list, start_date, end_date): - new_emp_list = [] - for employee_details in emp_list: - if not frappe.db.exists( - "Salary Slip", - { - "employee": employee_details.employee, - "start_date": start_date, - "end_date": end_date, - "docstatus": 1, - }, - ): - new_emp_list.append(employee_details) - - return new_emp_list - - -@frappe.whitelist() -def get_start_end_dates(payroll_frequency, start_date=None, company=None): - """Returns dict of start and end dates for given payroll frequency based on start_date""" - - if payroll_frequency == "Monthly" or payroll_frequency == "Bimonthly" or payroll_frequency == "": - fiscal_year = get_fiscal_year(start_date, company=company)[0] - month = "%02d" % getdate(start_date).month - m = get_month_details(fiscal_year, month) - if payroll_frequency == "Bimonthly": - if getdate(start_date).day <= 15: - start_date = m["month_start_date"] - end_date = m["month_mid_end_date"] - else: - start_date = m["month_mid_start_date"] - end_date = m["month_end_date"] - else: - start_date = m["month_start_date"] - end_date = m["month_end_date"] - - if payroll_frequency == "Weekly": - end_date = add_days(start_date, 6) - - if payroll_frequency == "Fortnightly": - end_date = add_days(start_date, 13) - - if payroll_frequency == "Daily": - end_date = start_date - - return frappe._dict({"start_date": start_date, "end_date": end_date}) - - -def get_frequency_kwargs(frequency_name): - frequency_dict = { - "monthly": {"months": 1}, - "fortnightly": {"days": 14}, - "weekly": {"days": 7}, - "daily": {"days": 1}, - } - return frequency_dict.get(frequency_name) - - -@frappe.whitelist() -def get_end_date(start_date, frequency): - start_date = getdate(start_date) - frequency = frequency.lower() if frequency else "monthly" - kwargs = ( - get_frequency_kwargs(frequency) if frequency != "bimonthly" else get_frequency_kwargs("monthly") - ) - - # weekly, fortnightly and daily intervals have fixed days so no problems - end_date = add_to_date(start_date, **kwargs) - relativedelta(days=1) - if frequency != "bimonthly": - return dict(end_date=end_date.strftime(DATE_FORMAT)) - - else: - return dict(end_date="") - - -def get_month_details(year, month): - ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date") - if ysd: - import calendar - import datetime - - diff_mnt = cint(month) - cint(ysd.month) - if diff_mnt < 0: - diff_mnt = 12 - int(ysd.month) + cint(month) - msd = ysd + relativedelta(months=diff_mnt) # month start date - month_days = cint(calendar.monthrange(cint(msd.year), cint(month))[1]) # days in month - mid_start = datetime.date(msd.year, cint(month), 16) # month mid start date - mid_end = datetime.date(msd.year, cint(month), 15) # month mid end date - med = datetime.date(msd.year, cint(month), month_days) # month end date - return frappe._dict( - { - "year": msd.year, - "month_start_date": msd, - "month_end_date": med, - "month_mid_start_date": mid_start, - "month_mid_end_date": mid_end, - "month_days": month_days, - } - ) - else: - frappe.throw(_("Fiscal Year {0} not found").format(year)) - - -def get_payroll_entry_bank_entries(payroll_entry_name): - journal_entries = frappe.db.sql( - "select name from `tabJournal Entry Account` " - 'where reference_type="Payroll Entry" ' - "and reference_name=%s and docstatus=1", - payroll_entry_name, - as_dict=1, - ) - - return journal_entries - - -@frappe.whitelist() -def payroll_entry_has_bank_entries(name): - response = {} - bank_entries = get_payroll_entry_bank_entries(name) - response["submitted"] = 1 if bank_entries else 0 - - return response - - -def log_payroll_failure(process, payroll_entry, error): - error_log = frappe.log_error( - title=_("Salary Slip {0} failed for Payroll Entry {1}").format(process, payroll_entry.name) - ) - message_log = frappe.message_log.pop() if frappe.message_log else str(error) - - try: - error_message = json.loads(message_log).get("message") - except Exception: - error_message = message_log - - error_message += "\n" + _("Check Error Log {0} for more details.").format( - get_link_to_form("Error Log", error_log.name) - ) - - payroll_entry.db_set({"error_message": error_message, "status": "Failed"}) - - -def create_salary_slips_for_employees(employees, args, publish_progress=True): - try: - payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry) - salary_slips_exist_for = get_existing_salary_slips(employees, args) - count = 0 - - for emp in employees: - if emp not in salary_slips_exist_for: - args.update({"doctype": "Salary Slip", "employee": emp}) - frappe.get_doc(args).insert() - - count += 1 - if publish_progress: - frappe.publish_progress( - count * 100 / len(set(employees) - set(salary_slips_exist_for)), - title=_("Creating Salary Slips..."), - ) - - payroll_entry.db_set({"status": "Submitted", "salary_slips_created": 1, "error_message": ""}) - - if salary_slips_exist_for: - frappe.msgprint( - _( - "Salary Slips already exist for employees {}, and will not be processed by this payroll." - ).format(frappe.bold(", ".join(emp for emp in salary_slips_exist_for))), - title=_("Message"), - indicator="orange", - ) - - except Exception as e: - frappe.db.rollback() - log_payroll_failure("creation", payroll_entry, e) - - finally: - frappe.db.commit() # nosemgrep - frappe.publish_realtime("completed_salary_slip_creation") - - -def show_payroll_submission_status(submitted, unsubmitted, payroll_entry): - if not submitted and not unsubmitted: - frappe.msgprint( - _( - "No salary slip found to submit for the above selected criteria OR salary slip already submitted" - ) - ) - elif submitted and not unsubmitted: - frappe.msgprint( - _("Salary Slips submitted for period from {0} to {1}").format( - payroll_entry.start_date, payroll_entry.end_date - ) - ) - elif unsubmitted: - frappe.msgprint( - _("Could not submit some Salary Slips: {}").format( - ", ".join(get_link_to_form("Salary Slip", entry) for entry in unsubmitted) - ) - ) - - -def get_existing_salary_slips(employees, args): - return frappe.db.sql_list( - """ - select distinct employee from `tabSalary Slip` - where docstatus!= 2 and company = %s and payroll_entry = %s - and start_date >= %s and end_date <= %s - and employee in (%s) - """ - % ("%s", "%s", "%s", "%s", ", ".join(["%s"] * len(employees))), - [args.company, args.payroll_entry, args.start_date, args.end_date] + employees, - ) - - -def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progress=True): - try: - submitted = [] - unsubmitted = [] - frappe.flags.via_payroll_entry = True - count = 0 - - for entry in salary_slips: - salary_slip = frappe.get_doc("Salary Slip", entry[0]) - if salary_slip.net_pay < 0: - unsubmitted.append(entry[0]) - else: - try: - salary_slip.submit() - submitted.append(salary_slip) - except frappe.ValidationError: - unsubmitted.append(entry[0]) - - count += 1 - if publish_progress: - frappe.publish_progress(count * 100 / len(salary_slips), title=_("Submitting Salary Slips...")) - - if submitted: - payroll_entry.make_accrual_jv_entry() - payroll_entry.email_salary_slip(submitted) - payroll_entry.db_set({"salary_slips_submitted": 1, "status": "Submitted", "error_message": ""}) - - show_payroll_submission_status(submitted, unsubmitted, payroll_entry) - - except Exception as e: - frappe.db.rollback() - log_payroll_failure("submission", payroll_entry, e) - - finally: - frappe.db.commit() # nosemgrep - frappe.publish_realtime("completed_salary_slip_submission") - - frappe.flags.via_payroll_entry = False - - -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters): - return frappe.db.sql( - """ - select name from `tabPayroll Entry` - where `{key}` LIKE %(txt)s - and name not in - (select reference_name from `tabJournal Entry Account` - where reference_type="Payroll Entry") - order by name limit %(start)s, %(page_len)s""".format( - key=searchfield - ), - {"txt": "%%%s%%" % txt, "start": start, "page_len": page_len}, - ) - - -def get_employee_list(filters): - cond = get_filter_condition(filters) - cond += get_joining_relieving_condition(filters.start_date, filters.end_date) - condition = """and payroll_frequency = '%(payroll_frequency)s'""" % { - "payroll_frequency": filters.payroll_frequency - } - sal_struct = get_sal_struct( - filters.company, filters.currency, filters.salary_slip_based_on_timesheet, condition - ) - if sal_struct: - cond += "and t2.salary_structure IN %(sal_struct)s " - cond += "and t2.payroll_payable_account = %(payroll_payable_account)s " - cond += "and %(from_date)s >= t2.from_date" - emp_list = get_emp_list(sal_struct, cond, filters.end_date, filters.payroll_payable_account) - emp_list = remove_payrolled_employees(emp_list, filters.start_date, filters.end_date) - return emp_list - - return [] - - -@frappe.whitelist() -@frappe.validate_and_sanitize_search_inputs -def employee_query(doctype, txt, searchfield, start, page_len, filters): - filters = frappe._dict(filters) - conditions = [] - include_employees = [] - emp_cond = "" - - if not filters.payroll_frequency: - frappe.throw(_("Select Payroll Frequency.")) - - if filters.start_date and filters.end_date: - employee_list = get_employee_list(filters) - emp = filters.get("employees") or [] - include_employees = [ - employee.employee for employee in employee_list if employee.employee not in emp - ] - filters.pop("start_date") - filters.pop("end_date") - filters.pop("salary_slip_based_on_timesheet") - filters.pop("payroll_frequency") - filters.pop("payroll_payable_account") - filters.pop("currency") - if filters.employees is not None: - filters.pop("employees") - - if include_employees: - emp_cond += "and employee in %(include_employees)s" - - return frappe.db.sql( - """select name, employee_name from `tabEmployee` - where status = 'Active' - and docstatus < 2 - and ({key} like %(txt)s - or employee_name like %(txt)s) - {emp_cond} - {fcond} {mcond} - order by - if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), - if(locate(%(_txt)s, employee_name), locate(%(_txt)s, employee_name), 99999), - idx desc, - name, employee_name - limit %(start)s, %(page_len)s""".format( - **{ - "key": searchfield, - "fcond": get_filters_cond(doctype, filters, conditions), - "mcond": get_match_cond(doctype), - "emp_cond": emp_cond, - } - ), - { - "txt": "%%%s%%" % txt, - "_txt": txt.replace("%", ""), - "start": start, - "page_len": page_len, - "include_employees": include_employees, - }, - ) diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py deleted file mode 100644 index eb93d688f9..0000000000 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py +++ /dev/null @@ -1,9 +0,0 @@ -def get_data(): - return { - "fieldname": "payroll_entry", - "non_standard_fieldnames": { - "Journal Entry": "reference_name", - "Payment Entry": "reference_name", - }, - "transactions": [{"items": ["Salary Slip", "Journal Entry"]}], - } diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry_list.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry_list.js deleted file mode 100644 index 56390b79d8..0000000000 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry_list.js +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -// render -frappe.listview_settings['Payroll Entry'] = { - has_indicator_for_draft: 1, - get_indicator: function(doc) { - var status_color = { - 'Draft': 'red', - 'Submitted': 'blue', - 'Queued': 'orange', - 'Failed': 'red', - 'Cancelled': 'red' - - }; - return [__(doc.status), status_color[doc.status], 'status,=,'+doc.status]; - } -}; diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py deleted file mode 100644 index 0363a0c3de..0000000000 --- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py +++ /dev/null @@ -1,484 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import unittest - -import frappe -from dateutil.relativedelta import relativedelta -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_months - -import erpnext -from erpnext.accounts.utils import get_fiscal_year, getdate, nowdate -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.loan_management.doctype.loan.test_loan import ( - create_loan, - create_loan_accounts, - create_loan_type, - make_loan_disbursement_entry, -) -from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import ( - process_loan_interest_accrual_for_term_loans, -) -from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_end_date, get_start_end_dates -from erpnext.payroll.doctype.salary_slip.test_salary_slip import ( - create_account, - make_deduction_salary_component, - make_earning_salary_component, - set_salary_component_account, -) -from erpnext.payroll.doctype.salary_structure.test_salary_structure import ( - create_salary_structure_assignment, - make_salary_structure, -) - -test_dependencies = ["Holiday List"] - - -class TestPayrollEntry(FrappeTestCase): - def setUp(self): - for dt in [ - "Salary Slip", - "Salary Component", - "Salary Component Account", - "Payroll Entry", - "Salary Structure", - "Salary Structure Assignment", - "Payroll Employee Detail", - "Additional Salary", - "Loan", - ]: - frappe.db.delete(dt) - - make_earning_salary_component(setup=True, company_list=["_Test Company"]) - make_deduction_salary_component(setup=True, test_tax=False, company_list=["_Test Company"]) - - frappe.db.set_value("Company", "_Test Company", "default_holiday_list", "_Test Holiday List") - frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0) - - # set default payable account - default_account = frappe.db.get_value( - "Company", "_Test Company", "default_payroll_payable_account" - ) - if not default_account or default_account != "_Test Payroll Payable - _TC": - create_account( - account_name="_Test Payroll Payable", - company="_Test Company", - parent_account="Current Liabilities - _TC", - account_type="Payable", - ) - frappe.db.set_value( - "Company", "_Test Company", "default_payroll_payable_account", "_Test Payroll Payable - _TC" - ) - - def test_payroll_entry(self): - company = frappe.get_doc("Company", "_Test Company") - employee = frappe.db.get_value("Employee", {"company": "_Test Company"}) - setup_salary_structure(employee, company) - - dates = get_start_end_dates("Monthly", nowdate()) - make_payroll_entry( - start_date=dates.start_date, - end_date=dates.end_date, - payable_account=company.default_payroll_payable_account, - currency=company.default_currency, - company=company.name, - ) - - def test_multi_currency_payroll_entry(self): - company = frappe.get_doc("Company", "_Test Company") - employee = make_employee( - "test_muti_currency_employee@payroll.com", company=company.name, department="Accounts - _TC" - ) - salary_structure = "_Test Multi Currency Salary Structure" - setup_salary_structure(employee, company, "USD", salary_structure) - - dates = get_start_end_dates("Monthly", nowdate()) - payroll_entry = make_payroll_entry( - start_date=dates.start_date, - end_date=dates.end_date, - payable_account=company.default_payroll_payable_account, - currency="USD", - exchange_rate=70, - company=company.name, - cost_center="Main - _TC", - ) - payroll_entry.make_payment_entry() - - salary_slip = frappe.db.get_value("Salary Slip", {"payroll_entry": payroll_entry.name}, "name") - salary_slip = frappe.get_doc("Salary Slip", salary_slip) - - payroll_entry.reload() - payroll_je = salary_slip.journal_entry - if payroll_je: - payroll_je_doc = frappe.get_doc("Journal Entry", payroll_je) - self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_debit) - self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_credit) - - payment_entry = frappe.db.sql( - """ - Select ifnull(sum(je.total_debit),0) as total_debit, ifnull(sum(je.total_credit),0) as total_credit from `tabJournal Entry` je, `tabJournal Entry Account` jea - Where je.name = jea.parent - And jea.reference_name = %s - """, - (payroll_entry.name), - as_dict=1, - ) - self.assertEqual(salary_slip.base_net_pay, payment_entry[0].total_debit) - self.assertEqual(salary_slip.base_net_pay, payment_entry[0].total_credit) - - def test_payroll_entry_with_employee_cost_center(self): - if not frappe.db.exists("Department", "cc - _TC"): - frappe.get_doc( - {"doctype": "Department", "department_name": "cc", "company": "_Test Company"} - ).insert() - - employee1 = make_employee( - "test_employee1@example.com", - payroll_cost_center="_Test Cost Center - _TC", - department="cc - _TC", - company="_Test Company", - ) - employee2 = make_employee( - "test_employee2@example.com", department="cc - _TC", company="_Test Company" - ) - - company = frappe.get_doc("Company", "_Test Company") - setup_salary_structure(employee1, company) - - ss = make_salary_structure( - "_Test Salary Structure 2", - "Monthly", - employee2, - company="_Test Company", - currency=company.default_currency, - test_tax=False, - ) - - # update cost centers in salary structure assignment for employee2 - ssa = frappe.db.get_value( - "Salary Structure Assignment", - {"employee": employee2, "salary_structure": ss.name, "docstatus": 1}, - "name", - ) - - ssa_doc = frappe.get_doc("Salary Structure Assignment", ssa) - ssa_doc.payroll_cost_centers = [] - ssa_doc.append( - "payroll_cost_centers", {"cost_center": "_Test Cost Center - _TC", "percentage": 60} - ) - ssa_doc.append( - "payroll_cost_centers", {"cost_center": "_Test Cost Center 2 - _TC", "percentage": 40} - ) - ssa_doc.save() - - dates = get_start_end_dates("Monthly", nowdate()) - pe = make_payroll_entry( - start_date=dates.start_date, - end_date=dates.end_date, - payable_account="_Test Payroll Payable - _TC", - currency=frappe.db.get_value("Company", "_Test Company", "default_currency"), - department="cc - _TC", - company="_Test Company", - payment_account="Cash - _TC", - cost_center="Main - _TC", - ) - je = frappe.db.get_value("Salary Slip", {"payroll_entry": pe.name}, "journal_entry") - je_entries = frappe.db.sql( - """ - select account, cost_center, debit, credit - from `tabJournal Entry Account` - where parent=%s - order by account, cost_center - """, - je, - ) - expected_je = ( - ("_Test Payroll Payable - _TC", "Main - _TC", 0.0, 155600.0), - ("Salary - _TC", "_Test Cost Center - _TC", 124800.0, 0.0), - ("Salary - _TC", "_Test Cost Center 2 - _TC", 31200.0, 0.0), - ("Salary Deductions - _TC", "_Test Cost Center - _TC", 0.0, 320.0), - ("Salary Deductions - _TC", "_Test Cost Center 2 - _TC", 0.0, 80.0), - ) - - self.assertEqual(je_entries, expected_je) - - def test_get_end_date(self): - self.assertEqual(get_end_date("2017-01-01", "monthly"), {"end_date": "2017-01-31"}) - self.assertEqual(get_end_date("2017-02-01", "monthly"), {"end_date": "2017-02-28"}) - self.assertEqual(get_end_date("2017-02-01", "fortnightly"), {"end_date": "2017-02-14"}) - self.assertEqual(get_end_date("2017-02-01", "bimonthly"), {"end_date": ""}) - self.assertEqual(get_end_date("2017-01-01", "bimonthly"), {"end_date": ""}) - self.assertEqual(get_end_date("2020-02-15", "bimonthly"), {"end_date": ""}) - self.assertEqual(get_end_date("2017-02-15", "monthly"), {"end_date": "2017-03-14"}) - self.assertEqual(get_end_date("2017-02-15", "daily"), {"end_date": "2017-02-15"}) - - def test_loan(self): - company = "_Test Company" - branch = "Test Employee Branch" - - if not frappe.db.exists("Branch", branch): - frappe.get_doc({"doctype": "Branch", "branch": branch}).insert() - holiday_list = make_holiday("test holiday for loan") - - applicant = make_employee( - "test_employee@loan.com", company="_Test Company", branch=branch, holiday_list=holiday_list - ) - company_doc = frappe.get_doc("Company", company) - - make_salary_structure( - "Test Salary Structure for Loan", - "Monthly", - employee=applicant, - company="_Test Company", - currency=company_doc.default_currency, - ) - - if not frappe.db.exists("Loan Type", "Car Loan"): - create_loan_accounts() - create_loan_type( - "Car Loan", - 500000, - 8.4, - is_term_loan=1, - mode_of_payment="Cash", - disbursement_account="Disbursement Account - _TC", - payment_account="Payment Account - _TC", - loan_account="Loan Account - _TC", - interest_income_account="Interest Income Account - _TC", - penalty_income_account="Penalty Income Account - _TC", - ) - - loan = create_loan( - applicant, - "Car Loan", - 280000, - "Repay Over Number of Periods", - 20, - posting_date=add_months(nowdate(), -1), - ) - loan.repay_from_salary = 1 - loan.submit() - - make_loan_disbursement_entry( - loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1) - ) - process_loan_interest_accrual_for_term_loans(posting_date=nowdate()) - - dates = get_start_end_dates("Monthly", nowdate()) - make_payroll_entry( - company="_Test Company", - start_date=dates.start_date, - payable_account=company_doc.default_payroll_payable_account, - currency=company_doc.default_currency, - end_date=dates.end_date, - branch=branch, - cost_center="Main - _TC", - payment_account="Cash - _TC", - ) - - name = frappe.db.get_value( - "Salary Slip", {"posting_date": nowdate(), "employee": applicant}, "name" - ) - - salary_slip = frappe.get_doc("Salary Slip", name) - for row in salary_slip.loans: - if row.loan == loan.name: - interest_amount = (280000 * 8.4) / (12 * 100) - principal_amount = loan.monthly_repayment_amount - interest_amount - self.assertEqual(row.interest_amount, interest_amount) - self.assertEqual(row.principal_amount, principal_amount) - self.assertEqual(row.total_payment, interest_amount + principal_amount) - - def test_salary_slip_operation_queueing(self): - company = "_Test Company" - company_doc = frappe.get_doc("Company", company) - employee = make_employee("test_employee@payroll.com", company=company) - setup_salary_structure(employee, company_doc) - - # enqueue salary slip creation via payroll entry - # Payroll Entry status should change to Queued - dates = get_start_end_dates("Monthly", nowdate()) - payroll_entry = get_payroll_entry( - start_date=dates.start_date, - end_date=dates.end_date, - payable_account=company_doc.default_payroll_payable_account, - currency=company_doc.default_currency, - company=company_doc.name, - cost_center="Main - _TC", - ) - frappe.flags.enqueue_payroll_entry = True - payroll_entry.submit() - payroll_entry.reload() - - self.assertEqual(payroll_entry.status, "Queued") - frappe.flags.enqueue_payroll_entry = False - - def test_salary_slip_operation_failure(self): - company = "_Test Company" - company_doc = frappe.get_doc("Company", company) - employee = make_employee("test_employee@payroll.com", company=company) - - salary_structure = make_salary_structure( - "_Test Salary Structure", - "Monthly", - employee, - company=company, - currency=company_doc.default_currency, - ) - - # reset account in component to test submission failure - component = frappe.get_doc("Salary Component", salary_structure.earnings[0].salary_component) - component.accounts = [] - component.save() - - # salary slip submission via payroll entry - # Payroll Entry status should change to Failed because of the missing account setup - dates = get_start_end_dates("Monthly", nowdate()) - payroll_entry = get_payroll_entry( - start_date=dates.start_date, - end_date=dates.end_date, - payable_account=company_doc.default_payroll_payable_account, - currency=company_doc.default_currency, - company=company_doc.name, - cost_center="Main - _TC", - ) - - # set employee as Inactive to check creation failure - frappe.db.set_value("Employee", employee, "status", "Inactive") - payroll_entry.submit() - payroll_entry.reload() - self.assertEqual(payroll_entry.status, "Failed") - self.assertIsNotNone(payroll_entry.error_message) - - frappe.db.set_value("Employee", employee, "status", "Active") - payroll_entry.submit() - payroll_entry.submit_salary_slips() - - payroll_entry.reload() - self.assertEqual(payroll_entry.status, "Failed") - self.assertIsNotNone(payroll_entry.error_message) - - # set accounts - for data in frappe.get_all("Salary Component", pluck="name"): - set_salary_component_account(data, company_list=[company]) - - # Payroll Entry successful, status should change to Submitted - payroll_entry.submit_salary_slips() - payroll_entry.reload() - - self.assertEqual(payroll_entry.status, "Submitted") - self.assertEqual(payroll_entry.error_message, "") - - def test_payroll_entry_status(self): - company = "_Test Company" - company_doc = frappe.get_doc("Company", company) - employee = make_employee("test_employee@payroll.com", company=company) - - setup_salary_structure(employee, company_doc) - - dates = get_start_end_dates("Monthly", nowdate()) - payroll_entry = get_payroll_entry( - start_date=dates.start_date, - end_date=dates.end_date, - payable_account=company_doc.default_payroll_payable_account, - currency=company_doc.default_currency, - company=company_doc.name, - cost_center="Main - _TC", - ) - payroll_entry.submit() - self.assertEqual(payroll_entry.status, "Submitted") - - payroll_entry.cancel() - self.assertEqual(payroll_entry.status, "Cancelled") - - -def get_payroll_entry(**args): - args = frappe._dict(args) - - payroll_entry = frappe.new_doc("Payroll Entry") - payroll_entry.company = args.company or erpnext.get_default_company() - payroll_entry.start_date = args.start_date or "2016-11-01" - payroll_entry.end_date = args.end_date or "2016-11-30" - payroll_entry.payment_account = get_payment_account() - payroll_entry.posting_date = nowdate() - payroll_entry.payroll_frequency = "Monthly" - payroll_entry.branch = args.branch or None - payroll_entry.department = args.department or None - payroll_entry.payroll_payable_account = args.payable_account - payroll_entry.currency = args.currency - payroll_entry.exchange_rate = args.exchange_rate or 1 - - if args.cost_center: - payroll_entry.cost_center = args.cost_center - - if args.payment_account: - payroll_entry.payment_account = args.payment_account - - payroll_entry.fill_employee_details() - payroll_entry.insert() - - # Commit so that the first salary slip creation failure does not rollback the Payroll Entry insert. - frappe.db.commit() # nosemgrep - - return payroll_entry - - -def make_payroll_entry(**args): - payroll_entry = get_payroll_entry(**args) - payroll_entry.submit() - payroll_entry.submit_salary_slips() - if payroll_entry.get_sal_slip_list(ss_status=1): - payroll_entry.make_payment_entry() - - return payroll_entry - - -def get_payment_account(): - return frappe.get_value( - "Account", - {"account_type": "Cash", "company": erpnext.get_default_company(), "is_group": 0}, - "name", - ) - - -def make_holiday(holiday_list_name): - if not frappe.db.exists("Holiday List", holiday_list_name): - current_fiscal_year = get_fiscal_year(nowdate(), as_dict=True) - dt = getdate(nowdate()) - - new_year = dt + relativedelta(month=1, day=1, year=dt.year) - republic_day = dt + relativedelta(month=1, day=26, year=dt.year) - test_holiday = dt + relativedelta(month=2, day=2, year=dt.year) - - frappe.get_doc( - { - "doctype": "Holiday List", - "from_date": current_fiscal_year.year_start_date, - "to_date": current_fiscal_year.year_end_date, - "holiday_list_name": holiday_list_name, - "holidays": [ - {"holiday_date": new_year, "description": "New Year"}, - {"holiday_date": republic_day, "description": "Republic Day"}, - {"holiday_date": test_holiday, "description": "Test Holiday"}, - ], - } - ).insert() - - return holiday_list_name - - -def setup_salary_structure(employee, company_doc, currency=None, salary_structure=None): - for data in frappe.get_all("Salary Component", pluck="name"): - if not frappe.db.get_value( - "Salary Component Account", {"parent": data, "company": company_doc.name}, "name" - ): - set_salary_component_account(data) - - make_salary_structure( - salary_structure or "_Test Salary Structure", - "Monthly", - employee, - company=company_doc.name, - currency=(currency or company_doc.default_currency), - ) diff --git a/erpnext/payroll/doctype/payroll_period/__init__.py b/erpnext/payroll/doctype/payroll_period/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.js b/erpnext/payroll/doctype/payroll_period/payroll_period.js deleted file mode 100644 index 67caf9d101..0000000000 --- a/erpnext/payroll/doctype/payroll_period/payroll_period.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Payroll Period', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.json b/erpnext/payroll/doctype/payroll_period/payroll_period.json deleted file mode 100644 index 0e0948475c..0000000000 --- a/erpnext/payroll/doctype/payroll_period/payroll_period.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "Prompt", - "creation": "2018-04-13 15:18:53.698553", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "company", - "column_break_2", - "start_date", - "end_date", - "section_break_5", - "periods" - ], - "fields": [ - { - "fieldname": "company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fieldname": "start_date", - "fieldtype": "Date", - "label": "Start Date", - "reqd": 1 - }, - { - "fieldname": "end_date", - "fieldtype": "Date", - "label": "End Date", - "reqd": 1 - }, - { - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 1, - "label": "Payroll Periods" - }, - { - "fieldname": "periods", - "fieldtype": "Table", - "label": "Payroll Periods", - "options": "Payroll Period Date" - } - ], - "links": [], - "modified": "2020-06-29 17:17:12.689089", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Payroll Period", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.py b/erpnext/payroll/doctype/payroll_period/payroll_period.py deleted file mode 100644 index e1f1cabbc7..0000000000 --- a/erpnext/payroll/doctype/payroll_period/payroll_period.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import add_months, cint, date_diff, flt, formatdate, getdate, month_diff - -from erpnext.hr.utils import get_holiday_dates_for_employee - - -class PayrollPeriod(Document): - def validate(self): - self.validate_dates() - self.validate_overlap() - - def validate_dates(self): - if getdate(self.start_date) > getdate(self.end_date): - frappe.throw(_("End date can not be less than start date")) - - def validate_overlap(self): - query = """ - select name - from `tab{0}` - where name != %(name)s - and company = %(company)s and (start_date between %(start_date)s and %(end_date)s \ - or end_date between %(start_date)s and %(end_date)s \ - or (start_date < %(start_date)s and end_date > %(end_date)s)) - """ - if not self.name: - # hack! if name is null, it could cause problems with != - self.name = "New " + self.doctype - - overlap_doc = frappe.db.sql( - query.format(self.doctype), - { - "start_date": self.start_date, - "end_date": self.end_date, - "name": self.name, - "company": self.company, - }, - as_dict=1, - ) - - if overlap_doc: - msg = ( - _("A {0} exists between {1} and {2} (").format( - self.doctype, formatdate(self.start_date), formatdate(self.end_date) - ) - + """ {1}""".format(self.doctype, overlap_doc[0].name) - + _(") for {0}").format(self.company) - ) - frappe.throw(msg) - - -def get_payroll_period_days(start_date, end_date, employee, company=None): - if not company: - company = frappe.db.get_value("Employee", employee, "company") - payroll_period = frappe.db.sql( - """ - select name, start_date, end_date - from `tabPayroll Period` - where - company=%(company)s - and %(start_date)s between start_date and end_date - and %(end_date)s between start_date and end_date - """, - {"company": company, "start_date": start_date, "end_date": end_date}, - ) - - if len(payroll_period) > 0: - actual_no_of_days = date_diff(getdate(payroll_period[0][2]), getdate(payroll_period[0][1])) + 1 - working_days = actual_no_of_days - if not cint( - frappe.db.get_value("Payroll Settings", None, "include_holidays_in_total_working_days") - ): - holidays = get_holiday_dates_for_employee( - employee, getdate(payroll_period[0][1]), getdate(payroll_period[0][2]) - ) - working_days -= len(holidays) - return payroll_period[0][0], working_days, actual_no_of_days - return False, False, False - - -def get_payroll_period(from_date, to_date, company): - payroll_period = frappe.db.sql( - """ - select name, start_date, end_date - from `tabPayroll Period` - where start_date<=%s and end_date>= %s and company=%s - """, - (from_date, to_date, company), - as_dict=1, - ) - - return payroll_period[0] if payroll_period else None - - -def get_period_factor( - employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days=0 -): - # TODO if both deduct checked update the factor to make tax consistent - period_start, period_end = payroll_period.start_date, payroll_period.end_date - joining_date, relieving_date = frappe.db.get_value( - "Employee", employee, ["date_of_joining", "relieving_date"] - ) - - if getdate(joining_date) > getdate(period_start): - period_start = joining_date - if relieving_date and getdate(relieving_date) < getdate(period_end): - period_end = relieving_date - if month_diff(period_end, start_date) > 1: - start_date = add_months(start_date, -(month_diff(period_end, start_date) + 1)) - - total_sub_periods, remaining_sub_periods = 0.0, 0.0 - - if payroll_frequency == "Monthly" and not depends_on_payment_days: - total_sub_periods = month_diff(payroll_period.end_date, payroll_period.start_date) - remaining_sub_periods = month_diff(period_end, start_date) - else: - salary_days = date_diff(end_date, start_date) + 1 - - days_in_payroll_period = date_diff(payroll_period.end_date, payroll_period.start_date) + 1 - total_sub_periods = flt(days_in_payroll_period) / flt(salary_days) - - remaining_days_in_payroll_period = date_diff(period_end, start_date) + 1 - remaining_sub_periods = flt(remaining_days_in_payroll_period) / flt(salary_days) - - return total_sub_periods, remaining_sub_periods diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py deleted file mode 100644 index 96632c5008..0000000000 --- a/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py +++ /dev/null @@ -1,7 +0,0 @@ -def get_data(): - return { - "fieldname": "payroll_period", - "transactions": [ - {"items": ["Employee Tax Exemption Proof Submission", "Employee Tax Exemption Declaration"]}, - ], - } diff --git a/erpnext/payroll/doctype/payroll_period/test_payroll_period.py b/erpnext/payroll/doctype/payroll_period/test_payroll_period.py deleted file mode 100644 index 61967c04bd..0000000000 --- a/erpnext/payroll/doctype/payroll_period/test_payroll_period.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestPayrollPeriod(unittest.TestCase): - pass diff --git a/erpnext/payroll/doctype/payroll_period_date/__init__.py b/erpnext/payroll/doctype/payroll_period_date/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json deleted file mode 100644 index 4a2f383b37..0000000000 --- a/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "actions": [], - "creation": "2018-04-13 15:17:30.513630", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "start_date", - "end_date" - ], - "fields": [ - { - "fieldname": "start_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Start Date", - "reqd": 1 - }, - { - "fieldname": "end_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "End Date", - "reqd": 1 - } - ], - "istable": 1, - "links": [], - "modified": "2020-06-22 23:30:15.943356", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Payroll Period Date", - "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/payroll_period_date/payroll_period_date.py b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py deleted file mode 100644 index c90a76a829..0000000000 --- a/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class PayrollPeriodDate(Document): - pass diff --git a/erpnext/payroll/doctype/payroll_settings/__init__.py b/erpnext/payroll/doctype/payroll_settings/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.js b/erpnext/payroll/doctype/payroll_settings/payroll_settings.js deleted file mode 100644 index 941464dc51..0000000000 --- a/erpnext/payroll/doctype/payroll_settings/payroll_settings.js +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Payroll Settings', { - encrypt_salary_slips_in_emails: function(frm) { - let encrypt_state = frm.doc.encrypt_salary_slips_in_emails; - frm.set_df_property('password_policy', 'reqd', encrypt_state); - }, - - validate: function(frm) { - let policy = frm.doc.password_policy; - if (policy) { - if (policy.includes(' ') || policy.includes('--')) { - frappe.msgprint(__("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically")); - } - frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-')); - } - }, -}); diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json deleted file mode 100644 index 54377e94b3..0000000000 --- a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "actions": [], - "creation": "2020-06-04 15:13:33.589685", - "doctype": "DocType", - "document_type": "Other", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "payroll_based_on", - "consider_unmarked_attendance_as", - "max_working_hours_against_timesheet", - "include_holidays_in_total_working_days", - "disable_rounded_total", - "column_break_11", - "daily_wages_fraction_for_half_day", - "email_salary_slip_to_employee", - "encrypt_salary_slips_in_emails", - "show_leave_balances_in_salary_slip", - "password_policy" - ], - "fields": [ - { - "default": "Leave", - "fieldname": "payroll_based_on", - "fieldtype": "Select", - "label": "Calculate Payroll Working Days Based On", - "options": "Leave\nAttendance" - }, - { - "fieldname": "max_working_hours_against_timesheet", - "fieldtype": "Float", - "label": "Max working hours against Timesheet" - }, - { - "default": "0", - "description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day", - "fieldname": "include_holidays_in_total_working_days", - "fieldtype": "Check", - "label": "Include holidays in Total no. of Working Days" - }, - { - "default": "0", - "description": "If checked, hides and disables Rounded Total field in Salary Slips", - "fieldname": "disable_rounded_total", - "fieldtype": "Check", - "label": "Disable Rounded Total" - }, - { - "fieldname": "column_break_11", - "fieldtype": "Column Break" - }, - { - "default": "0.5", - "description": "The fraction of daily wages to be paid for half-day attendance", - "fieldname": "daily_wages_fraction_for_half_day", - "fieldtype": "Float", - "label": "Fraction of Daily Salary for Half Day" - }, - { - "default": "1", - "description": "Emails salary slip to employee based on preferred email selected in Employee", - "fieldname": "email_salary_slip_to_employee", - "fieldtype": "Check", - "label": "Email Salary Slip to Employee" - }, - { - "default": "0", - "depends_on": "eval: doc.email_salary_slip_to_employee == 1;", - "description": "The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.", - "fieldname": "encrypt_salary_slips_in_emails", - "fieldtype": "Check", - "label": "Encrypt Salary Slips in Emails" - }, - { - "depends_on": "eval: doc.encrypt_salary_slips_in_emails == 1", - "description": "Example: SAL-{first_name}-{date_of_birth.year}
This will generate a password like SAL-Jane-1972", - "fieldname": "password_policy", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Password Policy" - }, - { - "depends_on": "eval:doc.payroll_based_on == 'Attendance'", - "fieldname": "consider_unmarked_attendance_as", - "fieldtype": "Select", - "label": "Consider Unmarked Attendance As", - "options": "Present\nAbsent" - }, - { - "default": "0", - "fieldname": "show_leave_balances_in_salary_slip", - "fieldtype": "Check", - "label": "Show Leave Balances in Salary Slip" - } - ], - "icon": "fa fa-cog", - "index_web_pages_for_search": 1, - "issingle": 1, - "links": [], - "modified": "2021-03-03 17:49:59.579723", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Payroll Settings", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.py b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py deleted file mode 100644 index 33614e9d30..0000000000 --- a/erpnext/payroll/doctype/payroll_settings/payroll_settings.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.custom.doctype.property_setter.property_setter import make_property_setter -from frappe.model.document import Document -from frappe.utils import cint - - -class PayrollSettings(Document): - def validate(self): - self.validate_password_policy() - - if not self.daily_wages_fraction_for_half_day: - self.daily_wages_fraction_for_half_day = 0.5 - - def validate_password_policy(self): - if self.email_salary_slip_to_employee and self.encrypt_salary_slips_in_emails: - if not self.password_policy: - frappe.throw(_("Password policy for Salary Slips is not set")) - - def on_update(self): - self.toggle_rounded_total() - frappe.clear_cache() - - def toggle_rounded_total(self): - self.disable_rounded_total = cint(self.disable_rounded_total) - make_property_setter( - "Salary Slip", - "rounded_total", - "hidden", - self.disable_rounded_total, - "Check", - validate_fields_for_doctype=False, - ) - make_property_setter( - "Salary Slip", - "rounded_total", - "print_hide", - self.disable_rounded_total, - "Check", - validate_fields_for_doctype=False, - ) diff --git a/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py b/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py deleted file mode 100644 index 3b96db6ed4..0000000000 --- a/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -import unittest - - -class TestPayrollSettings(unittest.TestCase): - pass diff --git a/erpnext/payroll/doctype/retention_bonus/__init__.py b/erpnext/payroll/doctype/retention_bonus/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.js b/erpnext/payroll/doctype/retention_bonus/retention_bonus.js deleted file mode 100644 index f8bb40a9cb..0000000000 --- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.js +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Retention Bonus', { - setup: function(frm) { - frm.set_query("employee", function() { - if (!frm.doc.company) { - frappe.msgprint(__("Please Select Company First")); - } - return { - filters: { - "status": "Active", - "company": frm.doc.company - } - }; - }); - - frm.set_query("salary_component", function() { - return { - filters: { - "type": "Earning" - } - }; - }); - }, - - employee: function(frm) { - if (frm.doc.employee) { - frappe.call({ - method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", - args: { - employee: frm.doc.employee, - }, - callback: function(r) { - if (r.message) { - frm.set_value('currency', r.message); - frm.refresh_fields(); - } - } - }); - } - } -}); diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.json b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json deleted file mode 100644 index f8d8bb46de..0000000000 --- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-RTB-.YYYY.-.#####", - "creation": "2018-05-13 14:59:42.038964", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "company", - "employee", - "bonus_payment_date", - "bonus_amount", - "salary_component", - "amended_from", - "column_break_6", - "employee_name", - "department", - "date_of_joining", - "currency" - ], - "fields": [ - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1 - }, - { - "fieldname": "bonus_payment_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Bonus Payment Date", - "reqd": 1 - }, - { - "fieldname": "bonus_amount", - "fieldtype": "Currency", - "label": "Bonus Amount", - "options": "currency", - "reqd": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Retention Bonus", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "column_break_6", - "fieldtype": "Column Break" - }, - { - "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.date_of_joining", - "fieldname": "date_of_joining", - "fieldtype": "Data", - "label": "Date of Joining", - "read_only": 1 - }, - { - "fieldname": "salary_component", - "fieldtype": "Link", - "label": "Salary Component", - "options": "Salary Component", - "reqd": 1 - }, - { - "depends_on": "eval:(doc.docstatus==1 || doc.employee)", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "print_hide": 1, - "read_only": 1, - "reqd": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-01-19 12:57:37.898953", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Retention Bonus", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1 - } - ], - "search_fields": "employee_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py deleted file mode 100644 index cdcd9a9025..0000000000 --- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import getdate - -from erpnext.hr.utils import validate_active_employee - - -class RetentionBonus(Document): - def validate(self): - validate_active_employee(self.employee) - if getdate(self.bonus_payment_date) < getdate(): - frappe.throw(_("Bonus Payment Date cannot be a past date")) - - def on_submit(self): - company = frappe.db.get_value("Employee", self.employee, "company") - additional_salary = self.get_additional_salary() - - if not additional_salary: - additional_salary = frappe.new_doc("Additional Salary") - additional_salary.employee = self.employee - additional_salary.salary_component = self.salary_component - additional_salary.amount = self.bonus_amount - additional_salary.payroll_date = self.bonus_payment_date - additional_salary.company = company - additional_salary.overwrite_salary_structure_amount = 0 - additional_salary.ref_doctype = self.doctype - additional_salary.ref_docname = self.name - additional_salary.submit() - # self.db_set('additional_salary', additional_salary.name) - - else: - bonus_added = ( - frappe.db.get_value("Additional Salary", additional_salary, "amount") + self.bonus_amount - ) - frappe.db.set_value("Additional Salary", additional_salary, "amount", bonus_added) - self.db_set("additional_salary", additional_salary) - - def on_cancel(self): - - additional_salary = self.get_additional_salary() - if self.additional_salary: - bonus_removed = ( - frappe.db.get_value("Additional Salary", self.additional_salary, "amount") - self.bonus_amount - ) - if bonus_removed == 0: - frappe.get_doc("Additional Salary", self.additional_salary).cancel() - else: - frappe.db.set_value("Additional Salary", self.additional_salary, "amount", bonus_removed) - - # self.db_set('additional_salary', '') - - def get_additional_salary(self): - return frappe.db.exists( - "Additional Salary", - { - "employee": self.employee, - "salary_component": self.salary_component, - "payroll_date": self.bonus_payment_date, - "company": self.company, - "docstatus": 1, - "ref_doctype": self.doctype, - "ref_docname": self.name, - }, - ) diff --git a/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py deleted file mode 100644 index c86bf33511..0000000000 --- a/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestRetentionBonus(unittest.TestCase): - pass diff --git a/erpnext/payroll/doctype/salary_component/README.md b/erpnext/payroll/doctype/salary_component/README.md deleted file mode 100644 index 9644192449..0000000000 --- a/erpnext/payroll/doctype/salary_component/README.md +++ /dev/null @@ -1 +0,0 @@ -Type of earning and deductions that is a part of the salary. diff --git a/erpnext/payroll/doctype/salary_component/__init__.py b/erpnext/payroll/doctype/salary_component/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/salary_component/salary_component.js b/erpnext/payroll/doctype/salary_component/salary_component.js deleted file mode 100644 index dbf75140ac..0000000000 --- a/erpnext/payroll/doctype/salary_component/salary_component.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Salary Component', { - setup: function(frm) { - frm.set_query("account", "accounts", function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "is_group": 0, - "company": d.company - } - }; - }); - frm.set_query("earning_component_group", function() { - return { - filters: { - "is_group": 1, - "is_flexible_benefit": 1 - } - }; - }); - }, - is_flexible_benefit: function(frm) { - if(frm.doc.is_flexible_benefit){ - set_value_for_condition_and_formula(frm); - frm.set_value("formula", ''); - frm.set_value("amount", 0); - } - }, - type: function(frm) { - if(frm.doc.type=="Earning"){ - frm.set_value("is_tax_applicable", 1); - frm.set_value("variable_based_on_taxable_salary", 0); - } - if(frm.doc.type=="Deduction"){ - frm.set_value("is_tax_applicable", 0); - frm.set_value("is_flexible_benefit", 0); - } - }, - variable_based_on_taxable_salary: function(frm) { - if(frm.doc.variable_based_on_taxable_salary){ - set_value_for_condition_and_formula(frm); - } - }, - create_separate_payment_entry_against_benefit_claim: function(frm) { - if(frm.doc.create_separate_payment_entry_against_benefit_claim){ - frm.set_df_property("accounts", "reqd", 1); - frm.set_value("only_tax_impact", 0); - } - else{ - frm.set_df_property("accounts", "reqd", 0); - } - }, - only_tax_impact: function(frm) { - if(frm.only_tax_impact){ - frm.set_value("create_separate_payment_entry_against_benefit_claim", 0); - } - } -}); - -var set_value_for_condition_and_formula = function(frm) { - frm.set_value("formula", null); - frm.set_value("condition", null); - frm.set_value("amount_based_on_formula", 0); - frm.set_value("statistical_component", 0); - frm.set_value("do_not_include_in_total", 0); - frm.set_value("depends_on_payment_days", 0); -}; diff --git a/erpnext/payroll/doctype/salary_component/salary_component.json b/erpnext/payroll/doctype/salary_component/salary_component.json deleted file mode 100644 index c97e45cd53..0000000000 --- a/erpnext/payroll/doctype/salary_component/salary_component.json +++ /dev/null @@ -1,272 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:salary_component", - "creation": "2016-06-30 15:42:43.631931", - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "salary_component", - "salary_component_abbr", - "type", - "description", - "column_break_4", - "depends_on_payment_days", - "is_tax_applicable", - "is_income_tax_component", - "deduct_full_tax_on_selected_payroll_date", - "variable_based_on_taxable_salary", - "exempted_from_income_tax", - "round_to_the_nearest_integer", - "statistical_component", - "do_not_include_in_total", - "disabled", - "flexible_benefits", - "is_flexible_benefit", - "max_benefit_amount", - "column_break_9", - "pay_against_benefit_claim", - "only_tax_impact", - "create_separate_payment_entry_against_benefit_claim", - "section_break_5", - "accounts", - "condition_and_formula", - "condition", - "amount", - "amount_based_on_formula", - "formula", - "column_break_28", - "help" - ], - "fields": [ - { - "fieldname": "salary_component", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Name", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "salary_component_abbr", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Abbr", - "print_width": "120px", - "reqd": 1, - "width": "120px" - }, - { - "fieldname": "type", - "fieldtype": "Select", - "in_standard_filter": 1, - "label": "Type", - "options": "Earning\nDeduction", - "reqd": 1 - }, - { - "default": "1", - "depends_on": "eval:doc.type == \"Earning\"", - "fieldname": "is_tax_applicable", - "fieldtype": "Check", - "label": "Is Tax Applicable" - }, - { - "default": "1", - "fieldname": "depends_on_payment_days", - "fieldtype": "Check", - "label": "Depends on Payment Days", - "print_hide": 1 - }, - { - "default": "0", - "fieldname": "do_not_include_in_total", - "fieldtype": "Check", - "label": "Do Not Include in Total" - }, - { - "default": "0", - "depends_on": "eval:doc.is_tax_applicable && doc.type=='Earning'", - "fieldname": "deduct_full_tax_on_selected_payroll_date", - "fieldtype": "Check", - "label": "Deduct Full Tax on Selected Payroll Date" - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "disabled", - "fieldtype": "Check", - "label": "Disabled" - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "in_list_view": 1, - "label": "Description" - }, - { - "default": "0", - "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", - "fieldname": "statistical_component", - "fieldtype": "Check", - "label": "Statistical Component" - }, - { - "depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1", - "fieldname": "flexible_benefits", - "fieldtype": "Section Break", - "label": "Flexible Benefits" - }, - { - "default": "0", - "fieldname": "is_flexible_benefit", - "fieldtype": "Check", - "label": "Is Flexible Benefit" - }, - { - "depends_on": "is_flexible_benefit", - "fieldname": "max_benefit_amount", - "fieldtype": "Currency", - "label": "Max Benefit Amount (Yearly)" - }, - { - "fieldname": "column_break_9", - "fieldtype": "Column Break" - }, - { - "default": "0", - "depends_on": "is_flexible_benefit", - "fieldname": "pay_against_benefit_claim", - "fieldtype": "Check", - "label": "Pay Against Benefit Claim" - }, - { - "default": "0", - "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.create_separate_payment_entry_against_benefit_claim !=1", - "fieldname": "only_tax_impact", - "fieldtype": "Check", - "label": "Only Tax Impact (Cannot Claim But Part of Taxable Income)" - }, - { - "default": "0", - "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.only_tax_impact !=1", - "fieldname": "create_separate_payment_entry_against_benefit_claim", - "fieldtype": "Check", - "label": "Create Separate Payment Entry Against Benefit Claim" - }, - { - "default": "0", - "depends_on": "eval:doc.type == \"Deduction\"", - "fieldname": "variable_based_on_taxable_salary", - "fieldtype": "Check", - "label": "Variable Based On Taxable Salary" - }, - { - "depends_on": "eval:doc.statistical_component != 1", - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "label": "Accounts" - }, - { - "fieldname": "accounts", - "fieldtype": "Table", - "label": "Accounts", - "options": "Salary Component Account" - }, - { - "collapsible": 1, - "depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1", - "fieldname": "condition_and_formula", - "fieldtype": "Section Break", - "label": "Condition and Formula" - }, - { - "fieldname": "condition", - "fieldtype": "Code", - "label": "Condition" - }, - { - "default": "0", - "fieldname": "amount_based_on_formula", - "fieldtype": "Check", - "label": "Amount based on formula" - }, - { - "depends_on": "amount_based_on_formula", - "fieldname": "formula", - "fieldtype": "Code", - "label": "Formula" - }, - { - "depends_on": "eval:doc.amount_based_on_formula!==1", - "fieldname": "amount", - "fieldtype": "Currency", - "label": "Amount" - }, - { - "fieldname": "column_break_28", - "fieldtype": "Column Break" - }, - { - "fieldname": "help", - "fieldtype": "HTML", - "label": "Help", - "options": "

Help

\n\n

Notes:

\n\n
    \n
  1. Use field base for using base salary of the Employee
  2. \n
  3. Use Salary Component abbreviations in conditions and formulas. BS = Basic Salary
  4. \n
  5. Use field name for employee details in conditions and formulas. Employment Type = employment_typeBranch = branch
  6. \n
  7. Use field name from Salary Slip in conditions and formulas. Payment Days = payment_daysLeave without pay = leave_without_pay
  8. \n
  9. Direct Amount can also be entered based on Condition. See example 3
\n\n

Examples

\n
    \n
  1. Calculating Basic Salary based on base\n
    Condition: base < 10000
    \n
    Formula: base * .2
  2. \n
  3. Calculating HRA based on Basic SalaryBS \n
    Condition: BS > 2000
    \n
    Formula: BS * .1
  4. \n
  5. Calculating TDS based on Employment Typeemployment_type \n
    Condition: employment_type==\"Intern\"
    \n
    Amount: 1000
  6. \n
" - }, - { - "default": "0", - "fieldname": "round_to_the_nearest_integer", - "fieldtype": "Check", - "label": "Round to the Nearest Integer" - }, - { - "default": "0", - "depends_on": "eval:doc.type == \"Deduction\" && !doc.variable_based_on_taxable_salary", - "description": "If checked, the full amount will be deducted from taxable income before calculating income tax without any declaration or proof submission.", - "fieldname": "exempted_from_income_tax", - "fieldtype": "Check", - "label": "Exempted from Income Tax" - }, - { - "default": "0", - "depends_on": "eval:doc.type == \"Deduction\"", - "fieldname": "is_income_tax_component", - "fieldtype": "Check", - "label": "Is Income Tax Component" - } - ], - "icon": "fa fa-flag", - "index_web_pages_for_search": 1, - "links": [], - "modified": "2020-10-07 20:38:33.795853", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Salary Component", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - }, - { - "read": 1, - "role": "Employee" - } - ], - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_component/salary_component.py b/erpnext/payroll/doctype/salary_component/salary_component.py deleted file mode 100644 index 409c4a1769..0000000000 --- a/erpnext/payroll/doctype/salary_component/salary_component.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document -from frappe.model.naming import append_number_if_name_exists - - -class SalaryComponent(Document): - def validate(self): - self.validate_abbr() - - def validate_abbr(self): - if not self.salary_component_abbr: - self.salary_component_abbr = "".join([c[0] for c in self.salary_component.split()]).upper() - - self.salary_component_abbr = self.salary_component_abbr.strip() - self.salary_component_abbr = append_number_if_name_exists( - "Salary Component", - self.salary_component_abbr, - "salary_component_abbr", - separator="_", - filters={"name": ["!=", self.name]}, - ) diff --git a/erpnext/payroll/doctype/salary_component/test_records.json b/erpnext/payroll/doctype/salary_component/test_records.json deleted file mode 100644 index 104b44ffa1..0000000000 --- a/erpnext/payroll/doctype/salary_component/test_records.json +++ /dev/null @@ -1,36 +0,0 @@ -[ - { - "doctype": "Salary Component", - "salary_component": "_Test Basic Salary", - "type": "Earning", - "is_tax_applicable": 1 - }, - { - "doctype": "Salary Component", - "salary_component": "_Test Allowance", - "type": "Earning", - "is_tax_applicable": 1 - }, - { - "doctype": "Salary Component", - "salary_component": "_Test Professional Tax", - "type": "Deduction" - }, - { - "doctype": "Salary Component", - "salary_component": "_Test TDS", - "type": "Deduction" - }, - { - "doctype": "Salary Component", - "salary_component": "Basic", - "type": "Earning", - "is_tax_applicable": 1 - }, - { - "doctype": "Salary Component", - "salary_component": "Leave Encashment", - "type": "Earning", - "is_tax_applicable": 1 - } -] \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_component/test_salary_component.py b/erpnext/payroll/doctype/salary_component/test_salary_component.py deleted file mode 100644 index cd729e8240..0000000000 --- a/erpnext/payroll/doctype/salary_component/test_salary_component.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe - -# test_records = frappe.get_test_records('Salary Component') - - -class TestSalaryComponent(unittest.TestCase): - pass - - -def create_salary_component(component_name, **args): - if not frappe.db.exists("Salary Component", component_name): - frappe.get_doc( - { - "doctype": "Salary Component", - "salary_component": component_name, - "type": args.get("type") or "Earning", - "is_tax_applicable": args.get("is_tax_applicable") or 1, - } - ).insert() diff --git a/erpnext/payroll/doctype/salary_detail/__init__.py b/erpnext/payroll/doctype/salary_detail/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json deleted file mode 100644 index 665f0a8297..0000000000 --- a/erpnext/payroll/doctype/salary_detail/salary_detail.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "actions": [], - "creation": "2016-06-30 15:32:36.385111", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "salary_component", - "abbr", - "column_break_3", - "amount", - "year_to_date", - "section_break_5", - "additional_salary", - "is_recurring_additional_salary", - "statistical_component", - "depends_on_payment_days", - "exempted_from_income_tax", - "is_tax_applicable", - "column_break_11", - "is_flexible_benefit", - "variable_based_on_taxable_salary", - "do_not_include_in_total", - "deduct_full_tax_on_selected_payroll_date", - "section_break_2", - "condition", - "column_break_18", - "amount_based_on_formula", - "formula", - "section_break_19", - "default_amount", - "additional_amount", - "column_break_24", - "tax_on_flexible_benefit", - "tax_on_additional_salary" - ], - "fields": [ - { - "fieldname": "salary_component", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Component", - "options": "Salary Component", - "reqd": 1 - }, - { - "columns": 1, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_from": "salary_component.salary_component_abbr", - "fieldname": "abbr", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Abbr", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "default": "0", - "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", - "fetch_from": "salary_component.statistical_component", - "fieldname": "statistical_component", - "fieldtype": "Check", - "in_list_view": 1, - "label": "Statistical Component" - }, - { - "default": "0", - "depends_on": "eval:doc.parentfield=='earnings'", - "fetch_from": "salary_component.is_tax_applicable", - "fieldname": "is_tax_applicable", - "fieldtype": "Check", - "label": "Is Tax Applicable", - "print_hide": 1, - "read_only": 1 - }, - { - "default": "0", - "depends_on": "eval:doc.parentfield=='earnings'", - "fetch_from": "salary_component.is_flexible_benefit", - "fieldname": "is_flexible_benefit", - "fieldtype": "Check", - "label": "Is Flexible Benefit", - "print_hide": 1, - "read_only": 1 - }, - { - "default": "0", - "depends_on": "eval:doc.parentfield=='deductions'", - "fetch_from": "salary_component.variable_based_on_taxable_salary", - "fieldname": "variable_based_on_taxable_salary", - "fieldtype": "Check", - "label": "Variable Based On Taxable Salary", - "print_hide": 1, - "read_only": 1 - }, - { - "default": "0", - "fetch_from": "salary_component.depends_on_payment_days", - "fieldname": "depends_on_payment_days", - "fieldtype": "Check", - "label": "Depends on Payment Days", - "print_hide": 1, - "read_only": 1 - }, - { - "default": "0", - "fieldname": "deduct_full_tax_on_selected_payroll_date", - "fieldtype": "Check", - "label": "Deduct Full Tax on Selected Payroll Date", - "print_hide": 1, - "read_only": 1 - }, - { - "collapsible": 1, - "depends_on": "eval:doc.is_flexible_benefit != 1", - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "label": "Condition and formula" - }, - { - "allow_on_submit": 1, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fieldname": "condition", - "fieldtype": "Code", - "label": "Condition" - }, - { - "default": "0", - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fieldname": "amount_based_on_formula", - "fieldtype": "Check", - "label": "Amount based on formula" - }, - { - "allow_on_submit": 1, - "depends_on": "eval:doc.amount_based_on_formula!==0 && doc.parenttype==='Salary Structure'", - "fieldname": "formula", - "fieldtype": "Code", - "in_list_view": 1, - "label": "Formula" - }, - { - "depends_on": "eval:doc.amount_based_on_formula!==1 || doc.parenttype==='Salary Slip'", - "fieldname": "amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Amount", - "options": "currency" - }, - { - "default": "0", - "fieldname": "do_not_include_in_total", - "fieldtype": "Check", - "label": "Do not include in total" - }, - { - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fieldname": "default_amount", - "fieldtype": "Currency", - "label": "Default Amount", - "options": "currency", - "print_hide": 1 - }, - { - "fieldname": "additional_amount", - "fieldtype": "Currency", - "hidden": 1, - "label": "Additional Amount", - "no_copy": 1, - "options": "currency", - "print_hide": 1, - "read_only": 1 - }, - { - "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", - "fieldname": "tax_on_flexible_benefit", - "fieldtype": "Currency", - "label": "Tax on flexible benefit", - "options": "currency", - "read_only": 1 - }, - { - "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", - "fieldname": "tax_on_additional_salary", - "fieldtype": "Currency", - "label": "Tax on additional salary", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "additional_salary", - "fieldtype": "Link", - "label": "Additional Salary ", - "options": "Additional Salary", - "read_only": 1 - }, - { - "default": "0", - "depends_on": "eval:doc.parentfield=='deductions'", - "fetch_from": "salary_component.exempted_from_income_tax", - "fieldname": "exempted_from_income_tax", - "fieldtype": "Check", - "label": "Exempted from Income Tax", - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "label": "Component properties and references " - }, - { - "fieldname": "column_break_11", - "fieldtype": "Column Break" - }, - { - "fieldname": "section_break_19", - "fieldtype": "Section Break" - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" - }, - { - "fieldname": "column_break_24", - "fieldtype": "Column Break" - }, - { - "description": "Total salary booked against this component for this employee from the beginning of the year (payroll period or fiscal year) up to the current salary slip's end date.", - "fieldname": "year_to_date", - "fieldtype": "Currency", - "label": "Year To Date", - "options": "currency", - "read_only": 1 - }, - { - "default": "0", - "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.additional_salary", - "fieldname": "is_recurring_additional_salary", - "fieldtype": "Check", - "label": "Is Recurring Additional Salary", - "read_only": 1 - } - ], - "istable": 1, - "links": [], - "modified": "2021-08-30 13:39:15.847158", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Salary Detail", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.py b/erpnext/payroll/doctype/salary_detail/salary_detail.py deleted file mode 100644 index c74bd546eb..0000000000 --- a/erpnext/payroll/doctype/salary_detail/salary_detail.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class SalaryDetail(Document): - pass diff --git a/erpnext/payroll/doctype/salary_slip/README.md b/erpnext/payroll/doctype/salary_slip/README.md deleted file mode 100644 index 736550e42c..0000000000 --- a/erpnext/payroll/doctype/salary_slip/README.md +++ /dev/null @@ -1 +0,0 @@ -Details of monthly salary paid for an Employee. \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_slip/__init__.py b/erpnext/payroll/doctype/salary_slip/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js deleted file mode 100644 index 3ef9762a83..0000000000 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.js +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -cur_frm.add_fetch('employee', 'company', 'company'); -cur_frm.add_fetch('time_sheet', 'total_hours', 'working_hours'); - -frappe.ui.form.on("Salary Slip", { - setup: function(frm) { - $.each(["earnings", "deductions"], function(i, table_fieldname) { - frm.get_field(table_fieldname).grid.editable_fields = [ - {fieldname: 'salary_component', columns: 6}, - {fieldname: 'amount', columns: 4} - ]; - }); - - frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function() { - return { - filters: { - employee: frm.doc.employee - } - }; - }; - - frm.set_query("salary_component", "earnings", function() { - return { - filters: { - type: "earning" - } - }; - }); - - frm.set_query("salary_component", "deductions", function() { - return { - filters: { - type: "deduction" - } - }; - }); - - frm.set_query("employee", function() { - return { - query: "erpnext.controllers.queries.employee_query", - filters: { - company: frm.doc.company - } - }; - }); - }, - - start_date: function(frm) { - if (frm.doc.start_date) { - frm.trigger("set_end_date"); - } - }, - - end_date: function(frm) { - frm.events.get_emp_and_working_day_details(frm); - }, - - set_end_date: function(frm) { - frappe.call({ - method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_end_date', - args: { - frequency: frm.doc.payroll_frequency, - start_date: frm.doc.start_date - }, - callback: function (r) { - if (r.message) { - frm.set_value('end_date', r.message.end_date); - } - } - }); - }, - - company: function(frm) { - var company = locals[':Company'][frm.doc.company]; - if (!frm.doc.letter_head && company.default_letter_head) { - frm.set_value('letter_head', company.default_letter_head); - } - }, - - currency: function(frm) { - frm.trigger("set_dynamic_labels"); - }, - - set_dynamic_labels: function(frm) { - var company_currency = frm.doc.company? erpnext.get_currency(frm.doc.company): frappe.defaults.get_default("currency"); - if (frm.doc.employee && frm.doc.currency) { - frappe.run_serially([ - () => frm.events.set_exchange_rate(frm, company_currency), - () => frm.events.change_form_labels(frm, company_currency), - () => frm.events.change_grid_labels(frm), - () => frm.refresh_fields() - ]); - } - }, - - set_exchange_rate: function(frm, company_currency) { - if (frm.doc.docstatus === 0) { - if (frm.doc.currency) { - var from_currency = frm.doc.currency; - if (from_currency != company_currency) { - frm.events.hide_loan_section(frm); - frappe.call({ - method: "erpnext.setup.utils.get_exchange_rate", - args: { - from_currency: from_currency, - to_currency: company_currency, - }, - callback: function(r) { - if (r.message) { - frm.set_value("exchange_rate", flt(r.message)); - frm.set_df_property('exchange_rate', 'hidden', 0); - frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency - + " = [?] " + company_currency); - } - } - }); - } else { - frm.set_value("exchange_rate", 1.0); - frm.set_df_property('exchange_rate', 'hidden', 1); - frm.set_df_property("exchange_rate", "description", "" ); - } - } - } - }, - - exchange_rate: function(frm) { - set_totals(frm); - }, - - hide_loan_section: function(frm) { - frm.set_df_property('section_break_43', 'hidden', 1); - }, - - change_form_labels: function(frm, company_currency) { - frm.set_currency_labels(["base_hour_rate", "base_gross_pay", "base_total_deduction", - "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date", "gross_base_year_to_date"], - company_currency); - - frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words", "year_to_date", "month_to_date", "gross_year_to_date"], - frm.doc.currency); - - // toggle fields - frm.toggle_display(["exchange_rate", "base_hour_rate", "base_gross_pay", "base_total_deduction", - "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date", "base_gross_year_to_date"], - frm.doc.currency != company_currency); - }, - - change_grid_labels: function(frm) { - let fields = ["amount", "year_to_date", "default_amount", "additional_amount", "tax_on_flexible_benefit", - "tax_on_additional_salary"]; - - frm.set_currency_labels(fields, frm.doc.currency, "earnings"); - frm.set_currency_labels(fields, frm.doc.currency, "deductions"); - }, - - refresh: function(frm) { - frm.trigger("toggle_fields"); - - var salary_detail_fields = ["formula", "abbr", "statistical_component", "variable_based_on_taxable_salary"]; - frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields, false); - frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields, false); - frm.trigger("set_dynamic_labels"); - }, - - salary_slip_based_on_timesheet: function(frm) { - frm.trigger("toggle_fields"); - frm.events.get_emp_and_working_day_details(frm); - }, - - payroll_frequency: function(frm) { - frm.trigger("toggle_fields"); - frm.set_value('end_date', ''); - }, - - employee: function(frm) { - frm.events.get_emp_and_working_day_details(frm); - }, - - leave_without_pay: function(frm) { - if (frm.doc.employee && frm.doc.start_date && frm.doc.end_date) { - return frappe.call({ - method: 'process_salary_based_on_working_days', - doc: frm.doc, - callback: function() { - frm.refresh(); - } - }); - } - }, - - toggle_fields: function(frm) { - frm.toggle_display(['hourly_wages', 'timesheets'], cint(frm.doc.salary_slip_based_on_timesheet)===1); - - frm.toggle_display(['payment_days', 'total_working_days', 'leave_without_pay'], - frm.doc.payroll_frequency != ""); - }, - - get_emp_and_working_day_details: function(frm) { - if (frm.doc.employee) { - return frappe.call({ - method: 'get_emp_and_working_day_details', - doc: frm.doc, - callback: function(r) { - if (r.message[1] !== "Leave" && r.message[0]) { - frm.fields_dict.absent_days.set_description(__("Unmarked Days is treated as {0}. You can can change this in {1}", [r.message, frappe.utils.get_form_link("Payroll Settings", "Payroll Settings", true)])); - } - frm.refresh(); - } - }); - } - } -}); - -frappe.ui.form.on('Salary Slip Timesheet', { - time_sheet: function(frm) { - set_totals(frm); - }, - timesheets_remove: function(frm) { - set_totals(frm); - } -}); - -var set_totals = function(frm) { - if (frm.doc.docstatus === 0 && frm.doc.doctype === "Salary Slip") { - if (frm.doc.earnings || frm.doc.deductions) { - frappe.call({ - method: "set_totals", - doc: frm.doc, - callback: function() { - frm.refresh_fields(); - } - }); - } - } -}; - -frappe.ui.form.on('Salary Detail', { - amount: function(frm) { - set_totals(frm); - }, - - earnings_remove: function(frm) { - set_totals(frm); - }, - - deductions_remove: function(frm) { - set_totals(frm); - }, - - salary_component: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if (child.salary_component) { - frappe.call({ - method: "frappe.client.get", - args: { - doctype: "Salary Component", - name: child.salary_component - }, - callback: function(data) { - if (data.message) { - var result = data.message; - frappe.model.set_value(cdt, cdn, 'condition', result.condition); - frappe.model.set_value(cdt, cdn, 'amount_based_on_formula', result.amount_based_on_formula); - if (result.amount_based_on_formula === 1) { - frappe.model.set_value(cdt, cdn, 'formula', result.formula); - } else { - frappe.model.set_value(cdt, cdn, 'amount', result.amount); - } - frappe.model.set_value(cdt, cdn, 'statistical_component', result.statistical_component); - frappe.model.set_value(cdt, cdn, 'depends_on_payment_days', result.depends_on_payment_days); - frappe.model.set_value(cdt, cdn, 'do_not_include_in_total', result.do_not_include_in_total); - frappe.model.set_value(cdt, cdn, 'variable_based_on_taxable_salary', result.variable_based_on_taxable_salary); - frappe.model.set_value(cdt, cdn, 'is_tax_applicable', result.is_tax_applicable); - frappe.model.set_value(cdt, cdn, 'is_flexible_benefit', result.is_flexible_benefit); - refresh_field("earnings"); - refresh_field("deductions"); - } - } - }); - } - }, - - amount_based_on_formula: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if (child.amount_based_on_formula === 1) { - frappe.model.set_value(cdt, cdn, 'amount', null); - } else { - frappe.model.set_value(cdt, cdn, 'formula', null); - } - } -}); diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json deleted file mode 100644 index fe8e22cedf..0000000000 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.json +++ /dev/null @@ -1,683 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "creation": "2013-01-10 16:34:15", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "posting_date", - "employee", - "employee_name", - "department", - "designation", - "branch", - "column_break1", - "status", - "journal_entry", - "payroll_entry", - "company", - "currency", - "exchange_rate", - "letter_head", - "section_break_10", - "start_date", - "end_date", - "salary_structure", - "column_break_18", - "salary_slip_based_on_timesheet", - "payroll_frequency", - "section_break_20", - "total_working_days", - "unmarked_days", - "leave_without_pay", - "column_break_24", - "absent_days", - "payment_days", - "hourly_wages", - "timesheets", - "column_break_20", - "total_working_hours", - "hour_rate", - "base_hour_rate", - "section_break_26", - "bank_name", - "bank_account_no", - "mode_of_payment", - "section_break_32", - "deduct_tax_for_unclaimed_employee_benefits", - "deduct_tax_for_unsubmitted_tax_exemption_proof", - "earning_deduction", - "earning", - "earnings", - "deduction", - "deductions", - "totals", - "gross_pay", - "base_gross_pay", - "gross_year_to_date", - "base_gross_year_to_date", - "column_break_25", - "total_deduction", - "base_total_deduction", - "loan_repayment", - "loans", - "section_break_43", - "total_principal_amount", - "total_interest_amount", - "column_break_45", - "total_loan_repayment", - "net_pay_info", - "net_pay", - "base_net_pay", - "year_to_date", - "base_year_to_date", - "column_break_53", - "rounded_total", - "base_rounded_total", - "month_to_date", - "base_month_to_date", - "section_break_55", - "total_in_words", - "column_break_69", - "base_total_in_words", - "leave_details_section", - "leave_details", - "section_break_75", - "amended_from" - ], - "fields": [ - { - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Posting Date", - "reqd": 1 - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Employee", - "oldfieldname": "employee", - "oldfieldtype": "Link", - "options": "Employee", - "reqd": 1, - "search_index": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Read Only", - "in_global_search": 1, - "in_list_view": 1, - "label": "Employee Name", - "oldfieldname": "employee_name", - "oldfieldtype": "Data", - "reqd": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Department", - "oldfieldname": "department", - "oldfieldtype": "Link", - "options": "Department", - "read_only": 1 - }, - { - "depends_on": "eval:doc.designation", - "fetch_from": "employee.designation", - "fieldname": "designation", - "fieldtype": "Link", - "label": "Designation", - "oldfieldname": "designation", - "oldfieldtype": "Link", - "options": "Designation", - "read_only": 1 - }, - { - "fetch_from": "employee.branch", - "fieldname": "branch", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Branch", - "oldfieldname": "branch", - "oldfieldtype": "Link", - "options": "Branch", - "read_only": 1 - }, - { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, - { - "fieldname": "status", - "fieldtype": "Select", - "label": "Status", - "options": "Draft\nSubmitted\nCancelled", - "read_only": 1 - }, - { - "fieldname": "journal_entry", - "fieldtype": "Link", - "label": "Journal Entry", - "options": "Journal Entry", - "read_only": 1 - }, - { - "fieldname": "payroll_entry", - "fieldtype": "Link", - "label": "Payroll Entry", - "options": "Payroll Entry", - "read_only": 1 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Company", - "options": "Company", - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "letter_head", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Letter Head", - "options": "Letter Head", - "print_hide": 1 - }, - { - "fieldname": "section_break_10", - "fieldtype": "Section Break" - }, - { - "default": "0", - "fieldname": "salary_slip_based_on_timesheet", - "fieldtype": "Check", - "label": "Salary Slip Based on Timesheet", - "read_only": 1 - }, - { - "fieldname": "start_date", - "fieldtype": "Date", - "label": "Start Date" - }, - { - "fieldname": "end_date", - "fieldtype": "Date", - "label": "End Date" - }, - { - "fieldname": "salary_structure", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Salary Structure", - "options": "Salary Structure", - "read_only": 1, - "reqd": 1, - "search_index": 1 - }, - { - "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)", - "fieldname": "payroll_frequency", - "fieldtype": "Select", - "label": "Payroll Frequency", - "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" - }, - { - "fieldname": "total_working_days", - "fieldtype": "Float", - "label": "Working Days", - "oldfieldname": "total_days_in_month", - "oldfieldtype": "Int", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "leave_without_pay", - "fieldtype": "Float", - "label": "Leave Without Pay", - "oldfieldname": "leave_without_pay", - "oldfieldtype": "Currency" - }, - { - "fieldname": "payment_days", - "fieldtype": "Float", - "label": "Payment Days", - "oldfieldname": "payment_days", - "oldfieldtype": "Float", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "hourly_wages", - "fieldtype": "Section Break" - }, - { - "fieldname": "timesheets", - "fieldtype": "Table", - "label": "Salary Slip Timesheet", - "options": "Salary Slip Timesheet" - }, - { - "fieldname": "column_break_20", - "fieldtype": "Column Break" - }, - { - "fieldname": "total_working_hours", - "fieldtype": "Float", - "label": "Total Working Hours", - "print_hide_if_no_value": 1 - }, - { - "fieldname": "hour_rate", - "fieldtype": "Currency", - "label": "Hour Rate", - "options": "currency", - "print_hide_if_no_value": 1 - }, - { - "fieldname": "section_break_26", - "fieldtype": "Section Break" - }, - { - "fieldname": "bank_name", - "fieldtype": "Data", - "label": "Bank Name", - "oldfieldname": "bank_name", - "oldfieldtype": "Data", - "read_only": 1 - }, - { - "fieldname": "bank_account_no", - "fieldtype": "Data", - "label": "Bank Account No.", - "oldfieldname": "bank_account_no", - "oldfieldtype": "Data", - "read_only": 1 - }, - { - "fieldname": "section_break_32", - "fieldtype": "Section Break" - }, - { - "default": "0", - "fieldname": "deduct_tax_for_unclaimed_employee_benefits", - "fieldtype": "Check", - "label": "Deduct Tax For Unclaimed Employee Benefits" - }, - { - "default": "0", - "fieldname": "deduct_tax_for_unsubmitted_tax_exemption_proof", - "fieldtype": "Check", - "label": "Deduct Tax For Unsubmitted Tax Exemption Proof" - }, - { - "fieldname": "earning_deduction", - "fieldtype": "Section Break", - "label": "Earnings & Deductions", - "oldfieldtype": "Section Break" - }, - { - "fieldname": "earning", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, - { - "fieldname": "earnings", - "fieldtype": "Table", - "label": "Earnings", - "oldfieldname": "earning_details", - "oldfieldtype": "Table", - "options": "Salary Detail" - }, - { - "fieldname": "deduction", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, - { - "fieldname": "deductions", - "fieldtype": "Table", - "label": "Deductions", - "oldfieldname": "deduction_details", - "oldfieldtype": "Table", - "options": "Salary Detail" - }, - { - "fieldname": "totals", - "fieldtype": "Section Break", - "oldfieldtype": "Section Break" - }, - { - "fieldname": "gross_pay", - "fieldtype": "Currency", - "label": "Gross Pay", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "column_break_25", - "fieldtype": "Column Break" - }, - { - "depends_on": "total_loan_repayment", - "fieldname": "loan_repayment", - "fieldtype": "Section Break", - "label": "Loan Repayment" - }, - { - "fieldname": "loans", - "fieldtype": "Table", - "label": "Employee Loan", - "options": "Salary Slip Loan", - "print_hide": 1 - }, - { - "depends_on": "eval:doc.docstatus != 0", - "fieldname": "section_break_43", - "fieldtype": "Section Break" - }, - { - "default": "0", - "fieldname": "total_principal_amount", - "fieldtype": "Currency", - "label": "Total Principal Amount", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "default": "0", - "fieldname": "total_interest_amount", - "fieldtype": "Currency", - "label": "Total Interest Amount", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "column_break_45", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "total_loan_repayment", - "fieldtype": "Currency", - "label": "Total Loan Repayment", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "net_pay_info", - "fieldtype": "Section Break", - "label": "Net Pay Info" - }, - { - "fieldname": "net_pay", - "fieldtype": "Currency", - "label": "Net Pay", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "column_break_53", - "fieldtype": "Column Break" - }, - { - "bold": 1, - "fieldname": "rounded_total", - "fieldtype": "Currency", - "label": "Rounded Total", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "section_break_55", - "fieldtype": "Section Break" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Amended From", - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Salary Slip", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "mode_of_payment", - "fieldtype": "Select", - "label": "Mode Of Payment", - "read_only": 1 - }, - { - "fieldname": "absent_days", - "fieldtype": "Float", - "label": "Absent Days", - "read_only": 1 - }, - { - "fieldname": "unmarked_days", - "fieldtype": "Float", - "hidden": 1, - "label": "Unmarked days" - }, - { - "fieldname": "section_break_20", - "fieldtype": "Section Break" - }, - { - "fieldname": "column_break_24", - "fieldtype": "Column Break" - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" - }, - { - "depends_on": "eval:(doc.docstatus==1 || doc.salary_structure)", - "fetch_from": "salary_structure.currency", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "print_hide": 1, - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "total_deduction", - "fieldtype": "Currency", - "label": "Total Deduction", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "total_in_words", - "fieldtype": "Data", - "label": "Total in words", - "length": 240, - "read_only": 1 - }, - { - "fieldname": "section_break_75", - "fieldtype": "Section Break" - }, - { - "fieldname": "base_hour_rate", - "fieldtype": "Currency", - "label": "Hour Rate (Company Currency)", - "options": "Company:company:default_currency", - "print_hide_if_no_value": 1 - }, - { - "fieldname": "base_gross_pay", - "fieldtype": "Currency", - "label": "Gross Pay (Company Currency)", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "default": "1.0", - "fieldname": "exchange_rate", - "fieldtype": "Float", - "hidden": 1, - "label": "Exchange Rate", - "print_hide": 1, - "reqd": 1 - }, - { - "fieldname": "base_total_deduction", - "fieldtype": "Currency", - "label": "Total Deduction (Company Currency)", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "base_net_pay", - "fieldtype": "Currency", - "label": "Net Pay (Company Currency)", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "bold": 1, - "fieldname": "base_rounded_total", - "fieldtype": "Currency", - "label": "Rounded Total (Company Currency)", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "base_total_in_words", - "fieldtype": "Data", - "label": "Total in words (Company Currency)", - "length": 240, - "read_only": 1 - }, - { - "fieldname": "column_break_69", - "fieldtype": "Column Break" - }, - { - "description": "Total salary booked for this employee from the beginning of the year (payroll period or fiscal year) up to the current salary slip's end date.", - "fieldname": "year_to_date", - "fieldtype": "Currency", - "label": "Year To Date", - "options": "currency", - "read_only": 1 - }, - { - "description": "Total salary booked for this employee from the beginning of the month up to the current salary slip's end date.", - "fieldname": "month_to_date", - "fieldtype": "Currency", - "label": "Month To Date", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "base_year_to_date", - "fieldtype": "Currency", - "label": "Year To Date(Company Currency)", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "base_month_to_date", - "fieldtype": "Currency", - "label": "Month To Date(Company Currency)", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "leave_details_section", - "fieldtype": "Section Break", - "label": "Leave Details" - }, - { - "fieldname": "leave_details", - "fieldtype": "Table", - "label": "Leave Details", - "options": "Salary Slip Leave", - "read_only": 1 - }, - { - "fieldname": "gross_year_to_date", - "fieldtype": "Currency", - "label": "Gross Year To Date", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "base_gross_year_to_date", - "fieldtype": "Currency", - "label": "Gross Year To Date(Company Currency)", - "options": "Company:company:default_currency", - "read_only": 1 - } - ], - "icon": "fa fa-file-text", - "idx": 9, - "is_submittable": 1, - "links": [], - "modified": "2022-01-19 12:45:54.999345", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Salary Slip", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "read": 1, - "role": "Employee" - } - ], - "search_fields": "employee_name", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "timeline_field": "employee", - "title_field": "employee_name" -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py deleted file mode 100644 index 6a35985e64..0000000000 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ /dev/null @@ -1,1764 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import datetime -import math - -import frappe -from frappe import _, msgprint -from frappe.model.naming import make_autoname -from frappe.utils import ( - add_days, - cint, - cstr, - date_diff, - flt, - formatdate, - get_first_day, - getdate, - money_in_words, - rounded, -) -from frappe.utils.background_jobs import enqueue - -import erpnext -from erpnext.accounts.utils import get_fiscal_year -from erpnext.hr.utils import get_holiday_dates_for_employee, validate_active_employee -from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( - calculate_amounts, - create_repayment_entry, -) -from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import ( - process_loan_interest_accrual_for_term_loans, -) -from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salaries -from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import ( - get_benefit_component_amount, -) -from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import ( - get_benefit_claim_amount, - get_last_payroll_period_benefits, -) -from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates -from erpnext.payroll.doctype.payroll_period.payroll_period import ( - get_payroll_period, - get_period_factor, -) -from erpnext.utilities.transaction_base import TransactionBase - - -class SalarySlip(TransactionBase): - def __init__(self, *args, **kwargs): - super(SalarySlip, self).__init__(*args, **kwargs) - self.series = "Sal Slip/{0}/.#####".format(self.employee) - self.whitelisted_globals = { - "int": int, - "float": float, - "long": int, - "round": round, - "date": datetime.date, - "getdate": getdate, - } - - def autoname(self): - self.name = make_autoname(self.series) - - def validate(self): - self.status = self.get_status() - validate_active_employee(self.employee) - self.validate_dates() - self.check_existing() - if not self.salary_slip_based_on_timesheet: - self.get_date_details() - - if not (len(self.get("earnings")) or len(self.get("deductions"))): - # get details from salary structure - self.get_emp_and_working_day_details() - else: - self.get_working_days_details(lwp=self.leave_without_pay) - - self.calculate_net_pay() - self.compute_year_to_date() - self.compute_month_to_date() - self.compute_component_wise_year_to_date() - self.add_leave_balances() - - if frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet"): - max_working_hours = frappe.db.get_single_value( - "Payroll Settings", "max_working_hours_against_timesheet" - ) - if self.salary_slip_based_on_timesheet and (self.total_working_hours > int(max_working_hours)): - frappe.msgprint( - _("Total working hours should not be greater than max working hours {0}").format( - max_working_hours - ), - alert=True, - ) - - def set_net_total_in_words(self): - doc_currency = self.currency - company_currency = erpnext.get_company_currency(self.company) - total = self.net_pay if self.is_rounding_total_disabled() else self.rounded_total - base_total = self.base_net_pay if self.is_rounding_total_disabled() else self.base_rounded_total - self.total_in_words = money_in_words(total, doc_currency) - self.base_total_in_words = money_in_words(base_total, company_currency) - - def on_submit(self): - if self.net_pay < 0: - frappe.throw(_("Net Pay cannot be less than 0")) - else: - self.set_status() - self.update_status(self.name) - self.make_loan_repayment_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.update_payment_status_for_gratuity() - - def update_payment_status_for_gratuity(self): - additional_salary = frappe.db.get_all( - "Additional Salary", - filters={ - "payroll_date": ("between", [self.start_date, self.end_date]), - "employee": self.employee, - "ref_doctype": "Gratuity", - "docstatus": 1, - }, - fields=["ref_docname", "name"], - limit=1, - ) - - if additional_salary: - status = "Paid" if self.docstatus == 1 else "Unpaid" - if additional_salary[0].name in [entry.additional_salary for entry in self.earnings]: - frappe.db.set_value("Gratuity", additional_salary[0].ref_docname, "status", status) - - def on_cancel(self): - self.set_status() - self.update_status() - self.update_payment_status_for_gratuity() - self.cancel_loan_repayment_entry() - - def on_trash(self): - from frappe.model.naming import revert_series_if_last - - revert_series_if_last(self.series, self.name) - - def get_status(self): - if self.docstatus == 0: - status = "Draft" - elif self.docstatus == 1: - status = "Submitted" - elif self.docstatus == 2: - status = "Cancelled" - return status - - def validate_dates(self, joining_date=None, relieving_date=None): - if date_diff(self.end_date, self.start_date) < 0: - frappe.throw(_("To date cannot be before From date")) - - if not joining_date: - joining_date, relieving_date = frappe.get_cached_value( - "Employee", self.employee, ("date_of_joining", "relieving_date") - ) - - if date_diff(self.end_date, joining_date) < 0: - frappe.throw(_("Cannot create Salary Slip for Employee joining after Payroll Period")) - - if relieving_date and date_diff(relieving_date, self.start_date) < 0: - frappe.throw(_("Cannot create Salary Slip for Employee who has left before Payroll Period")) - - def is_rounding_total_disabled(self): - return cint(frappe.db.get_single_value("Payroll Settings", "disable_rounded_total")) - - def check_existing(self): - if not self.salary_slip_based_on_timesheet: - cond = "" - if self.payroll_entry: - cond += "and payroll_entry = '{0}'".format(self.payroll_entry) - ret_exist = frappe.db.sql( - """select name from `tabSalary Slip` - where start_date = %s and end_date = %s and docstatus != 2 - and employee = %s and name != %s {0}""".format( - cond - ), - (self.start_date, self.end_date, self.employee, self.name), - ) - if ret_exist: - frappe.throw( - _("Salary Slip of employee {0} already created for this period").format(self.employee) - ) - else: - for data in self.timesheets: - if frappe.db.get_value("Timesheet", data.time_sheet, "status") == "Payrolled": - frappe.throw( - _("Salary Slip of employee {0} already created for time sheet {1}").format( - self.employee, data.time_sheet - ) - ) - - def get_date_details(self): - if not self.end_date: - date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date) - self.start_date = date_details.start_date - self.end_date = date_details.end_date - - @frappe.whitelist() - def get_emp_and_working_day_details(self): - """First time, load all the components from salary structure""" - if self.employee: - self.set("earnings", []) - self.set("deductions", []) - - if not self.salary_slip_based_on_timesheet: - self.get_date_details() - - joining_date, relieving_date = frappe.get_cached_value( - "Employee", self.employee, ("date_of_joining", "relieving_date") - ) - - self.validate_dates(joining_date, relieving_date) - - # getin leave details - self.get_working_days_details(joining_date, relieving_date) - struct = self.check_sal_struct(joining_date, relieving_date) - - if struct: - self._salary_structure_doc = frappe.get_doc("Salary Structure", struct) - self.salary_slip_based_on_timesheet = ( - self._salary_structure_doc.salary_slip_based_on_timesheet or 0 - ) - self.set_time_sheet() - self.pull_sal_struct() - ps = frappe.db.get_value( - "Payroll Settings", None, ["payroll_based_on", "consider_unmarked_attendance_as"], as_dict=1 - ) - return [ps.payroll_based_on, ps.consider_unmarked_attendance_as] - - def set_time_sheet(self): - if self.salary_slip_based_on_timesheet: - self.set("timesheets", []) - timesheets = frappe.db.sql( - """ select * from `tabTimesheet` where employee = %(employee)s and start_date BETWEEN %(start_date)s AND %(end_date)s and (status = 'Submitted' or - status = 'Billed')""", - {"employee": self.employee, "start_date": self.start_date, "end_date": self.end_date}, - as_dict=1, - ) - - for data in timesheets: - self.append("timesheets", {"time_sheet": data.name, "working_hours": data.total_hours}) - - def check_sal_struct(self, joining_date, relieving_date): - cond = """and sa.employee=%(employee)s and (sa.from_date <= %(start_date)s or - sa.from_date <= %(end_date)s or sa.from_date <= %(joining_date)s)""" - if self.payroll_frequency: - cond += """and ss.payroll_frequency = '%(payroll_frequency)s'""" % { - "payroll_frequency": self.payroll_frequency - } - - st_name = frappe.db.sql( - """ - select sa.salary_structure - from `tabSalary Structure Assignment` sa join `tabSalary Structure` ss - where sa.salary_structure=ss.name - and sa.docstatus = 1 and ss.docstatus = 1 and ss.is_active ='Yes' %s - order by sa.from_date desc - limit 1 - """ - % cond, - { - "employee": self.employee, - "start_date": self.start_date, - "end_date": self.end_date, - "joining_date": joining_date, - }, - ) - - if st_name: - self.salary_structure = st_name[0][0] - return self.salary_structure - - else: - self.salary_structure = None - frappe.msgprint( - _("No active or default Salary Structure found for employee {0} for the given dates").format( - self.employee - ), - title=_("Salary Structure Missing"), - ) - - def pull_sal_struct(self): - from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip - - if self.salary_slip_based_on_timesheet: - self.salary_structure = self._salary_structure_doc.name - self.hour_rate = self._salary_structure_doc.hour_rate - self.base_hour_rate = flt(self.hour_rate) * flt(self.exchange_rate) - self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0 - wages_amount = self.hour_rate * self.total_working_hours - - self.add_earning_for_hourly_wages( - self, self._salary_structure_doc.salary_component, wages_amount - ) - - make_salary_slip(self._salary_structure_doc.name, self) - - def get_working_days_details( - self, joining_date=None, relieving_date=None, lwp=None, for_preview=0 - ): - payroll_based_on = frappe.db.get_value("Payroll Settings", None, "payroll_based_on") - include_holidays_in_total_working_days = frappe.db.get_single_value( - "Payroll Settings", "include_holidays_in_total_working_days" - ) - - working_days = date_diff(self.end_date, self.start_date) + 1 - if for_preview: - self.total_working_days = working_days - self.payment_days = working_days - return - - holidays = self.get_holidays_for_employee(self.start_date, self.end_date) - - if not cint(include_holidays_in_total_working_days): - working_days -= len(holidays) - if working_days < 0: - frappe.throw(_("There are more holidays than working days this month.")) - - if not payroll_based_on: - frappe.throw(_("Please set Payroll based on in Payroll settings")) - - if payroll_based_on == "Attendance": - actual_lwp, absent = self.calculate_lwp_ppl_and_absent_days_based_on_attendance(holidays) - self.absent_days = absent - else: - actual_lwp = self.calculate_lwp_or_ppl_based_on_leave_application(holidays, working_days) - - if not lwp: - lwp = actual_lwp - elif lwp != actual_lwp: - frappe.msgprint( - _("Leave Without Pay does not match with approved {} records").format(payroll_based_on) - ) - - self.leave_without_pay = lwp - self.total_working_days = working_days - - payment_days = self.get_payment_days( - joining_date, relieving_date, include_holidays_in_total_working_days - ) - - if flt(payment_days) > flt(lwp): - self.payment_days = flt(payment_days) - flt(lwp) - - if payroll_based_on == "Attendance": - self.payment_days -= flt(absent) - - consider_unmarked_attendance_as = ( - frappe.db.get_value("Payroll Settings", None, "consider_unmarked_attendance_as") or "Present" - ) - - if payroll_based_on == "Attendance" and consider_unmarked_attendance_as == "Absent": - unmarked_days = self.get_unmarked_days(include_holidays_in_total_working_days) - self.absent_days += unmarked_days # will be treated as absent - self.payment_days -= unmarked_days - else: - self.payment_days = 0 - - def get_unmarked_days(self, include_holidays_in_total_working_days): - unmarked_days = self.total_working_days - joining_date, relieving_date = frappe.get_cached_value( - "Employee", self.employee, ["date_of_joining", "relieving_date"] - ) - start_date = self.start_date - end_date = self.end_date - - if joining_date and (getdate(self.start_date) < joining_date <= getdate(self.end_date)): - start_date = joining_date - unmarked_days = self.get_unmarked_days_based_on_doj_or_relieving( - unmarked_days, - include_holidays_in_total_working_days, - self.start_date, - add_days(joining_date, -1), - ) - - if relieving_date and (getdate(self.start_date) <= relieving_date < getdate(self.end_date)): - end_date = relieving_date - unmarked_days = self.get_unmarked_days_based_on_doj_or_relieving( - unmarked_days, - include_holidays_in_total_working_days, - add_days(relieving_date, 1), - self.end_date, - ) - - # exclude days for which attendance has been marked - unmarked_days -= frappe.get_all( - "Attendance", - filters={ - "attendance_date": ["between", [start_date, end_date]], - "employee": self.employee, - "docstatus": 1, - }, - fields=["COUNT(*) as marked_days"], - )[0].marked_days - - return unmarked_days - - def get_unmarked_days_based_on_doj_or_relieving( - self, unmarked_days, include_holidays_in_total_working_days, start_date, end_date - ): - """ - Exclude days before DOJ or after - Relieving Date from unmarked days - """ - from erpnext.hr.doctype.employee.employee import is_holiday - - if include_holidays_in_total_working_days: - unmarked_days -= date_diff(end_date, start_date) + 1 - else: - # exclude only if not holidays - for days in range(date_diff(end_date, start_date) + 1): - date = add_days(end_date, -days) - if not is_holiday(self.employee, date): - unmarked_days -= 1 - - return unmarked_days - - def get_payment_days(self, joining_date, relieving_date, include_holidays_in_total_working_days): - if not joining_date: - joining_date, relieving_date = frappe.get_cached_value( - "Employee", self.employee, ["date_of_joining", "relieving_date"] - ) - - start_date = getdate(self.start_date) - if joining_date: - if getdate(self.start_date) <= joining_date <= getdate(self.end_date): - start_date = joining_date - elif joining_date > getdate(self.end_date): - return - - end_date = getdate(self.end_date) - if relieving_date: - if getdate(self.start_date) <= relieving_date <= getdate(self.end_date): - end_date = relieving_date - elif relieving_date < getdate(self.start_date): - frappe.throw(_("Employee relieved on {0} must be set as 'Left'").format(relieving_date)) - - payment_days = date_diff(end_date, start_date) + 1 - - if not cint(include_holidays_in_total_working_days): - holidays = self.get_holidays_for_employee(start_date, end_date) - payment_days -= len(holidays) - - return payment_days - - def get_holidays_for_employee(self, start_date, end_date): - return get_holiday_dates_for_employee(self.employee, start_date, end_date) - - def calculate_lwp_or_ppl_based_on_leave_application(self, holidays, working_days): - lwp = 0 - holidays = "','".join(holidays) - daily_wages_fraction_for_half_day = ( - flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 - ) - - for d in range(working_days): - date = add_days(cstr(getdate(self.start_date)), d) - leave = get_lwp_or_ppl_for_date(date, self.employee, holidays) - - if leave: - equivalent_lwp_count = 0 - is_half_day_leave = cint(leave[0].is_half_day) - is_partially_paid_leave = cint(leave[0].is_ppl) - fraction_of_daily_salary_per_leave = flt(leave[0].fraction_of_daily_salary_per_leave) - - equivalent_lwp_count = (1 - daily_wages_fraction_for_half_day) if is_half_day_leave else 1 - - if is_partially_paid_leave: - equivalent_lwp_count *= ( - fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1 - ) - - lwp += equivalent_lwp_count - - return lwp - - def calculate_lwp_ppl_and_absent_days_based_on_attendance(self, holidays): - lwp = 0 - absent = 0 - - daily_wages_fraction_for_half_day = ( - flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 - ) - - leave_types = frappe.get_all( - "Leave Type", - or_filters=[["is_ppl", "=", 1], ["is_lwp", "=", 1]], - fields=["name", "is_lwp", "is_ppl", "fraction_of_daily_salary_per_leave", "include_holiday"], - ) - - leave_type_map = {} - for leave_type in leave_types: - leave_type_map[leave_type.name] = leave_type - - attendances = frappe.db.sql( - """ - SELECT attendance_date, status, leave_type - FROM `tabAttendance` - WHERE - status in ("Absent", "Half Day", "On leave") - AND employee = %s - AND docstatus = 1 - AND attendance_date between %s and %s - """, - values=(self.employee, self.start_date, self.end_date), - as_dict=1, - ) - - for d in attendances: - if ( - d.status in ("Half Day", "On Leave") - and d.leave_type - and d.leave_type not in leave_type_map.keys() - ): - continue - - if formatdate(d.attendance_date, "yyyy-mm-dd") in holidays: - if d.status == "Absent" or ( - d.leave_type - and d.leave_type in leave_type_map.keys() - and not leave_type_map[d.leave_type]["include_holiday"] - ): - continue - - if d.leave_type: - fraction_of_daily_salary_per_leave = leave_type_map[d.leave_type][ - "fraction_of_daily_salary_per_leave" - ] - - if d.status == "Half Day": - equivalent_lwp = 1 - daily_wages_fraction_for_half_day - - if d.leave_type in leave_type_map.keys() and leave_type_map[d.leave_type]["is_ppl"]: - equivalent_lwp *= ( - fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1 - ) - lwp += equivalent_lwp - elif d.status == "On Leave" and d.leave_type and d.leave_type in leave_type_map.keys(): - equivalent_lwp = 1 - if leave_type_map[d.leave_type]["is_ppl"]: - equivalent_lwp *= ( - fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1 - ) - lwp += equivalent_lwp - elif d.status == "Absent": - absent += 1 - return lwp, absent - - def add_earning_for_hourly_wages(self, doc, salary_component, amount): - row_exists = False - for row in doc.earnings: - if row.salary_component == salary_component: - row.amount = amount - row_exists = True - break - - if not row_exists: - wages_row = { - "salary_component": salary_component, - "abbr": frappe.db.get_value("Salary Component", salary_component, "salary_component_abbr"), - "amount": self.hour_rate * self.total_working_hours, - "default_amount": 0.0, - "additional_amount": 0.0, - } - doc.append("earnings", wages_row) - - def calculate_net_pay(self): - if self.salary_structure: - self.calculate_component_amounts("earnings") - self.gross_pay = self.get_component_totals("earnings", depends_on_payment_days=1) - self.base_gross_pay = flt( - flt(self.gross_pay) * flt(self.exchange_rate), self.precision("base_gross_pay") - ) - - if self.salary_structure: - self.calculate_component_amounts("deductions") - - self.set_loan_repayment() - self.set_precision_for_component_amounts() - self.set_net_pay() - - def set_net_pay(self): - self.total_deduction = self.get_component_totals("deductions") - self.base_total_deduction = flt( - flt(self.total_deduction) * flt(self.exchange_rate), self.precision("base_total_deduction") - ) - self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment)) - self.rounded_total = rounded(self.net_pay) - self.base_net_pay = flt( - flt(self.net_pay) * flt(self.exchange_rate), self.precision("base_net_pay") - ) - self.base_rounded_total = flt(rounded(self.base_net_pay), self.precision("base_net_pay")) - if self.hour_rate: - self.base_hour_rate = flt( - flt(self.hour_rate) * flt(self.exchange_rate), self.precision("base_hour_rate") - ) - self.set_net_total_in_words() - - def calculate_component_amounts(self, component_type): - if not getattr(self, "_salary_structure_doc", None): - self._salary_structure_doc = frappe.get_doc("Salary Structure", self.salary_structure) - - payroll_period = get_payroll_period(self.start_date, self.end_date, self.company) - - self.add_structure_components(component_type) - self.add_additional_salary_components(component_type) - if component_type == "earnings": - self.add_employee_benefits(payroll_period) - else: - self.add_tax_components(payroll_period) - - def add_structure_components(self, component_type): - data = self.get_data_for_eval() - for struct_row in self._salary_structure_doc.get(component_type): - amount = self.eval_condition_and_formula(struct_row, data) - if amount and struct_row.statistical_component == 0: - self.update_component_row(struct_row, amount, component_type) - - def get_data_for_eval(self): - """Returns data for evaluating formula""" - data = frappe._dict() - employee = frappe.get_doc("Employee", self.employee).as_dict() - - start_date = getdate(self.start_date) - date_to_validate = ( - employee.date_of_joining if employee.date_of_joining > start_date else start_date - ) - - salary_structure_assignment = frappe.get_value( - "Salary Structure Assignment", - { - "employee": self.employee, - "salary_structure": self.salary_structure, - "from_date": ("<=", date_to_validate), - "docstatus": 1, - }, - "*", - order_by="from_date desc", - as_dict=True, - ) - - if not salary_structure_assignment: - frappe.throw( - _( - "Please assign a Salary Structure for Employee {0} " "applicable from or before {1} first" - ).format( - frappe.bold(self.employee_name), - frappe.bold(formatdate(date_to_validate)), - ) - ) - - data.update(salary_structure_assignment) - data.update(employee) - data.update(self.as_dict()) - - # set values for components - salary_components = frappe.get_all("Salary Component", fields=["salary_component_abbr"]) - for sc in salary_components: - data.setdefault(sc.salary_component_abbr, 0) - - for key in ("earnings", "deductions"): - for d in self.get(key): - data[d.abbr] = d.amount - - return data - - def eval_condition_and_formula(self, d, data): - try: - condition = d.condition.strip().replace("\n", " ") if d.condition else None - if condition: - if not frappe.safe_eval(condition, self.whitelisted_globals, data): - return None - amount = d.amount - if d.amount_based_on_formula: - formula = d.formula.strip().replace("\n", " ") if d.formula else None - if formula: - amount = flt(frappe.safe_eval(formula, self.whitelisted_globals, data), d.precision("amount")) - if amount: - data[d.abbr] = amount - - return amount - - except NameError as err: - frappe.throw( - _("{0}
This error can be due to missing or deleted field.").format(err), - title=_("Name error"), - ) - except SyntaxError as err: - frappe.throw(_("Syntax error in formula or condition: {0}").format(err)) - except Exception as e: - frappe.throw(_("Error in formula or condition: {0}").format(e)) - raise - - def add_employee_benefits(self, payroll_period): - for struct_row in self._salary_structure_doc.get("earnings"): - if struct_row.is_flexible_benefit == 1: - if ( - frappe.db.get_value( - "Salary Component", struct_row.salary_component, "pay_against_benefit_claim" - ) - != 1 - ): - benefit_component_amount = get_benefit_component_amount( - self.employee, - self.start_date, - self.end_date, - struct_row.salary_component, - self._salary_structure_doc, - self.payroll_frequency, - payroll_period, - ) - if benefit_component_amount: - self.update_component_row(struct_row, benefit_component_amount, "earnings") - else: - benefit_claim_amount = get_benefit_claim_amount( - self.employee, self.start_date, self.end_date, struct_row.salary_component - ) - if benefit_claim_amount: - self.update_component_row(struct_row, benefit_claim_amount, "earnings") - - self.adjust_benefits_in_last_payroll_period(payroll_period) - - def adjust_benefits_in_last_payroll_period(self, payroll_period): - if payroll_period: - if getdate(payroll_period.end_date) <= getdate(self.end_date): - last_benefits = get_last_payroll_period_benefits( - self.employee, self.start_date, self.end_date, payroll_period, self._salary_structure_doc - ) - if last_benefits: - for last_benefit in last_benefits: - last_benefit = frappe._dict(last_benefit) - amount = last_benefit.amount - self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings") - - def add_additional_salary_components(self, component_type): - additional_salaries = get_additional_salaries( - self.employee, self.start_date, self.end_date, component_type - ) - - for additional_salary in additional_salaries: - self.update_component_row( - get_salary_component_data(additional_salary.component), - additional_salary.amount, - component_type, - additional_salary, - is_recurring=additional_salary.is_recurring, - ) - - def add_tax_components(self, payroll_period): - # Calculate variable_based_on_taxable_salary after all components updated in salary slip - tax_components, other_deduction_components = [], [] - for d in self._salary_structure_doc.get("deductions"): - if d.variable_based_on_taxable_salary == 1 and not d.formula and not flt(d.amount): - tax_components.append(d.salary_component) - else: - other_deduction_components.append(d.salary_component) - - if not tax_components: - tax_components = [ - d.name - for d in frappe.get_all("Salary Component", filters={"variable_based_on_taxable_salary": 1}) - if d.name not in other_deduction_components - ] - - for d in tax_components: - tax_amount = self.calculate_variable_based_on_taxable_salary(d, payroll_period) - tax_row = get_salary_component_data(d) - self.update_component_row(tax_row, tax_amount, "deductions") - - def update_component_row( - self, component_data, amount, component_type, additional_salary=None, is_recurring=0 - ): - component_row = None - for d in self.get(component_type): - if d.salary_component != component_data.salary_component: - continue - - if (not d.additional_salary and (not additional_salary or additional_salary.overwrite)) or ( - additional_salary and additional_salary.name == d.additional_salary - ): - component_row = d - break - - if additional_salary and additional_salary.overwrite: - # Additional Salary with overwrite checked, remove default rows of same component - self.set( - component_type, - [ - d - for d in self.get(component_type) - if d.salary_component != component_data.salary_component - or (d.additional_salary and additional_salary.name != d.additional_salary) - or d == component_row - ], - ) - - if not component_row: - if not amount: - return - - component_row = self.append(component_type) - for attr in ( - "depends_on_payment_days", - "salary_component", - "abbr", - "do_not_include_in_total", - "is_tax_applicable", - "is_flexible_benefit", - "variable_based_on_taxable_salary", - "exempted_from_income_tax", - ): - component_row.set(attr, component_data.get(attr)) - - if additional_salary: - if additional_salary.overwrite: - component_row.additional_amount = flt( - flt(amount) - flt(component_row.get("default_amount", 0)), - component_row.precision("additional_amount"), - ) - else: - component_row.default_amount = 0 - component_row.additional_amount = amount - - component_row.is_recurring_additional_salary = is_recurring - component_row.additional_salary = additional_salary.name - component_row.deduct_full_tax_on_selected_payroll_date = ( - additional_salary.deduct_full_tax_on_selected_payroll_date - ) - else: - component_row.default_amount = amount - component_row.additional_amount = 0 - component_row.deduct_full_tax_on_selected_payroll_date = ( - component_data.deduct_full_tax_on_selected_payroll_date - ) - - component_row.amount = amount - - self.update_component_amount_based_on_payment_days(component_row) - - def update_component_amount_based_on_payment_days(self, component_row): - joining_date, relieving_date = self.get_joining_and_relieving_dates() - component_row.amount = self.get_amount_based_on_payment_days( - component_row, joining_date, relieving_date - )[0] - - def set_precision_for_component_amounts(self): - for component_type in ("earnings", "deductions"): - for component_row in self.get(component_type): - component_row.amount = flt(component_row.amount, component_row.precision("amount")) - - def calculate_variable_based_on_taxable_salary(self, tax_component, payroll_period): - if not payroll_period: - frappe.msgprint( - _("Start and end dates not in a valid Payroll Period, cannot calculate {0}.").format( - tax_component - ) - ) - return - - # Deduct taxes forcefully for unsubmitted tax exemption proof and unclaimed benefits in the last period - if payroll_period.end_date <= getdate(self.end_date): - self.deduct_tax_for_unsubmitted_tax_exemption_proof = 1 - self.deduct_tax_for_unclaimed_employee_benefits = 1 - - return self.calculate_variable_tax(payroll_period, tax_component) - - def calculate_variable_tax(self, payroll_period, tax_component): - # get Tax slab from salary structure assignment for the employee and payroll period - tax_slab = self.get_income_tax_slabs(payroll_period) - - # get remaining numbers of sub-period (period for which one salary is processed) - remaining_sub_periods = get_period_factor( - self.employee, self.start_date, self.end_date, self.payroll_frequency, payroll_period - )[1] - # get taxable_earnings, paid_taxes for previous period - previous_taxable_earnings = self.get_taxable_earnings_for_prev_period( - payroll_period.start_date, self.start_date, tax_slab.allow_tax_exemption - ) - previous_total_paid_taxes = self.get_tax_paid_in_period( - payroll_period.start_date, self.start_date, tax_component - ) - - # get taxable_earnings for current period (all days) - current_taxable_earnings = self.get_taxable_earnings( - tax_slab.allow_tax_exemption, payroll_period=payroll_period - ) - future_structured_taxable_earnings = current_taxable_earnings.taxable_earnings * ( - math.ceil(remaining_sub_periods) - 1 - ) - - # get taxable_earnings, addition_earnings for current actual payment days - current_taxable_earnings_for_payment_days = self.get_taxable_earnings( - tax_slab.allow_tax_exemption, based_on_payment_days=1, payroll_period=payroll_period - ) - current_structured_taxable_earnings = current_taxable_earnings_for_payment_days.taxable_earnings - current_additional_earnings = current_taxable_earnings_for_payment_days.additional_income - current_additional_earnings_with_full_tax = ( - current_taxable_earnings_for_payment_days.additional_income_with_full_tax - ) - - # Get taxable unclaimed benefits - unclaimed_taxable_benefits = 0 - if self.deduct_tax_for_unclaimed_employee_benefits: - unclaimed_taxable_benefits = self.calculate_unclaimed_taxable_benefits(payroll_period) - unclaimed_taxable_benefits += current_taxable_earnings_for_payment_days.flexi_benefits - - # Total exemption amount based on tax exemption declaration - total_exemption_amount = self.get_total_exemption_amount(payroll_period, tax_slab) - - # Employee Other Incomes - other_incomes = self.get_income_form_other_sources(payroll_period) or 0.0 - - # Total taxable earnings including additional and other incomes - total_taxable_earnings = ( - previous_taxable_earnings - + current_structured_taxable_earnings - + future_structured_taxable_earnings - + current_additional_earnings - + other_incomes - + unclaimed_taxable_benefits - - total_exemption_amount - ) - - # Total taxable earnings without additional earnings with full tax - total_taxable_earnings_without_full_tax_addl_components = ( - total_taxable_earnings - current_additional_earnings_with_full_tax - ) - - # Structured tax amount - eval_locals = self.get_data_for_eval() - total_structured_tax_amount = calculate_tax_by_tax_slab( - total_taxable_earnings_without_full_tax_addl_components, - tax_slab, - self.whitelisted_globals, - eval_locals, - ) - current_structured_tax_amount = ( - total_structured_tax_amount - previous_total_paid_taxes - ) / remaining_sub_periods - - # Total taxable earnings with additional earnings with full tax - full_tax_on_additional_earnings = 0.0 - if current_additional_earnings_with_full_tax: - total_tax_amount = calculate_tax_by_tax_slab( - total_taxable_earnings, tax_slab, self.whitelisted_globals, eval_locals - ) - full_tax_on_additional_earnings = total_tax_amount - total_structured_tax_amount - - current_tax_amount = current_structured_tax_amount + full_tax_on_additional_earnings - if flt(current_tax_amount) < 0: - current_tax_amount = 0 - - return current_tax_amount - - def get_income_tax_slabs(self, payroll_period): - income_tax_slab, ss_assignment_name = frappe.db.get_value( - "Salary Structure Assignment", - {"employee": self.employee, "salary_structure": self.salary_structure, "docstatus": 1}, - ["income_tax_slab", "name"], - ) - - if not income_tax_slab: - frappe.throw( - _("Income Tax Slab not set in Salary Structure Assignment: {0}").format(ss_assignment_name) - ) - - income_tax_slab_doc = frappe.get_doc("Income Tax Slab", income_tax_slab) - if income_tax_slab_doc.disabled: - frappe.throw(_("Income Tax Slab: {0} is disabled").format(income_tax_slab)) - - if getdate(income_tax_slab_doc.effective_from) > getdate(payroll_period.start_date): - frappe.throw( - _("Income Tax Slab must be effective on or before Payroll Period Start Date: {0}").format( - payroll_period.start_date - ) - ) - - return income_tax_slab_doc - - def get_taxable_earnings_for_prev_period(self, start_date, end_date, allow_tax_exemption=False): - taxable_earnings = frappe.db.sql( - """ - select sum(sd.amount) - from - `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name - where - sd.parentfield='earnings' - and sd.is_tax_applicable=1 - and is_flexible_benefit=0 - and ss.docstatus=1 - and ss.employee=%(employee)s - and ss.start_date between %(from_date)s and %(to_date)s - and ss.end_date between %(from_date)s and %(to_date)s - """, - {"employee": self.employee, "from_date": start_date, "to_date": end_date}, - ) - taxable_earnings = flt(taxable_earnings[0][0]) if taxable_earnings else 0 - - exempted_amount = 0 - if allow_tax_exemption: - exempted_amount = frappe.db.sql( - """ - select sum(sd.amount) - from - `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name - where - sd.parentfield='deductions' - and sd.exempted_from_income_tax=1 - and is_flexible_benefit=0 - and ss.docstatus=1 - and ss.employee=%(employee)s - and ss.start_date between %(from_date)s and %(to_date)s - and ss.end_date between %(from_date)s and %(to_date)s - """, - {"employee": self.employee, "from_date": start_date, "to_date": end_date}, - ) - exempted_amount = flt(exempted_amount[0][0]) if exempted_amount else 0 - - return taxable_earnings - exempted_amount - - def get_tax_paid_in_period(self, start_date, end_date, tax_component): - # find total_tax_paid, tax paid for benefit, additional_salary - total_tax_paid = flt( - frappe.db.sql( - """ - select - sum(sd.amount) - from - `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name - where - sd.parentfield='deductions' - and sd.salary_component=%(salary_component)s - and sd.variable_based_on_taxable_salary=1 - and ss.docstatus=1 - and ss.employee=%(employee)s - and ss.start_date between %(from_date)s and %(to_date)s - and ss.end_date between %(from_date)s and %(to_date)s - """, - { - "salary_component": tax_component, - "employee": self.employee, - "from_date": start_date, - "to_date": end_date, - }, - )[0][0] - ) - - return total_tax_paid - - def get_taxable_earnings( - self, allow_tax_exemption=False, based_on_payment_days=0, payroll_period=None - ): - joining_date, relieving_date = self.get_joining_and_relieving_dates() - - taxable_earnings = 0 - additional_income = 0 - additional_income_with_full_tax = 0 - flexi_benefits = 0 - - for earning in self.earnings: - if based_on_payment_days: - amount, additional_amount = self.get_amount_based_on_payment_days( - earning, joining_date, relieving_date - ) - else: - if earning.additional_amount: - amount, additional_amount = earning.amount, earning.additional_amount - else: - amount, additional_amount = earning.default_amount, earning.additional_amount - - if earning.is_tax_applicable: - if earning.is_flexible_benefit: - flexi_benefits += amount - else: - taxable_earnings += amount - additional_amount - additional_income += additional_amount - - # Get additional amount based on future recurring additional salary - if additional_amount and earning.is_recurring_additional_salary: - additional_income += self.get_future_recurring_additional_amount( - earning.additional_salary, earning.additional_amount, payroll_period - ) # Used earning.additional_amount to consider the amount for the full month - - if earning.deduct_full_tax_on_selected_payroll_date: - additional_income_with_full_tax += additional_amount - - if allow_tax_exemption: - for ded in self.deductions: - if ded.exempted_from_income_tax: - amount, additional_amount = ded.amount, ded.additional_amount - if based_on_payment_days: - amount, additional_amount = self.get_amount_based_on_payment_days( - ded, joining_date, relieving_date - ) - - taxable_earnings -= flt(amount - additional_amount) - additional_income -= additional_amount - - if additional_amount and ded.is_recurring_additional_salary: - additional_income -= self.get_future_recurring_additional_amount( - ded.additional_salary, ded.additional_amount, payroll_period - ) # Used ded.additional_amount to consider the amount for the full month - - return frappe._dict( - { - "taxable_earnings": taxable_earnings, - "additional_income": additional_income, - "additional_income_with_full_tax": additional_income_with_full_tax, - "flexi_benefits": flexi_benefits, - } - ) - - def get_future_recurring_additional_amount( - self, additional_salary, monthly_additional_amount, payroll_period - ): - future_recurring_additional_amount = 0 - to_date = frappe.db.get_value("Additional Salary", additional_salary, "to_date") - - # future month count excluding current - from_date, to_date = getdate(self.start_date), getdate(to_date) - - # If recurring period end date is beyond the payroll period, - # last day of payroll period should be considered for recurring period calculation - if getdate(to_date) > getdate(payroll_period.end_date): - to_date = getdate(payroll_period.end_date) - - future_recurring_period = ((to_date.year - from_date.year) * 12) + ( - to_date.month - from_date.month - ) - - if future_recurring_period > 0: - future_recurring_additional_amount = ( - monthly_additional_amount * future_recurring_period - ) # Used earning.additional_amount to consider the amount for the full month - return future_recurring_additional_amount - - def get_amount_based_on_payment_days(self, row, joining_date, relieving_date): - amount, additional_amount = row.amount, row.additional_amount - timesheet_component = frappe.db.get_value( - "Salary Structure", self.salary_structure, "salary_component" - ) - - if ( - self.salary_structure - and cint(row.depends_on_payment_days) - and cint(self.total_working_days) - and not ( - row.additional_salary and row.default_amount - ) # to identify overwritten additional salary - and ( - row.salary_component != timesheet_component - or getdate(self.start_date) < joining_date - or (relieving_date and getdate(self.end_date) > relieving_date) - ) - ): - additional_amount = flt( - (flt(row.additional_amount) * flt(self.payment_days) / cint(self.total_working_days)), - row.precision("additional_amount"), - ) - amount = ( - flt( - (flt(row.default_amount) * flt(self.payment_days) / cint(self.total_working_days)), - row.precision("amount"), - ) - + additional_amount - ) - - elif ( - not self.payment_days - and row.salary_component != timesheet_component - and cint(row.depends_on_payment_days) - ): - amount, additional_amount = 0, 0 - elif not row.amount: - amount = flt(row.default_amount) + flt(row.additional_amount) - - # apply rounding - if frappe.get_cached_value( - "Salary Component", row.salary_component, "round_to_the_nearest_integer" - ): - amount, additional_amount = rounded(amount or 0), rounded(additional_amount or 0) - - return amount, additional_amount - - def calculate_unclaimed_taxable_benefits(self, payroll_period): - # get total sum of benefits paid - total_benefits_paid = flt( - frappe.db.sql( - """ - select sum(sd.amount) - from `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name - where - sd.parentfield='earnings' - and sd.is_tax_applicable=1 - and is_flexible_benefit=1 - and ss.docstatus=1 - and ss.employee=%(employee)s - and ss.start_date between %(start_date)s and %(end_date)s - and ss.end_date between %(start_date)s and %(end_date)s - """, - { - "employee": self.employee, - "start_date": payroll_period.start_date, - "end_date": self.start_date, - }, - )[0][0] - ) - - # get total benefits claimed - total_benefits_claimed = flt( - frappe.db.sql( - """ - select sum(claimed_amount) - from `tabEmployee Benefit Claim` - where - docstatus=1 - and employee=%s - and claim_date between %s and %s - """, - (self.employee, payroll_period.start_date, self.end_date), - )[0][0] - ) - - return total_benefits_paid - total_benefits_claimed - - def get_total_exemption_amount(self, payroll_period, tax_slab): - total_exemption_amount = 0 - if tax_slab.allow_tax_exemption: - if self.deduct_tax_for_unsubmitted_tax_exemption_proof: - exemption_proof = frappe.db.get_value( - "Employee Tax Exemption Proof Submission", - {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, - ["exemption_amount"], - ) - if exemption_proof: - total_exemption_amount = exemption_proof - else: - declaration = frappe.db.get_value( - "Employee Tax Exemption Declaration", - {"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1}, - ["total_exemption_amount"], - ) - if declaration: - total_exemption_amount = declaration - - total_exemption_amount += flt(tax_slab.standard_tax_exemption_amount) - - return total_exemption_amount - - def get_income_form_other_sources(self, payroll_period): - return frappe.get_all( - "Employee Other Income", - filters={ - "employee": self.employee, - "payroll_period": payroll_period.name, - "company": self.company, - "docstatus": 1, - }, - fields="SUM(amount) as total_amount", - )[0].total_amount - - def get_component_totals(self, component_type, depends_on_payment_days=0): - joining_date, relieving_date = frappe.get_cached_value( - "Employee", self.employee, ["date_of_joining", "relieving_date"] - ) - - total = 0.0 - for d in self.get(component_type): - if not d.do_not_include_in_total: - if depends_on_payment_days: - amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0] - else: - amount = flt(d.amount, d.precision("amount")) - total += amount - return total - - def get_joining_and_relieving_dates(self): - joining_date, relieving_date = frappe.get_cached_value( - "Employee", self.employee, ["date_of_joining", "relieving_date"] - ) - - if not relieving_date: - relieving_date = getdate(self.end_date) - - if not joining_date: - frappe.throw( - _("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)) - ) - - return joining_date, relieving_date - - def set_loan_repayment(self): - self.total_loan_repayment = 0 - self.total_interest_amount = 0 - self.total_principal_amount = 0 - - if not self.get("loans"): - for loan in self.get_loan_details(): - - amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment") - - if amounts["interest_amount"] or amounts["payable_principal_amount"]: - self.append( - "loans", - { - "loan": loan.name, - "total_payment": amounts["interest_amount"] + amounts["payable_principal_amount"], - "interest_amount": amounts["interest_amount"], - "principal_amount": amounts["payable_principal_amount"], - "loan_account": loan.loan_account, - "interest_income_account": loan.interest_income_account, - }, - ) - - for payment in self.get("loans"): - amounts = calculate_amounts(payment.loan, self.posting_date, "Regular Payment") - total_amount = amounts["interest_amount"] + amounts["payable_principal_amount"] - if payment.total_payment > total_amount: - frappe.throw( - _( - """Row {0}: Paid amount {1} is greater than pending accrued amount {2} against loan {3}""" - ).format( - payment.idx, - frappe.bold(payment.total_payment), - frappe.bold(total_amount), - frappe.bold(payment.loan), - ) - ) - - self.total_interest_amount += payment.interest_amount - self.total_principal_amount += payment.principal_amount - - self.total_loan_repayment += payment.total_payment - - def get_loan_details(self): - loan_details = frappe.get_all( - "Loan", - fields=["name", "interest_income_account", "loan_account", "loan_type", "is_term_loan"], - filters={ - "applicant": self.employee, - "docstatus": 1, - "repay_from_salary": 1, - "company": self.company, - }, - ) - - if loan_details: - for loan in loan_details: - if loan.is_term_loan: - process_loan_interest_accrual_for_term_loans( - posting_date=self.posting_date, loan_type=loan.loan_type, loan=loan.name - ) - - return loan_details - - def make_loan_repayment_entry(self): - payroll_payable_account = get_payroll_payable_account(self.company, self.payroll_entry) - for loan in self.loans: - if loan.total_payment: - repayment_entry = create_repayment_entry( - loan.loan, - self.employee, - self.company, - self.posting_date, - loan.loan_type, - "Regular Payment", - loan.interest_amount, - loan.principal_amount, - loan.total_payment, - payroll_payable_account=payroll_payable_account, - ) - - repayment_entry.save() - repayment_entry.submit() - - frappe.db.set_value( - "Salary Slip Loan", loan.name, "loan_repayment_entry", repayment_entry.name - ) - - def cancel_loan_repayment_entry(self): - for loan in self.loans: - if loan.loan_repayment_entry: - repayment_entry = frappe.get_doc("Loan Repayment", loan.loan_repayment_entry) - repayment_entry.cancel() - - def email_salary_slip(self): - receiver = frappe.db.get_value("Employee", self.employee, "prefered_email") - payroll_settings = frappe.get_single("Payroll Settings") - message = "Please see attachment" - password = None - if payroll_settings.encrypt_salary_slips_in_emails: - password = generate_password_for_pdf(payroll_settings.password_policy, self.employee) - message += """
Note: Your salary slip is password protected, - the password to unlock the PDF is of the format {0}. """.format( - payroll_settings.password_policy - ) - - if receiver: - email_args = { - "recipients": [receiver], - "message": _(message), - "subject": "Salary Slip - from {0} to {1}".format(self.start_date, self.end_date), - "attachments": [ - frappe.attach_print(self.doctype, self.name, file_name=self.name, password=password) - ], - "reference_doctype": self.doctype, - "reference_name": self.name, - } - if not frappe.flags.in_test: - enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args) - else: - frappe.sendmail(**email_args) - else: - msgprint(_("{0}: Employee email not found, hence email not sent").format(self.employee_name)) - - def update_status(self, salary_slip=None): - for data in self.timesheets: - if data.time_sheet: - timesheet = frappe.get_doc("Timesheet", data.time_sheet) - timesheet.salary_slip = salary_slip - timesheet.flags.ignore_validate_update_after_submit = True - timesheet.set_status() - timesheet.save() - - def set_status(self, status=None): - """Get and update status""" - if not status: - status = self.get_status() - self.db_set("status", status) - - def process_salary_structure(self, for_preview=0): - """Calculate salary after salary structure details have been updated""" - if not self.salary_slip_based_on_timesheet: - self.get_date_details() - self.pull_emp_details() - self.get_working_days_details(for_preview=for_preview) - self.calculate_net_pay() - - def pull_emp_details(self): - emp = frappe.db.get_value( - "Employee", self.employee, ["bank_name", "bank_ac_no", "salary_mode"], as_dict=1 - ) - if emp: - self.mode_of_payment = emp.salary_mode - self.bank_name = emp.bank_name - self.bank_account_no = emp.bank_ac_no - - @frappe.whitelist() - def process_salary_based_on_working_days(self): - self.get_working_days_details(lwp=self.leave_without_pay) - self.calculate_net_pay() - - @frappe.whitelist() - def set_totals(self): - self.gross_pay = 0.0 - if self.salary_slip_based_on_timesheet == 1: - self.calculate_total_for_salary_slip_based_on_timesheet() - else: - self.total_deduction = 0.0 - if hasattr(self, "earnings"): - for earning in self.earnings: - self.gross_pay += flt(earning.amount, earning.precision("amount")) - if hasattr(self, "deductions"): - for deduction in self.deductions: - self.total_deduction += flt(deduction.amount, deduction.precision("amount")) - self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) - flt(self.total_loan_repayment) - self.set_base_totals() - - def set_base_totals(self): - self.base_gross_pay = flt(self.gross_pay) * flt(self.exchange_rate) - self.base_total_deduction = flt(self.total_deduction) * flt(self.exchange_rate) - self.rounded_total = rounded(self.net_pay or 0) - self.base_net_pay = flt(self.net_pay) * flt(self.exchange_rate) - self.base_rounded_total = rounded(self.base_net_pay or 0) - self.set_net_total_in_words() - - # calculate total working hours, earnings based on hourly wages and totals - def calculate_total_for_salary_slip_based_on_timesheet(self): - if self.timesheets: - self.total_working_hours = 0 - for timesheet in self.timesheets: - if timesheet.working_hours: - self.total_working_hours += timesheet.working_hours - - wages_amount = self.total_working_hours * self.hour_rate - self.base_hour_rate = flt(self.hour_rate) * flt(self.exchange_rate) - salary_component = frappe.db.get_value( - "Salary Structure", {"name": self.salary_structure}, "salary_component" - ) - if self.earnings: - for i, earning in enumerate(self.earnings): - if earning.salary_component == salary_component: - self.earnings[i].amount = wages_amount - self.gross_pay += flt(self.earnings[i].amount, earning.precision("amount")) - self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) - - def compute_year_to_date(self): - year_to_date = 0 - period_start_date, period_end_date = self.get_year_to_date_period() - - salary_slip_sum = frappe.get_list( - "Salary Slip", - fields=["sum(net_pay) as net_sum", "sum(gross_pay) as gross_sum"], - filters={ - "employee": self.employee, - "start_date": [">=", period_start_date], - "end_date": ["<", period_end_date], - "name": ["!=", self.name], - "docstatus": 1, - }, - ) - - year_to_date = flt(salary_slip_sum[0].net_sum) if salary_slip_sum else 0.0 - gross_year_to_date = flt(salary_slip_sum[0].gross_sum) if salary_slip_sum else 0.0 - - year_to_date += self.net_pay - gross_year_to_date += self.gross_pay - self.year_to_date = year_to_date - self.gross_year_to_date = gross_year_to_date - - def compute_month_to_date(self): - month_to_date = 0 - first_day_of_the_month = get_first_day(self.start_date) - salary_slip_sum = frappe.get_list( - "Salary Slip", - fields=["sum(net_pay) as sum"], - filters={ - "employee": self.employee, - "start_date": [">=", first_day_of_the_month], - "end_date": ["<", self.start_date], - "name": ["!=", self.name], - "docstatus": 1, - }, - ) - - month_to_date = flt(salary_slip_sum[0].sum) if salary_slip_sum else 0.0 - - month_to_date += self.net_pay - self.month_to_date = month_to_date - - def compute_component_wise_year_to_date(self): - period_start_date, period_end_date = self.get_year_to_date_period() - - for key in ("earnings", "deductions"): - for component in self.get(key): - year_to_date = 0 - component_sum = frappe.db.sql( - """ - SELECT sum(detail.amount) as sum - FROM `tabSalary Detail` as detail - INNER JOIN `tabSalary Slip` as salary_slip - ON detail.parent = salary_slip.name - WHERE - salary_slip.employee = %(employee)s - AND detail.salary_component = %(component)s - AND salary_slip.start_date >= %(period_start_date)s - AND salary_slip.end_date < %(period_end_date)s - AND salary_slip.name != %(docname)s - AND salary_slip.docstatus = 1""", - { - "employee": self.employee, - "component": component.salary_component, - "period_start_date": period_start_date, - "period_end_date": period_end_date, - "docname": self.name, - }, - ) - - year_to_date = flt(component_sum[0][0]) if component_sum else 0.0 - year_to_date += component.amount - component.year_to_date = year_to_date - - def get_year_to_date_period(self): - payroll_period = get_payroll_period(self.start_date, self.end_date, self.company) - - if payroll_period: - period_start_date = payroll_period.start_date - period_end_date = payroll_period.end_date - else: - # get dates based on fiscal year if no payroll period exists - fiscal_year = get_fiscal_year(date=self.start_date, company=self.company, as_dict=1) - period_start_date = fiscal_year.year_start_date - period_end_date = fiscal_year.year_end_date - - return period_start_date, period_end_date - - def add_leave_balances(self): - self.set("leave_details", []) - - if frappe.db.get_single_value("Payroll Settings", "show_leave_balances_in_salary_slip"): - from erpnext.hr.doctype.leave_application.leave_application import get_leave_details - - leave_details = get_leave_details(self.employee, self.end_date) - - for leave_type, leave_values in leave_details["leave_allocation"].items(): - self.append( - "leave_details", - { - "leave_type": leave_type, - "total_allocated_leaves": flt(leave_values.get("total_leaves")), - "expired_leaves": flt(leave_values.get("expired_leaves")), - "used_leaves": flt(leave_values.get("leaves_taken")), - "pending_leaves": flt(leave_values.get("leaves_pending_approval")), - "available_leaves": flt(leave_values.get("remaining_leaves")), - }, - ) - - -def unlink_ref_doc_from_salary_slip(ref_no): - linked_ss = frappe.db.sql_list( - """select name from `tabSalary Slip` - where journal_entry=%s and docstatus < 2""", - (ref_no), - ) - if linked_ss: - for ss in linked_ss: - ss_doc = frappe.get_doc("Salary Slip", ss) - frappe.db.set_value("Salary Slip", ss_doc.name, "journal_entry", "") - - -def generate_password_for_pdf(policy_template, employee): - employee = frappe.get_doc("Employee", employee) - return policy_template.format(**employee.as_dict()) - - -def get_salary_component_data(component): - return frappe.get_value( - "Salary Component", - component, - [ - "name as salary_component", - "depends_on_payment_days", - "salary_component_abbr as abbr", - "do_not_include_in_total", - "is_tax_applicable", - "is_flexible_benefit", - "variable_based_on_taxable_salary", - ], - as_dict=1, - ) - - -def get_payroll_payable_account(company, payroll_entry): - if payroll_entry: - payroll_payable_account = frappe.db.get_value( - "Payroll Entry", payroll_entry, "payroll_payable_account" - ) - else: - payroll_payable_account = frappe.db.get_value( - "Company", company, "default_payroll_payable_account" - ) - - return payroll_payable_account - - -def calculate_tax_by_tax_slab( - annual_taxable_earning, tax_slab, eval_globals=None, eval_locals=None -): - eval_locals.update({"annual_taxable_earning": annual_taxable_earning}) - tax_amount = 0 - for slab in tax_slab.slabs: - cond = cstr(slab.condition).strip() - if cond and not eval_tax_slab_condition(cond, eval_globals, eval_locals): - continue - if not slab.to_amount and annual_taxable_earning >= slab.from_amount: - tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction * 0.01 - continue - if annual_taxable_earning >= slab.from_amount and annual_taxable_earning < slab.to_amount: - tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction * 0.01 - elif annual_taxable_earning >= slab.from_amount and annual_taxable_earning >= slab.to_amount: - tax_amount += (slab.to_amount - slab.from_amount + 1) * slab.percent_deduction * 0.01 - - # other taxes and charges on income tax - for d in tax_slab.other_taxes_and_charges: - if flt(d.min_taxable_income) and flt(d.min_taxable_income) > annual_taxable_earning: - continue - - if flt(d.max_taxable_income) and flt(d.max_taxable_income) < annual_taxable_earning: - continue - - tax_amount += tax_amount * flt(d.percent) / 100 - - return tax_amount - - -def eval_tax_slab_condition(condition, eval_globals=None, eval_locals=None): - if not eval_globals: - eval_globals = { - "int": int, - "float": float, - "long": int, - "round": round, - "date": datetime.date, - "getdate": getdate, - } - - try: - condition = condition.strip() - if condition: - return frappe.safe_eval(condition, eval_globals, eval_locals) - except NameError as err: - frappe.throw( - _("{0}
This error can be due to missing or deleted field.").format(err), - title=_("Name error"), - ) - except SyntaxError as err: - frappe.throw(_("Syntax error in condition: {0} in Income Tax Slab").format(err)) - except Exception as e: - frappe.throw(_("Error in formula or condition: {0} in Income Tax Slab").format(e)) - raise - - -def get_lwp_or_ppl_for_date(date, employee, holidays): - LeaveApplication = frappe.qb.DocType("Leave Application") - LeaveType = frappe.qb.DocType("Leave Type") - - is_half_day = ( - frappe.qb.terms.Case() - .when( - ( - (LeaveApplication.half_day_date == date) - | (LeaveApplication.from_date == LeaveApplication.to_date) - ), - LeaveApplication.half_day, - ) - .else_(0) - ).as_("is_half_day") - - query = ( - frappe.qb.from_(LeaveApplication) - .inner_join(LeaveType) - .on((LeaveType.name == LeaveApplication.leave_type)) - .select( - LeaveApplication.name, - LeaveType.is_ppl, - LeaveType.fraction_of_daily_salary_per_leave, - (is_half_day), - ) - .where( - (((LeaveType.is_lwp == 1) | (LeaveType.is_ppl == 1))) - & (LeaveApplication.docstatus == 1) - & (LeaveApplication.status == "Approved") - & (LeaveApplication.employee == employee) - & ((LeaveApplication.salary_slip.isnull()) | (LeaveApplication.salary_slip == "")) - & ((LeaveApplication.from_date <= date) & (date <= LeaveApplication.to_date)) - ) - ) - - # if it's a holiday only include if leave type has "include holiday" enabled - if date in holidays: - query = query.where((LeaveType.include_holiday == "1")) - - return query.run(as_dict=True) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip_list.js b/erpnext/payroll/doctype/salary_slip/salary_slip_list.js deleted file mode 100644 index 33d5bd786f..0000000000 --- a/erpnext/payroll/doctype/salary_slip/salary_slip_list.js +++ /dev/null @@ -1,3 +0,0 @@ -frappe.listview_settings['Salary Slip'] = { - add_fields: ["employee", "employee_name"], -}; diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py deleted file mode 100644 index a8b6bb5714..0000000000 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ /dev/null @@ -1,1556 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import calendar -import random -import unittest - -import frappe -from frappe.model.document import Document -from frappe.tests.utils import change_settings -from frappe.utils import ( - add_days, - add_months, - cstr, - date_diff, - flt, - get_first_day, - get_last_day, - getdate, - nowdate, -) -from frappe.utils.make_random import get_random - -import erpnext -from erpnext.accounts.utils import get_fiscal_year -from erpnext.hr.doctype.attendance.attendance import mark_attendance -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation -from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type -from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import ( - create_exemption_category, - create_payroll_period, -) -from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details -from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip - - -class TestSalarySlip(unittest.TestCase): - def setUp(self): - setup_test() - frappe.flags.pop("via_payroll_entry", None) - - def tearDown(self): - frappe.db.rollback() - frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) - frappe.set_user("Administrator") - - @change_settings( - "Payroll Settings", {"payroll_based_on": "Attendance", "daily_wages_fraction_for_half_day": 0.75} - ) - def test_payment_days_based_on_attendance(self): - no_of_days = get_no_of_days() - - emp_id = make_employee("test_payment_days_based_on_attendance@salary.com") - frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"}) - - frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0) - - month_start_date = get_first_day(nowdate()) - month_end_date = get_last_day(nowdate()) - - first_sunday = frappe.db.sql( - """ - select holiday_date from `tabHoliday` - where parent = 'Salary Slip Test Holiday List' - and holiday_date between %s and %s - order by holiday_date - """, - (month_start_date, month_end_date), - )[0][0] - - mark_attendance(emp_id, first_sunday, "Absent", ignore_validate=True) # invalid lwp - mark_attendance( - emp_id, add_days(first_sunday, 1), "Absent", ignore_validate=True - ) # counted as absent - mark_attendance( - emp_id, - add_days(first_sunday, 2), - "Half Day", - leave_type="Leave Without Pay", - ignore_validate=True, - ) # valid 0.75 lwp - mark_attendance( - emp_id, - add_days(first_sunday, 3), - "On Leave", - leave_type="Leave Without Pay", - ignore_validate=True, - ) # valid lwp - mark_attendance( - emp_id, add_days(first_sunday, 4), "On Leave", leave_type="Casual Leave", ignore_validate=True - ) # invalid lwp - mark_attendance( - emp_id, - add_days(first_sunday, 7), - "On Leave", - leave_type="Leave Without Pay", - ignore_validate=True, - ) # invalid lwp - - ss = make_employee_salary_slip( - "test_payment_days_based_on_attendance@salary.com", - "Monthly", - "Test Payment Based On Attendence", - ) - - self.assertEqual(ss.leave_without_pay, 1.25) - self.assertEqual(ss.absent_days, 1) - - days_in_month = no_of_days[0] - no_of_holidays = no_of_days[1] - - self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 2.25) - - # Gross pay calculation based on attendances - gross_pay = 78000 - ( - (78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay + ss.absent_days) - ) - - self.assertEqual(ss.gross_pay, gross_pay) - - @change_settings( - "Payroll Settings", - { - "payroll_based_on": "Attendance", - "consider_unmarked_attendance_as": "Absent", - "include_holidays_in_total_working_days": True, - }, - ) - def test_payment_days_for_mid_joinee_including_holidays(self): - no_of_days = get_no_of_days() - month_start_date, month_end_date = get_first_day(nowdate()), get_last_day(nowdate()) - - new_emp_id = make_employee("test_payment_days_based_on_joining_date@salary.com") - joining_date, relieving_date = add_days(month_start_date, 3), add_days(month_end_date, -5) - - for days in range(date_diff(month_end_date, month_start_date) + 1): - date = add_days(month_start_date, days) - mark_attendance(new_emp_id, date, "Present", ignore_validate=True) - - # Case 1: relieving in mid month - frappe.db.set_value( - "Employee", - new_emp_id, - {"date_of_joining": month_start_date, "relieving_date": relieving_date, "status": "Active"}, - ) - - new_ss = make_employee_salary_slip( - "test_payment_days_based_on_joining_date@salary.com", - "Monthly", - "Test Payment Based On Attendence", - ) - self.assertEqual(new_ss.payment_days, no_of_days[0] - 5) - - # Case 2: joining in mid month - frappe.db.set_value( - "Employee", - new_emp_id, - {"date_of_joining": joining_date, "relieving_date": month_end_date, "status": "Active"}, - ) - - frappe.delete_doc("Salary Slip", new_ss.name, force=True) - new_ss = make_employee_salary_slip( - "test_payment_days_based_on_joining_date@salary.com", - "Monthly", - "Test Payment Based On Attendence", - ) - self.assertEqual(new_ss.payment_days, no_of_days[0] - 3) - - # Case 3: joining and relieving in mid-month - frappe.db.set_value( - "Employee", - new_emp_id, - {"date_of_joining": joining_date, "relieving_date": relieving_date, "status": "Left"}, - ) - - frappe.delete_doc("Salary Slip", new_ss.name, force=True) - new_ss = make_employee_salary_slip( - "test_payment_days_based_on_joining_date@salary.com", - "Monthly", - "Test Payment Based On Attendence", - ) - - self.assertEqual(new_ss.total_working_days, no_of_days[0]) - self.assertEqual(new_ss.payment_days, no_of_days[0] - 8) - - @change_settings( - "Payroll Settings", - { - "payroll_based_on": "Attendance", - "consider_unmarked_attendance_as": "Absent", - "include_holidays_in_total_working_days": True, - }, - ) - def test_payment_days_for_mid_joinee_including_holidays_and_unmarked_days(self): - # tests mid month joining and relieving along with unmarked days - from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday - - no_of_days = get_no_of_days() - month_start_date, month_end_date = get_first_day(nowdate()), get_last_day(nowdate()) - - new_emp_id = make_employee("test_payment_days_based_on_joining_date@salary.com") - joining_date, relieving_date = add_days(month_start_date, 3), add_days(month_end_date, -5) - holidays = 0 - - for days in range(date_diff(relieving_date, joining_date) + 1): - date = add_days(joining_date, days) - if not is_holiday("Salary Slip Test Holiday List", date): - mark_attendance(new_emp_id, date, "Present", ignore_validate=True) - else: - holidays += 1 - - frappe.db.set_value( - "Employee", - new_emp_id, - {"date_of_joining": joining_date, "relieving_date": relieving_date, "status": "Left"}, - ) - - new_ss = make_employee_salary_slip( - "test_payment_days_based_on_joining_date@salary.com", - "Monthly", - "Test Payment Based On Attendence", - ) - - self.assertEqual(new_ss.total_working_days, no_of_days[0]) - self.assertEqual(new_ss.payment_days, no_of_days[0] - holidays - 8) - - @change_settings( - "Payroll Settings", - { - "payroll_based_on": "Attendance", - "consider_unmarked_attendance_as": "Absent", - "include_holidays_in_total_working_days": False, - }, - ) - def test_payment_days_for_mid_joinee_excluding_holidays(self): - from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday - - no_of_days = get_no_of_days() - month_start_date, month_end_date = get_first_day(nowdate()), get_last_day(nowdate()) - - new_emp_id = make_employee("test_payment_days_based_on_joining_date@salary.com") - joining_date, relieving_date = add_days(month_start_date, 3), add_days(month_end_date, -5) - frappe.db.set_value( - "Employee", - new_emp_id, - {"date_of_joining": joining_date, "relieving_date": relieving_date, "status": "Left"}, - ) - - holidays = 0 - - for days in range(date_diff(relieving_date, joining_date) + 1): - date = add_days(joining_date, days) - if not is_holiday("Salary Slip Test Holiday List", date): - mark_attendance(new_emp_id, date, "Present", ignore_validate=True) - else: - holidays += 1 - - new_ss = make_employee_salary_slip( - "test_payment_days_based_on_joining_date@salary.com", - "Monthly", - "Test Payment Based On Attendence", - ) - - self.assertEqual(new_ss.total_working_days, no_of_days[0] - no_of_days[1]) - self.assertEqual(new_ss.payment_days, no_of_days[0] - holidays - 8) - - @change_settings("Payroll Settings", {"payroll_based_on": "Leave"}) - def test_payment_days_based_on_leave_application(self): - no_of_days = get_no_of_days() - - emp_id = make_employee("test_payment_days_based_on_leave_application@salary.com") - frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"}) - - frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0) - - month_start_date = get_first_day(nowdate()) - month_end_date = get_last_day(nowdate()) - - first_sunday = frappe.db.sql( - """ - select holiday_date from `tabHoliday` - where parent = 'Salary Slip Test Holiday List' - and holiday_date between %s and %s - order by holiday_date - """, - (month_start_date, month_end_date), - )[0][0] - - make_leave_application(emp_id, first_sunday, add_days(first_sunday, 3), "Leave Without Pay") - - leave_type_ppl = create_leave_type(leave_type_name="Test Partially Paid Leave", is_ppl=1) - leave_type_ppl.save() - - alloc = create_leave_allocation( - employee=emp_id, - from_date=add_days(first_sunday, 4), - to_date=add_days(first_sunday, 10), - new_leaves_allocated=3, - leave_type="Test Partially Paid Leave", - ) - alloc.save() - alloc.submit() - - # two day leave ppl with fraction_of_daily_salary_per_leave = 0.5 equivalent to single day lwp - make_leave_application( - emp_id, add_days(first_sunday, 4), add_days(first_sunday, 5), "Test Partially Paid Leave" - ) - - ss = make_employee_salary_slip( - "test_payment_days_based_on_leave_application@salary.com", - "Monthly", - "Test Payment Based On Leave Application", - ) - - self.assertEqual(ss.leave_without_pay, 4) - - days_in_month = no_of_days[0] - no_of_holidays = no_of_days[1] - - self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 4) - - @change_settings("Payroll Settings", {"payroll_based_on": "Attendance"}) - def test_payment_days_in_salary_slip_based_on_timesheet(self): - from erpnext.hr.doctype.attendance.attendance import mark_attendance - from erpnext.projects.doctype.timesheet.test_timesheet import ( - make_salary_structure_for_timesheet, - make_timesheet, - ) - from erpnext.projects.doctype.timesheet.timesheet import ( - make_salary_slip as make_salary_slip_for_timesheet, - ) - - emp = make_employee( - "test_employee_timesheet@salary.com", - company="_Test Company", - holiday_list="Salary Slip Test Holiday List", - ) - frappe.db.set_value("Employee", emp, {"relieving_date": None, "status": "Active"}) - - # mark attendance - month_start_date = get_first_day(nowdate()) - month_end_date = get_last_day(nowdate()) - - first_sunday = frappe.db.sql( - """ - select holiday_date from `tabHoliday` - where parent = 'Salary Slip Test Holiday List' - and holiday_date between %s and %s - order by holiday_date - """, - (month_start_date, month_end_date), - )[0][0] - - mark_attendance( - emp, add_days(first_sunday, 1), "Absent", ignore_validate=True - ) # counted as absent - - # salary structure based on timesheet - make_salary_structure_for_timesheet(emp) - timesheet = make_timesheet(emp, simulate=True, is_billable=1) - salary_slip = make_salary_slip_for_timesheet(timesheet.name) - salary_slip.start_date = month_start_date - salary_slip.end_date = month_end_date - salary_slip.save() - salary_slip.submit() - salary_slip.reload() - - no_of_days = get_no_of_days() - days_in_month = no_of_days[0] - no_of_holidays = no_of_days[1] - - self.assertEqual(salary_slip.payment_days, days_in_month - no_of_holidays - 1) - - # gross pay calculation based on attendance (payment days) - gross_pay = 78100 - ( - (78000 / (days_in_month - no_of_holidays)) - * flt(salary_slip.leave_without_pay + salary_slip.absent_days) - ) - - self.assertEqual(salary_slip.gross_pay, flt(gross_pay, 2)) - - @change_settings("Payroll Settings", {"payroll_based_on": "Attendance"}) - def test_component_amount_dependent_on_another_payment_days_based_component(self): - from erpnext.hr.doctype.attendance.attendance import mark_attendance - from erpnext.payroll.doctype.salary_structure.test_salary_structure import ( - create_salary_structure_assignment, - ) - - salary_structure = make_salary_structure_for_payment_days_based_component_dependency() - employee = make_employee("test_payment_days_based_component@salary.com", company="_Test Company") - - # base = 50000 - create_salary_structure_assignment( - employee, salary_structure.name, company="_Test Company", currency="INR" - ) - - # mark employee absent for a day since this case works fine if payment days are equal to working days - month_start_date = get_first_day(nowdate()) - month_end_date = get_last_day(nowdate()) - - first_sunday = frappe.db.sql( - """ - select holiday_date from `tabHoliday` - where parent = 'Salary Slip Test Holiday List' - and holiday_date between %s and %s - order by holiday_date - """, - (month_start_date, month_end_date), - )[0][0] - - mark_attendance( - employee, add_days(first_sunday, 1), "Absent", ignore_validate=True - ) # counted as absent - - # make salary slip and assert payment days - ss = make_salary_slip_for_payment_days_dependency_test( - "test_payment_days_based_component@salary.com", salary_structure.name - ) - self.assertEqual(ss.absent_days, 1) - - ss.reload() - payment_days_based_comp_amount = 0 - for component in ss.earnings: - if component.salary_component == "HRA - Payment Days": - payment_days_based_comp_amount = flt(component.amount, component.precision("amount")) - break - - # check if the dependent component is calculated using the amount updated after payment days - actual_amount = 0 - precision = 0 - for component in ss.deductions: - if component.salary_component == "P - Employee Provident Fund": - precision = component.precision("amount") - actual_amount = flt(component.amount, precision) - break - - expected_amount = flt((flt(ss.gross_pay) - payment_days_based_comp_amount) * 0.12, precision) - - self.assertEqual(actual_amount, expected_amount) - - @change_settings("Payroll Settings", {"include_holidays_in_total_working_days": 1}) - def test_salary_slip_with_holidays_included(self): - no_of_days = get_no_of_days() - make_employee("test_salary_slip_with_holidays_included@salary.com") - frappe.db.set_value( - "Employee", - frappe.get_value( - "Employee", {"employee_name": "test_salary_slip_with_holidays_included@salary.com"}, "name" - ), - "relieving_date", - None, - ) - frappe.db.set_value( - "Employee", - frappe.get_value( - "Employee", {"employee_name": "test_salary_slip_with_holidays_included@salary.com"}, "name" - ), - "status", - "Active", - ) - ss = make_employee_salary_slip( - "test_salary_slip_with_holidays_included@salary.com", - "Monthly", - "Test Salary Slip With Holidays Included", - ) - - self.assertEqual(ss.total_working_days, no_of_days[0]) - self.assertEqual(ss.payment_days, no_of_days[0]) - self.assertEqual(ss.earnings[0].amount, 50000) - self.assertEqual(ss.earnings[1].amount, 3000) - self.assertEqual(ss.gross_pay, 78000) - - @change_settings("Payroll Settings", {"include_holidays_in_total_working_days": 0}) - def test_salary_slip_with_holidays_excluded(self): - no_of_days = get_no_of_days() - make_employee("test_salary_slip_with_holidays_excluded@salary.com") - frappe.db.set_value( - "Employee", - frappe.get_value( - "Employee", {"employee_name": "test_salary_slip_with_holidays_excluded@salary.com"}, "name" - ), - "relieving_date", - None, - ) - frappe.db.set_value( - "Employee", - frappe.get_value( - "Employee", {"employee_name": "test_salary_slip_with_holidays_excluded@salary.com"}, "name" - ), - "status", - "Active", - ) - ss = make_employee_salary_slip( - "test_salary_slip_with_holidays_excluded@salary.com", - "Monthly", - "Test Salary Slip With Holidays Excluded", - ) - - self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1]) - self.assertEqual(ss.payment_days, no_of_days[0] - no_of_days[1]) - self.assertEqual(ss.earnings[0].amount, 50000) - self.assertEqual(ss.earnings[0].default_amount, 50000) - self.assertEqual(ss.earnings[1].amount, 3000) - self.assertEqual(ss.gross_pay, 78000) - - @change_settings("Payroll Settings", {"include_holidays_in_total_working_days": 1}) - def test_payment_days(self): - from erpnext.payroll.doctype.salary_structure.test_salary_structure import ( - create_salary_structure_assignment, - ) - - no_of_days = get_no_of_days() - - # set joinng date in the same month - employee = make_employee("test_payment_days@salary.com") - if getdate(nowdate()).day >= 15: - relieving_date = getdate(add_days(nowdate(), -10)) - date_of_joining = getdate(add_days(nowdate(), -10)) - elif getdate(nowdate()).day < 15 and getdate(nowdate()).day >= 5: - date_of_joining = getdate(add_days(nowdate(), -3)) - relieving_date = getdate(add_days(nowdate(), -3)) - elif getdate(nowdate()).day < 5 and not getdate(nowdate()).day == 1: - date_of_joining = getdate(add_days(nowdate(), -1)) - relieving_date = getdate(add_days(nowdate(), -1)) - elif getdate(nowdate()).day == 1: - date_of_joining = getdate(nowdate()) - relieving_date = getdate(nowdate()) - - frappe.db.set_value( - "Employee", - employee, - {"date_of_joining": date_of_joining, "relieving_date": None, "status": "Active"}, - ) - - salary_structure = "Test Payment Days" - ss = make_employee_salary_slip("test_payment_days@salary.com", "Monthly", salary_structure) - - self.assertEqual(ss.total_working_days, no_of_days[0]) - self.assertEqual(ss.payment_days, (no_of_days[0] - getdate(date_of_joining).day + 1)) - - # set relieving date in the same month - frappe.db.set_value( - "Employee", - employee, - { - "date_of_joining": add_days(nowdate(), -60), - "relieving_date": relieving_date, - "status": "Left", - }, - ) - - if date_of_joining.day > 1: - self.assertRaises(frappe.ValidationError, ss.save) - - create_salary_structure_assignment(employee, salary_structure) - ss.reload() - ss.save() - - self.assertEqual(ss.total_working_days, no_of_days[0]) - self.assertEqual(ss.payment_days, getdate(relieving_date).day) - - frappe.db.set_value( - "Employee", - frappe.get_value("Employee", {"employee_name": "test_payment_days@salary.com"}, "name"), - "relieving_date", - None, - ) - frappe.db.set_value( - "Employee", - frappe.get_value("Employee", {"employee_name": "test_payment_days@salary.com"}, "name"), - "status", - "Active", - ) - - def test_employee_salary_slip_read_permission(self): - make_employee("test_employee_salary_slip_read_permission@salary.com") - - salary_slip_test_employee = make_employee_salary_slip( - "test_employee_salary_slip_read_permission@salary.com", - "Monthly", - "Test Employee Salary Slip Read Permission", - ) - frappe.set_user("test_employee_salary_slip_read_permission@salary.com") - self.assertTrue(salary_slip_test_employee.has_permission("read")) - - @change_settings("Payroll Settings", {"email_salary_slip_to_employee": 1}) - def test_email_salary_slip(self): - frappe.db.delete("Email Queue") - - user_id = "test_email_salary_slip@salary.com" - - make_employee(user_id, company="_Test Company") - ss = make_employee_salary_slip(user_id, "Monthly", "Test Salary Slip Email") - ss.company = "_Test Company" - ss.save() - ss.submit() - - email_queue = frappe.db.a_row_exists("Email Queue") - self.assertTrue(email_queue) - - def test_loan_repayment_salary_slip(self): - from erpnext.loan_management.doctype.loan.test_loan import ( - create_loan, - create_loan_accounts, - create_loan_type, - make_loan_disbursement_entry, - ) - from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import ( - process_loan_interest_accrual_for_term_loans, - ) - from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - - applicant = make_employee("test_loan_repayment_salary_slip@salary.com", company="_Test Company") - - create_loan_accounts() - - create_loan_type( - "Car Loan", - 500000, - 8.4, - is_term_loan=1, - mode_of_payment="Cash", - disbursement_account="Disbursement Account - _TC", - payment_account="Payment Account - _TC", - loan_account="Loan Account - _TC", - interest_income_account="Interest Income Account - _TC", - penalty_income_account="Penalty Income Account - _TC", - ) - - payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company") - - make_salary_structure( - "Test Loan Repayment Salary Structure", - "Monthly", - employee=applicant, - currency="INR", - payroll_period=payroll_period, - ) - - frappe.db.sql( - "delete from tabLoan where applicant = 'test_loan_repayment_salary_slip@salary.com'" - ) - loan = create_loan( - applicant, - "Car Loan", - 11000, - "Repay Over Number of Periods", - 20, - posting_date=add_months(nowdate(), -1), - ) - loan.repay_from_salary = 1 - loan.submit() - - make_loan_disbursement_entry( - loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1) - ) - - process_loan_interest_accrual_for_term_loans(posting_date=nowdate()) - - ss = make_employee_salary_slip( - "test_loan_repayment_salary_slip@salary.com", "Monthly", "Test Loan Repayment Salary Structure" - ) - ss.submit() - - self.assertEqual(ss.total_loan_repayment, 592) - self.assertEqual( - ss.net_pay, (flt(ss.gross_pay) - (flt(ss.total_deduction) + flt(ss.total_loan_repayment))) - ) - - def test_payroll_frequency(self): - fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())[0] - month = "%02d" % getdate(nowdate()).month - m = get_month_details(fiscal_year, month) - - for payroll_frequency in ["Monthly", "Bimonthly", "Fortnightly", "Weekly", "Daily"]: - make_employee(payroll_frequency + "_test_employee@salary.com") - ss = make_employee_salary_slip( - payroll_frequency + "_test_employee@salary.com", - payroll_frequency, - payroll_frequency + "_Test Payroll Frequency", - ) - if payroll_frequency == "Monthly": - self.assertEqual(ss.end_date, m["month_end_date"]) - elif payroll_frequency == "Bimonthly": - if getdate(ss.start_date).day <= 15: - self.assertEqual(ss.end_date, m["month_mid_end_date"]) - else: - self.assertEqual(ss.end_date, m["month_end_date"]) - elif payroll_frequency == "Fortnightly": - self.assertEqual(ss.end_date, add_days(nowdate(), 13)) - elif payroll_frequency == "Weekly": - self.assertEqual(ss.end_date, add_days(nowdate(), 6)) - elif payroll_frequency == "Daily": - self.assertEqual(ss.end_date, nowdate()) - - def test_multi_currency_salary_slip(self): - from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - - applicant = make_employee("test_multi_currency_salary_slip@salary.com", company="_Test Company") - frappe.db.sql( - """delete from `tabSalary Structure` where name='Test Multi Currency Salary Slip'""" - ) - salary_structure = make_salary_structure( - "Test Multi Currency Salary Slip", - "Monthly", - employee=applicant, - company="_Test Company", - currency="USD", - ) - salary_slip = make_salary_slip(salary_structure.name, employee=applicant) - salary_slip.exchange_rate = 70 - salary_slip.calculate_net_pay() - - self.assertEqual(salary_slip.gross_pay, 78000) - self.assertEqual(salary_slip.base_gross_pay, 78000 * 70) - - def test_year_to_date_computation(self): - from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - - applicant = make_employee("test_ytd@salary.com", company="_Test Company") - - payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company") - - create_tax_slab( - payroll_period, - allow_tax_exemption=True, - currency="INR", - effective_date=getdate("2019-04-01"), - company="_Test Company", - ) - - salary_structure = make_salary_structure( - "Monthly Salary Structure Test for Salary Slip YTD", - "Monthly", - employee=applicant, - company="_Test Company", - currency="INR", - payroll_period=payroll_period, - ) - - # clear salary slip for this employee - frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = 'test_ytd@salary.com'") - - create_salary_slips_for_payroll_period( - applicant, salary_structure.name, payroll_period, deduct_random=False, num=6 - ) - - salary_slips = frappe.get_all( - "Salary Slip", - fields=["year_to_date", "net_pay"], - filters={"employee_name": "test_ytd@salary.com"}, - order_by="posting_date", - ) - - year_to_date = 0 - for slip in salary_slips: - year_to_date += flt(slip.net_pay) - self.assertEqual(slip.year_to_date, year_to_date) - - def test_component_wise_year_to_date_computation(self): - from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - - employee_name = "test_component_wise_ytd@salary.com" - applicant = make_employee(employee_name, company="_Test Company") - - payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company") - - create_tax_slab( - payroll_period, - allow_tax_exemption=True, - currency="INR", - effective_date=getdate("2019-04-01"), - company="_Test Company", - ) - - salary_structure = make_salary_structure( - "Monthly Salary Structure Test for Salary Slip YTD", - "Monthly", - employee=applicant, - company="_Test Company", - currency="INR", - payroll_period=payroll_period, - ) - - # clear salary slip for this employee - frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = '%s'" % employee_name) - - create_salary_slips_for_payroll_period( - applicant, salary_structure.name, payroll_period, deduct_random=False, num=3 - ) - - salary_slips = frappe.get_all( - "Salary Slip", - fields=["name"], - filters={"employee_name": employee_name}, - order_by="posting_date", - ) - - year_to_date = dict() - for slip in salary_slips: - doc = frappe.get_doc("Salary Slip", slip.name) - for entry in doc.get("earnings"): - if not year_to_date.get(entry.salary_component): - year_to_date[entry.salary_component] = 0 - - year_to_date[entry.salary_component] += entry.amount - self.assertEqual(year_to_date[entry.salary_component], entry.year_to_date) - - def test_tax_for_payroll_period(self): - data = {} - # test the impact of tax exemption declaration, tax exemption proof submission - # and deduct check boxes in annual tax calculation - # as per assigned salary structure 40500 in monthly salary so 236000*5/100/12 - frappe.db.sql("""delete from `tabPayroll Period`""") - frappe.db.sql("""delete from `tabSalary Component`""") - - payroll_period = create_payroll_period() - - create_tax_slab(payroll_period, allow_tax_exemption=True) - - employee = make_employee("test_tax@salary.slip") - delete_docs = [ - "Salary Slip", - "Additional Salary", - "Employee Tax Exemption Declaration", - "Employee Tax Exemption Proof Submission", - "Employee Benefit Claim", - "Salary Structure Assignment", - ] - for doc in delete_docs: - frappe.db.sql("delete from `tab%s` where employee='%s'" % (doc, employee)) - - from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - - salary_structure = make_salary_structure( - "Stucture to test tax", - "Monthly", - other_details={"max_benefits": 100000}, - test_tax=True, - include_flexi_benefits=True, - employee=employee, - payroll_period=payroll_period, - ) - - # create salary slip for whole period deducting tax only on last period - # to find the total tax amount paid - create_salary_slips_for_payroll_period( - employee, salary_structure.name, payroll_period, deduct_random=False - ) - tax_paid = get_tax_paid_in_period(employee) - - annual_tax = 113589.0 - try: - self.assertEqual(tax_paid, annual_tax) - except AssertionError: - print("\nSalary Slip - Annual tax calculation failed\n") - raise - frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) - - # create exemption declaration so the tax amount varies - create_exemption_declaration(employee, payroll_period.name) - - # create for payroll deducting in random months - data["deducted_dates"] = create_salary_slips_for_payroll_period( - employee, salary_structure.name, payroll_period - ) - tax_paid = get_tax_paid_in_period(employee) - - # No proof, benefit claim sumitted, total tax paid, should not change - try: - self.assertEqual(tax_paid, annual_tax) - except AssertionError: - print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") - raise - - # Submit proof for total 120000 - data["proof"] = create_proof_submission(employee, payroll_period, 120000) - - # Submit benefit claim for total 50000 - data["benefit-1"] = create_benefit_claim(employee, payroll_period, 15000, "Medical Allowance") - data["benefit-2"] = create_benefit_claim( - employee, payroll_period, 35000, "Leave Travel Allowance" - ) - - frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) - data["deducted_dates"] = create_salary_slips_for_payroll_period( - employee, salary_structure.name, payroll_period - ) - tax_paid = get_tax_paid_in_period(employee) - - # total taxable income 416000, 166000 @ 5% ie. 8300 - try: - self.assertEqual(tax_paid, 82389.0) - except AssertionError: - print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") - raise - - # create additional salary of 150000 - frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) - data["additional-1"] = create_additional_salary(employee, payroll_period, 150000) - data["deducted_dates"] = create_salary_slips_for_payroll_period( - employee, salary_structure.name, payroll_period - ) - - # total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200 - tax_paid = get_tax_paid_in_period(employee) - try: - self.assertEqual(tax_paid, annual_tax) - except AssertionError: - print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") - raise - frappe.db.sql("""delete from `tabAdditional Salary` where employee=%s""", (employee)) - - # undelete fixture data - frappe.db.rollback() - - def test_tax_for_recurring_additional_salary(self): - frappe.db.sql("""delete from `tabPayroll Period`""") - frappe.db.sql("""delete from `tabSalary Component`""") - - payroll_period = create_payroll_period() - - create_tax_slab(payroll_period, allow_tax_exemption=True) - - employee = make_employee("test_tax@salary.slip") - delete_docs = [ - "Salary Slip", - "Additional Salary", - "Employee Tax Exemption Declaration", - "Employee Tax Exemption Proof Submission", - "Employee Benefit Claim", - "Salary Structure Assignment", - ] - for doc in delete_docs: - frappe.db.sql("delete from `tab%s` where employee='%s'" % (doc, employee)) - - from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - - salary_structure = make_salary_structure( - "Stucture to test tax", - "Monthly", - other_details={"max_benefits": 100000}, - test_tax=True, - include_flexi_benefits=True, - employee=employee, - payroll_period=payroll_period, - ) - - create_salary_slips_for_payroll_period( - employee, salary_structure.name, payroll_period, deduct_random=False, num=3 - ) - - tax_paid = get_tax_paid_in_period(employee) - - annual_tax = 23196.0 - self.assertEqual(tax_paid, annual_tax) - - frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) - - # ------------------------------------ - # Recurring additional salary - start_date = add_months(payroll_period.start_date, 3) - end_date = add_months(payroll_period.start_date, 5) - create_recurring_additional_salary(employee, "Performance Bonus", 20000, start_date, end_date) - - frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) - - create_salary_slips_for_payroll_period( - employee, salary_structure.name, payroll_period, deduct_random=False, num=4 - ) - - tax_paid = get_tax_paid_in_period(employee) - - annual_tax = 32315.0 - self.assertEqual(tax_paid, annual_tax) - - frappe.db.rollback() - - def make_activity_for_employee(self): - activity_type = frappe.get_doc("Activity Type", "_Test Activity Type") - activity_type.billing_rate = 50 - activity_type.costing_rate = 20 - activity_type.wage_rate = 25 - activity_type.save() - - -def get_no_of_days(): - no_of_days_in_month = calendar.monthrange(getdate(nowdate()).year, getdate(nowdate()).month) - no_of_holidays_in_month = len( - [ - 1 - for i in calendar.monthcalendar(getdate(nowdate()).year, getdate(nowdate()).month) - if i[6] != 0 - ] - ) - - return [no_of_days_in_month[1], no_of_holidays_in_month] - - -def make_employee_salary_slip(user, payroll_frequency, salary_structure=None, posting_date=None): - from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - - if not salary_structure: - salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip" - - employee = frappe.db.get_value( - "Employee", {"user_id": user}, ["name", "company", "employee_name"], as_dict=True - ) - - salary_structure_doc = make_salary_structure( - salary_structure, - payroll_frequency, - employee=employee.name, - company=employee.company, - from_date=posting_date, - ) - salary_slip_name = frappe.db.get_value( - "Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})} - ) - - if not salary_slip_name: - salary_slip = make_salary_slip(salary_structure_doc.name, employee=employee.name) - salary_slip.employee_name = employee.employee_name - salary_slip.payroll_frequency = payroll_frequency - salary_slip.posting_date = posting_date or nowdate() - salary_slip.insert() - else: - salary_slip = frappe.get_doc("Salary Slip", salary_slip_name) - - return salary_slip - - -def make_salary_component(salary_components, test_tax, company_list=None): - for salary_component in salary_components: - if frappe.db.exists("Salary Component", salary_component["salary_component"]): - continue - - if test_tax: - if salary_component["type"] == "Earning": - salary_component["is_tax_applicable"] = 1 - elif salary_component["salary_component"] == "TDS": - salary_component["variable_based_on_taxable_salary"] = 1 - salary_component["amount_based_on_formula"] = 0 - salary_component["amount"] = 0 - salary_component["formula"] = "" - salary_component["condition"] = "" - - salary_component["salary_component_abbr"] = salary_component["abbr"] - doc = frappe.new_doc("Salary Component") - doc.update(salary_component) - doc.insert() - - set_salary_component_account(doc, company_list) - - -def set_salary_component_account(sal_comp, company_list=None): - company = erpnext.get_default_company() - - if company_list and company not in company_list: - company_list.append(company) - - if not isinstance(sal_comp, Document): - sal_comp = frappe.get_doc("Salary Component", sal_comp) - - if not sal_comp.get("accounts"): - for d in company_list: - company_abbr = frappe.get_cached_value("Company", d, "abbr") - - if sal_comp.type == "Earning": - account_name = "Salary" - parent_account = "Indirect Expenses - " + company_abbr - else: - account_name = "Salary Deductions" - parent_account = "Current Liabilities - " + company_abbr - - sal_comp.append( - "accounts", {"company": d, "account": create_account(account_name, d, parent_account)} - ) - sal_comp.save() - - -def create_account(account_name, company, parent_account, account_type=None): - company_abbr = frappe.get_cached_value("Company", company, "abbr") - account = frappe.db.get_value("Account", account_name + " - " + company_abbr) - if not account: - frappe.get_doc( - { - "doctype": "Account", - "account_name": account_name, - "parent_account": parent_account, - "company": company, - } - ).insert() - return account - - -def make_earning_salary_component( - setup=False, test_tax=False, company_list=None, include_flexi_benefits=False -): - data = [ - { - "salary_component": "Basic Salary", - "abbr": "BS", - "condition": "base > 10000", - "formula": "base", - "type": "Earning", - "amount_based_on_formula": 1, - }, - {"salary_component": "HRA", "abbr": "H", "amount": 3000, "type": "Earning"}, - { - "salary_component": "Special Allowance", - "abbr": "SA", - "condition": "H < 10000", - "formula": "BS*.5", - "type": "Earning", - "amount_based_on_formula": 1, - }, - {"salary_component": "Leave Encashment", "abbr": "LE", "type": "Earning"}, - ] - if include_flexi_benefits: - data.extend( - [ - { - "salary_component": "Leave Travel Allowance", - "abbr": "B", - "is_flexible_benefit": 1, - "type": "Earning", - "pay_against_benefit_claim": 1, - "max_benefit_amount": 100000, - "depends_on_payment_days": 0, - }, - { - "salary_component": "Medical Allowance", - "abbr": "B", - "is_flexible_benefit": 1, - "pay_against_benefit_claim": 0, - "type": "Earning", - "max_benefit_amount": 15000, - "depends_on_payment_days": 1, - }, - ] - ) - if test_tax: - data.extend( - [ - {"salary_component": "Performance Bonus", "abbr": "B", "type": "Earning"}, - ] - ) - - if setup or test_tax: - make_salary_component(data, test_tax, company_list) - - data.append( - { - "salary_component": "Basic Salary", - "abbr": "BS", - "condition": "base < 10000", - "formula": "base*.2", - "type": "Earning", - "amount_based_on_formula": 1, - } - ) - return data - - -def make_deduction_salary_component(setup=False, test_tax=False, company_list=None): - data = [ - { - "salary_component": "Professional Tax", - "abbr": "PT", - "type": "Deduction", - "amount": 200, - "exempted_from_income_tax": 1, - } - ] - if not test_tax: - data.append( - { - "salary_component": "TDS", - "abbr": "T", - "condition": 'employment_type=="Intern"', - "type": "Deduction", - "round_to_the_nearest_integer": 1, - } - ) - else: - data.append( - { - "salary_component": "TDS", - "abbr": "T", - "type": "Deduction", - "depends_on_payment_days": 0, - "variable_based_on_taxable_salary": 1, - "round_to_the_nearest_integer": 1, - } - ) - if setup or test_tax: - make_salary_component(data, test_tax, company_list) - - return data - - -def get_tax_paid_in_period(employee): - tax_paid_amount = frappe.db.sql( - """select sum(sd.amount) from `tabSalary Detail` - sd join `tabSalary Slip` ss where ss.name=sd.parent and ss.employee=%s - and ss.docstatus=1 and sd.salary_component='TDS'""", - (employee), - ) - return tax_paid_amount[0][0] - - -def create_exemption_declaration(employee, payroll_period): - create_exemption_category() - declaration = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Declaration", - "employee": employee, - "payroll_period": payroll_period, - "company": erpnext.get_default_company(), - "currency": erpnext.get_default_currency(), - } - ) - declaration.append( - "declarations", - { - "exemption_sub_category": "_Test Sub Category", - "exemption_category": "_Test Category", - "amount": 100000, - }, - ) - declaration.submit() - - -def create_proof_submission(employee, payroll_period, amount): - submission_date = add_months(payroll_period.start_date, random.randint(0, 11)) - proof_submission = frappe.get_doc( - { - "doctype": "Employee Tax Exemption Proof Submission", - "employee": employee, - "payroll_period": payroll_period.name, - "submission_date": submission_date, - "currency": erpnext.get_default_currency(), - } - ) - proof_submission.append( - "tax_exemption_proofs", - { - "exemption_sub_category": "_Test Sub Category", - "exemption_category": "_Test Category", - "type_of_proof": "Test", - "amount": amount, - }, - ) - proof_submission.submit() - return submission_date - - -def create_benefit_claim(employee, payroll_period, amount, component): - claim_date = add_months(payroll_period.start_date, random.randint(0, 11)) - frappe.get_doc( - { - "doctype": "Employee Benefit Claim", - "employee": employee, - "claimed_amount": amount, - "claim_date": claim_date, - "earning_component": component, - "currency": erpnext.get_default_currency(), - } - ).submit() - return claim_date - - -def create_tax_slab( - payroll_period, - effective_date=None, - allow_tax_exemption=False, - dont_submit=False, - currency=None, - company=None, -): - if not currency: - currency = erpnext.get_default_currency() - - if company: - currency = erpnext.get_company_currency(company) - - slabs = [ - { - "from_amount": 250000, - "to_amount": 500000, - "percent_deduction": 5, - "condition": "annual_taxable_earning > 500000", - }, - {"from_amount": 500001, "to_amount": 1000000, "percent_deduction": 20}, - {"from_amount": 1000001, "percent_deduction": 30}, - ] - - income_tax_slab_name = frappe.db.get_value("Income Tax Slab", {"currency": currency}) - if not income_tax_slab_name: - income_tax_slab = frappe.new_doc("Income Tax Slab") - income_tax_slab.name = "Tax Slab: " + payroll_period.name + " " + cstr(currency) - income_tax_slab.effective_from = effective_date or add_days(payroll_period.start_date, -2) - income_tax_slab.company = company or "" - income_tax_slab.currency = currency - - if allow_tax_exemption: - income_tax_slab.allow_tax_exemption = 1 - income_tax_slab.standard_tax_exemption_amount = 50000 - - for item in slabs: - income_tax_slab.append("slabs", item) - - income_tax_slab.append("other_taxes_and_charges", {"description": "cess", "percent": 4}) - - income_tax_slab.save() - if not dont_submit: - income_tax_slab.submit() - - return income_tax_slab.name - else: - return income_tax_slab_name - - -def create_salary_slips_for_payroll_period( - employee, salary_structure, payroll_period, deduct_random=True, num=12 -): - deducted_dates = [] - i = 0 - while i < num: - slip = frappe.get_doc( - { - "doctype": "Salary Slip", - "employee": employee, - "salary_structure": salary_structure, - "frequency": "Monthly", - } - ) - if i == 0: - posting_date = add_days(payroll_period.start_date, 25) - else: - posting_date = add_months(posting_date, 1) - if i == 11: - slip.deduct_tax_for_unsubmitted_tax_exemption_proof = 1 - slip.deduct_tax_for_unclaimed_employee_benefits = 1 - if deduct_random and not random.randint(0, 2): - slip.deduct_tax_for_unsubmitted_tax_exemption_proof = 1 - deducted_dates.append(posting_date) - slip.posting_date = posting_date - slip.start_date = get_first_day(posting_date) - slip.end_date = get_last_day(posting_date) - doc = make_salary_slip(salary_structure, slip, employee) - doc.submit() - i += 1 - return deducted_dates - - -def create_additional_salary(employee, payroll_period, amount): - salary_date = add_months(payroll_period.start_date, random.randint(0, 11)) - frappe.get_doc( - { - "doctype": "Additional Salary", - "employee": employee, - "company": erpnext.get_default_company(), - "salary_component": "Performance Bonus", - "payroll_date": salary_date, - "amount": amount, - "type": "Earning", - "currency": erpnext.get_default_currency(), - } - ).submit() - return salary_date - - -def make_leave_application( - employee, - from_date, - to_date, - leave_type, - company=None, - half_day=False, - half_day_date=None, - submit=True, -): - leave_application = frappe.get_doc( - dict( - doctype="Leave Application", - employee=employee, - leave_type=leave_type, - from_date=from_date, - to_date=to_date, - half_day=half_day, - half_day_date=half_day_date, - company=company or erpnext.get_default_company() or "_Test Company", - status="Approved", - leave_approver="test@example.com", - ) - ).insert() - - if submit: - leave_application.submit() - - return leave_application - - -def setup_test(): - make_earning_salary_component(setup=True, company_list=["_Test Company"]) - make_deduction_salary_component(setup=True, company_list=["_Test Company"]) - - for dt in [ - "Leave Application", - "Leave Allocation", - "Salary Slip", - "Attendance", - "Additional Salary", - ]: - frappe.db.sql("delete from `tab%s`" % dt) - - make_holiday_list() - - frappe.db.set_value( - "Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List" - ) - frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0) - frappe.db.set_value("HR Settings", None, "leave_status_notification_template", None) - frappe.db.set_value("HR Settings", None, "leave_approval_notification_template", None) - - -def make_holiday_list(list_name=None, from_date=None, to_date=None): - fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company()) - name = list_name or "Salary Slip Test Holiday List" - - frappe.delete_doc_if_exists("Holiday List", name, force=True) - - holiday_list = frappe.get_doc( - { - "doctype": "Holiday List", - "holiday_list_name": name, - "from_date": from_date or fiscal_year[1], - "to_date": to_date or fiscal_year[2], - "weekly_off": "Sunday", - } - ).insert() - holiday_list.get_weekly_off_dates() - holiday_list.save() - holiday_list = holiday_list.name - - return holiday_list - - -def make_salary_structure_for_payment_days_based_component_dependency(): - earnings = [ - { - "salary_component": "Basic Salary - Payment Days", - "abbr": "P_BS", - "type": "Earning", - "formula": "base", - "amount_based_on_formula": 1, - }, - { - "salary_component": "HRA - Payment Days", - "abbr": "P_HRA", - "type": "Earning", - "depends_on_payment_days": 1, - "amount_based_on_formula": 1, - "formula": "base * 0.20", - }, - ] - - make_salary_component(earnings, False, company_list=["_Test Company"]) - - deductions = [ - { - "salary_component": "P - Professional Tax", - "abbr": "P_PT", - "type": "Deduction", - "depends_on_payment_days": 1, - "amount": 200.00, - }, - { - "salary_component": "P - Employee Provident Fund", - "abbr": "P_EPF", - "type": "Deduction", - "exempted_from_income_tax": 1, - "amount_based_on_formula": 1, - "depends_on_payment_days": 0, - "formula": "(gross_pay - P_HRA) * 0.12", - }, - ] - - make_salary_component(deductions, False, company_list=["_Test Company"]) - - salary_structure = "Salary Structure with PF" - if frappe.db.exists("Salary Structure", salary_structure): - frappe.db.delete("Salary Structure", salary_structure) - - details = { - "doctype": "Salary Structure", - "name": salary_structure, - "company": "_Test Company", - "payroll_frequency": "Monthly", - "payment_account": get_random("Account", filters={"account_currency": "INR"}), - "currency": "INR", - } - - salary_structure_doc = frappe.get_doc(details) - - for entry in earnings: - salary_structure_doc.append("earnings", entry) - - for entry in deductions: - salary_structure_doc.append("deductions", entry) - - salary_structure_doc.insert() - salary_structure_doc.submit() - - return salary_structure_doc - - -def make_salary_slip_for_payment_days_dependency_test(employee, salary_structure): - employee = frappe.db.get_value( - "Employee", {"user_id": employee}, ["name", "company", "employee_name"], as_dict=True - ) - - salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": employee.name}) - - if not salary_slip_name: - salary_slip = make_salary_slip(salary_structure, employee=employee.name) - salary_slip.employee_name = employee.employee_name - salary_slip.payroll_frequency = "Monthly" - salary_slip.posting_date = nowdate() - salary_slip.insert() - else: - salary_slip = frappe.get_doc("Salary Slip", salary_slip_name) - - return salary_slip - - -def create_recurring_additional_salary( - employee, salary_component, amount, from_date, to_date, company=None -): - frappe.get_doc( - { - "doctype": "Additional Salary", - "employee": employee, - "company": company or erpnext.get_default_company(), - "salary_component": salary_component, - "is_recurring": 1, - "from_date": from_date, - "to_date": to_date, - "amount": amount, - "type": "Earning", - "currency": erpnext.get_default_currency(), - } - ).submit() diff --git a/erpnext/payroll/doctype/salary_slip_leave/__init__.py b/erpnext/payroll/doctype/salary_slip_leave/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.json b/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.json deleted file mode 100644 index 60ed453938..0000000000 --- a/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "actions": [], - "creation": "2021-02-19 11:45:18.173417", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "leave_type", - "total_allocated_leaves", - "expired_leaves", - "used_leaves", - "pending_leaves", - "available_leaves" - ], - "fields": [ - { - "fieldname": "leave_type", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Leave Type", - "no_copy": 1, - "options": "Leave Type", - "read_only": 1 - }, - { - "fieldname": "total_allocated_leaves", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Total Allocated Leave(s)", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "expired_leaves", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Expired Leave(s)", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "used_leaves", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Used Leave(s)", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "pending_leaves", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Leave(s) Pending Approval", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "available_leaves", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Available Leave(s)", - "no_copy": 1, - "read_only": 1 - } - ], - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2022-02-28 14:01:32.327204", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Salary Slip Leave", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py b/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py deleted file mode 100644 index b29a60bd4a..0000000000 --- a/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 SalarySlipLeave(Document): - pass diff --git a/erpnext/payroll/doctype/salary_slip_timesheet/__init__.py b/erpnext/payroll/doctype/salary_slip_timesheet/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json deleted file mode 100644 index 9930c53ec9..0000000000 --- a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "actions": [], - "creation": "2016-06-14 19:22:29.811658", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "time_sheet", - "working_hours" - ], - "fields": [ - { - "fieldname": "time_sheet", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Time Sheet", - "options": "Timesheet", - "reqd": 1 - }, - { - "fieldname": "working_hours", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Working Hours", - "no_copy": 1, - "read_only": 1 - } - ], - "istable": 1, - "links": [], - "modified": "2020-06-22 23:27:43.463532", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Salary Slip Timesheet", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py deleted file mode 100644 index 022eba05fd..0000000000 --- a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class SalarySlipTimesheet(Document): - pass diff --git a/erpnext/payroll/doctype/salary_structure/README.md b/erpnext/payroll/doctype/salary_structure/README.md deleted file mode 100644 index 3795971c11..0000000000 --- a/erpnext/payroll/doctype/salary_structure/README.md +++ /dev/null @@ -1 +0,0 @@ -Salary Template for an Employee, basis of which monthly Salary is calculated. \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_structure/__init__.py b/erpnext/payroll/doctype/salary_structure/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html deleted file mode 100644 index 0f6cc37851..0000000000 --- a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html +++ /dev/null @@ -1,47 +0,0 @@ -

Variables

-
    -
  • - Variables from Salary Structure Assignment:
    - base = Base, variable = Variable etc. -
  • -
  • - Variables from Employee:
    Employment Type = employment_type, Branch = branch etc. -
  • -
  • - Variables from Salary Slip:
    - Payment Days = payment_days, Leave without pay = leave_without_pay etc. -
  • -
  • - Abbreviation from Salary Component:
    - BS = Basic Salary etc. -
  • -
  • - Some additional variable:
    - gross_pay and annual_taxable_earning can also be used. -
  • -
  • Direct Amount can also be used
  • -
- -

Examples for Conditions and formula

-
    -
  • - Calculating Basic Salary based on base -
    Condition: base < 10000
    -
    Formula: base * .2
    -
  • -
  • - Calculating HRA based on Basic SalaryBS -
    Condition: BS > 2000
    -
    Formula: BS * .1
    -
  • -
  • - Calculating TDS based on Employment Typeemployment_type -
    Condition: employment_type=="Intern"
    -
    Amount: 1000
    -
  • -
  • - Calculating Income Tax based on annual_taxable_earning -
    Condition: annual_taxable_earning > 20000000
    -
    Formula: annual_taxable_earning * 0.10 
    -
  • -
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js deleted file mode 100755 index 7853b4860e..0000000000 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.js +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt -{% include "erpnext/public/js/controllers/accounts.js" %} - -cur_frm.add_fetch('company', 'default_letter_head', 'letter_head'); - - -cur_frm.cscript.onload = function(doc, dt, dn){ - var e_tbl = doc.earnings || []; - var d_tbl = doc.deductions || []; - if (e_tbl.length == 0 && d_tbl.length == 0) - return function(r, rt) { refresh_many(['earnings', 'deductions']);}; -} - -frappe.ui.form.on('Salary Structure', { - onload: function(frm) { - - let help_button = $(` - ${__("Condition and Formula Help")} - `).click(()=>{ - - let d = new frappe.ui.Dialog({ - title: __('Condition and Formula Help'), - fields: [ - { - fieldname: 'msg_wrapper', - fieldtype: 'HTML' - } - ] - }); - - let message_html = frappe.render_template("condition_and_formula_help") - - d.fields_dict.msg_wrapper.$wrapper.append(message_html) - - d.show() - }); - let help_button_wrapper = frm.get_field("conditions_and_formula_variable_and_example").$wrapper; - help_button_wrapper.empty(); - help_button_wrapper.append(frm.doc.filters_html).append(help_button) - - frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet) - - frm.set_query("payment_account", function () { - var account_types = ["Bank", "Cash"]; - return { - filters: { - "account_type": ["in", account_types], - "is_group": 0, - "company": frm.doc.company - } - }; - }); - frm.trigger('set_earning_deduction_component'); - }, - - set_earning_deduction_component: function(frm) { - if(!frm.doc.company) return; - frm.set_query("salary_component", "earnings", function() { - return { - filters: {type: "earning", company: frm.doc.company} - }; - }); - frm.set_query("salary_component", "deductions", function() { - return { - filters: {type: "deduction", company: frm.doc.company} - }; - }); - }, - - company: function(frm) { - frm.trigger('set_earning_deduction_component'); - }, - - currency: function(frm) { - calculate_totals(frm.doc); - frm.trigger("set_dynamic_labels") - frm.refresh() - }, - - set_dynamic_labels: function(frm) { - frm.set_currency_labels(["net_pay","hour_rate", "leave_encashment_amount_per_day", "max_benefits", "total_earning", - "total_deduction"], frm.doc.currency); - - frm.set_currency_labels(["amount", "additional_amount", "tax_on_flexible_benefit", "tax_on_additional_salary"], - frm.doc.currency, "earnings"); - - frm.set_currency_labels(["amount", "additional_amount", "tax_on_flexible_benefit", "tax_on_additional_salary"], - frm.doc.currency, "deductions"); - - frm.refresh_fields(); - }, - - refresh: function(frm) { - frm.trigger("set_dynamic_labels") - frm.trigger("toggle_fields"); - frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false); - frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false); - - if(frm.doc.docstatus === 1) { - frm.add_custom_button(__("Preview Salary Slip"), function() { - frm.trigger('preview_salary_slip'); - }); - } - - if(frm.doc.docstatus==1) { - frm.add_custom_button(__("Assign Salary Structure"), function() { - var doc = frappe.model.get_new_doc('Salary Structure Assignment'); - doc.salary_structure = frm.doc.name; - doc.company = frm.doc.company; - frappe.set_route('Form', 'Salary Structure Assignment', doc.name); - }); - frm.add_custom_button(__("Assign to Employees"),function () { - frm.trigger('assign_to_employees') - }) - } - - // set columns read-only - let fields_read_only = ["is_tax_applicable", "is_flexible_benefit", "variable_based_on_taxable_salary"]; - fields_read_only.forEach(function(field) { - frm.fields_dict.earnings.grid.update_docfield_property( - field, 'read_only', 1 - ); - frm.fields_dict.deductions.grid.update_docfield_property( - field, 'read_only', 1 - ); - }); - frm.trigger('set_earning_deduction_component'); - }, - - assign_to_employees:function (frm) { - var d = new frappe.ui.Dialog({ - title: __("Assign to Employees"), - fields: [ - {fieldname: "sec_break", fieldtype: "Section Break", label: __("Filter Employees By (Optional)")}, - {fieldname: "grade", fieldtype: "Link", options: "Employee Grade", label: __("Employee Grade")}, - {fieldname:'department', fieldtype:'Link', options: 'Department', label: __('Department')}, - {fieldname:'designation', fieldtype:'Link', options: 'Designation', label: __('Designation')}, - {fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")}, - {fieldname:"payroll_payable_account", fieldtype: "Link", options: "Account", filters: {"company": frm.doc.company, "root_type": "Liability", "is_group": 0, "account_currency": frm.doc.currency}, label: __("Payroll Payable Account")}, - {fieldname:'base_variable', fieldtype:'Section Break'}, - {fieldname:'from_date', fieldtype:'Date', label: __('From Date'), "reqd": 1}, - {fieldname:'income_tax_slab', fieldtype:'Link', label: __('Income Tax Slab'), options: 'Income Tax Slab'}, - {fieldname:'base_col_br', fieldtype:'Column Break'}, - {fieldname:'base', fieldtype:'Currency', label: __('Base')}, - {fieldname:'variable', fieldtype:'Currency', label: __('Variable')} - ], - primary_action: function() { - var data = d.get_values(); - delete data.company - delete data.currency - frappe.call({ - doc: frm.doc, - method: "assign_salary_structure", - args: data, - callback: function(r) { - if(!r.exc) { - d.hide(); - frm.reload_doc(); - } - } - }); - }, - primary_action_label: __('Assign') - }); - - d.fields_dict.grade.df.onchange = function() { - const grade = d.fields_dict.grade.value; - if (grade) { - frappe.db.get_value('Employee Grade', grade, 'default_base_pay') - .then(({ message }) => { - d.set_value('base', message.default_base_pay); - }); - } - }; - - d.show(); - }, - - salary_slip_based_on_timesheet: function(frm) { - frm.trigger("toggle_fields") - }, - - preview_salary_slip: function(frm) { - frappe.call({ - method: "erpnext.payroll.doctype.salary_structure.salary_structure.get_employees", - args: { - salary_structure: frm.doc.name - }, - callback: function(r) { - var employees = r.message; - if(!employees) return; - if (employees.length == 1){ - frm.events.open_salary_slip(frm, employees[0]); - } else { - var d = new frappe.ui.Dialog({ - title: __("Preview Salary Slip"), - fields: [ - { - "label":__("Employee"), - "fieldname":"employee", - "fieldtype":"Select", - "reqd": true, - options: employees - }, { - fieldname:"fetch", - "label":__("Show Salary Slip"), - "fieldtype":"Button" - } - ] - }); - d.get_input("fetch").on("click", function() { - var values = d.get_values(); - if(!values) return; - frm.events.open_salary_slip(frm, values.employee) - - }); - d.show(); - } - } - }); - }, - - open_salary_slip: function(frm, employee){ - var print_format = frm.doc.salary_slip_based_on_timesheet ? "Salary Slip based on Timesheet" : "Salary Slip Standard"; - frappe.call({ - method: "erpnext.payroll.doctype.salary_structure.salary_structure.make_salary_slip", - args: { - source_name: frm.doc.name, - employee: employee, - as_print: 1, - print_format: print_format, - for_preview: 1 - }, - callback: function(r) { - var new_window = window.open(); - new_window.document.write(r.message); - } - }); - }, - - toggle_fields: function(frm) { - frm.toggle_display(['salary_component', 'hour_rate'], frm.doc.salary_slip_based_on_timesheet); - frm.toggle_reqd(['salary_component', 'hour_rate'], frm.doc.salary_slip_based_on_timesheet); - frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet); - } -}); - -var validate_date = function(frm, cdt, cdn) { - var doc = locals[cdt][cdn]; - if(doc.to_date && doc.from_date) { - var from_date = frappe.datetime.str_to_obj(doc.from_date); - var to_date = frappe.datetime.str_to_obj(doc.to_date); - - if(to_date < from_date) { - frappe.model.set_value(cdt, cdn, "to_date", ""); - frappe.throw(__("From Date cannot be greater than To Date")); - } - } -} - - -cur_frm.cscript.amount = function(doc, cdt, cdn){ - calculate_totals(doc, cdt, cdn); -}; - -var calculate_totals = function(doc) { - var tbl1 = doc.earnings || []; - var tbl2 = doc.deductions || []; - - var total_earn = 0; var total_ded = 0; - for(var i = 0; i < tbl1.length; i++){ - total_earn += flt(tbl1[i].amount); - } - for(var j = 0; j < tbl2.length; j++){ - total_ded += flt(tbl2[j].amount); - } - doc.total_earning = total_earn; - doc.total_deduction = total_ded; - doc.net_pay = 0.0 - if(doc.salary_slip_based_on_timesheet == 0){ - doc.net_pay = flt(total_earn) - flt(total_ded); - } - - refresh_many(['total_earning', 'total_deduction', 'net_pay']); -} - -cur_frm.cscript.validate = function(doc, cdt, cdn) { - calculate_totals(doc); -} - - -frappe.ui.form.on('Salary Detail', { - amount: function(frm) { - calculate_totals(frm.doc); - }, - - earnings_remove: function(frm) { - calculate_totals(frm.doc); - }, - - deductions_remove: function(frm) { - calculate_totals(frm.doc); - }, - - salary_component: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if(child.salary_component){ - frappe.call({ - method: "frappe.client.get", - args: { - doctype: "Salary Component", - name: child.salary_component - }, - callback: function(data) { - if(data.message){ - var result = data.message; - frappe.model.set_value(cdt, cdn, 'condition', result.condition); - frappe.model.set_value(cdt, cdn, 'amount_based_on_formula', result.amount_based_on_formula); - if(result.amount_based_on_formula == 1){ - frappe.model.set_value(cdt, cdn, 'formula', result.formula); - } - else{ - frappe.model.set_value(cdt, cdn, 'amount', result.amount); - } - frappe.model.set_value(cdt, cdn, 'statistical_component', result.statistical_component); - frappe.model.set_value(cdt, cdn, 'depends_on_payment_days', result.depends_on_payment_days); - frappe.model.set_value(cdt, cdn, 'do_not_include_in_total', result.do_not_include_in_total); - frappe.model.set_value(cdt, cdn, 'variable_based_on_taxable_salary', result.variable_based_on_taxable_salary); - frappe.model.set_value(cdt, cdn, 'is_tax_applicable', result.is_tax_applicable); - frappe.model.set_value(cdt, cdn, 'is_flexible_benefit', result.is_flexible_benefit); - refresh_field("earnings"); - refresh_field("deductions"); - } - } - }); - } - }, - - amount_based_on_formula: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if(child.amount_based_on_formula == 1){ - frappe.model.set_value(cdt, cdn, 'amount', null); - } - else{ - frappe.model.set_value(cdt, cdn, 'formula', null); - } - } -}) diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.json b/erpnext/payroll/doctype/salary_structure/salary_structure.json deleted file mode 100644 index 8df995769d..0000000000 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.json +++ /dev/null @@ -1,278 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "allow_rename": 1, - "autoname": "Prompt", - "creation": "2013-03-07 18:50:29", - "doctype": "DocType", - "document_type": "Document", - "engine": "InnoDB", - "field_order": [ - "company", - "letter_head", - "column_break1", - "is_active", - "payroll_frequency", - "currency", - "is_default", - "time_sheet_earning_detail", - "salary_slip_based_on_timesheet", - "column_break_17", - "salary_component", - "hour_rate", - "leave_encashment_amount_per_day", - "max_benefits", - "earning_deduction", - "earnings", - "deductions", - "conditions_and_formula_variable_and_example", - "net_pay_detail", - "total_earning", - "total_deduction", - "column_break2", - "net_pay", - "account", - "mode_of_payment", - "column_break_28", - "payment_account", - "amended_from" - ], - "fields": [ - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "fieldname": "letter_head", - "fieldtype": "Link", - "label": "Letter Head", - "options": "Letter Head" - }, - { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "width": "50%" - }, - { - "allow_on_submit": 1, - "default": "Yes", - "fieldname": "is_active", - "fieldtype": "Select", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Is Active", - "oldfieldname": "is_active", - "oldfieldtype": "Select", - "options": "\nYes\nNo", - "reqd": 1 - }, - { - "default": "Monthly", - "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)", - "fieldname": "payroll_frequency", - "fieldtype": "Select", - "label": "Payroll Frequency", - "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" - }, - { - "default": "No", - "fieldname": "is_default", - "fieldtype": "Select", - "hidden": 1, - "label": "Is Default", - "no_copy": 1, - "options": "Yes\nNo", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "time_sheet_earning_detail", - "fieldtype": "Section Break" - }, - { - "default": "0", - "fieldname": "salary_slip_based_on_timesheet", - "fieldtype": "Check", - "label": "Salary Slip Based on Timesheet" - }, - { - "fieldname": "column_break_17", - "fieldtype": "Column Break" - }, - { - "description": "Salary Component for timesheet based payroll.", - "fieldname": "salary_component", - "fieldtype": "Link", - "label": "Salary Component", - "options": "Salary Component" - }, - { - "fieldname": "hour_rate", - "fieldtype": "Currency", - "label": "Hour Rate", - "options": "currency" - }, - { - "fieldname": "leave_encashment_amount_per_day", - "fieldtype": "Currency", - "label": "Leave Encashment Amount Per Day", - "options": "currency" - }, - { - "fieldname": "max_benefits", - "fieldtype": "Currency", - "label": "Max Benefits (Amount)", - "options": "currency" - }, - { - "description": "Salary breakup based on Earning and Deduction.", - "fieldname": "earning_deduction", - "fieldtype": "Section Break", - "oldfieldname": "earning_deduction", - "oldfieldtype": "Section Break", - "precision": "2" - }, - { - "fieldname": "earnings", - "fieldtype": "Table", - "label": "Earnings", - "oldfieldname": "earning_details", - "oldfieldtype": "Table", - "options": "Salary Detail" - }, - { - "fieldname": "deductions", - "fieldtype": "Table", - "label": "Deductions", - "oldfieldname": "deduction_details", - "oldfieldtype": "Table", - "options": "Salary Detail" - }, - { - "fieldname": "net_pay_detail", - "fieldtype": "Section Break", - "options": "Simple" - }, - { - "fieldname": "column_break2", - "fieldtype": "Column Break", - "width": "50%" - }, - { - "fieldname": "total_earning", - "fieldtype": "Currency", - "hidden": 1, - "label": "Total Earning", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "total_deduction", - "fieldtype": "Currency", - "hidden": 1, - "label": "Total Deduction", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "net_pay", - "fieldtype": "Currency", - "hidden": 1, - "label": "Net Pay", - "options": "currency", - "read_only": 1 - }, - { - "fieldname": "account", - "fieldtype": "Section Break", - "label": "Account" - }, - { - "fieldname": "mode_of_payment", - "fieldtype": "Link", - "label": "Mode of Payment", - "options": "Mode of Payment" - }, - { - "fieldname": "column_break_28", - "fieldtype": "Column Break" - }, - { - "fieldname": "payment_account", - "fieldtype": "Link", - "label": "Payment Account", - "options": "Account" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Salary Structure", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "conditions_and_formula_variable_and_example", - "fieldtype": "HTML", - "label": "Conditions and Formula variable and example" - }, - { - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "reqd": 1 - } - ], - "icon": "fa fa-file-text", - "idx": 1, - "is_submittable": 1, - "links": [], - "modified": "2022-02-03 23:50:10.205676", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Salary Structure", - "naming_rule": "Set by user", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "import": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "states": [] -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py deleted file mode 100644 index edf17dbfb1..0000000000 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.py +++ /dev/null @@ -1,319 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.model.mapper import get_mapped_doc -from frappe.utils import cint, cstr, flt - -import erpnext - - -class SalaryStructure(Document): - def validate(self): - self.set_missing_values() - self.validate_amount() - self.strip_condition_and_formula_fields() - self.validate_max_benefits_with_flexi() - self.validate_component_based_on_tax_slab() - - def set_missing_values(self): - overwritten_fields = [ - "depends_on_payment_days", - "variable_based_on_taxable_salary", - "is_tax_applicable", - "is_flexible_benefit", - ] - overwritten_fields_if_missing = ["amount_based_on_formula", "formula", "amount"] - for table in ["earnings", "deductions"]: - for d in self.get(table): - component_default_value = frappe.db.get_value( - "Salary Component", - cstr(d.salary_component), - overwritten_fields + overwritten_fields_if_missing, - as_dict=1, - ) - if component_default_value: - for fieldname in overwritten_fields: - value = component_default_value.get(fieldname) - if d.get(fieldname) != value: - d.set(fieldname, value) - - if not (d.get("amount") or d.get("formula")): - for fieldname in overwritten_fields_if_missing: - d.set(fieldname, component_default_value.get(fieldname)) - - def validate_component_based_on_tax_slab(self): - for row in self.deductions: - if row.variable_based_on_taxable_salary and (row.amount or row.formula): - frappe.throw( - _( - "Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary" - ).format(row.idx, row.salary_component) - ) - - def validate_amount(self): - if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet: - frappe.throw(_("Net pay cannot be negative")) - - def strip_condition_and_formula_fields(self): - # remove whitespaces from condition and formula fields - for row in self.earnings: - row.condition = row.condition.strip() if row.condition else "" - row.formula = row.formula.strip() if row.formula else "" - - for row in self.deductions: - row.condition = row.condition.strip() if row.condition else "" - row.formula = row.formula.strip() if row.formula else "" - - def validate_max_benefits_with_flexi(self): - have_a_flexi = False - if self.earnings: - flexi_amount = 0 - for earning_component in self.earnings: - if earning_component.is_flexible_benefit == 1: - have_a_flexi = True - max_of_component = frappe.db.get_value( - "Salary Component", earning_component.salary_component, "max_benefit_amount" - ) - flexi_amount += max_of_component - - if have_a_flexi and flt(self.max_benefits) == 0: - frappe.throw(_("Max benefits should be greater than zero to dispense benefits")) - if have_a_flexi and flexi_amount and flt(self.max_benefits) > flexi_amount: - frappe.throw( - _( - "Total flexible benefit component amount {0} should not be less than max benefits {1}" - ).format(flexi_amount, self.max_benefits) - ) - if not have_a_flexi and flt(self.max_benefits) > 0: - frappe.throw( - _("Salary Structure should have flexible benefit component(s) to dispense benefit amount") - ) - - def get_employees(self, **kwargs): - conditions, values = [], [] - for field, value in kwargs.items(): - if value: - conditions.append("{0}=%s".format(field)) - values.append(value) - - condition_str = " and " + " and ".join(conditions) if conditions else "" - - employees = frappe.db.sql_list( - "select name from tabEmployee where status='Active' {condition}".format( - condition=condition_str - ), - tuple(values), - ) - - return employees - - @frappe.whitelist() - def assign_salary_structure( - self, - grade=None, - department=None, - designation=None, - employee=None, - payroll_payable_account=None, - from_date=None, - base=None, - variable=None, - income_tax_slab=None, - ): - employees = self.get_employees( - company=self.company, grade=grade, department=department, designation=designation, name=employee - ) - - if employees: - if len(employees) > 20: - frappe.enqueue( - assign_salary_structure_for_employees, - timeout=600, - employees=employees, - salary_structure=self, - payroll_payable_account=payroll_payable_account, - from_date=from_date, - base=base, - variable=variable, - income_tax_slab=income_tax_slab, - ) - else: - assign_salary_structure_for_employees( - employees, - self, - payroll_payable_account=payroll_payable_account, - from_date=from_date, - base=base, - variable=variable, - income_tax_slab=income_tax_slab, - ) - else: - frappe.msgprint(_("No Employee Found")) - - -def assign_salary_structure_for_employees( - employees, - salary_structure, - payroll_payable_account=None, - from_date=None, - base=None, - variable=None, - income_tax_slab=None, -): - salary_structures_assignments = [] - existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date) - count = 0 - for employee in employees: - if employee in existing_assignments_for: - continue - count += 1 - - salary_structures_assignment = create_salary_structures_assignment( - employee, salary_structure, payroll_payable_account, from_date, base, variable, income_tax_slab - ) - salary_structures_assignments.append(salary_structures_assignment) - frappe.publish_progress( - count * 100 / len(set(employees) - set(existing_assignments_for)), - title=_("Assigning Structures..."), - ) - - if salary_structures_assignments: - frappe.msgprint(_("Structures have been assigned successfully")) - - -def create_salary_structures_assignment( - employee, - salary_structure, - payroll_payable_account, - from_date, - base, - variable, - income_tax_slab=None, -): - if not payroll_payable_account: - payroll_payable_account = frappe.db.get_value( - "Company", salary_structure.company, "default_payroll_payable_account" - ) - if not payroll_payable_account: - frappe.throw(_('Please set "Default Payroll Payable Account" in Company Defaults')) - payroll_payable_account_currency = frappe.db.get_value( - "Account", payroll_payable_account, "account_currency" - ) - company_curency = erpnext.get_company_currency(salary_structure.company) - if ( - payroll_payable_account_currency != salary_structure.currency - and payroll_payable_account_currency != company_curency - ): - frappe.throw( - _("Invalid Payroll Payable Account. The account currency must be {0} or {1}").format( - salary_structure.currency, company_curency - ) - ) - - assignment = frappe.new_doc("Salary Structure Assignment") - assignment.employee = employee - assignment.salary_structure = salary_structure.name - assignment.company = salary_structure.company - assignment.currency = salary_structure.currency - assignment.payroll_payable_account = payroll_payable_account - assignment.from_date = from_date - assignment.base = base - assignment.variable = variable - assignment.income_tax_slab = income_tax_slab - assignment.save(ignore_permissions=True) - assignment.submit() - return assignment.name - - -def get_existing_assignments(employees, salary_structure, from_date): - salary_structures_assignments = frappe.db.sql_list( - """ - select distinct employee from `tabSalary Structure Assignment` - where salary_structure=%s and employee in (%s) - and from_date=%s and company= %s and docstatus=1 - """ - % ("%s", ", ".join(["%s"] * len(employees)), "%s", "%s"), - [salary_structure.name] + employees + [from_date] + [salary_structure.company], - ) - if salary_structures_assignments: - frappe.msgprint( - _( - "Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}" - ).format("\n".join(salary_structures_assignments)) - ) - return salary_structures_assignments - - -@frappe.whitelist() -def make_salary_slip( - source_name, - target_doc=None, - employee=None, - posting_date=None, - as_print=False, - print_format=None, - for_preview=0, - ignore_permissions=False, -): - def postprocess(source, target): - if employee: - employee_details = frappe.db.get_value( - "Employee", employee, ["employee_name", "branch", "designation", "department"], as_dict=1 - ) - target.employee = employee - target.employee_name = employee_details.employee_name - target.branch = employee_details.branch - target.designation = employee_details.designation - target.department = employee_details.department - - if posting_date: - target.posting_date = posting_date - - target.run_method("process_salary_structure", for_preview=for_preview) - - doc = get_mapped_doc( - "Salary Structure", - source_name, - { - "Salary Structure": { - "doctype": "Salary Slip", - "field_map": { - "total_earning": "gross_pay", - "name": "salary_structure", - "currency": "currency", - }, - } - }, - target_doc, - postprocess, - ignore_child_tables=True, - ignore_permissions=ignore_permissions, - ) - - if cint(as_print): - doc.name = "Preview for {0}".format(employee) - return frappe.get_print(doc.doctype, doc.name, doc=doc, print_format=print_format) - else: - return doc - - -@frappe.whitelist() -def get_employees(salary_structure): - employees = frappe.get_list( - "Salary Structure Assignment", - filters={"salary_structure": salary_structure, "docstatus": 1}, - fields=["employee"], - ) - - if not employees: - frappe.throw( - _( - "There's no Employee with Salary Structure: {0}. Assign {1} to an Employee to preview Salary Slip" - ).format(salary_structure, salary_structure) - ) - - return list(set([d.employee for d in employees])) diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py b/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py deleted file mode 100644 index cf363b410d..0000000000 --- a/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py +++ /dev/null @@ -1,9 +0,0 @@ -def get_data(): - return { - "fieldname": "salary_structure", - "non_standard_fieldnames": {"Employee Grade": "default_salary_structure"}, - "transactions": [ - {"items": ["Salary Structure Assignment", "Salary Slip"]}, - {"items": ["Employee Grade"]}, - ], - } diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py deleted file mode 100644 index 8cc2ea3314..0000000000 --- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py +++ /dev/null @@ -1,283 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_years, date_diff, get_first_day, nowdate -from frappe.utils.make_random import get_random - -import erpnext -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import ( - create_payroll_period, -) -from erpnext.payroll.doctype.salary_slip.test_salary_slip import ( - create_tax_slab, - make_deduction_salary_component, - make_earning_salary_component, - make_employee_salary_slip, -) -from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip - -test_dependencies = ["Fiscal Year"] - - -class TestSalaryStructure(FrappeTestCase): - def setUp(self): - for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment"]: - frappe.db.sql("delete from `tab%s`" % dt) - - self.make_holiday_list() - frappe.db.set_value( - "Company", - erpnext.get_default_company(), - "default_holiday_list", - "Salary Structure Test Holiday List", - ) - make_employee("test_employee@salary.com") - make_employee("test_employee_2@salary.com") - - def make_holiday_list(self): - if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"): - holiday_list = frappe.get_doc( - { - "doctype": "Holiday List", - "holiday_list_name": "Salary Structure Test Holiday List", - "from_date": nowdate(), - "to_date": add_years(nowdate(), 1), - "weekly_off": "Sunday", - } - ).insert() - holiday_list.get_weekly_off_dates() - holiday_list.save() - - def test_salary_structure_deduction_based_on_gross_pay(self): - - emp = make_employee("test_employee_3@salary.com") - - sal_struct = make_salary_structure("Salary Structure 2", "Monthly", dont_submit=True) - - sal_struct.earnings = [sal_struct.earnings[0]] - sal_struct.earnings[0].amount_based_on_formula = 1 - sal_struct.earnings[0].formula = "base" - - sal_struct.deductions = [sal_struct.deductions[0]] - - sal_struct.deductions[0].amount_based_on_formula = 1 - sal_struct.deductions[0].condition = "gross_pay > 100" - sal_struct.deductions[0].formula = "gross_pay * 0.2" - - sal_struct.submit() - - assignment = create_salary_structure_assignment(emp, "Salary Structure 2") - ss = make_salary_slip(sal_struct.name, employee=emp) - - self.assertEqual(assignment.base * 0.2, ss.deductions[0].amount) - - def test_amount_totals(self): - frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) - sal_slip = frappe.get_value("Salary Slip", {"employee_name": "test_employee_2@salary.com"}) - if not sal_slip: - sal_slip = make_employee_salary_slip( - "test_employee_2@salary.com", "Monthly", "Salary Structure Sample" - ) - self.assertEqual(sal_slip.get("salary_structure"), "Salary Structure Sample") - self.assertEqual(sal_slip.get("earnings")[0].amount, 50000) - self.assertEqual(sal_slip.get("earnings")[1].amount, 3000) - self.assertEqual(sal_slip.get("earnings")[2].amount, 25000) - self.assertEqual(sal_slip.get("gross_pay"), 78000) - self.assertEqual(sal_slip.get("deductions")[0].amount, 200) - self.assertEqual(sal_slip.get("net_pay"), 78000 - sal_slip.get("total_deduction")) - - def test_whitespaces_in_formula_conditions_fields(self): - salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True) - - for row in salary_structure.earnings: - row.formula = "\n%s\n\n" % row.formula - row.condition = "\n%s\n\n" % row.condition - - for row in salary_structure.deductions: - row.formula = "\n%s\n\n" % row.formula - row.condition = "\n%s\n\n" % row.condition - - salary_structure.save() - - for row in salary_structure.earnings: - self.assertFalse("\n" in row.formula or "\n" in row.condition) - - for row in salary_structure.deductions: - self.assertFalse(("\n" in row.formula) or ("\n" in row.condition)) - - def test_salary_structures_assignment(self): - company_currency = erpnext.get_default_currency() - salary_structure = make_salary_structure( - "Salary Structure Sample", "Monthly", currency=company_currency - ) - employee = "test_assign_stucture@salary.com" - employee_doc_name = make_employee(employee) - # clear the already assigned stuctures - frappe.db.sql( - """delete from `tabSalary Structure Assignment` where employee=%s and salary_structure=%s """, - ("test_assign_stucture@salary.com", salary_structure.name), - ) - # test structure_assignment - salary_structure.assign_salary_structure( - employee=employee_doc_name, from_date="2013-01-01", base=5000, variable=200 - ) - salary_structure_assignment = frappe.get_doc( - "Salary Structure Assignment", {"employee": employee_doc_name, "from_date": "2013-01-01"} - ) - self.assertEqual(salary_structure_assignment.docstatus, 1) - self.assertEqual(salary_structure_assignment.base, 5000) - self.assertEqual(salary_structure_assignment.variable, 200) - - def test_employee_grade_defaults(self): - salary_structure = make_salary_structure( - "Salary Structure - Lead", "Monthly", currency="INR", company="_Test Company" - ) - create_employee_grade("Lead", salary_structure.name) - employee = make_employee("test_employee_grade@salary.com", company="_Test Company", grade="Lead") - - # structure assignment should have the default salary structure and base pay - salary_structure.assign_salary_structure(employee=employee, from_date=nowdate()) - structure, base = frappe.db.get_value( - "Salary Structure Assignment", - {"employee": employee, "salary_structure": salary_structure.name, "from_date": nowdate()}, - ["salary_structure", "base"], - ) - self.assertEqual(structure, salary_structure.name) - self.assertEqual(base, 50000) - - def test_multi_currency_salary_structure(self): - make_employee("test_muti_currency_employee@salary.com") - sal_struct = make_salary_structure("Salary Structure Multi Currency", "Monthly", currency="USD") - self.assertEqual(sal_struct.currency, "USD") - - -def make_salary_structure( - salary_structure, - payroll_frequency, - employee=None, - from_date=None, - dont_submit=False, - other_details=None, - test_tax=False, - company=None, - currency=erpnext.get_default_currency(), - payroll_period=None, - include_flexi_benefits=False, -): - if frappe.db.exists("Salary Structure", salary_structure): - frappe.db.delete("Salary Structure", salary_structure) - - details = { - "doctype": "Salary Structure", - "name": salary_structure, - "company": company or erpnext.get_default_company(), - "earnings": make_earning_salary_component( - setup=True, - test_tax=test_tax, - company_list=["_Test Company"], - include_flexi_benefits=include_flexi_benefits, - ), - "deductions": make_deduction_salary_component( - setup=True, test_tax=test_tax, company_list=["_Test Company"] - ), - "payroll_frequency": payroll_frequency, - "payment_account": get_random("Account", filters={"account_currency": currency}), - "currency": currency, - } - if other_details and isinstance(other_details, dict): - details.update(other_details) - salary_structure_doc = frappe.get_doc(details) - salary_structure_doc.insert() - if not dont_submit: - salary_structure_doc.submit() - - filters = {"employee": employee, "docstatus": 1} - if not from_date and payroll_period: - from_date = payroll_period.start_date - - if from_date: - filters["from_date"] = from_date - - if ( - employee - and not frappe.db.get_value("Salary Structure Assignment", filters) - and salary_structure_doc.docstatus == 1 - ): - create_salary_structure_assignment( - employee, - salary_structure, - from_date=from_date, - company=company, - currency=currency, - payroll_period=payroll_period, - ) - - return salary_structure_doc - - -def create_salary_structure_assignment( - employee, - salary_structure, - from_date=None, - company=None, - currency=erpnext.get_default_currency(), - payroll_period=None, - base=None, - allow_duplicate=False, -): - if not allow_duplicate and frappe.db.exists( - "Salary Structure Assignment", {"employee": employee} - ): - frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""", (employee)) - - if not payroll_period: - payroll_period = create_payroll_period() - - income_tax_slab = frappe.db.get_value("Income Tax Slab", {"currency": currency}) - - if not income_tax_slab: - income_tax_slab = create_tax_slab(payroll_period, allow_tax_exemption=True, currency=currency) - - salary_structure_assignment = frappe.new_doc("Salary Structure Assignment") - salary_structure_assignment.employee = employee - salary_structure_assignment.base = base or 50000 - salary_structure_assignment.variable = 5000 - - if not from_date: - from_date = get_first_day(nowdate()) - joining_date = frappe.get_cached_value("Employee", employee, "date_of_joining") - if date_diff(joining_date, from_date) > 0: - from_date = joining_date - - salary_structure_assignment.from_date = from_date - salary_structure_assignment.salary_structure = salary_structure - salary_structure_assignment.currency = currency - salary_structure_assignment.payroll_payable_account = get_payable_account(company) - salary_structure_assignment.company = company or erpnext.get_default_company() - salary_structure_assignment.save(ignore_permissions=True) - salary_structure_assignment.income_tax_slab = income_tax_slab - salary_structure_assignment.submit() - return salary_structure_assignment - - -def get_payable_account(company=None): - if not company: - company = erpnext.get_default_company() - return frappe.db.get_value("Company", company, "default_payroll_payable_account") - - -def create_employee_grade(grade, default_structure=None): - if not frappe.db.exists("Employee Grade", grade): - frappe.get_doc( - { - "doctype": "Employee Grade", - "__newname": grade, - "default_salary_structure": default_structure, - "default_base_pay": 50000, - } - ).insert() diff --git a/erpnext/payroll/doctype/salary_structure_assignment/__init__.py b/erpnext/payroll/doctype/salary_structure_assignment/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js deleted file mode 100644 index 220bfbfd65..0000000000 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Salary Structure Assignment', { - setup: function(frm) { - frm.set_query("employee", function() { - return { - query: "erpnext.controllers.queries.employee_query", - } - }); - frm.set_query("salary_structure", function() { - return { - filters: { - company: frm.doc.company, - docstatus: 1, - is_active: "Yes" - } - } - }); - - frm.set_query("income_tax_slab", function() { - return { - filters: { - company: frm.doc.company, - docstatus: 1, - disabled: 0, - currency: frm.doc.currency - } - }; - }); - - frm.set_query("payroll_payable_account", function() { - var company_currency = erpnext.get_currency(frm.doc.company); - return { - filters: { - "company": frm.doc.company, - "root_type": "Liability", - "is_group": 0, - "account_currency": ["in", [frm.doc.currency, company_currency]], - } - } - }); - - frm.set_query("cost_center", "payroll_cost_centers", function() { - return { - filters: { - "company": frm.doc.company, - "is_group": 0 - } - }; - }); - }, - - employee: function(frm) { - if (frm.doc.employee) { - frappe.call({ - method: "set_payroll_cost_centers", - doc: frm.doc, - callback: function(data) { - refresh_field("payroll_cost_centers"); - } - }); - } - else { - frm.set_value("payroll_cost_centers", []); - } - }, - - company: function(frm) { - if (frm.doc.company) { - frappe.db.get_value("Company", frm.doc.company, "default_payroll_payable_account", (r) => { - frm.set_value("payroll_payable_account", r.default_payroll_payable_account); - }); - } - } -}); diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json deleted file mode 100644 index 91d49a0738..0000000000 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json +++ /dev/null @@ -1,231 +0,0 @@ -{ - "actions": [], - "allow_import": 1, - "autoname": "HR-SSA-.YY.-.MM.-.#####", - "creation": "2018-04-13 16:38:41.769237", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "employee", - "employee_name", - "department", - "grade", - "company", - "payroll_payable_account", - "column_break_6", - "designation", - "salary_structure", - "from_date", - "income_tax_slab", - "currency", - "section_break_7", - "base", - "column_break_9", - "variable", - "amended_from", - "section_break_17", - "payroll_cost_centers" - ], - "fields": [ - { - "fieldname": "employee", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1, - "search_index": 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", - "in_standard_filter": 1, - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fetch_from": "employee.designation", - "fieldname": "designation", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Designation", - "options": "Designation", - "read_only": 1 - }, - { - "fieldname": "column_break_6", - "fieldtype": "Column Break" - }, - { - "fetch_from": "grade.default_salary_structure", - "fetch_if_empty": 1, - "fieldname": "salary_structure", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Salary Structure", - "options": "Salary Structure", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "from_date", - "fieldtype": "Date", - "label": "From Date", - "reqd": 1 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "label": "Base & Variable" - }, - { - "fetch_from": "grade.default_base_pay", - "fetch_if_empty": 1, - "fieldname": "base", - "fieldtype": "Currency", - "label": "Base", - "options": "currency" - }, - { - "fieldname": "column_break_9", - "fieldtype": "Column Break" - }, - { - "fieldname": "variable", - "fieldtype": "Currency", - "label": "Variable", - "options": "currency" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Salary Structure Assignment", - "print_hide": 1, - "read_only": 1 - }, - { - "depends_on": "salary_structure", - "fieldname": "income_tax_slab", - "fieldtype": "Link", - "label": "Income Tax Slab", - "options": "Income Tax Slab" - }, - { - "depends_on": "eval:(doc.docstatus==1 || doc.salary_structure)", - "fetch_from": "salary_structure.currency", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Currency", - "options": "Currency", - "print_hide": 1, - "read_only": 1, - "reqd": 1 - }, - { - "depends_on": "employee", - "fieldname": "payroll_payable_account", - "fieldtype": "Link", - "label": "Payroll Payable Account", - "options": "Account" - }, - { - "collapsible": 1, - "depends_on": "employee", - "fieldname": "section_break_17", - "fieldtype": "Section Break", - "label": "Payroll Cost Centers" - }, - { - "allow_on_submit": 1, - "fieldname": "payroll_cost_centers", - "fieldtype": "Table", - "label": "Cost Centers", - "options": "Employee Cost Center" - }, - { - "fetch_from": "employee.grade", - "fieldname": "grade", - "fieldtype": "Link", - "label": "Grade", - "options": "Employee Grade", - "read_only": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2022-05-06 12:18:36.972336", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Salary Structure Assignment", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "employee_name, salary_structure", - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "title_field": "employee_name", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py deleted file mode 100644 index 28b5eb136b..0000000000 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import flt, getdate - - -class DuplicateAssignment(frappe.ValidationError): - pass - - -class SalaryStructureAssignment(Document): - def validate(self): - self.validate_dates() - self.validate_income_tax_slab() - self.set_payroll_payable_account() - if not self.get("payroll_cost_centers"): - self.set_payroll_cost_centers() - - self.validate_cost_center_distribution() - - def validate_dates(self): - joining_date, relieving_date = frappe.db.get_value( - "Employee", self.employee, ["date_of_joining", "relieving_date"] - ) - - if self.from_date: - if frappe.db.exists( - "Salary Structure Assignment", - {"employee": self.employee, "from_date": self.from_date, "docstatus": 1}, - ): - frappe.throw(_("Salary Structure Assignment for Employee already exists"), DuplicateAssignment) - - if joining_date and getdate(self.from_date) < joining_date: - frappe.throw( - _("From Date {0} cannot be before employee's joining Date {1}").format( - self.from_date, joining_date - ) - ) - - # flag - old_employee is for migrating the old employees data via patch - if relieving_date and getdate(self.from_date) > relieving_date and not self.flags.old_employee: - frappe.throw( - _("From Date {0} cannot be after employee's relieving Date {1}").format( - self.from_date, relieving_date - ) - ) - - def validate_income_tax_slab(self): - if not self.income_tax_slab: - return - - income_tax_slab_currency = frappe.db.get_value( - "Income Tax Slab", self.income_tax_slab, "currency" - ) - if self.currency != income_tax_slab_currency: - frappe.throw( - _("Currency of selected Income Tax Slab should be {0} instead of {1}").format( - self.currency, income_tax_slab_currency - ) - ) - - def set_payroll_payable_account(self): - if not self.payroll_payable_account: - payroll_payable_account = frappe.db.get_value( - "Company", self.company, "default_payroll_payable_account" - ) - if not payroll_payable_account: - payroll_payable_account = frappe.db.get_value( - "Account", - { - "account_name": _("Payroll Payable"), - "company": self.company, - "account_currency": frappe.db.get_value("Company", self.company, "default_currency"), - "is_group": 0, - }, - ) - self.payroll_payable_account = payroll_payable_account - - @frappe.whitelist() - def set_payroll_cost_centers(self): - self.payroll_cost_centers = [] - default_payroll_cost_center = self.get_payroll_cost_center() - if default_payroll_cost_center: - self.append( - "payroll_cost_centers", {"cost_center": default_payroll_cost_center, "percentage": 100} - ) - - def get_payroll_cost_center(self): - payroll_cost_center = frappe.db.get_value("Employee", self.employee, "payroll_cost_center") - if not payroll_cost_center and self.department: - payroll_cost_center = frappe.db.get_value("Department", self.department, "payroll_cost_center") - - return payroll_cost_center - - def validate_cost_center_distribution(self): - if self.get("payroll_cost_centers"): - total_percentage = sum([flt(d.percentage) for d in self.get("payroll_cost_centers", [])]) - if total_percentage != 100: - frappe.throw(_("Total percentage against cost centers should be 100")) - - -def get_assigned_salary_structure(employee, on_date): - if not employee or not on_date: - return None - salary_structure = frappe.db.sql( - """ - select salary_structure from `tabSalary Structure Assignment` - where employee=%(employee)s - and docstatus = 1 - and %(on_date)s >= from_date order by from_date desc limit 1""", - { - "employee": employee, - "on_date": on_date, - }, - ) - return salary_structure[0][0] if salary_structure else None - - -@frappe.whitelist() -def get_employee_currency(employee): - employee_currency = frappe.db.get_value( - "Salary Structure Assignment", {"employee": employee}, "currency" - ) - if not employee_currency: - frappe.throw( - _("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format( - employee - ) - ) - return employee_currency diff --git a/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py deleted file mode 100644 index 56dd0d0fe5..0000000000 --- a/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestSalaryStructureAssignment(unittest.TestCase): - pass diff --git a/erpnext/payroll/doctype/taxable_salary_slab/__init__.py b/erpnext/payroll/doctype/taxable_salary_slab/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json deleted file mode 100644 index 65d3824f3a..0000000000 --- a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "actions": [], - "creation": "2018-04-13 17:42:13.516032", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "from_amount", - "to_amount", - "percent_deduction", - "condition", - "column_break_5", - "html_6" - ], - "fields": [ - { - "default": "0", - "fieldname": "from_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "From Amount", - "options": "currency", - "reqd": 1 - }, - { - "fieldname": "to_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "To Amount", - "options": "currency" - }, - { - "default": "0", - "fieldname": "percent_deduction", - "fieldtype": "Percent", - "in_list_view": 1, - "label": "Percent Deduction", - "reqd": 1 - }, - { - "fieldname": "condition", - "fieldtype": "Code", - "in_list_view": 1, - "label": "Condition" - }, - { - "fieldname": "column_break_5", - "fieldtype": "Column Break" - }, - { - "fieldname": "html_6", - "fieldtype": "HTML", - "options": "

Condition Examples

\n
    \n
  1. Applying tax if employee born between 31-12-1937 and 01-01-1958 (Employees aged 60 to 80)
    \nCondition: date_of_birth>date(1937, 12, 31) and date_of_birth<date(1958, 01, 01)

  2. Applying tax by employee gender
    \nCondition: gender==\"Male\"

  3. \n
  4. Applying tax by Salary Component
    \nCondition: base > 10000
" - } - ], - "istable": 1, - "links": [], - "modified": "2020-10-19 13:44:39.549337", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Taxable Salary Slab", - "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/taxable_salary_slab/taxable_salary_slab.py b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py deleted file mode 100644 index d1ccbe3858..0000000000 --- a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -# import frappe -from frappe.model.document import Document - - -class TaxableSalarySlab(Document): - pass diff --git a/erpnext/payroll/module_onboarding/payroll/payroll.json b/erpnext/payroll/module_onboarding/payroll/payroll.json deleted file mode 100644 index b5226b2aca..0000000000 --- a/erpnext/payroll/module_onboarding/payroll/payroll.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "allow_roles": [ - { - "role": "HR Manager" - }, - { - "role": "HR User" - } - ], - "creation": "2020-06-01 12:10:52.560472", - "docstatus": 0, - "doctype": "Module Onboarding", - "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources/payroll-entry", - "idx": 0, - "is_complete": 0, - "modified": "2020-07-08 14:06:13.994310", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Payroll", - "owner": "Administrator", - "steps": [ - { - "step": "Create Employee" - }, - { - "step": "Create Salary Component" - }, - { - "step": "Create Payroll Period" - }, - { - "step": "Create Income Tax Slab" - }, - { - "step": "Create Salary Structure" - }, - { - "step": "Assign Salary Structure" - }, - { - "step": "Create Salary Slip" - }, - { - "step": "Payroll Settings" - } - ], - "subtitle": "Salary, Compensation, and more.", - "success_message": "The Payroll Module is all set up!", - "title": "Let's Set Up the Payroll Module. " -} \ No newline at end of file diff --git a/erpnext/payroll/notification/as b/erpnext/payroll/notification/as deleted file mode 100644 index 05c2c1bec2..0000000000 --- a/erpnext/payroll/notification/as +++ /dev/null @@ -1 +0,0 @@ -update from `tabNotification` set module='Payroll' where name = "Retention Bonus" diff --git a/erpnext/payroll/notification/retention_bonus/__init__.py b/erpnext/payroll/notification/retention_bonus/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/notification/retention_bonus/retention_bonus.json b/erpnext/payroll/notification/retention_bonus/retention_bonus.json deleted file mode 100644 index 37381fa942..0000000000 --- a/erpnext/payroll/notification/retention_bonus/retention_bonus.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "attach_print": 0, - "channel": "Email", - "condition": "doc.docstatus==1", - "creation": "2018-05-15 18:52:36.362838", - "date_changed": "bonus_payment_date", - "days_in_advance": 14, - "docstatus": 0, - "doctype": "Notification", - "document_type": "Retention Bonus", - "enabled": 1, - "event": "Days Before", - "idx": 0, - "is_standard": 1, - "message": "

{{ _(\"Hello\") }},

\n\n

{{ _(\"Retention Bonus for\") }} {{ doc.employee_name }} {{ _(\"due on\") }} {{ doc.bonus_payment_date }}

", - "modified": "2018-05-15 19:00:24.294418", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Retention Bonus", - "owner": "Administrator", - "recipients": [ - { - "email_by_role": "HR Manager" - } - ], - "subject": "Retention Bonus alert for {{ doc.employee }}" -} \ No newline at end of file diff --git a/erpnext/payroll/notification/retention_bonus/retention_bonus.md b/erpnext/payroll/notification/retention_bonus/retention_bonus.md deleted file mode 100644 index 8f48193994..0000000000 --- a/erpnext/payroll/notification/retention_bonus/retention_bonus.md +++ /dev/null @@ -1,3 +0,0 @@ -

{{ _("Hello") }},

- -

{{ _("Retention Bonus for") }} {{ doc.employee_name }} {{ _("due on") }} {{ doc.bonus_payment_date }}

\ No newline at end of file diff --git a/erpnext/payroll/notification/retention_bonus/retention_bonus.py b/erpnext/payroll/notification/retention_bonus/retention_bonus.py deleted file mode 100644 index 02e3e93333..0000000000 --- a/erpnext/payroll/notification/retention_bonus/retention_bonus.py +++ /dev/null @@ -1,3 +0,0 @@ -def get_context(context): - # do your magic here - pass diff --git a/erpnext/payroll/number_card/total_declaration_submitted/total_declaration_submitted.json b/erpnext/payroll/number_card/total_declaration_submitted/total_declaration_submitted.json deleted file mode 100644 index fa5739b2f3..0000000000 --- a/erpnext/payroll/number_card/total_declaration_submitted/total_declaration_submitted.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "creation": "2020-07-22 11:56:34.575627", - "docstatus": 0, - "doctype": "Number Card", - "document_type": "Employee Tax Exemption Declaration", - "dynamic_filters_json": "[[\"Employee Tax Exemption Declaration\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", - "filters_json": "[[\"Employee Tax Exemption Declaration\",\"creation\",\"Timespan\",\"last year\",false],[\"Employee Tax Exemption Declaration\",\"docstatus\",\"=\",\"1\",false]]", - "function": "Count", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "label": "Total Declaration Submitted", - "modified": "2020-07-22 13:22:46.001099", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Total Declaration Submitted", - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Monthly", - "type": "Document Type" -} \ No newline at end of file diff --git a/erpnext/payroll/number_card/total_incentive_given(last_month)/total_incentive_given(last_month).json b/erpnext/payroll/number_card/total_incentive_given(last_month)/total_incentive_given(last_month).json deleted file mode 100644 index 2106706173..0000000000 --- a/erpnext/payroll/number_card/total_incentive_given(last_month)/total_incentive_given(last_month).json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "aggregate_function_based_on": "incentive_amount", - "creation": "2020-07-22 11:56:34.599047", - "docstatus": 0, - "doctype": "Number Card", - "document_type": "Employee Incentive", - "dynamic_filters_json": "", - "filters_json": "[[\"Employee Incentive\",\"docstatus\",\"=\",\"1\",false],[\"Employee Incentive\",\"payroll_date\",\"Timespan\",\"last year\",false]]", - "function": "Sum", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "label": "Total Incentive Given(Last month)", - "modified": "2020-07-23 12:05:26.963616", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Total Incentive Given(Last month)", - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Monthly", - "type": "Document Type" -} \ No newline at end of file diff --git a/erpnext/payroll/number_card/total_outgoing_salary(last_month)/total_outgoing_salary(last_month).json b/erpnext/payroll/number_card/total_outgoing_salary(last_month)/total_outgoing_salary(last_month).json deleted file mode 100644 index 44ee72203f..0000000000 --- a/erpnext/payroll/number_card/total_outgoing_salary(last_month)/total_outgoing_salary(last_month).json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "aggregate_function_based_on": "rounded_total", - "creation": "2020-07-22 11:56:34.626019", - "docstatus": 0, - "doctype": "Number Card", - "document_type": "Salary Slip", - "dynamic_filters_json": "[[\"Salary Slip\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", - "filters_json": "[[\"Salary Slip\",\"docstatus\",\"=\",\"1\",false],[\"Salary Slip\",\"start_date\",\"Timespan\",\"last month\",false]]", - "function": "Sum", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "label": "Total Outgoing Salary(Last month)", - "modified": "2020-07-22 13:54:14.678954", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Total Outgoing Salary(Last month)", - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Monthly", - "type": "Document Type" -} \ No newline at end of file diff --git a/erpnext/payroll/number_card/total_salary_structure/total_salary_structure.json b/erpnext/payroll/number_card/total_salary_structure/total_salary_structure.json deleted file mode 100644 index 030935f96d..0000000000 --- a/erpnext/payroll/number_card/total_salary_structure/total_salary_structure.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "creation": "2020-07-22 11:56:34.688843", - "docstatus": 0, - "doctype": "Number Card", - "document_type": "Salary Structure", - "dynamic_filters_json": "[[\"Salary Structure\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", - "filters_json": "[[\"Salary Structure\",\"docstatus\",\"=\",\"1\",false]]", - "function": "Count", - "idx": 0, - "is_public": 1, - "is_standard": 1, - "label": "Total Salary Structure", - "modified": "2020-07-22 13:24:03.938846", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Total Salary Structure", - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Monthly", - "type": "Document Type" -} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json b/erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json deleted file mode 100644 index 8a07b10276..0000000000 --- a/erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-06-01 11:58:43.927590", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 1, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-06-01 11:58:43.927590", - "modified_by": "Administrator", - "name": "Assign Salary Structure", - "owner": "Administrator", - "reference_document": "Salary Structure Assignment", - "show_full_form": 1, - "title": "Assign Salary Structure", - "validate_action": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_employee/create_employee.json b/erpnext/payroll/onboarding_step/create_employee/create_employee.json deleted file mode 100644 index 3aa33c6d86..0000000000 --- a/erpnext/payroll/onboarding_step/create_employee/create_employee.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-05-14 11:43:25.561152", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 1, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-05-14 12:26:28.629074", - "modified_by": "Administrator", - "name": "Create Employee", - "owner": "Administrator", - "reference_document": "Employee", - "show_full_form": 0, - "title": "Create Employee", - "validate_action": 0 -} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json b/erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json deleted file mode 100644 index faada7e411..0000000000 --- a/erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-06-01 11:54:54.823796", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-06-01 11:54:54.823796", - "modified_by": "Administrator", - "name": "Create Income Tax Slab", - "owner": "Administrator", - "reference_document": "Income Tax Slab", - "show_full_form": 1, - "title": "Create Income Tax Slab", - "validate_action": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json deleted file mode 100644 index b1a7cc2734..0000000000 --- a/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-06-01 11:53:54.553947", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 1, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-06-29 11:53:54.553947", - "modified_by": "Administrator", - "name": "Create Payroll Period", - "owner": "Administrator", - "reference_document": "Payroll Period", - "show_full_form": 0, - "title": "Create Payroll Period", - "validate_action": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json b/erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json deleted file mode 100644 index 002d819618..0000000000 --- a/erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-06-01 11:57:04.002073", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-06-01 11:57:04.002073", - "modified_by": "Administrator", - "name": "Create Salary Component", - "owner": "Administrator", - "reference_document": "Salary Component", - "show_full_form": 1, - "title": "Create Salary Component", - "validate_action": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json b/erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json deleted file mode 100644 index 2aa31f485f..0000000000 --- a/erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-06-01 11:59:29.972393", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 1, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-06-01 11:59:29.972393", - "modified_by": "Administrator", - "name": "Create Salary Slip", - "owner": "Administrator", - "reference_document": "Salary Slip", - "show_full_form": 1, - "title": "Create Salary Slip", - "validate_action": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json b/erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json deleted file mode 100644 index 11d8327259..0000000000 --- a/erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-06-01 11:57:54.527808", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 1, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-06-01 11:57:54.527808", - "modified_by": "Administrator", - "name": "Create Salary Structure", - "owner": "Administrator", - "reference_document": "Salary Structure", - "show_full_form": 1, - "title": "Create Salary Structure", - "validate_action": 1 -} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json deleted file mode 100644 index a7cf7bf988..0000000000 --- a/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Update Settings", - "creation": "2020-06-04 16:34:29.664917", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 1, - "is_skipped": 0, - "modified": "2020-06-29 16:34:29.664917", - "modified_by": "Administrator", - "name": "Payroll Settings", - "owner": "Administrator", - "reference_document": "Payroll Settings", - "show_full_form": 0, - "title": "Payroll Settings", - "validate_action": 0 -} \ No newline at end of file diff --git a/erpnext/payroll/payroll_dashboard/payroll/payroll.json b/erpnext/payroll/payroll_dashboard/payroll/payroll.json deleted file mode 100644 index fb49d88de7..0000000000 --- a/erpnext/payroll/payroll_dashboard/payroll/payroll.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "cards": [ - { - "card": "Total Declaration Submitted" - }, - { - "card": "Total Salary Structure" - }, - { - "card": "Total Incentive Given(Last month)" - }, - { - "card": "Total Outgoing Salary(Last month)" - } - ], - "charts": [ - { - "chart": "Outgoing Salary", - "width": "Full" - }, - { - "chart": "Designation Wise Salary(Last Month)", - "width": "Half" - }, - { - "chart": "Department Wise Salary(Last Month)", - "width": "Half" - } - ], - "creation": "2020-07-22 11:56:34.727185", - "dashboard_name": "Payroll", - "docstatus": 0, - "doctype": "Dashboard", - "idx": 0, - "is_default": 1, - "is_standard": 1, - "modified": "2020-07-22 13:20:18.608969", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Payroll", - "owner": "Administrator" -} \ No newline at end of file diff --git a/erpnext/payroll/print_format/__init__.py b/erpnext/payroll/print_format/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/print_format/salary_slip_based_on_timesheet/__init__.py b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json deleted file mode 100644 index d5fee6b5b8..0000000000 --- a/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "creation": "2016-07-07 12:38:32.447281", - "custom_format": 0, - "disabled": 0, - "doc_type": "Salary Slip", - "docstatus": 0, - "doctype": "Print Format", - "font": "Default", - "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"HTML\", \"options\": \"

{{doc.name}}


\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"employee\"}, {\"print_hide\": 0, \"fieldname\": \"employee_name\"}, {\"print_hide\": 0, \"fieldname\": \"department\"}, {\"print_hide\": 0, \"fieldname\": \"designation\"}, {\"print_hide\": 0, \"fieldname\": \"branch\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"start_date\"}, {\"print_hide\": 0, \"fieldname\": \"end_date\"}, {\"print_hide\": 0, \"fieldname\": \"total_working_hours\"}, {\"print_hide\": 0, \"fieldname\": \"hour_rate\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"time_sheet\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"working_hours\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"timesheets\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"earnings\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"deductions\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"gross_pay\"}, {\"print_hide\": 0, \"fieldname\": \"total_deduction\"}, {\"print_hide\": 0, \"fieldname\": \"net_pay\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\"}, {\"print_hide\": 0, \"fieldname\": \"total_in_words\"}]", - "idx": 0, - "modified": "2016-08-21 21:02:59.896033", - "modified_by": "Administrator", - "name": "Salary Slip based on Timesheet", - "owner": "Administrator", - "print_format_builder": 1, - "print_format_type": "Jinja", - "standard": "Yes" -} \ No newline at end of file diff --git a/erpnext/payroll/print_format/salary_slip_standard/__init__.py b/erpnext/payroll/print_format/salary_slip_standard/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json b/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json deleted file mode 100644 index 98a4435a50..0000000000 --- a/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "align_labels_right": 0, - "creation": "2016-07-07 11:45:14.872204", - "custom_format": 0, - "disabled": 0, - "doc_type": "Salary Slip", - "docstatus": 0, - "doctype": "Print Format", - "font": "Default", - "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"

{{doc.name}}

\\n
\\n
\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"employee\", \"label\": \"Employee\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"employee_name\", \"label\": \"Employee Name\"}, {\"print_hide\": 0, \"fieldname\": \"department\", \"label\": \"Department\"}, {\"print_hide\": 0, \"fieldname\": \"designation\", \"label\": \"Designation\"}, {\"print_hide\": 0, \"fieldname\": \"branch\", \"label\": \"Branch\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"start_date\", \"label\": \"Start Date\"}, {\"print_hide\": 0, \"fieldname\": \"end_date\", \"label\": \"End Date\"}, {\"print_hide\": 0, \"fieldname\": \"total_working_days\", \"label\": \"Working Days\"}, {\"print_hide\": 0, \"fieldname\": \"leave_without_pay\", \"label\": \"Leave Without Pay\"}, {\"print_hide\": 0, \"fieldname\": \"payment_days\", \"label\": \"Payment Days\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"earnings\", \"label\": \"Earnings\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"deductions\", \"label\": \"Deductions\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"gross_pay\", \"label\": \"Gross Pay\"}, {\"print_hide\": 0, \"fieldname\": \"total_deduction\", \"label\": \"Total Deduction\"}, {\"print_hide\": 0, \"fieldname\": \"net_pay\", \"label\": \"Net Pay\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\", \"label\": \"Rounded Total\"}, {\"print_hide\": 0, \"fieldname\": \"total_in_words\", \"label\": \"Total in words\"}]", - "idx": 0, - "line_breaks": 0, - "modified": "2018-07-24 19:31:39.040701", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Slip Standard", - "owner": "Administrator", - "print_format_builder": 1, - "print_format_type": "Jinja", - "show_section_headings": 0, - "standard": "Yes" -} \ No newline at end of file diff --git a/erpnext/payroll/print_format/salary_slip_with_year_to_date/__init__.py b/erpnext/payroll/print_format/salary_slip_with_year_to_date/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/print_format/salary_slip_with_year_to_date/salary_slip_with_year_to_date.json b/erpnext/payroll/print_format/salary_slip_with_year_to_date/salary_slip_with_year_to_date.json deleted file mode 100644 index 71ba37f6ed..0000000000 --- a/erpnext/payroll/print_format/salary_slip_with_year_to_date/salary_slip_with_year_to_date.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "absolute_value": 0, - "align_labels_right": 0, - "creation": "2021-01-14 09:56:42.393623", - "custom_format": 0, - "default_print_language": "en", - "disabled": 0, - "doc_type": "Salary Slip", - "docstatus": 0, - "doctype": "Print Format", - "font": "Default", - "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"

{{doc.name}}

\\n
\\n
\\n
\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"employee\", \"print_hide\": 0, \"label\": \"Employee\"}, {\"fieldname\": \"company\", \"print_hide\": 0, \"label\": \"Company\"}, {\"fieldname\": \"employee_name\", \"print_hide\": 0, \"label\": \"Employee Name\"}, {\"fieldname\": \"department\", \"print_hide\": 0, \"label\": \"Department\"}, {\"fieldname\": \"designation\", \"print_hide\": 0, \"label\": \"Designation\"}, {\"fieldname\": \"branch\", \"print_hide\": 0, \"label\": \"Branch\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"start_date\", \"print_hide\": 0, \"label\": \"Start Date\"}, {\"fieldname\": \"end_date\", \"print_hide\": 0, \"label\": \"End Date\"}, {\"fieldname\": \"total_working_days\", \"print_hide\": 0, \"label\": \"Working Days\"}, {\"fieldname\": \"leave_without_pay\", \"print_hide\": 0, \"label\": \"Leave Without Pay\"}, {\"fieldname\": \"payment_days\", \"print_hide\": 0, \"label\": \"Payment Days\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"earnings\", \"print_hide\": 0, \"label\": \"Earnings\", \"visible_columns\": [{\"fieldname\": \"salary_component\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"amount\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"year_to_date\", \"print_width\": \"\", \"print_hide\": 0}]}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"deductions\", \"print_hide\": 0, \"label\": \"Deductions\", \"visible_columns\": [{\"fieldname\": \"salary_component\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"amount\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"year_to_date\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\", \"print_hide\": 0}]}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"gross_pay\", \"print_hide\": 0, \"label\": \"Gross Pay\"}, {\"fieldname\": \"total_deduction\", \"print_hide\": 0, \"label\": \"Total Deduction\"}, {\"fieldname\": \"net_pay\", \"print_hide\": 0, \"label\": \"Net Pay\"}, {\"fieldname\": \"rounded_total\", \"print_hide\": 0, \"label\": \"Rounded Total\"}, {\"fieldname\": \"total_in_words\", \"print_hide\": 0, \"label\": \"Total in words\"}, {\"fieldtype\": \"Section Break\", \"label\": \"net pay info\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"year_to_date\", \"print_hide\": 0, \"label\": \"Year To Date\"}, {\"fieldname\": \"month_to_date\", \"print_hide\": 0, \"label\": \"Month To Date\"}]", - "idx": 0, - "line_breaks": 0, - "modified": "2021-01-14 10:03:45.283725", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Salary Slip with Year to Date", - "owner": "Administrator", - "print_format_builder": 0, - "print_format_type": "Jinja", - "raw_printing": 0, - "show_section_headings": 0, - "standard": "Yes" -} \ No newline at end of file diff --git a/erpnext/payroll/report/__init__.py b/erpnext/payroll/report/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/report/bank_remittance/__init__.py b/erpnext/payroll/report/bank_remittance/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.js b/erpnext/payroll/report/bank_remittance/bank_remittance.js deleted file mode 100644 index 8b75b4face..0000000000 --- a/erpnext/payroll/report/bank_remittance/bank_remittance.js +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Bank Remittance"] = { - "filters": [ - { - fieldname:"company", - label: __("Company"), - fieldtype: "Link", - options: "Company", - default: frappe.defaults.get_user_default("Company"), - reqd: 1 - }, - { - fieldname:"from_date", - label: __("From Date"), - fieldtype: "Date", - }, - { - fieldname:"to_date", - label: __("To Date"), - fieldtype: "Date", - }, - - ] -} diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.json b/erpnext/payroll/report/bank_remittance/bank_remittance.json deleted file mode 100644 index 2a697b2589..0000000000 --- a/erpnext/payroll/report/bank_remittance/bank_remittance.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2019-03-26 16:57:52.558895", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2020-05-28 00:08:08.097494", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Bank Remittance", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Payroll Entry", - "report_name": "Bank Remittance", - "report_type": "Script Report", - "roles": [ - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py deleted file mode 100644 index 9d8efff821..0000000000 --- a/erpnext/payroll/report/bank_remittance/bank_remittance.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _, get_all - - -def execute(filters=None): - columns = [ - { - "label": _("Payroll Number"), - "fieldtype": "Link", - "fieldname": "payroll_no", - "options": "Payroll Entry", - "width": 150, - }, - { - "label": _("Debit A/C Number"), - "fieldtype": "Int", - "fieldname": "debit_account", - "hidden": 1, - "width": 200, - }, - {"label": _("Payment Date"), "fieldtype": "Data", "fieldname": "payment_date", "width": 100}, - { - "label": _("Employee Name"), - "fieldtype": "Link", - "fieldname": "employee_name", - "options": "Employee", - "width": 200, - }, - {"label": _("Bank Name"), "fieldtype": "Data", "fieldname": "bank_name", "width": 50}, - { - "label": _("Employee A/C Number"), - "fieldtype": "Int", - "fieldname": "employee_account_no", - "width": 50, - }, - ] - - if frappe.db.has_column("Employee", "ifsc_code"): - columns.append( - {"label": _("IFSC Code"), "fieldtype": "Data", "fieldname": "bank_code", "width": 100} - ) - - columns += [ - {"label": _("Currency"), "fieldtype": "Data", "fieldname": "currency", "width": 50}, - { - "label": _("Net Salary Amount"), - "fieldtype": "Currency", - "options": "currency", - "fieldname": "amount", - "width": 100, - }, - ] - - data = [] - - accounts = get_bank_accounts() - payroll_entries = get_payroll_entries(accounts, filters) - salary_slips = get_salary_slips(payroll_entries) - - if frappe.db.has_column("Employee", "ifsc_code"): - get_emp_bank_ifsc_code(salary_slips) - - for salary in salary_slips: - if ( - salary.bank_name - and salary.bank_account_no - and salary.debit_acc_no - and salary.status in ["Submitted", "Paid"] - ): - row = { - "payroll_no": salary.payroll_entry, - "debit_account": salary.debit_acc_no, - "payment_date": frappe.utils.formatdate(salary.modified.strftime("%Y-%m-%d")), - "bank_name": salary.bank_name, - "employee_account_no": salary.bank_account_no, - "bank_code": salary.ifsc_code, - "employee_name": salary.employee + ": " + salary.employee_name, - "currency": frappe.get_cached_value("Company", filters.company, "default_currency"), - "amount": salary.net_pay, - } - data.append(row) - - return columns, data - - -def get_bank_accounts(): - accounts = [d.name for d in get_all("Account", filters={"account_type": "Bank"})] - return accounts - - -def get_payroll_entries(accounts, filters): - payroll_filter = [ - ("payment_account", "IN", accounts), - ("number_of_employees", ">", 0), - ("Company", "=", filters.company), - ] - if filters.to_date: - payroll_filter.append(("posting_date", "<", filters.to_date)) - - if filters.from_date: - payroll_filter.append(("posting_date", ">", filters.from_date)) - - entries = get_all("Payroll Entry", payroll_filter, ["name", "payment_account"]) - - payment_accounts = [d.payment_account for d in entries] - entries = set_company_account(payment_accounts, entries) - return entries - - -def get_salary_slips(payroll_entries): - payroll = [d.name for d in payroll_entries] - salary_slips = get_all( - "Salary Slip", - filters=[("payroll_entry", "IN", payroll)], - fields=[ - "modified", - "net_pay", - "bank_name", - "bank_account_no", - "payroll_entry", - "employee", - "employee_name", - "status", - ], - ) - - payroll_entry_map = {} - for entry in payroll_entries: - payroll_entry_map[entry.name] = entry - - # appending company debit accounts - for slip in salary_slips: - if slip.payroll_entry: - slip["debit_acc_no"] = payroll_entry_map[slip.payroll_entry]["company_account"] - else: - slip["debit_acc_no"] = None - - return salary_slips - - -def get_emp_bank_ifsc_code(salary_slips): - emp_names = [d.employee for d in salary_slips] - ifsc_codes = get_all("Employee", [("name", "IN", emp_names)], ["ifsc_code", "name"]) - - ifsc_codes_map = {} - for code in ifsc_codes: - ifsc_codes_map[code.name] = code - - for slip in salary_slips: - slip["ifsc_code"] = ifsc_codes_map[code.name]["ifsc_code"] - - return salary_slips - - -def set_company_account(payment_accounts, payroll_entries): - company_accounts = get_all( - "Bank Account", [("account", "in", payment_accounts)], ["account", "bank_account_no"] - ) - company_accounts_map = {} - for acc in company_accounts: - company_accounts_map[acc.account] = acc - - for entry in payroll_entries: - company_account = "" - if entry.payment_account in company_accounts_map: - company_account = company_accounts_map[entry.payment_account]["bank_account_no"] - entry["company_account"] = company_account - - return payroll_entries diff --git a/erpnext/payroll/report/income_tax_computation/__init__.py b/erpnext/payroll/report/income_tax_computation/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/report/income_tax_computation/income_tax_computation.js b/erpnext/payroll/report/income_tax_computation/income_tax_computation.js deleted file mode 100644 index 26e463f268..0000000000 --- a/erpnext/payroll/report/income_tax_computation/income_tax_computation.js +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Income Tax Computation"] = { - "filters": [ - { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "width": "100px", - "reqd": 1 - }, - { - "fieldname":"payroll_period", - "label": __("Payroll Period"), - "fieldtype": "Link", - "options": "Payroll Period", - "width": "100px", - "reqd": 1 - }, - { - "fieldname":"employee", - "label": __("Employee"), - "fieldtype": "Link", - "options": "Employee", - "width": "100px" - }, - { - "fieldname":"department", - "label": __("Department"), - "fieldtype": "Link", - "options": "Department", - "width": "100px", - }, - { - "fieldname":"consider_tax_exemption_declaration", - "label": __("Consider Tax Exemption Declaration"), - "fieldtype": "Check", - "width": "180px" - } - ] -}; - - diff --git a/erpnext/payroll/report/income_tax_computation/income_tax_computation.json b/erpnext/payroll/report/income_tax_computation/income_tax_computation.json deleted file mode 100644 index 7cb5b2270c..0000000000 --- a/erpnext/payroll/report/income_tax_computation/income_tax_computation.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "add_total_row": 0, - "columns": [], - "creation": "2022-02-17 17:19:30.921422", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "filters": [], - "idx": 0, - "is_standard": "Yes", - "letter_head": "", - "modified": "2022-02-23 13:07:30.347861", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Income Tax Computation", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Salary Slip", - "report_name": "Income Tax Computation", - "report_type": "Script Report", - "roles": [ - { - "role": "Employee" - }, - { - "role": "HR User" - }, - { - "role": "HR Manager" - }, - { - "role": "Employee Self Service" - } - ] -} \ No newline at end of file diff --git a/erpnext/payroll/report/income_tax_computation/income_tax_computation.py b/erpnext/payroll/report/income_tax_computation/income_tax_computation.py deleted file mode 100644 index 739ed8eb2d..0000000000 --- a/erpnext/payroll/report/income_tax_computation/income_tax_computation.py +++ /dev/null @@ -1,513 +0,0 @@ -# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -import frappe -from frappe import _, scrub -from frappe.query_builder.functions import Sum -from frappe.utils import add_days, flt, getdate, rounded - -from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates -from erpnext.payroll.doctype.salary_slip.salary_slip import calculate_tax_by_tax_slab - - -def execute(filters=None): - return IncomeTaxComputationReport(filters).run() - - -class IncomeTaxComputationReport(object): - def __init__(self, filters=None): - self.filters = frappe._dict(filters or {}) - self.columns = [] - self.data = [] - self.employees = frappe._dict() - self.payroll_period_start_date = None - self.payroll_period_end_date = None - if self.filters.payroll_period: - self.payroll_period_start_date, self.payroll_period_end_date = frappe.db.get_value( - "Payroll Period", self.filters.payroll_period, ["start_date", "end_date"] - ) - - def run(self): - self.get_fixed_columns() - self.get_data() - return self.columns, self.data - - def get_data(self): - self.get_employee_details() - self.get_future_salary_slips() - self.get_ctc() - self.get_tax_exempted_earnings_and_deductions() - self.get_employee_tax_exemptions() - self.get_hra() - self.get_standard_tax_exemption() - self.get_total_taxable_amount() - self.get_applicable_tax() - self.get_total_deducted_tax() - self.get_payable_tax() - - self.data = list(self.employees.values()) - - def get_employee_details(self): - filters, or_filters = self.get_employee_filters() - fields = [ - "name as employee", - "employee_name", - "department", - "designation", - "date_of_joining", - "relieving_date", - ] - - employees = frappe.get_all("Employee", filters=filters, or_filters=or_filters, fields=fields) - ss_assignments = self.get_ss_assignments([d.employee for d in employees]) - - for d in employees: - if d.employee in list(ss_assignments.keys()): - d.update(ss_assignments[d.employee]) - self.employees.setdefault(d.employee, d) - - if not self.employees: - frappe.throw(_("No employees found with selected filters and active salary structure")) - - def get_employee_filters(self): - filters = {"company": self.filters.company} - or_filters = { - "status": "Active", - "relieving_date": ["between", [self.payroll_period_start_date, self.payroll_period_end_date]], - } - if self.filters.employee: - filters = {"name": self.filters.employee} - elif self.filters.department: - filters.update({"department": self.filters.department}) - - return filters, or_filters - - def get_ss_assignments(self, employees): - ss_assignments = frappe.get_all( - "Salary Structure Assignment", - filters={ - "employee": ["in", employees], - "docstatus": 1, - "salary_structure": ["is", "set"], - "income_tax_slab": ["is", "set"], - }, - fields=["employee", "income_tax_slab", "salary_structure"], - order_by="from_date desc", - ) - - employee_ss_assignments = frappe._dict() - for d in ss_assignments: - if d.employee not in list(employee_ss_assignments.keys()): - tax_slab = frappe.get_cached_value( - "Income Tax Slab", d.income_tax_slab, ["allow_tax_exemption", "disabled"], as_dict=1 - ) - - if tax_slab and not tax_slab.disabled: - employee_ss_assignments.setdefault( - d.employee, - { - "salary_structure": d.salary_structure, - "income_tax_slab": d.income_tax_slab, - "allow_tax_exemption": tax_slab.allow_tax_exemption, - }, - ) - return employee_ss_assignments - - def get_future_salary_slips(self): - self.future_salary_slips = frappe._dict() - for employee in list(self.employees.keys()): - last_ss = self.get_last_salary_slip(employee) - if last_ss and last_ss.end_date == self.payroll_period_end_date: - continue - - relieving_date = self.employees[employee].get("relieving_date", "") - if last_ss: - ss_start_date = add_days(last_ss.end_date, 1) - else: - ss_start_date = self.payroll_period_start_date - last_ss = frappe._dict( - { - "payroll_frequency": "Monthly", - "salary_structure": self.employees[employee].get("salary_structure"), - } - ) - - while getdate(ss_start_date) < getdate(self.payroll_period_end_date) and ( - not relieving_date or getdate(ss_start_date) < relieving_date - ): - ss_end_date = get_start_end_dates(last_ss.payroll_frequency, ss_start_date).end_date - - ss = frappe.new_doc("Salary Slip") - ss.employee = employee - ss.start_date = ss_start_date - ss.end_date = ss_end_date - ss.salary_structure = last_ss.salary_structure - ss.payroll_frequency = last_ss.payroll_frequency - ss.company = self.filters.company - try: - ss.process_salary_structure(for_preview=1) - self.future_salary_slips.setdefault(employee, []).append(ss.as_dict()) - except Exception: - break - - ss_start_date = add_days(ss_end_date, 1) - - def get_last_salary_slip(self, employee): - last_salary_slip = frappe.db.get_value( - "Salary Slip", - { - "employee": employee, - "docstatus": 1, - "start_date": ["between", [self.payroll_period_start_date, self.payroll_period_end_date]], - }, - ["start_date", "end_date", "salary_structure", "payroll_frequency"], - order_by="start_date desc", - as_dict=1, - ) - - return last_salary_slip - - def get_ctc(self): - # Get total earnings from existing salary slip - ss = frappe.qb.DocType("Salary Slip") - existing_ss = frappe._dict( - ( - frappe.qb.from_(ss) - .select(ss.employee, Sum(ss.base_gross_pay).as_("amount")) - .where(ss.docstatus == 1) - .where(ss.employee.isin(list(self.employees.keys()))) - .where(ss.start_date >= self.payroll_period_start_date) - .where(ss.end_date <= self.payroll_period_end_date) - .groupby(ss.employee) - ).run() - ) - - for employee in list(self.employees.keys()): - future_ss_earnings = self.get_future_earnings(employee) - ctc = flt(existing_ss.get(employee)) + future_ss_earnings - - self.employees[employee].setdefault("ctc", ctc) - - def get_future_earnings(self, employee): - future_earnings = 0.0 - for ss in self.future_salary_slips.get(employee, []): - future_earnings += flt(ss.base_gross_pay) - - return future_earnings - - def get_tax_exempted_earnings_and_deductions(self): - tax_exempted_components = self.get_tax_exempted_components() - - # Get component totals from existing salary slips - ss = frappe.qb.DocType("Salary Slip") - ss_comps = frappe.qb.DocType("Salary Detail") - - records = ( - frappe.qb.from_(ss) - .inner_join(ss_comps) - .on(ss.name == ss_comps.parent) - .select(ss.name, ss.employee, ss_comps.salary_component, Sum(ss_comps.amount).as_("amount")) - .where(ss.docstatus == 1) - .where(ss.employee.isin(list(self.employees.keys()))) - .where(ss_comps.salary_component.isin(tax_exempted_components)) - .where(ss.start_date >= self.payroll_period_start_date) - .where(ss.end_date <= self.payroll_period_end_date) - .groupby(ss.employee, ss_comps.salary_component) - ).run(as_dict=True) - - existing_ss_exemptions = frappe._dict() - for d in records: - existing_ss_exemptions.setdefault(d.employee, {}).setdefault( - scrub(d.salary_component), d.amount - ) - - for employee in list(self.employees.keys()): - if not self.employees[employee]["allow_tax_exemption"]: - continue - - exemptions = existing_ss_exemptions.get(employee, {}) - self.add_exemptions_from_future_salary_slips(employee, exemptions) - self.employees[employee].update(exemptions) - - total_exemptions = sum(list(exemptions.values())) - self.add_to_total_exemption(employee, total_exemptions) - - def add_exemptions_from_future_salary_slips(self, employee, exemptions): - for ss in self.future_salary_slips.get(employee, []): - for e in ss.earnings: - if not e.is_tax_applicable: - exemptions.setdefault(scrub(e.salary_component), 0) - exemptions[scrub(e.salary_component)] += flt(e.amount) - - for d in ss.deductions: - if d.exempted_from_income_tax: - exemptions.setdefault(scrub(d.salary_component), 0) - exemptions[scrub(d.salary_component)] += flt(d.amount) - - return exemptions - - def get_tax_exempted_components(self): - # nontaxable earning components - nontaxable_earning_components = [ - d.name - for d in frappe.get_all( - "Salary Component", {"type": "Earning", "is_tax_applicable": 0, "disabled": 0} - ) - ] - - # tax exempted deduction components - tax_exempted_deduction_components = [ - d.name - for d in frappe.get_all( - "Salary Component", {"type": "Deduction", "exempted_from_income_tax": 1, "disabled": 0} - ) - ] - - tax_exempted_components = nontaxable_earning_components + tax_exempted_deduction_components - - # Add columns - for d in tax_exempted_components: - self.add_column(d) - - return tax_exempted_components - - def add_to_total_exemption(self, employee, amount): - self.employees[employee].setdefault("total_exemption", 0) - self.employees[employee]["total_exemption"] += amount - - def get_employee_tax_exemptions(self): - # add columns - exemption_categories = frappe.get_all("Employee Tax Exemption Category", {"is_active": 1}) - for d in exemption_categories: - self.add_column(d.name) - - self.employees_with_proofs = [] - self.get_tax_exemptions("Employee Tax Exemption Proof Submission") - if self.filters.consider_tax_exemption_declaration: - self.get_tax_exemptions("Employee Tax Exemption Declaration") - - def get_tax_exemptions(self, source): - # Get category-wise exmeptions based on submitted proofs or declarations - if source == "Employee Tax Exemption Proof Submission": - child_doctype = "Employee Tax Exemption Proof Submission Detail" - else: - child_doctype = "Employee Tax Exemption Declaration Category" - - max_exemptions = self.get_max_exemptions_based_on_category() - - par = frappe.qb.DocType(source) - child = frappe.qb.DocType(child_doctype) - - records = ( - frappe.qb.from_(par) - .inner_join(child) - .on(par.name == child.parent) - .select(par.employee, child.exemption_category, Sum(child.amount).as_("amount")) - .where(par.docstatus == 1) - .where(par.employee.isin(list(self.employees.keys()))) - .where(par.payroll_period == self.filters.payroll_period) - .groupby(par.employee, child.exemption_category) - ).run(as_dict=True) - - for d in records: - if not self.employees[d.employee]["allow_tax_exemption"]: - continue - - if source == "Employee Tax Exemption Declaration" and d.employee in self.employees_with_proofs: - continue - - amount = flt(d.amount) - max_eligible_amount = flt(max_exemptions.get(d.exemption_category)) - if max_eligible_amount and amount > max_eligible_amount: - amount = max_eligible_amount - - self.employees[d.employee].setdefault(scrub(d.exemption_category), amount) - self.add_to_total_exemption(d.employee, amount) - - if ( - source == "Employee Tax Exemption Proof Submission" - and d.employee not in self.employees_with_proofs - ): - self.employees_with_proofs.append(d.employee) - - def get_max_exemptions_based_on_category(self): - return dict( - frappe.get_all( - "Employee Tax Exemption Category", - filters={"is_active": 1}, - fields=["name", "max_amount"], - as_list=1, - ) - ) - - def get_hra(self): - if not frappe.get_meta("Employee Tax Exemption Declaration").has_field("monthly_house_rent"): - return - - self.add_column("HRA") - - self.employees_with_proofs = [] - self.get_eligible_hra("Employee Tax Exemption Proof Submission") - if self.filters.consider_tax_exemption_declaration: - self.get_eligible_hra("Employee Tax Exemption Declaration") - - def get_eligible_hra(self, source): - if source == "Employee Tax Exemption Proof Submission": - hra_amount_field = "total_eligible_hra_exemption" - else: - hra_amount_field = "annual_hra_exemption" - - records = frappe.get_all( - source, - filters={ - "docstatus": 1, - "employee": ["in", list(self.employees.keys())], - "payroll_period": self.filters.payroll_period, - }, - fields=["employee", hra_amount_field], - as_list=1, - ) - - for d in records: - if not self.employees[d[0]]["allow_tax_exemption"]: - continue - - if d[0] not in self.employees_with_proofs: - self.employees[d[0]].setdefault("hra", d[1]) - self.add_to_total_exemption(d[0], d[1]) - self.employees_with_proofs.append(d[0]) - - def get_standard_tax_exemption(self): - self.add_column("Standard Tax Exemption") - - standard_exemptions_per_slab = dict( - frappe.get_all( - "Income Tax Slab", - filters={"company": self.filters.company, "docstatus": 1, "disabled": 0}, - fields=["name", "standard_tax_exemption_amount"], - as_list=1, - ) - ) - - for emp, emp_details in self.employees.items(): - if not self.employees[emp]["allow_tax_exemption"]: - continue - - income_tax_slab = emp_details.get("income_tax_slab") - standard_exemption = standard_exemptions_per_slab.get(income_tax_slab, 0) - emp_details["standard_tax_exemption"] = standard_exemption - self.add_to_total_exemption(emp, standard_exemption) - - self.add_column("Total Exemption") - - def get_total_taxable_amount(self): - self.add_column("Total Taxable Amount") - for emp, emp_details in self.employees.items(): - emp_details["total_taxable_amount"] = flt(emp_details.get("ctc")) - flt( - emp_details.get("total_exemption") - ) - - def get_applicable_tax(self): - self.add_column("Applicable Tax") - - is_tax_rounded = frappe.db.get_value( - "Salary Component", - {"variable_based_on_taxable_salary": 1, "disabled": 0}, - "round_to_the_nearest_integer", - ) - - for emp, emp_details in self.employees.items(): - tax_slab = emp_details.get("income_tax_slab") - if tax_slab: - tax_slab = frappe.get_cached_doc("Income Tax Slab", tax_slab) - employee_dict = frappe.get_doc("Employee", emp).as_dict() - tax_amount = calculate_tax_by_tax_slab( - emp_details["total_taxable_amount"], tax_slab, eval_globals=None, eval_locals=employee_dict - ) - else: - tax_amount = 0.0 - - if is_tax_rounded: - tax_amount = rounded(tax_amount) - emp_details["applicable_tax"] = tax_amount - - def get_total_deducted_tax(self): - self.add_column("Total Tax Deducted") - - ss = frappe.qb.DocType("Salary Slip") - ss_ded = frappe.qb.DocType("Salary Detail") - - records = ( - frappe.qb.from_(ss) - .inner_join(ss_ded) - .on(ss.name == ss_ded.parent) - .select(ss.employee, Sum(ss_ded.amount).as_("amount")) - .where(ss.docstatus == 1) - .where(ss.employee.isin(list(self.employees.keys()))) - .where(ss_ded.parentfield == "deductions") - .where(ss_ded.variable_based_on_taxable_salary == 1) - .where(ss.start_date >= self.payroll_period_start_date) - .where(ss.end_date <= self.payroll_period_end_date) - .groupby(ss.employee) - ).run(as_dict=True) - - for d in records: - self.employees[d.employee].setdefault("total_tax_deducted", d.amount) - - def get_payable_tax(self): - self.add_column("Payable Tax") - - for emp, emp_details in self.employees.items(): - emp_details["payable_tax"] = flt(emp_details.get("applicable_tax")) - flt( - emp_details.get("total_tax_deducted") - ) - - def add_column(self, label, fieldname=None, fieldtype=None, options=None, width=None): - col = { - "label": _(label), - "fieldname": fieldname or scrub(label), - "fieldtype": fieldtype or "Currency", - "options": options, - "width": width or "140px", - } - self.columns.append(col) - - def get_fixed_columns(self): - self.columns = [ - { - "label": _("Employee"), - "fieldname": "employee", - "fieldtype": "Link", - "options": "Employee", - "width": "140px", - }, - { - "label": _("Employee Name"), - "fieldname": "employee_name", - "fieldtype": "Data", - "width": "160px", - }, - { - "label": _("Department"), - "fieldname": "department", - "fieldtype": "Link", - "options": "Department", - "width": "140px", - }, - { - "label": _("Designation"), - "fieldname": "designation", - "fieldtype": "Link", - "options": "Designation", - "width": "140px", - }, - {"label": _("Date of Joining"), "fieldname": "date_of_joining", "fieldtype": "Date"}, - { - "label": _("Income Tax Slab"), - "fieldname": "income_tax_slab", - "fieldtype": "Link", - "options": "Income Tax Slab", - "width": "140px", - }, - {"label": _("CTC"), "fieldname": "ctc", "fieldtype": "Currency", "width": "140px"}, - ] diff --git a/erpnext/payroll/report/income_tax_computation/test_income_tax_computation.py b/erpnext/payroll/report/income_tax_computation/test_income_tax_computation.py deleted file mode 100644 index 57ca317160..0000000000 --- a/erpnext/payroll/report/income_tax_computation/test_income_tax_computation.py +++ /dev/null @@ -1,115 +0,0 @@ -import unittest - -import frappe -from frappe.utils import getdate - -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import ( - create_payroll_period, -) -from erpnext.payroll.doctype.salary_slip.test_salary_slip import ( - create_exemption_declaration, - create_salary_slips_for_payroll_period, - create_tax_slab, -) -from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure -from erpnext.payroll.report.income_tax_computation.income_tax_computation import execute - - -class TestIncomeTaxComputation(unittest.TestCase): - def setUp(self): - self.cleanup_records() - self.create_records() - - def tearDown(self): - frappe.db.rollback() - - def cleanup_records(self): - frappe.db.sql("delete from `tabEmployee Tax Exemption Declaration`") - frappe.db.sql("delete from `tabPayroll Period`") - frappe.db.sql("delete from `tabIncome Tax Slab`") - frappe.db.sql("delete from `tabSalary Component`") - frappe.db.sql("delete from `tabEmployee Benefit Application`") - frappe.db.sql("delete from `tabEmployee Benefit Claim`") - frappe.db.sql("delete from `tabEmployee` where company='_Test Company'") - frappe.db.sql("delete from `tabSalary Slip`") - - def create_records(self): - self.employee = make_employee( - "employee_tax_computation@example.com", - company="_Test Company", - date_of_joining=getdate("01-10-2021"), - ) - - self.payroll_period = create_payroll_period( - name="_Test Payroll Period 1", company="_Test Company" - ) - - self.income_tax_slab = create_tax_slab( - self.payroll_period, - allow_tax_exemption=True, - effective_date=getdate("2019-04-01"), - company="_Test Company", - ) - salary_structure = make_salary_structure( - "Monthly Salary Structure Test Income Tax Computation", - "Monthly", - employee=self.employee, - company="_Test Company", - currency="INR", - payroll_period=self.payroll_period, - test_tax=True, - ) - - create_exemption_declaration(self.employee, self.payroll_period.name) - - create_salary_slips_for_payroll_period( - self.employee, salary_structure.name, self.payroll_period, deduct_random=False, num=3 - ) - - def test_report(self): - filters = frappe._dict( - { - "company": "_Test Company", - "payroll_period": self.payroll_period.name, - "employee": self.employee, - } - ) - - result = execute(filters) - - expected_data = { - "employee": self.employee, - "employee_name": "employee_tax_computation@example.com", - "department": "All Departments", - "income_tax_slab": self.income_tax_slab, - "ctc": 936000.0, - "professional_tax": 2400.0, - "standard_tax_exemption": 50000, - "total_exemption": 52400.0, - "total_taxable_amount": 883600.0, - "applicable_tax": 92789.0, - "total_tax_deducted": 17997.0, - "payable_tax": 74792, - } - - for key, val in expected_data.items(): - self.assertEqual(result[1][0].get(key), val) - - # Run report considering tax exemption declaration - filters.consider_tax_exemption_declaration = 1 - - result = execute(filters) - - expected_data.update( - { - "_test_category": 100000.0, - "total_exemption": 152400.0, - "total_taxable_amount": 783600.0, - "applicable_tax": 71989.0, - "payable_tax": 53992.0, - } - ) - - for key, val in expected_data.items(): - self.assertEqual(result[1][0].get(key), val) diff --git a/erpnext/payroll/report/income_tax_deductions/__init__.py b/erpnext/payroll/report/income_tax_deductions/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js deleted file mode 100644 index 6ecf2b1960..0000000000 --- a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { - frappe.query_reports["Income Tax Deductions"] = erpnext.salary_slip_deductions_report_filters; -}); diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json deleted file mode 100644 index cf80398f98..0000000000 --- a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2020-05-30 00:07:56.744372", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2020-05-30 00:07:56.744372", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Income Tax Deductions", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Salary Slip", - "report_name": "Income Tax Deductions", - "report_type": "Script Report", - "roles": [ - { - "role": "HR User" - }, - { - "role": "HR Manager" - }, - { - "role": "Employee" - } - ] -} \ No newline at end of file diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py deleted file mode 100644 index ccf16565c1..0000000000 --- a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ - -import erpnext - - -def execute(filters=None): - data = get_data(filters) - columns = get_columns(filters) if len(data) else [] - - return columns, data - - -def get_columns(filters): - columns = [ - { - "label": _("Employee"), - "options": "Employee", - "fieldname": "employee", - "fieldtype": "Link", - "width": 200, - }, - { - "label": _("Employee Name"), - "options": "Employee", - "fieldname": "employee_name", - "fieldtype": "Link", - "width": 160, - }, - ] - - if erpnext.get_region() == "India": - columns.append( - {"label": _("PAN Number"), "fieldname": "pan_number", "fieldtype": "Data", "width": 140} - ) - - columns += [ - {"label": _("Income Tax Component"), "fieldname": "it_comp", "fieldtype": "Data", "width": 170}, - { - "label": _("Income Tax Amount"), - "fieldname": "it_amount", - "fieldtype": "Currency", - "options": "currency", - "width": 140, - }, - { - "label": _("Gross Pay"), - "fieldname": "gross_pay", - "fieldtype": "Currency", - "options": "currency", - "width": 140, - }, - {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 140}, - ] - - return columns - - -def get_conditions(filters): - conditions = [""] - - if filters.get("department"): - conditions.append("sal.department = '%s' " % (filters["department"])) - - if filters.get("branch"): - conditions.append("sal.branch = '%s' " % (filters["branch"])) - - if filters.get("company"): - conditions.append("sal.company = '%s' " % (filters["company"])) - - if filters.get("month"): - conditions.append("month(sal.start_date) = '%s' " % (filters["month"])) - - if filters.get("year"): - conditions.append("year(start_date) = '%s' " % (filters["year"])) - - return " and ".join(conditions) - - -def get_data(filters): - - data = [] - - if erpnext.get_region() == "India": - employee_pan_dict = frappe._dict( - frappe.db.sql(""" select employee, pan_number from `tabEmployee`""") - ) - - component_types = frappe.db.sql( - """ select name from `tabSalary Component` - where is_income_tax_component = 1 """ - ) - - component_types = [comp_type[0] for comp_type in component_types] - - if not len(component_types): - return [] - - conditions = get_conditions(filters) - - entry = frappe.db.sql( - """ select sal.employee, sal.employee_name, sal.posting_date, ded.salary_component, ded.amount,sal.gross_pay - from `tabSalary Slip` sal, `tabSalary Detail` ded - where sal.name = ded.parent - and ded.parentfield = 'deductions' - and ded.parenttype = 'Salary Slip' - and sal.docstatus = 1 %s - and ded.salary_component in (%s) - """ - % (conditions, ", ".join(["%s"] * len(component_types))), - tuple(component_types), - as_dict=1, - ) - - for d in entry: - - employee = { - "employee": d.employee, - "employee_name": d.employee_name, - "it_comp": d.salary_component, - "posting_date": d.posting_date, - # "pan_number": employee_pan_dict.get(d.employee), - "it_amount": d.amount, - "gross_pay": d.gross_pay, - } - - if erpnext.get_region() == "India": - employee["pan_number"] = employee_pan_dict.get(d.employee) - - data.append(employee) - - return data diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/__init__.py b/erpnext/payroll/report/salary_payments_based_on_payment_mode/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js deleted file mode 100644 index 9b82954169..0000000000 --- a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { - frappe.query_reports["Salary Payments Based On Payment Mode"] = erpnext.salary_slip_deductions_report_filters; -}); diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json deleted file mode 100644 index c04cc32b9b..0000000000 --- a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2020-06-16 18:43:43.107246", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2020-06-16 18:43:43.107246", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Salary Payments Based On Payment Mode", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Salary Slip", - "report_name": "Salary Payments Based On Payment Mode", - "report_type": "Script Report", - "roles": [ - { - "role": "HR User" - }, - { - "role": "HR Manager" - }, - { - "role": "Employee" - } - ] -} \ No newline at end of file diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py deleted file mode 100644 index 4223f9d4fb..0000000000 --- a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ - -import erpnext -from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import ( - get_conditions, -) - - -def execute(filters=None): - mode_of_payments = get_payment_modes() - - if not len(mode_of_payments): - return [], [] - - columns = get_columns(filters, mode_of_payments) - data, total_rows, report_summary = get_data(filters, mode_of_payments) - chart = get_chart(mode_of_payments, total_rows) - - return columns, data, None, chart, report_summary - - -def get_columns(filters, mode_of_payments): - columns = [ - { - "label": _("Branch"), - "options": "Branch", - "fieldname": "branch", - "fieldtype": "Link", - "width": 200, - } - ] - - for mode in mode_of_payments: - columns.append({"label": _(mode), "fieldname": mode, "fieldtype": "Currency", "width": 160}) - - columns.append({"label": _("Total"), "fieldname": "total", "fieldtype": "Currency", "width": 140}) - - return columns - - -def get_payment_modes(): - mode_of_payments = frappe.db.sql_list( - """ - select distinct mode_of_payment from `tabSalary Slip` where docstatus = 1 - """ - ) - return mode_of_payments - - -def prepare_data(entry): - branch_wise_entries = {} - gross_pay = 0 - - for d in entry: - gross_pay += d.gross_pay - if branch_wise_entries.get(d.branch): - branch_wise_entries[d.branch][d.mode_of_payment] = d.net_pay - else: - branch_wise_entries.setdefault(d.branch, {}).setdefault(d.mode_of_payment, d.net_pay) - - return branch_wise_entries, gross_pay - - -def get_data(filters, mode_of_payments): - data = [] - - conditions = get_conditions(filters) - - entry = frappe.db.sql( - """ - select branch, mode_of_payment, sum(net_pay) as net_pay, sum(gross_pay) as gross_pay - from `tabSalary Slip` sal - where docstatus = 1 %s - group by branch, mode_of_payment - """ - % (conditions), - as_dict=1, - ) - - branch_wise_entries, gross_pay = prepare_data(entry) - - branches = frappe.db.sql_list( - """ - select distinct branch from `tabSalary Slip` sal - where docstatus = 1 %s - """ - % (conditions) - ) - - total_row = {"total": 0, "branch": "Total"} - - for branch in branches: - total = 0 - row = {"branch": branch} - for mode in mode_of_payments: - if branch_wise_entries.get(branch).get(mode): - row[mode] = branch_wise_entries.get(branch).get(mode) - total += branch_wise_entries.get(branch).get(mode) - - row["total"] = total - data.append(row) - - total_row = get_total_based_on_mode_of_payment(data, mode_of_payments) - total_deductions = gross_pay - total_row.get("total") - - report_summary = [] - - if data: - data.append(total_row) - data.append({}) - data.append({"branch": "Total Gross Pay", mode_of_payments[0]: gross_pay}) - data.append({"branch": "Total Deductions", mode_of_payments[0]: total_deductions}) - data.append({"branch": "Total Net Pay", mode_of_payments[0]: total_row.get("total")}) - - currency = erpnext.get_company_currency(filters.company) - report_summary = get_report_summary( - gross_pay, total_deductions, total_row.get("total"), currency - ) - - return data, total_row, report_summary - - -def get_total_based_on_mode_of_payment(data, mode_of_payments): - - total = 0 - total_row = {"branch": "Total"} - for mode in mode_of_payments: - sum_of_payment = sum([detail[mode] for detail in data if mode in detail.keys()]) - total_row[mode] = sum_of_payment - total += sum_of_payment - - total_row["total"] = total - return total_row - - -def get_report_summary(gross_pay, total_deductions, net_pay, currency): - return [ - { - "value": gross_pay, - "label": _("Total Gross Pay"), - "indicator": "Green", - "datatype": "Currency", - "currency": currency, - }, - { - "value": total_deductions, - "label": _("Total Deduction"), - "datatype": "Currency", - "indicator": "Red", - "currency": currency, - }, - { - "value": net_pay, - "label": _("Total Net Pay"), - "datatype": "Currency", - "indicator": "Blue", - "currency": currency, - }, - ] - - -def get_chart(mode_of_payments, data): - if data: - values = [] - labels = [] - - for mode in mode_of_payments: - values.append(data[mode]) - labels.append([mode]) - - chart = { - "data": {"labels": labels, "datasets": [{"name": "Mode Of Payments", "values": values}]} - } - chart["type"] = "bar" - return chart diff --git a/erpnext/payroll/report/salary_payments_via_ecs/__init__.py b/erpnext/payroll/report/salary_payments_via_ecs/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js deleted file mode 100644 index e49fc112ff..0000000000 --- a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { - - let ecs_checklist_filter = erpnext.salary_slip_deductions_report_filters - ecs_checklist_filter['filters'].push({ - fieldname: "type", - label: __("Type"), - fieldtype: "Select", - options:["", "Bank", "Cash", "Cheque"] - }) - - frappe.query_reports["Salary Payments via ECS"] = ecs_checklist_filter -}); diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json deleted file mode 100644 index dd0ac7c4ef..0000000000 --- a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2020-06-16 18:35:30.508143", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2020-06-16 18:38:23.680185", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Salary Payments via ECS", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Salary Slip", - "report_name": "Salary Payments via ECS", - "report_type": "Script Report", - "roles": [ - { - "role": "HR Manager" - }, - { - "role": "HR User" - } - ] -} \ No newline at end of file diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py deleted file mode 100644 index 4f9370b742..0000000000 --- a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ - -import erpnext - - -def execute(filters=None): - columns = get_columns(filters) - data = get_data(filters) - - return columns, data - - -def get_columns(filters): - columns = [ - { - "label": _("Branch"), - "options": "Branch", - "fieldname": "branch", - "fieldtype": "Link", - "width": 200, - }, - { - "label": _("Employee Name"), - "options": "Employee", - "fieldname": "employee_name", - "fieldtype": "Link", - "width": 160, - }, - { - "label": _("Employee"), - "options": "Employee", - "fieldname": "employee", - "fieldtype": "Link", - "width": 140, - }, - { - "label": _("Gross Pay"), - "fieldname": "gross_pay", - "fieldtype": "Currency", - "options": "currency", - "width": 140, - }, - {"label": _("Bank"), "fieldname": "bank", "fieldtype": "Data", "width": 140}, - {"label": _("Account No"), "fieldname": "account_no", "fieldtype": "Data", "width": 140}, - ] - if erpnext.get_region() == "India": - columns += [ - {"label": _("IFSC"), "fieldname": "ifsc", "fieldtype": "Data", "width": 140}, - {"label": _("MICR"), "fieldname": "micr", "fieldtype": "Data", "width": 140}, - ] - - return columns - - -def get_conditions(filters): - conditions = [""] - - if filters.get("department"): - conditions.append("department = '%s' " % (filters["department"])) - - if filters.get("branch"): - conditions.append("branch = '%s' " % (filters["branch"])) - - if filters.get("company"): - conditions.append("company = '%s' " % (filters["company"])) - - if filters.get("month"): - conditions.append("month(start_date) = '%s' " % (filters["month"])) - - if filters.get("year"): - conditions.append("year(start_date) = '%s' " % (filters["year"])) - - return " and ".join(conditions) - - -def get_data(filters): - - data = [] - - fields = ["employee", "branch", "bank_name", "bank_ac_no", "salary_mode"] - if erpnext.get_region() == "India": - fields += ["ifsc_code", "micr_code"] - - employee_details = frappe.get_list("Employee", fields=fields) - employee_data_dict = {} - - for d in employee_details: - employee_data_dict.setdefault( - d.employee, - { - "bank_ac_no": d.bank_ac_no, - "ifsc_code": d.ifsc_code or None, - "micr_code": d.micr_code or None, - "branch": d.branch, - "salary_mode": d.salary_mode, - "bank_name": d.bank_name, - }, - ) - - conditions = get_conditions(filters) - - entry = frappe.db.sql( - """ select employee, employee_name, gross_pay - from `tabSalary Slip` - where docstatus = 1 %s """ - % (conditions), - as_dict=1, - ) - - for d in entry: - - employee = { - "branch": employee_data_dict.get(d.employee).get("branch"), - "employee_name": d.employee_name, - "employee": d.employee, - "gross_pay": d.gross_pay, - } - - if employee_data_dict.get(d.employee).get("salary_mode") == "Bank": - employee["bank"] = employee_data_dict.get(d.employee).get("bank_name") - employee["account_no"] = employee_data_dict.get(d.employee).get("bank_ac_no") - if erpnext.get_region() == "India": - employee["ifsc"] = employee_data_dict.get(d.employee).get("ifsc_code") - employee["micr"] = employee_data_dict.get(d.employee).get("micr_code") - else: - employee["account_no"] = employee_data_dict.get(d.employee).get("salary_mode") - - if filters.get("type") and employee_data_dict.get(d.employee).get("salary_mode") == filters.get( - "type" - ): - data.append(employee) - elif not filters.get("type"): - data.append(employee) - - return data diff --git a/erpnext/payroll/report/salary_register/__init__.py b/erpnext/payroll/report/salary_register/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/payroll/report/salary_register/salary_register.html b/erpnext/payroll/report/salary_register/salary_register.html deleted file mode 100644 index 3abc3a008d..0000000000 --- a/erpnext/payroll/report/salary_register/salary_register.html +++ /dev/null @@ -1,40 +0,0 @@ -{% - var report_columns = report.get_columns_for_print(); -%} -
- {%= frappe.boot.letter_heads[filters.letter_head || frappe.defaults.get_default("letter_head")].header %} -
-

{%= __(report.report_name) %}

-
{{ __("From") }} {%= filters.from_date %} {{ __("to") }} {%= filters.to_date %}
-
- - - - {% for(var i=1, l=report_columns.length; i{%= report_columns[i].label %} - {% } %} - - - - {% for(var j=0, k=data.length; j - {% for(var i=1, l=report_columns.length; i - {% var fieldname = report_columns[i].fieldname; %} - {% if (report_columns[i].fieldtype=='Currency' && !isNaN(row[fieldname])) { %} - {%= format_currency(row[fieldname]) %} - {% } else { %} - {% if (!is_null(row[fieldname])) { %} - {%= row[fieldname] %} - {% } %} - {% } %} - - {% } %} - - {% } %} - -
-

{{ __("Printed On") }} {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}

diff --git a/erpnext/payroll/report/salary_register/salary_register.js b/erpnext/payroll/report/salary_register/salary_register.js deleted file mode 100644 index eb4acb91a7..0000000000 --- a/erpnext/payroll/report/salary_register/salary_register.js +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.query_reports["Salary Register"] = { - "filters": [ - { - "fieldname":"from_date", - "label": __("From"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(),-1), - "reqd": 1, - "width": "100px" - }, - { - "fieldname":"to_date", - "label": __("To"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1, - "width": "100px" - }, - { - "fieldname": "currency", - "fieldtype": "Link", - "options": "Currency", - "label": __("Currency"), - "default": erpnext.get_currency(frappe.defaults.get_default("Company")), - "width": "50px" - }, - { - "fieldname":"employee", - "label": __("Employee"), - "fieldtype": "Link", - "options": "Employee", - "width": "100px" - }, - { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "width": "100px", - "reqd": 1 - }, - { - "fieldname":"docstatus", - "label":__("Document Status"), - "fieldtype":"Select", - "options":["Draft", "Submitted", "Cancelled"], - "default": "Submitted", - "width": "100px" - } - ] -} diff --git a/erpnext/payroll/report/salary_register/salary_register.json b/erpnext/payroll/report/salary_register/salary_register.json deleted file mode 100644 index 5a70c32593..0000000000 --- a/erpnext/payroll/report/salary_register/salary_register.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "add_total_row": 1, - "creation": "2017-01-10 17:36:58.153863", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 2, - "is_standard": "Yes", - "modified": "2020-05-28 00:07:18.576661", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Salary Register", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Salary Slip", - "report_name": "Salary Register", - "report_type": "Script Report", - "roles": [ - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/payroll/report/salary_register/salary_register.py b/erpnext/payroll/report/salary_register/salary_register.py deleted file mode 100644 index 0a62b43a8e..0000000000 --- a/erpnext/payroll/report/salary_register/salary_register.py +++ /dev/null @@ -1,229 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe import _ -from frappe.utils import flt - -import erpnext - - -def execute(filters=None): - if not filters: - filters = {} - currency = None - if filters.get("currency"): - currency = filters.get("currency") - company_currency = erpnext.get_company_currency(filters.get("company")) - salary_slips = get_salary_slips(filters, company_currency) - if not salary_slips: - return [], [] - - columns, earning_types, ded_types = get_columns(salary_slips) - ss_earning_map = get_ss_earning_map(salary_slips, currency, company_currency) - ss_ded_map = get_ss_ded_map(salary_slips, currency, company_currency) - doj_map = get_employee_doj_map() - - data = [] - for ss in salary_slips: - row = [ - ss.name, - ss.employee, - ss.employee_name, - doj_map.get(ss.employee), - ss.branch, - ss.department, - ss.designation, - ss.company, - ss.start_date, - ss.end_date, - ss.leave_without_pay, - ss.payment_days, - ] - - if ss.branch is not None: - columns[3] = columns[3].replace("-1", "120") - if ss.department is not None: - columns[4] = columns[4].replace("-1", "120") - if ss.designation is not None: - columns[5] = columns[5].replace("-1", "120") - if ss.leave_without_pay is not None: - columns[9] = columns[9].replace("-1", "130") - - for e in earning_types: - row.append(ss_earning_map.get(ss.name, {}).get(e)) - - if currency == company_currency: - row += [flt(ss.gross_pay) * flt(ss.exchange_rate)] - else: - row += [ss.gross_pay] - - for d in ded_types: - row.append(ss_ded_map.get(ss.name, {}).get(d)) - - row.append(ss.total_loan_repayment) - - if currency == company_currency: - row += [ - flt(ss.total_deduction) * flt(ss.exchange_rate), - flt(ss.net_pay) * flt(ss.exchange_rate), - ] - else: - row += [ss.total_deduction, ss.net_pay] - row.append(currency or company_currency) - data.append(row) - - return columns, data - - -def get_columns(salary_slips): - """ - columns = [ - _("Salary Slip ID") + ":Link/Salary Slip:150", - _("Employee") + ":Link/Employee:120", - _("Employee Name") + "::140", - _("Date of Joining") + "::80", - _("Branch") + ":Link/Branch:120", - _("Department") + ":Link/Department:120", - _("Designation") + ":Link/Designation:120", - _("Company") + ":Link/Company:120", - _("Start Date") + "::80", - _("End Date") + "::80", - _("Leave Without Pay") + ":Float:130", - _("Payment Days") + ":Float:120", - _("Currency") + ":Link/Currency:80" - ] - """ - columns = [ - _("Salary Slip ID") + ":Link/Salary Slip:150", - _("Employee") + ":Link/Employee:120", - _("Employee Name") + "::140", - _("Date of Joining") + "::80", - _("Branch") + ":Link/Branch:-1", - _("Department") + ":Link/Department:-1", - _("Designation") + ":Link/Designation:120", - _("Company") + ":Link/Company:120", - _("Start Date") + "::80", - _("End Date") + "::80", - _("Leave Without Pay") + ":Float:50", - _("Payment Days") + ":Float:120", - ] - - salary_components = {_("Earning"): [], _("Deduction"): []} - - for component in frappe.db.sql( - """select distinct sd.salary_component, sc.type - from `tabSalary Detail` sd, `tabSalary Component` sc - where sc.name=sd.salary_component and sd.amount != 0 and sd.parent in (%s)""" - % (", ".join(["%s"] * len(salary_slips))), - tuple([d.name for d in salary_slips]), - as_dict=1, - ): - salary_components[_(component.type)].append(component.salary_component) - - columns = ( - columns - + [(e + ":Currency:120") for e in salary_components[_("Earning")]] - + [_("Gross Pay") + ":Currency:120"] - + [(d + ":Currency:120") for d in salary_components[_("Deduction")]] - + [ - _("Loan Repayment") + ":Currency:120", - _("Total Deduction") + ":Currency:120", - _("Net Pay") + ":Currency:120", - ] - ) - - return columns, salary_components[_("Earning")], salary_components[_("Deduction")] - - -def get_salary_slips(filters, company_currency): - filters.update({"from_date": filters.get("from_date"), "to_date": filters.get("to_date")}) - conditions, filters = get_conditions(filters, company_currency) - salary_slips = frappe.db.sql( - """select * from `tabSalary Slip` where %s - order by employee""" - % conditions, - filters, - as_dict=1, - ) - - return salary_slips or [] - - -def get_conditions(filters, company_currency): - conditions = "" - doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2} - - if filters.get("docstatus"): - conditions += "docstatus = {0}".format(doc_status[filters.get("docstatus")]) - - if filters.get("from_date"): - conditions += " and start_date >= %(from_date)s" - if filters.get("to_date"): - conditions += " and end_date <= %(to_date)s" - if filters.get("company"): - conditions += " and company = %(company)s" - if filters.get("employee"): - conditions += " and employee = %(employee)s" - if filters.get("currency") and filters.get("currency") != company_currency: - conditions += " and currency = %(currency)s" - - return conditions, filters - - -def get_employee_doj_map(): - return frappe._dict( - frappe.db.sql( - """ - SELECT - employee, - date_of_joining - FROM `tabEmployee` - """ - ) - ) - - -def get_ss_earning_map(salary_slips, currency, company_currency): - ss_earnings = frappe.db.sql( - """select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name - from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)""" - % (", ".join(["%s"] * len(salary_slips))), - tuple([d.name for d in salary_slips]), - as_dict=1, - ) - - ss_earning_map = {} - for d in ss_earnings: - ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0) - if currency == company_currency: - ss_earning_map[d.parent][d.salary_component] += flt(d.amount) * flt( - d.exchange_rate if d.exchange_rate else 1 - ) - else: - ss_earning_map[d.parent][d.salary_component] += flt(d.amount) - - return ss_earning_map - - -def get_ss_ded_map(salary_slips, currency, company_currency): - ss_deductions = frappe.db.sql( - """select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name - from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)""" - % (", ".join(["%s"] * len(salary_slips))), - tuple([d.name for d in salary_slips]), - as_dict=1, - ) - - ss_ded_map = {} - for d in ss_deductions: - ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0) - if currency == company_currency: - ss_ded_map[d.parent][d.salary_component] += flt(d.amount) * flt( - d.exchange_rate if d.exchange_rate else 1 - ) - else: - ss_ded_map[d.parent][d.salary_component] += flt(d.amount) - - return ss_ded_map diff --git a/erpnext/payroll/workspace/payroll/payroll.json b/erpnext/payroll/workspace/payroll/payroll.json deleted file mode 100644 index 5629e63021..0000000000 --- a/erpnext/payroll/workspace/payroll/payroll.json +++ /dev/null @@ -1,372 +0,0 @@ -{ - "charts": [ - { - "chart_name": "Outgoing Salary", - "label": "Outgoing Salary" - } - ], - "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Payroll\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Outgoing Salary\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Salary Structure\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payroll Entry\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Salary Slip\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Income Tax Slab\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Salary Register\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Payroll\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Taxation\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Compensations\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]", - "creation": "2020-05-27 19:54:23.405607", - "docstatus": 0, - "doctype": "Workspace", - "for_user": "", - "hide_custom": 0, - "icon": "money-coins-1", - "idx": 0, - "label": "Payroll", - "links": [ - { - "hidden": 0, - "is_query_report": 0, - "label": "Payroll", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Salary Component", - "link_count": 0, - "link_to": "Salary Component", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Salary Structure", - "link_count": 0, - "link_to": "Salary Structure", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Salary Structure Assignment", - "link_count": 0, - "link_to": "Salary Structure Assignment", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Payroll Entry", - "link_count": 0, - "link_to": "Payroll Entry", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Salary Slip", - "link_count": 0, - "link_to": "Salary Slip", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Taxation", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Payroll Period", - "link_count": 0, - "link_to": "Payroll Period", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Income Tax Slab", - "link_count": 0, - "link_to": "Income Tax Slab", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Other Income", - "link_count": 0, - "link_to": "Employee Other Income", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Tax Exemption Declaration", - "link_count": 0, - "link_to": "Employee Tax Exemption Declaration", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Tax Exemption Proof Submission", - "link_count": 0, - "link_to": "Employee Tax Exemption Proof Submission", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Tax Exemption Category", - "link_count": 0, - "link_to": "Employee Tax Exemption Category", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Tax Exemption Sub Category", - "link_count": 0, - "link_to": "Employee Tax Exemption Sub Category", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Compensations", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Additional Salary", - "link_count": 0, - "link_to": "Additional Salary", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Retention Bonus", - "link_count": 0, - "link_to": "Retention Bonus", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Incentive", - "link_count": 0, - "link_to": "Employee Incentive", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Benefit Application", - "link_count": 0, - "link_to": "Employee Benefit Application", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Employee Benefit Claim", - "link_count": 0, - "link_to": "Employee Benefit Claim", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Reports", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "Salary Slip", - "hidden": 0, - "is_query_report": 1, - "label": "Salary Register", - "link_count": 0, - "link_to": "Salary Register", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Salary Structure", - "hidden": 0, - "is_query_report": 1, - "label": "Income Tax Computation", - "link_count": 0, - "link_to": "Income Tax Computation", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Salary Slip", - "hidden": 0, - "is_query_report": 1, - "label": "Salary Payments Based On Payment Mode", - "link_count": 0, - "link_to": "Salary Payments Based On Payment Mode", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Salary Slip", - "hidden": 0, - "is_query_report": 1, - "label": "Salary Payments via ECS", - "link_count": 0, - "link_to": "Salary Payments via ECS", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Salary Slip", - "hidden": 0, - "is_query_report": 1, - "label": "Income Tax Deductions", - "link_count": 0, - "link_to": "Income Tax Deductions", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Salary Slip", - "hidden": 0, - "is_query_report": 1, - "label": "Professional Tax Deductions", - "link_count": 0, - "link_to": "Professional Tax Deductions", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Salary Slip", - "hidden": 0, - "is_query_report": 1, - "label": "Provident Fund Deductions", - "link_count": 0, - "link_to": "Provident Fund Deductions", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Payroll Entry", - "hidden": 0, - "is_query_report": 1, - "label": "Bank Remittance", - "link_count": 0, - "link_to": "Bank Remittance", - "link_type": "Report", - "onboard": 0, - "type": "Link" - } - ], - "modified": "2022-02-23 17:41:19.098813", - "modified_by": "Administrator", - "module": "Payroll", - "name": "Payroll", - "owner": "Administrator", - "parent_page": "", - "public": 1, - "restrict_to_domain": "", - "roles": [], - "sequence_id": 19.0, - "shortcuts": [ - { - "label": "Salary Structure", - "link_to": "Salary Structure", - "type": "DocType" - }, - { - "label": "Payroll Entry", - "link_to": "Payroll Entry", - "type": "DocType" - }, - { - "color": "", - "format": "{} Pending", - "label": "Salary Slip", - "link_to": "Salary Slip", - "stats_filter": "{\"status\": \"Draft\"}", - "type": "DocType" - }, - { - "label": "Income Tax Slab", - "link_to": "Income Tax Slab", - "type": "DocType" - }, - { - "label": "Salary Register", - "link_to": "Salary Register", - "type": "Report" - }, - { - "label": "Dashboard", - "link_to": "Payroll", - "type": "Dashboard" - } - ], - "title": "Payroll" -} \ No newline at end of file