From cd0a25ca174d88dac8cc8a9ae637189ceff8ed66 Mon Sep 17 00:00:00 2001 From: Krishna Shirsath Date: Tue, 17 Feb 2026 13:14:31 +0530 Subject: [PATCH 01/21] feat(employee): Create User button and form. (cherry picked from commit 3b521b74ea41622ee20ea0e9725fa92dd66205c2) --- erpnext/setup/doctype/employee/employee.js | 66 ++++++--- erpnext/setup/doctype/employee/employee.json | 9 +- erpnext/setup/doctype/employee/employee.py | 140 ++++++++++++++++++- 3 files changed, 182 insertions(+), 33 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.js b/erpnext/setup/doctype/employee/employee.js index 21d67d70e78..2a525f18f7f 100755 --- a/erpnext/setup/doctype/employee/employee.js +++ b/erpnext/setup/doctype/employee/employee.js @@ -45,6 +45,54 @@ frappe.ui.form.on("Employee", { refresh: function (frm) { frm.fields_dict.date_of_birth.datepicker.update({ maxDate: new Date() }); + + if (!frm.is_new() && !frm.doc.user_id) { + frm.add_custom_button(__("Create User"), () => { + const dialog = new frappe.ui.Dialog({ + title: __("Create User"), + fields: [ + { + fieldtype: "Data", + fieldname: "email", + label: __("Email"), + reqd: 1, + default: frm.doc.company_email || frm.doc.personal_email || frm.doc.user_id, + }, + { + fieldtype: "Check", + fieldname: "create_user_permission", + label: __("Create User Permission"), + default: 0, + }, + ], + primary_action_label: __("Create"), + primary_action: (values) => { + if (!values.email) { + frappe.msgprint(__("Email is required to create a user.")); + return; + } + + frappe + .call({ + method: "erpnext.setup.doctype.employee.employee.create_user", + args: { + employee: frm.doc.name, + email: values.email, + create_user_permission: values.create_user_permission ? 1 : 0, + }, + freeze: true, + freeze_message: __("Creating User..."), + }) + .then(() => { + dialog.hide(); + frm.reload_doc(); + }); + }, + }); + + dialog.show(); + }); + } }, prefered_contact_email: function (frm) { @@ -77,24 +125,6 @@ frappe.ui.form.on("Employee", { }, }); }, - - create_user: function (frm) { - if (!frm.doc.prefered_email) { - frappe.throw(__("Please enter Preferred Contact Email")); - } - frappe.call({ - method: "erpnext.setup.doctype.employee.employee.create_user", - args: { - employee: frm.doc.name, - email: frm.doc.prefered_email, - }, - freeze: true, - freeze_message: __("Creating User..."), - callback: function (r) { - frm.reload_doc(); - }, - }); - }, }); cur_frm.cscript = new erpnext.setup.EmployeeController({ diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index e663913e8d7..4069813e318 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -28,7 +28,6 @@ "status", "erpnext_user", "user_id", - "create_user", "create_user_permission", "company_details_section", "company", @@ -285,12 +284,6 @@ "label": "User ID", "options": "User" }, - { - "depends_on": "eval:(!doc.user_id)", - "fieldname": "create_user", - "fieldtype": "Button", - "label": "Create User" - }, { "default": "1", "depends_on": "user_id", @@ -823,7 +816,7 @@ "image_field": "image", "is_tree": 1, "links": [], - "modified": "2025-08-29 11:52:12.819878", + "modified": "2026-02-16 13:06:01.752904", "modified_by": "Administrator", "module": "Setup", "name": "Employee", diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py index 1ecbb4b9ac7..48819a70ef4 100755 --- a/erpnext/setup/doctype/employee/employee.py +++ b/erpnext/setup/doctype/employee/employee.py @@ -8,7 +8,7 @@ from frappe.permissions import ( get_doc_permissions, remove_user_permission, ) -from frappe.utils import cstr, getdate, today, validate_email_address +from frappe.utils import cint, cstr, getdate, today, validate_email_address from frappe.utils.nestedset import NestedSet from erpnext.utilities.transaction_base import delete_events @@ -23,6 +23,93 @@ class InactiveEmployeeStatusError(frappe.ValidationError): class Employee(NestedSet): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + from erpnext.setup.doctype.employee_education.employee_education import EmployeeEducation + from erpnext.setup.doctype.employee_external_work_history.employee_external_work_history import ( + EmployeeExternalWorkHistory, + ) + from erpnext.setup.doctype.employee_internal_work_history.employee_internal_work_history import ( + EmployeeInternalWorkHistory, + ) + + attendance_device_id: DF.Data | None + bank_ac_no: DF.Data | None + bank_name: DF.Data | None + bio: DF.TextEditor | None + blood_group: DF.Literal["", "A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"] + branch: DF.Link | None + cell_number: DF.Data | None + company: DF.Link + company_email: DF.Data | None + contract_end_date: DF.Date | None + create_user_permission: DF.Check + ctc: DF.Currency + current_accommodation_type: DF.Literal["", "Rented", "Owned"] + current_address: DF.SmallText | None + date_of_birth: DF.Date + date_of_issue: DF.Date | None + date_of_joining: DF.Date + date_of_retirement: DF.Date | None + department: DF.Link | None + designation: DF.Link | None + education: DF.Table[EmployeeEducation] + emergency_phone_number: DF.Data | None + employee: DF.Data | None + employee_name: DF.Data | None + employee_number: DF.Data | None + encashment_date: DF.Date | None + external_work_history: DF.Table[EmployeeExternalWorkHistory] + family_background: DF.SmallText | None + feedback: DF.SmallText | None + final_confirmation_date: DF.Date | None + first_name: DF.Data + gender: DF.Link + health_details: DF.SmallText | None + held_on: DF.Date | None + holiday_list: DF.Link | None + iban: DF.Data | None + image: DF.AttachImage | None + internal_work_history: DF.Table[EmployeeInternalWorkHistory] + last_name: DF.Data | None + leave_encashed: DF.Literal["", "Yes", "No"] + lft: DF.Int + marital_status: DF.Literal["", "Single", "Married", "Divorced", "Widowed"] + middle_name: DF.Data | None + naming_series: DF.Literal["HR-EMP-"] + new_workplace: DF.Data | None + notice_number_of_days: DF.Int + old_parent: DF.Data | None + passport_number: DF.Data | None + permanent_accommodation_type: DF.Literal["", "Rented", "Owned"] + permanent_address: DF.SmallText | None + person_to_be_contacted: DF.Data | None + personal_email: DF.Data | None + place_of_issue: DF.Data | None + prefered_contact_email: DF.Literal["", "Company Email", "Personal Email", "User ID"] + prefered_email: DF.Data | None + reason_for_leaving: DF.SmallText | None + relation: DF.Data | None + relieving_date: DF.Date | None + reports_to: DF.Link | None + resignation_letter_date: DF.Date | None + rgt: DF.Int + salary_currency: DF.Link | None + salary_mode: DF.Literal["", "Bank", "Cash", "Cheque"] + salutation: DF.Link | None + scheduled_confirmation_date: DF.Date | None + status: DF.Literal["Active", "Inactive", "Suspended", "Left"] + unsubscribed: DF.Check + user_id: DF.Link | None + valid_upto: DF.Date | None + # end: auto-generated types + nsm_parent_field = "reports_to" def autoname(self): @@ -310,9 +397,28 @@ def deactivate_sales_person(status=None, employee=None): @frappe.whitelist() -def create_user(employee, user=None, email=None): +def create_user(employee, user=None, email=None, create_user_permission=0): + if not employee: + frappe.throw(_("Employee is required")) + emp = frappe.get_doc("Employee", employee) + if email: + email = cstr(email).strip().lower() + else: + email = emp.prefered_email + + if not email: + frappe.throw(_("Email is required to create a user")) + + validate_email_address(email, True) + + if emp.user_id: + frappe.throw(_("Employee {0} already has a linked user").format(emp.name)) + + if frappe.db.exists("User", email): + frappe.throw(_("User {0} already exists").format(email)) + employee_name = emp.employee_name.split(" ") middle_name = last_name = "" @@ -324,14 +430,14 @@ def create_user(employee, user=None, email=None): first_name = employee_name[0] - if email: - emp.prefered_email = email + frappe.db.set_value("Employee", emp.name, "user_id", email, update_modified=False) + frappe.db.commit() user = frappe.new_doc("User") user.update( { "name": emp.employee_name, - "email": emp.prefered_email, + "email": email, "enabled": 1, "first_name": first_name, "middle_name": middle_name, @@ -340,11 +446,31 @@ def create_user(employee, user=None, email=None): "birth_date": emp.date_of_birth, "phone": emp.cell_number, "bio": emp.bio, + "send_welcome_email": 1, } ) - user.insert() - emp.user_id = user.name + user.append_roles("Employee") + user.insert(ignore_permissions=True) + + emp.reload() + emp.company_email = email + if not emp.prefered_contact_email: + emp.prefered_contact_email = "Company Email" emp.save() + + if cint(create_user_permission): + if not frappe.db.exists( + "User Permission", + {"allow": "Employee", "for_value": emp.name, "user": user.name}, + ): + add_user_permission("Employee", emp.name, user.name) + + if not frappe.db.exists( + "User Permission", + {"allow": "Company", "for_value": emp.company, "user": user.name}, + ): + add_user_permission("Company", emp.company, user.name) + return user.name From 8f8b48746baf0572a39c7a5f2272bd8b75131f50 Mon Sep 17 00:00:00 2001 From: Krishna Shirsath Date: Thu, 19 Feb 2026 15:51:53 +0530 Subject: [PATCH 02/21] feat(employee): Add automatic user creation feature and related validations. Create User on Import. (cherry picked from commit 57f3048d2739479de9448ee347a421f306c5b558) --- erpnext/setup/doctype/employee/employee.json | 19 ++++++++++++++-- erpnext/setup/doctype/employee/employee.py | 24 +++++++++++++++++--- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index 4069813e318..94dca869eda 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -29,6 +29,8 @@ "erpnext_user", "user_id", "create_user_permission", + "column_break_xwnm", + "create_user_automatically", "company_details_section", "company", "department", @@ -286,12 +288,20 @@ }, { "default": "1", - "depends_on": "user_id", + "depends_on": "eval:doc.user_id || doc.create_user_automatically", "description": "This will restrict user access to other employee records", "fieldname": "create_user_permission", "fieldtype": "Check", "label": "Create User Permission" }, + { + "default": "0", + "depends_on": "eval:doc.__islocal", + "description": "This will create User for this employee depending on the Company Email.", + "fieldname": "create_user_automatically", + "fieldtype": "Check", + "label": "Create User Automatically" + }, { "allow_in_quick_entry": 1, "collapsible": 1, @@ -447,6 +457,7 @@ "fieldname": "company_email", "fieldtype": "Data", "label": "Company Email", + "mandatory_depends_on": "create_user_automatically", "oldfieldname": "company_email", "oldfieldtype": "Data", "options": "Email" @@ -809,6 +820,10 @@ "fieldtype": "Data", "label": "IBAN", "options": "IBAN" + }, + { + "fieldname": "column_break_xwnm", + "fieldtype": "Column Break" } ], "icon": "fa fa-user", @@ -816,7 +831,7 @@ "image_field": "image", "is_tree": 1, "links": [], - "modified": "2026-02-16 13:06:01.752904", + "modified": "2026-02-19 12:48:22.080419", "modified_by": "Administrator", "module": "Setup", "name": "Employee", diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py index 48819a70ef4..cb5e2504b3e 100755 --- a/erpnext/setup/doctype/employee/employee.py +++ b/erpnext/setup/doctype/employee/employee.py @@ -49,6 +49,7 @@ class Employee(NestedSet): company: DF.Link company_email: DF.Data | None contract_end_date: DF.Date | None + create_user_automatically: DF.Check create_user_permission: DF.Check ctc: DF.Currency current_accommodation_type: DF.Literal["", "Rented", "Owned"] @@ -125,6 +126,7 @@ class Employee(NestedSet): self.set_employee_name() self.validate_date() self.validate_email() + self.validate_auto_user_creation() self.validate_status() self.validate_reports_to() self.set_preferred_email() @@ -159,6 +161,10 @@ class Employee(NestedSet): self.validate_for_enabled_user_id(data.get("enabled", 0)) self.validate_duplicate_user_id() + def validate_auto_user_creation(self): + if self.create_user_automatically and not self.company_email: + frappe.throw(_("Email is mandatory when Create User Automatically is enabled")) + def update_nsm_model(self): frappe.utils.nestedset.update_nsm(self) @@ -170,6 +176,19 @@ class Employee(NestedSet): self.update_user_permissions() self.reset_employee_emails_cache() + def after_insert(self): + if not self.create_user_automatically: + return + + if self.user_id: + return + + create_user( + employee=self.name, + email=self.company_email, + create_user_permission=self.create_user_permission, + ) + def update_user_permissions(self): if not self.has_value_changed("user_id") and not self.has_value_changed("create_user_permission"): return @@ -406,7 +425,7 @@ def create_user(employee, user=None, email=None, create_user_permission=0): if email: email = cstr(email).strip().lower() else: - email = emp.prefered_email + email = emp.company_email if not email: frappe.throw(_("Email is required to create a user")) @@ -436,7 +455,7 @@ def create_user(employee, user=None, email=None, create_user_permission=0): user = frappe.new_doc("User") user.update( { - "name": emp.employee_name, + "name": email, "email": email, "enabled": 1, "first_name": first_name, @@ -446,7 +465,6 @@ def create_user(employee, user=None, email=None, create_user_permission=0): "birth_date": emp.date_of_birth, "phone": emp.cell_number, "bio": emp.bio, - "send_welcome_email": 1, } ) user.append_roles("Employee") From f2c4a8b1c4c2fa40af3bc416c6b9b8a4d277a8fd Mon Sep 17 00:00:00 2001 From: Krishna Shirsath Date: Thu, 19 Feb 2026 17:00:35 +0530 Subject: [PATCH 03/21] refactor(employee): create user function -removed useless function calls (cherry picked from commit 6513185cb7e5a2b757bfe09478bb9ee0604055a0) --- erpnext/setup/doctype/employee/employee.py | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py index cb5e2504b3e..3f1ca36c675 100755 --- a/erpnext/setup/doctype/employee/employee.py +++ b/erpnext/setup/doctype/employee/employee.py @@ -449,9 +449,6 @@ def create_user(employee, user=None, email=None, create_user_permission=0): first_name = employee_name[0] - frappe.db.set_value("Employee", emp.name, "user_id", email, update_modified=False) - frappe.db.commit() - user = frappe.new_doc("User") user.update( { @@ -467,8 +464,9 @@ def create_user(employee, user=None, email=None, create_user_permission=0): "bio": emp.bio, } ) + frappe.db.set_value("Employee", emp.name, "user_id", email) user.append_roles("Employee") - user.insert(ignore_permissions=True) + user.insert() emp.reload() emp.company_email = email @@ -477,17 +475,8 @@ def create_user(employee, user=None, email=None, create_user_permission=0): emp.save() if cint(create_user_permission): - if not frappe.db.exists( - "User Permission", - {"allow": "Employee", "for_value": emp.name, "user": user.name}, - ): - add_user_permission("Employee", emp.name, user.name) - - if not frappe.db.exists( - "User Permission", - {"allow": "Company", "for_value": emp.company, "user": user.name}, - ): - add_user_permission("Company", emp.company, user.name) + add_user_permission("Employee", emp.name, user.name) + add_user_permission("Company", emp.company, user.name) return user.name From b0145512edf4912ce85c5a749a0c406c13c89e78 Mon Sep 17 00:00:00 2001 From: Krishna Shirsath Date: Thu, 19 Feb 2026 17:14:50 +0530 Subject: [PATCH 04/21] refactor(employee): reorganize joining and employee exit tabs at the end. (cherry picked from commit 870254b7104d33b5a04aea34093845274452f6bf) --- erpnext/setup/doctype/employee/employee.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index 94dca869eda..1d717b2b4c6 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -40,14 +40,6 @@ "reports_to", "column_break_18", "branch", - "employment_details", - "scheduled_confirmation_date", - "column_break_32", - "final_confirmation_date", - "contract_end_date", - "col_break_22", - "notice_number_of_days", - "date_of_retirement", "contact_details", "cell_number", "column_break_40", @@ -118,6 +110,14 @@ "lft", "rgt", "old_parent", + "employment_details", + "scheduled_confirmation_date", + "column_break_32", + "final_confirmation_date", + "contract_end_date", + "col_break_22", + "notice_number_of_days", + "date_of_retirement", "connections_tab" ], "fields": [ @@ -831,7 +831,7 @@ "image_field": "image", "is_tree": 1, "links": [], - "modified": "2026-02-19 12:48:22.080419", + "modified": "2026-02-19 17:07:42.691107", "modified_by": "Administrator", "module": "Setup", "name": "Employee", From 0b3c9120c36aed755e496925f2e05527fe96176f Mon Sep 17 00:00:00 2001 From: Krishna Shirsath Date: Wed, 25 Feb 2026 13:21:06 +0530 Subject: [PATCH 05/21] feat(employee): Add birthdays and work anniversaries indicator in form ,list view enhancements and new empty state. (cherry picked from commit 4f43f655cfce7d86c07b9da5ed1c572fb1aedf18) --- erpnext/setup/doctype/employee/employee.js | 57 +++++++++++++++++++ erpnext/setup/doctype/employee/employee.json | 4 +- .../setup/doctype/employee/employee_list.js | 25 +++++++- 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.js b/erpnext/setup/doctype/employee/employee.js index 2a525f18f7f..ed210f8e4fa 100755 --- a/erpnext/setup/doctype/employee/employee.js +++ b/erpnext/setup/doctype/employee/employee.js @@ -46,6 +46,8 @@ frappe.ui.form.on("Employee", { refresh: function (frm) { frm.fields_dict.date_of_birth.datepicker.update({ maxDate: new Date() }); + frm.trigger("add_anniversary_indicator"); + if (!frm.is_new() && !frm.doc.user_id) { frm.add_custom_button(__("Create User"), () => { const dialog = new frappe.ui.Dialog({ @@ -95,6 +97,61 @@ frappe.ui.form.on("Employee", { } }, + date_of_birth: function (frm) { + frm.trigger("add_anniversary_indicator"); + }, + + date_of_joining: function (frm) { + frm.trigger("add_anniversary_indicator"); + }, + + add_anniversary_indicator: function (frm) { + if (!frm.sidebar || !frm.sidebar.sidebar) return; + + let $sidebar = frm.sidebar.sidebar; + let $indicator_section = $sidebar.find(".anniversary-indicator-section"); + + if (!$indicator_section.length) { + $indicator_section = $(` + + `).insertAfter($sidebar.find(".sidebar-meta-details")); + } + + let content = ""; + let today = moment().startOf("day"); + + if (frm.doc.date_of_birth) { + let dob = moment(frm.doc.date_of_birth); + if (dob.date() === today.date() && dob.month() === today.month()) { + content += `
${__( + "Today is their Birthday!" + )}
`; + } + } + + if (frm.doc.date_of_joining) { + let doj = moment(frm.doc.date_of_joining); + if (doj.date() === today.date() && doj.month() === today.month()) { + let years = today.year() - doj.year(); + if (years > 0) { + content += `
${__( + "Today is their {0} Year Work Anniversary!", + [years] + )}
`; + } + } + } + + if (content) { + $indicator_section.find(".anniversary-content").html(content); + $indicator_section.show(); + } else { + $indicator_section.hide(); + } + }, + prefered_contact_email: function (frm) { frm.events.update_contact(frm); }, diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index 1d717b2b4c6..b2176bd55e9 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -351,6 +351,7 @@ { "fieldname": "department", "fieldtype": "Link", + "in_list_view": 1, "in_standard_filter": 1, "label": "Department", "oldfieldname": "department", @@ -380,6 +381,7 @@ { "fieldname": "branch", "fieldtype": "Link", + "in_list_view": 1, "label": "Branch", "oldfieldname": "branch", "oldfieldtype": "Link", @@ -831,7 +833,7 @@ "image_field": "image", "is_tree": 1, "links": [], - "modified": "2026-02-19 17:07:42.691107", + "modified": "2026-02-25 11:23:10.689232", "modified_by": "Administrator", "module": "Setup", "name": "Employee", diff --git a/erpnext/setup/doctype/employee/employee_list.js b/erpnext/setup/doctype/employee/employee_list.js index b50eb381c95..33856414537 100644 --- a/erpnext/setup/doctype/employee/employee_list.js +++ b/erpnext/setup/doctype/employee/employee_list.js @@ -1,11 +1,34 @@ frappe.listview_settings["Employee"] = { add_fields: ["status", "branch", "department", "designation", "image"], filters: [["status", "=", "Active"]], - get_indicator: function (doc) { + get_indicator(doc) { return [ __(doc.status, null, "Employee"), { Active: "green", Inactive: "red", Left: "gray", Suspended: "orange" }[doc.status], "status,=," + doc.status, ]; }, + + onload(listview) { + listview.get_no_result_message = () => { + return ` +
+
+ + + +
+

${__("No Active Employees Found. Prefer importing if you have many records.")}

+

+ + +

+
+ `; + }; + }, }; From b115913fc9ebc23bb55197c4a78cbd567c1e3dc0 Mon Sep 17 00:00:00 2001 From: Krishna Shirsath Date: Fri, 27 Feb 2026 12:53:44 +0530 Subject: [PATCH 06/21] fix: add missing type hints to whitelisted function arguments (cherry picked from commit 124ec4d3c239cdc3a71166f0b080882c984d07a7) --- .../manufacturing/doctype/production_plan/production_plan.py | 2 +- erpnext/setup/doctype/employee/employee.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 30b3968fc80..3dc32ef4dab 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -680,7 +680,7 @@ class ProductionPlan(Document): frappe.delete_doc("Work Order", d.name) @frappe.whitelist() - def set_status(self, close=None, update_bin=False): + def set_status(self, close: bool | None = None, update_bin: bool = False) -> None: self.status = {0: "Draft", 1: "Submitted", 2: "Cancelled"}.get(self.docstatus) if close: diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py index 3f1ca36c675..7b93e11a354 100755 --- a/erpnext/setup/doctype/employee/employee.py +++ b/erpnext/setup/doctype/employee/employee.py @@ -416,7 +416,9 @@ def deactivate_sales_person(status=None, employee=None): @frappe.whitelist() -def create_user(employee, user=None, email=None, create_user_permission=0): +def create_user( + employee: str, user: str | None = None, email: str | None = None, create_user_permission: int = 0 +) -> str: if not employee: frappe.throw(_("Employee is required")) From eadf78d69424e2fb64d0ef7c944e79d1ad9b6b64 Mon Sep 17 00:00:00 2001 From: Krishna Shirsath Date: Mon, 9 Mar 2026 11:12:12 +0530 Subject: [PATCH 07/21] fix(employee): add 'set_only_once' property to 'Create User Automatically' field (cherry picked from commit 053242d5bd79842094732bd0186610a8d301e3a5) --- erpnext/setup/doctype/employee/employee.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index b2176bd55e9..bfa45a3178f 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -300,7 +300,8 @@ "description": "This will create User for this employee depending on the Company Email.", "fieldname": "create_user_automatically", "fieldtype": "Check", - "label": "Create User Automatically" + "label": "Create User Automatically", + "set_only_once": 1 }, { "allow_in_quick_entry": 1, @@ -833,7 +834,7 @@ "image_field": "image", "is_tree": 1, "links": [], - "modified": "2026-02-25 11:23:10.689232", + "modified": "2026-03-09 11:06:08.050335", "modified_by": "Administrator", "module": "Setup", "name": "Employee", From c33cd5ce15c2f4a50af15d9925264bd4af264a67 Mon Sep 17 00:00:00 2001 From: Krishna Shirsath Date: Mon, 9 Mar 2026 12:50:56 +0530 Subject: [PATCH 08/21] refactor(employee): remove anniversary indicator logic from employee form (cherry picked from commit 1f19175fef68074ee5638faa951dcb3f95393186) --- erpnext/setup/doctype/employee/employee.js | 57 ---------------------- 1 file changed, 57 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.js b/erpnext/setup/doctype/employee/employee.js index ed210f8e4fa..2a525f18f7f 100755 --- a/erpnext/setup/doctype/employee/employee.js +++ b/erpnext/setup/doctype/employee/employee.js @@ -46,8 +46,6 @@ frappe.ui.form.on("Employee", { refresh: function (frm) { frm.fields_dict.date_of_birth.datepicker.update({ maxDate: new Date() }); - frm.trigger("add_anniversary_indicator"); - if (!frm.is_new() && !frm.doc.user_id) { frm.add_custom_button(__("Create User"), () => { const dialog = new frappe.ui.Dialog({ @@ -97,61 +95,6 @@ frappe.ui.form.on("Employee", { } }, - date_of_birth: function (frm) { - frm.trigger("add_anniversary_indicator"); - }, - - date_of_joining: function (frm) { - frm.trigger("add_anniversary_indicator"); - }, - - add_anniversary_indicator: function (frm) { - if (!frm.sidebar || !frm.sidebar.sidebar) return; - - let $sidebar = frm.sidebar.sidebar; - let $indicator_section = $sidebar.find(".anniversary-indicator-section"); - - if (!$indicator_section.length) { - $indicator_section = $(` - - `).insertAfter($sidebar.find(".sidebar-meta-details")); - } - - let content = ""; - let today = moment().startOf("day"); - - if (frm.doc.date_of_birth) { - let dob = moment(frm.doc.date_of_birth); - if (dob.date() === today.date() && dob.month() === today.month()) { - content += `
${__( - "Today is their Birthday!" - )}
`; - } - } - - if (frm.doc.date_of_joining) { - let doj = moment(frm.doc.date_of_joining); - if (doj.date() === today.date() && doj.month() === today.month()) { - let years = today.year() - doj.year(); - if (years > 0) { - content += `
${__( - "Today is their {0} Year Work Anniversary!", - [years] - )}
`; - } - } - } - - if (content) { - $indicator_section.find(".anniversary-content").html(content); - $indicator_section.show(); - } else { - $indicator_section.hide(); - } - }, - prefered_contact_email: function (frm) { frm.events.update_contact(frm); }, From 7414a9a69450112c0ff5d65bba1c1ad083a99009 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 11:27:00 +0530 Subject: [PATCH 09/21] fix: move Joining section before Exit, relabel Employee Exit -> Exit (cherry picked from commit 000b5b72d5891fe8a08436b521155d4760d397b7) --- erpnext/setup/doctype/employee/employee.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index bfa45a3178f..6552e91621f 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -94,6 +94,14 @@ "external_work_history", "history_in_company", "internal_work_history", + "employment_details", + "scheduled_confirmation_date", + "column_break_32", + "final_confirmation_date", + "contract_end_date", + "col_break_22", + "notice_number_of_days", + "date_of_retirement", "exit", "resignation_letter_date", "relieving_date", @@ -110,14 +118,6 @@ "lft", "rgt", "old_parent", - "employment_details", - "scheduled_confirmation_date", - "column_break_32", - "final_confirmation_date", - "contract_end_date", - "col_break_22", - "notice_number_of_days", - "date_of_retirement", "connections_tab" ], "fields": [ @@ -607,7 +607,7 @@ "collapsible": 1, "fieldname": "exit", "fieldtype": "Tab Break", - "label": "Employee Exit", + "label": "Exit", "oldfieldtype": "Section Break" }, { @@ -834,7 +834,7 @@ "image_field": "image", "is_tree": 1, "links": [], - "modified": "2026-03-09 11:06:08.050335", + "modified": "2026-03-23 11:06:35.539765", "modified_by": "Administrator", "module": "Setup", "name": "Employee", From 341bfb0bd963a9ae3704c706888390c3fcc2af64 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 11:59:05 +0530 Subject: [PATCH 10/21] fix: reset employee listview empty state, add import btn instead (cherry picked from commit d99d16423a3789b254e996a4697a9a9b75865cb7) --- .../setup/doctype/employee/employee_list.js | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee_list.js b/erpnext/setup/doctype/employee/employee_list.js index 33856414537..33cf7225626 100644 --- a/erpnext/setup/doctype/employee/employee_list.js +++ b/erpnext/setup/doctype/employee/employee_list.js @@ -10,25 +10,16 @@ frappe.listview_settings["Employee"] = { }, onload(listview) { - listview.get_no_result_message = () => { - return ` -
-
- - - -
-

${__("No Active Employees Found. Prefer importing if you have many records.")}

-

- - -

-
- `; - }; + if (frappe.perm.has_perm("Employee", 0, "create")) { + frappe.db.count("Employee").then((count) => { + if (count === 0) { + listview.page.add_inner_button(__("Import Employees"), () => { + frappe.new_doc("Data Import", { + reference_doctype: "Employee", + }); + }); + } + }); + } }, }; From 1ddadb72b7a2762bb179fdf141065485b08d6001 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 12:52:23 +0530 Subject: [PATCH 11/21] fix: employee user creation - consider prefered email as default in employee creation - remove unused user parameter from `create_user` API - remove unnecessary validations on user ID, already checked by user doctype hooks - set company email only if empty (cherry picked from commit 613d36a1393b4e94144ad7a975512a3467dec906) --- erpnext/setup/doctype/employee/employee.js | 3 +- erpnext/setup/doctype/employee/employee.py | 33 +++++++--------------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.js b/erpnext/setup/doctype/employee/employee.js index 2a525f18f7f..4422c9048d1 100755 --- a/erpnext/setup/doctype/employee/employee.js +++ b/erpnext/setup/doctype/employee/employee.js @@ -56,7 +56,8 @@ frappe.ui.form.on("Employee", { fieldname: "email", label: __("Email"), reqd: 1, - default: frm.doc.company_email || frm.doc.personal_email || frm.doc.user_id, + default: + frm.doc.prefered_email || frm.doc.company_email || frm.doc.personal_email, }, { fieldtype: "Check", diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py index 7b93e11a354..9cf1ce6aa35 100755 --- a/erpnext/setup/doctype/employee/employee.py +++ b/erpnext/setup/doctype/employee/employee.py @@ -416,31 +416,19 @@ def deactivate_sales_person(status=None, employee=None): @frappe.whitelist() -def create_user( - employee: str, user: str | None = None, email: str | None = None, create_user_permission: int = 0 -) -> str: - if not employee: - frappe.throw(_("Employee is required")) - +def create_user(employee: str, email: str | None = None, create_user_permission: int = 0) -> str: emp = frappe.get_doc("Employee", employee) - - if email: - email = cstr(email).strip().lower() - else: - email = emp.company_email - - if not email: - frappe.throw(_("Email is required to create a user")) - - validate_email_address(email, True) - if emp.user_id: frappe.throw(_("Employee {0} already has a linked user").format(emp.name)) - if frappe.db.exists("User", email): - frappe.throw(_("User {0} already exists").format(email)) + if not email: + email = emp.company_email + if not email: + frappe.throw(_("Email is required to create a user")) + email = validate_email_address(email, True) employee_name = emp.employee_name.split(" ") + first_name = employee_name[0] middle_name = last_name = "" if len(employee_name) >= 3: @@ -449,12 +437,9 @@ def create_user( elif len(employee_name) == 2: last_name = employee_name[1] - first_name = employee_name[0] - user = frappe.new_doc("User") user.update( { - "name": email, "email": email, "enabled": 1, "first_name": first_name, @@ -471,7 +456,9 @@ def create_user( user.insert() emp.reload() - emp.company_email = email + emp.user_id = user.name + if not emp.company_email: + emp.company_email = email if not emp.prefered_contact_email: emp.prefered_contact_email = "Company Email" emp.save() From 2f13b33e3da4a27645a0e8bc5f0cb67dc3b07020 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 13:37:07 +0530 Subject: [PATCH 12/21] fix: only validate auto user creation before insert (cherry picked from commit ee1aa10328f46fd868fce217831f40f36186764b) --- erpnext/setup/doctype/employee/employee.json | 3 +-- erpnext/setup/doctype/employee/employee.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index 6552e91621f..fd9b5e26c3d 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -460,7 +460,6 @@ "fieldname": "company_email", "fieldtype": "Data", "label": "Company Email", - "mandatory_depends_on": "create_user_automatically", "oldfieldname": "company_email", "oldfieldtype": "Data", "options": "Email" @@ -834,7 +833,7 @@ "image_field": "image", "is_tree": 1, "links": [], - "modified": "2026-03-23 11:06:35.539765", + "modified": "2026-03-23 13:36:13.708549", "modified_by": "Administrator", "module": "Setup", "name": "Employee", diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py index 9cf1ce6aa35..879aa80dd2b 100755 --- a/erpnext/setup/doctype/employee/employee.py +++ b/erpnext/setup/doctype/employee/employee.py @@ -126,7 +126,6 @@ class Employee(NestedSet): self.set_employee_name() self.validate_date() self.validate_email() - self.validate_auto_user_creation() self.validate_status() self.validate_reports_to() self.set_preferred_email() @@ -162,8 +161,14 @@ class Employee(NestedSet): self.validate_duplicate_user_id() def validate_auto_user_creation(self): - if self.create_user_automatically and not self.company_email: - frappe.throw(_("Email is mandatory when Create User Automatically is enabled")) + if self.create_user_automatically and not (self.prefered_email or self.company_email): + frappe.throw( + _( + "Company Email or Preferred Email is mandatory when 'Create User Automatically' is enabled" + ), + frappe.MandatoryError, + title=_("Auto User Creation Error"), + ) def update_nsm_model(self): frappe.utils.nestedset.update_nsm(self) @@ -176,6 +181,9 @@ class Employee(NestedSet): self.update_user_permissions() self.reset_employee_emails_cache() + def before_insert(self): + self.validate_auto_user_creation() + def after_insert(self): if not self.create_user_automatically: return @@ -185,7 +193,7 @@ class Employee(NestedSet): create_user( employee=self.name, - email=self.company_email, + email=self.prefered_email or self.company_email, create_user_permission=self.create_user_permission, ) From d093b719463685ff7150770ef0a806c98e345a25 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 14:06:24 +0530 Subject: [PATCH 13/21] fix: uncollapse User Details section in new form (cherry picked from commit 1466df91bdc4f439ddc4d45df78f21e84cbc5d41) --- erpnext/setup/doctype/employee/employee.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index fd9b5e26c3d..2a559876388 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -274,6 +274,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval:doc.__islocal", "fieldname": "erpnext_user", "fieldtype": "Section Break", "label": "User Details" @@ -833,7 +834,7 @@ "image_field": "image", "is_tree": 1, "links": [], - "modified": "2026-03-23 13:36:13.708549", + "modified": "2026-03-23 14:04:26.818864", "modified_by": "Administrator", "module": "Setup", "name": "Employee", From c12ad7910a62481f8df91a95c4353fa48f5000c8 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 14:06:56 +0530 Subject: [PATCH 14/21] fix: hide Create User Automatically checkbox if user is already selected (cherry picked from commit ec3302d1c1e97a4a88d84fab472e59bf0255a537) --- erpnext/setup/doctype/employee/employee.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index 2a559876388..dbaa1a168f2 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -297,7 +297,7 @@ }, { "default": "0", - "depends_on": "eval:doc.__islocal", + "depends_on": "eval:doc.__islocal && !doc.user_id", "description": "This will create User for this employee depending on the Company Email.", "fieldname": "create_user_automatically", "fieldtype": "Check", @@ -834,7 +834,7 @@ "image_field": "image", "is_tree": 1, "links": [], - "modified": "2026-03-23 14:04:26.818864", + "modified": "2026-03-23 14:05:42.144641", "modified_by": "Administrator", "module": "Setup", "name": "Employee", From e8ca394e8b3d918f405dacfb5ddfa7586940eddb Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 14:23:01 +0530 Subject: [PATCH 15/21] fix: set create user perm to 1 by default + persist option while saving employee (cherry picked from commit 091899d0dfe32e2b09e7a132323665911f33591d) --- erpnext/setup/doctype/employee/employee.js | 2 +- erpnext/setup/doctype/employee/employee.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.js b/erpnext/setup/doctype/employee/employee.js index 4422c9048d1..2c121828034 100755 --- a/erpnext/setup/doctype/employee/employee.js +++ b/erpnext/setup/doctype/employee/employee.js @@ -63,7 +63,7 @@ frappe.ui.form.on("Employee", { fieldtype: "Check", fieldname: "create_user_permission", label: __("Create User Permission"), - default: 0, + default: 1, }, ], primary_action_label: __("Create"), diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py index 879aa80dd2b..b0a74a37c8c 100755 --- a/erpnext/setup/doctype/employee/employee.py +++ b/erpnext/setup/doctype/employee/employee.py @@ -459,12 +459,12 @@ def create_user(employee: str, email: str | None = None, create_user_permission: "bio": emp.bio, } ) - frappe.db.set_value("Employee", emp.name, "user_id", email) + emp.db_set("user_id", email) user.append_roles("Employee") user.insert() - emp.reload() emp.user_id = user.name + emp.create_user_permission = cint(create_user_permission) if not emp.company_email: emp.company_email = email if not emp.prefered_contact_email: From 3023302700d5638c45f3a7158ce56feff73179e6 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 15:06:15 +0530 Subject: [PATCH 16/21] fix: avoid setting unnecessary fields (cherry picked from commit 97bb10001002d5bceeab42c2e149d8ad484cf322) --- erpnext/setup/doctype/employee/employee.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py index b0a74a37c8c..b577b2f0885 100755 --- a/erpnext/setup/doctype/employee/employee.py +++ b/erpnext/setup/doctype/employee/employee.py @@ -429,8 +429,6 @@ def create_user(employee: str, email: str | None = None, create_user_permission: if emp.user_id: frappe.throw(_("Employee {0} already has a linked user").format(emp.name)) - if not email: - email = emp.company_email if not email: frappe.throw(_("Email is required to create a user")) @@ -465,10 +463,6 @@ def create_user(employee: str, email: str | None = None, create_user_permission: emp.user_id = user.name emp.create_user_permission = cint(create_user_permission) - if not emp.company_email: - emp.company_email = email - if not emp.prefered_contact_email: - emp.prefered_contact_email = "Company Email" emp.save() if cint(create_user_permission): From 553bc87ac7665dad83ec56eaafa6888da441e63f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 15:31:51 +0530 Subject: [PATCH 17/21] fix: fallback to Personal Email for user creation just like client-side (cherry picked from commit 31af13a5e6fa776041ef9ba2afcfc10c8f46eba2) --- erpnext/setup/doctype/employee/employee.json | 6 +++--- erpnext/setup/doctype/employee/employee.py | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index dbaa1a168f2..03f68b91dc5 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -43,8 +43,8 @@ "contact_details", "cell_number", "column_break_40", - "personal_email", "company_email", + "personal_email", "column_break4", "prefered_contact_email", "prefered_email", @@ -298,7 +298,7 @@ { "default": "0", "depends_on": "eval:doc.__islocal && !doc.user_id", - "description": "This will create User for this employee depending on the Company Email.", + "description": "Creates a User account for this employee using the Preferred, Company, or Personal email.", "fieldname": "create_user_automatically", "fieldtype": "Check", "label": "Create User Automatically", @@ -834,7 +834,7 @@ "image_field": "image", "is_tree": 1, "links": [], - "modified": "2026-03-23 14:05:42.144641", + "modified": "2026-03-23 15:26:05.149280", "modified_by": "Administrator", "module": "Setup", "name": "Employee", diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py index b577b2f0885..d66d091320b 100755 --- a/erpnext/setup/doctype/employee/employee.py +++ b/erpnext/setup/doctype/employee/employee.py @@ -161,11 +161,11 @@ class Employee(NestedSet): self.validate_duplicate_user_id() def validate_auto_user_creation(self): - if self.create_user_automatically and not (self.prefered_email or self.company_email): + if self.create_user_automatically and not ( + self.prefered_email or self.company_email or self.personal_email + ): frappe.throw( - _( - "Company Email or Preferred Email is mandatory when 'Create User Automatically' is enabled" - ), + _("Company or Personal Email is mandatory when 'Create User Automatically' is enabled"), frappe.MandatoryError, title=_("Auto User Creation Error"), ) @@ -193,7 +193,7 @@ class Employee(NestedSet): create_user( employee=self.name, - email=self.prefered_email or self.company_email, + email=self.prefered_email or self.company_email or self.personal_email, create_user_permission=self.create_user_permission, ) From af94ed865a8b157258cec5785b7ef84319b1e8e7 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 15:42:13 +0530 Subject: [PATCH 18/21] fix: reset User ID and make it read-only if 'Create User Automatically' is set (cherry picked from commit 2be6bb694fd8cbb98babed517054ae4adc3fa5ef) --- erpnext/setup/doctype/employee/employee.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/setup/doctype/employee/employee.js b/erpnext/setup/doctype/employee/employee.js index 2c121828034..b4adc01b102 100755 --- a/erpnext/setup/doctype/employee/employee.js +++ b/erpnext/setup/doctype/employee/employee.js @@ -96,6 +96,15 @@ frappe.ui.form.on("Employee", { } }, + create_user_automatically: function (frm) { + if (frm.doc.create_user_automatically) { + frm.set_value("user_id", ""); + frm.set_df_property("user_id", "read_only", 1); + } else { + frm.set_df_property("user_id", "read_only", 0); + } + }, + prefered_contact_email: function (frm) { frm.events.update_contact(frm); }, From dfd9aa56bedf333c159fdc1573e64b320621da06 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 16:09:58 +0530 Subject: [PATCH 19/21] test: Create User Automatically (cherry picked from commit d4ecede3c3a17eb4757794d13a19611538cb47d2) # Conflicts: # erpnext/setup/doctype/employee/test_employee.py --- .../setup/doctype/employee/test_employee.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/erpnext/setup/doctype/employee/test_employee.py b/erpnext/setup/doctype/employee/test_employee.py index c022f724a66..6560c2f883b 100644 --- a/erpnext/setup/doctype/employee/test_employee.py +++ b/erpnext/setup/doctype/employee/test_employee.py @@ -64,6 +64,64 @@ class TestEmployee(ERPNextTestSuite): self.assertEqual(qb_employee_list, employee_list) frappe.set_user("Administrator") +<<<<<<< HEAD +======= + def test_create_user_automatically(self): + def get_new_employee(email: str, create_user_permission: int): + return frappe.get_doc( + { + "doctype": "Employee", + "first_name": "Test Auto User 1", + "company": erpnext.get_default_company(), + "date_of_birth": "2000-05-08", + "date_of_joining": "2013-01-01", + "gender": "Female", + "personal_email": email, + "status": "Active", + "create_user_automatically": 1, + "create_user_permission": create_user_permission, + } + ).insert() + + employee1 = get_new_employee("test_auto_user1@example.com", True) + user = frappe.db.get_value("User", "test_auto_user1@example.com") + self.assertTrue(user) + self.assertEqual(employee1.user_id, user) + + # Verify user permissions are created + self.assertTrue( + frappe.db.exists( + "User Permission", {"allow": "Employee", "for_value": employee1.name, "user": user} + ) + ) + self.assertTrue( + frappe.db.exists( + "User Permission", {"allow": "Company", "for_value": employee1.company, "user": user} + ) + ) + + # Test disabled create_user_permission + employee2 = get_new_employee("test_auto_user2@example.com", False) + user2 = frappe.db.get_value("User", "test_auto_user2@example.com") + self.assertTrue(user2) + self.assertEqual(employee2.user_id, user2) + + # Verify user permissions are not created + self.assertFalse( + frappe.db.exists( + "User Permission", {"allow": "Employee", "for_value": employee2.name, "user": user2} + ) + ) + self.assertFalse( + frappe.db.exists( + "User Permission", {"allow": "Company", "for_value": employee2.company, "user": user2} + ) + ) + + def tearDown(self): + frappe.db.rollback() + +>>>>>>> d4ecede3c3 (test: Create User Automatically) def make_employee(user, company=None, **kwargs): if not frappe.db.get_value("User", user): From 33d868f41536ded4c7bfb179888f3059d4e437e5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 16:52:11 +0530 Subject: [PATCH 20/21] test(fix): set company in employee (cherry picked from commit a14f834589b5edb952a52c3b2f8a73642ff6b022) # Conflicts: # erpnext/setup/doctype/employee/test_employee.py --- erpnext/setup/doctype/employee/test_employee.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/employee/test_employee.py b/erpnext/setup/doctype/employee/test_employee.py index 6560c2f883b..c1361829cea 100644 --- a/erpnext/setup/doctype/employee/test_employee.py +++ b/erpnext/setup/doctype/employee/test_employee.py @@ -72,7 +72,7 @@ class TestEmployee(ERPNextTestSuite): { "doctype": "Employee", "first_name": "Test Auto User 1", - "company": erpnext.get_default_company(), + "company": "_Test Company", "date_of_birth": "2000-05-08", "date_of_joining": "2013-01-01", "gender": "Female", @@ -118,10 +118,13 @@ class TestEmployee(ERPNextTestSuite): ) ) +<<<<<<< HEAD def tearDown(self): frappe.db.rollback() >>>>>>> d4ecede3c3 (test: Create User Automatically) +======= +>>>>>>> a14f834589 (test(fix): set company in employee) def make_employee(user, company=None, **kwargs): if not frappe.db.get_value("User", user): From 03510d96be2da0fe2e85c6e45c9ab83e8865f1dd Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 23 Mar 2026 17:57:30 +0530 Subject: [PATCH 21/21] chore: fix conflicts --- .../doctype/production_plan/production_plan.py | 2 +- erpnext/setup/doctype/employee/test_employee.py | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 3dc32ef4dab..30b3968fc80 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -680,7 +680,7 @@ class ProductionPlan(Document): frappe.delete_doc("Work Order", d.name) @frappe.whitelist() - def set_status(self, close: bool | None = None, update_bin: bool = False) -> None: + def set_status(self, close=None, update_bin=False): self.status = {0: "Draft", 1: "Submitted", 2: "Cancelled"}.get(self.docstatus) if close: diff --git a/erpnext/setup/doctype/employee/test_employee.py b/erpnext/setup/doctype/employee/test_employee.py index c1361829cea..b553898dc49 100644 --- a/erpnext/setup/doctype/employee/test_employee.py +++ b/erpnext/setup/doctype/employee/test_employee.py @@ -64,8 +64,6 @@ class TestEmployee(ERPNextTestSuite): self.assertEqual(qb_employee_list, employee_list) frappe.set_user("Administrator") -<<<<<<< HEAD -======= def test_create_user_automatically(self): def get_new_employee(email: str, create_user_permission: int): return frappe.get_doc( @@ -118,13 +116,6 @@ class TestEmployee(ERPNextTestSuite): ) ) -<<<<<<< HEAD - def tearDown(self): - frappe.db.rollback() - ->>>>>>> d4ecede3c3 (test: Create User Automatically) -======= ->>>>>>> a14f834589 (test(fix): set company in employee) def make_employee(user, company=None, **kwargs): if not frappe.db.get_value("User", user):