Merge pull request #24408 from rohitwaghchaure/custom-user-type-feat

feat: Employee Self Service
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 98d5966..bb6cd8b 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -307,6 +307,8 @@
 	"Inpatient Medication Entry"
 ]
 
+after_migrate = ["erpnext.setup.install.update_select_perm_after_install"]
+
 scheduler_events = {
 	"cron": {
 		"0/30 * * * *": [
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 14f1ab8..7098a24 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -771,3 +771,4 @@
 erpnext.patches.v12_0.purchase_receipt_status
 erpnext.patches.v13_0.fix_non_unique_represents_company
 erpnext.patches.v12_0.add_document_type_field_for_italy_einvoicing
+erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/make_non_standard_user_type.py b/erpnext/patches/v13_0/make_non_standard_user_type.py
new file mode 100644
index 0000000..a9d7883
--- /dev/null
+++ b/erpnext/patches/v13_0/make_non_standard_user_type.py
@@ -0,0 +1,24 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from six import iteritems
+from erpnext.setup.install import add_non_standard_user_types
+
+def execute():
+	doctype_dict = {
+		'projects': ['Timesheet'],
+		'payroll': ['Salary Slip', 'Employee Tax Exemption Declaration', 'Employee Tax Exemption Proof Submission'],
+		'hr': ['Employee', 'Expense Claim', 'Leave Application', 'Attendance Request', 'Compensatory Leave Request']
+	}
+
+	for module, doctypes in iteritems(doctype_dict):
+		for doctype in doctypes:
+			frappe.reload_doc(module, 'doctype', doctype)
+
+
+	frappe.flags.ignore_select_perm = True
+	frappe.flags.update_select_perm_after_migrate = True
+
+	add_non_standard_user_types()
\ No newline at end of file
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index 82f191d..c7220cb 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -8,9 +8,11 @@
 from .default_success_action import get_default_success_action
 from frappe import _
 from frappe.utils import cint
+from frappe.installer import update_site_config
 from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
 from frappe.custom.doctype.custom_field.custom_field import create_custom_field
 from erpnext.setup.default_energy_point_rules import get_default_energy_point_rules
+from six import iteritems
 
 default_mail_footer = """<div style="padding: 7px; text-align: right; color: #888"><small>Sent via
 	<a style="color: #888" href="http://erpnext.org">ERPNext</a></div>"""
@@ -29,6 +31,7 @@
 	add_company_to_session_defaults()
 	add_standard_navbar_items()
 	add_app_name()
+	add_non_standard_user_types()
 	frappe.db.commit()
 
 
@@ -164,3 +167,81 @@
 
 def add_app_name():
 	frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
+
+def add_non_standard_user_types():
+	user_types = get_user_types_data()
+
+	user_type_limit = {}
+	for user_type, data in iteritems(user_types):
+		user_type_limit.setdefault(frappe.scrub(user_type), 10)
+
+	update_site_config('user_type_doctype_limit', user_type_limit)
+
+	for user_type, data in iteritems(user_types):
+		create_custom_role(data)
+		create_user_type(user_type, data)
+
+def get_user_types_data():
+	return {
+		'Employee Self Service': {
+			'role': 'Employee Self Service',
+			'apply_user_permission_on': 'Employee',
+			'user_id_field': 'user_id',
+			'doctypes': {
+				'Salary Slip': ['read'],
+				'Employee': ['read', 'write'],
+				'Expense Claim': ['read', 'write', 'create', 'delete'],
+				'Leave Application': ['read', 'write', 'create', 'delete'],
+				'Attendance Request': ['read', 'write', 'create', 'delete'],
+				'Compensatory Leave Request': ['read', 'write', 'create', 'delete'],
+				'Employee Tax Exemption Declaration': ['read', 'write', 'create', 'delete'],
+				'Employee Tax Exemption Proof Submission': ['read', 'write', 'create', 'delete'],
+				'Timesheet': ['read', 'write', 'create', 'delete', 'submit', 'cancel', 'amend']
+			}
+		}
+	}
+
+def create_custom_role(data):
+	if data.get('role') and not frappe.db.exists('Role', data.get('role')):
+		frappe.get_doc({
+			'doctype': 'Role',
+			'role_name': data.get('role'),
+			'desk_access': 1,
+			'is_custom': 1
+		}).insert(ignore_permissions=True)
+
+def create_user_type(user_type, data):
+	if frappe.db.exists('User Type', user_type):
+		doc = frappe.get_cached_doc('User Type', user_type)
+		doc.user_doctypes = []
+	else:
+		doc = frappe.new_doc('User Type')
+		doc.update({
+			'name': user_type,
+			'role': data.get('role'),
+			'user_id_field': data.get('user_id_field'),
+			'apply_user_permission_on': data.get('apply_user_permission_on')
+		})
+
+	create_role_permissions_for_doctype(doc, data)
+	doc.save(ignore_permissions=True)
+
+def create_role_permissions_for_doctype(doc, data):
+	for doctype, perms in iteritems(data.get('doctypes')):
+		args = {'document_type': doctype}
+		for perm in perms:
+			args[perm] = 1
+
+		doc.append('user_doctypes', args)
+
+def update_select_perm_after_install():
+	if not frappe.flags.update_select_perm_after_migrate:
+		return
+
+	frappe.flags.ignore_select_perm = False
+	for row in frappe.get_all('User Type', filters= {'is_standard': 0}):
+		print('Updating user type :- ', row.name)
+		doc = frappe.get_doc('User Type', row.name)
+		doc.save()
+
+	frappe.flags.update_select_perm_after_migrate = False