Merge branch 'ESS-LLP-employee_promotion_transfer' into enterprise_sprint
diff --git a/erpnext/docs/assets/img/human-resources/employee_promotion.png b/erpnext/docs/assets/img/human-resources/employee_promotion.png
new file mode 100644
index 0000000..d9ca321
--- /dev/null
+++ b/erpnext/docs/assets/img/human-resources/employee_promotion.png
Binary files differ
diff --git a/erpnext/docs/assets/img/human-resources/employee_promotion_1.png b/erpnext/docs/assets/img/human-resources/employee_promotion_1.png
new file mode 100644
index 0000000..a0bc9cc
--- /dev/null
+++ b/erpnext/docs/assets/img/human-resources/employee_promotion_1.png
Binary files differ
diff --git a/erpnext/docs/assets/img/human-resources/employee_transfer.png b/erpnext/docs/assets/img/human-resources/employee_transfer.png
new file mode 100644
index 0000000..edc641e
--- /dev/null
+++ b/erpnext/docs/assets/img/human-resources/employee_transfer.png
Binary files differ
diff --git a/erpnext/docs/assets/img/human-resources/employee_transfer_1.png b/erpnext/docs/assets/img/human-resources/employee_transfer_1.png
new file mode 100644
index 0000000..f9f87e6
--- /dev/null
+++ b/erpnext/docs/assets/img/human-resources/employee_transfer_1.png
Binary files differ
diff --git a/erpnext/docs/user/manual/en/human-resources/employee_promption.md b/erpnext/docs/user/manual/en/human-resources/employee_promption.md
new file mode 100644
index 0000000..a3f1c2d
--- /dev/null
+++ b/erpnext/docs/user/manual/en/human-resources/employee_promption.md
@@ -0,0 +1,15 @@
+# Employee Promotion
+
+You can manage Employee Promotions using this document.
+
+To record an Employee Promotion go to
+
+> Human Resource > Employee Promotion > New Employee Promotion
+
+Select Employee and add all details to be updated to Promotion Details table.
+
+<img class="screenshot" alt="Employee Promotion" src="{{docs_base_url}}/assets/img/human-resources/employee_promotion.png">
+
+Promotion document can be submitted on or after Promotion Date. Once submitted all the changes added to Promotion Details table will applied to Employee.
+
+<img class="screenshot" alt="Employee Promotion" src="{{docs_base_url}}/assets/img/human-resources/employee_promotion_1.png">
diff --git a/erpnext/docs/user/manual/en/human-resources/employee_transfer.md b/erpnext/docs/user/manual/en/human-resources/employee_transfer.md
new file mode 100644
index 0000000..307f9b9
--- /dev/null
+++ b/erpnext/docs/user/manual/en/human-resources/employee_transfer.md
@@ -0,0 +1,17 @@
+# Employee Transfer
+
+You can transfer Employees to different Company or Department by using Employee Transfer.
+
+To reocord an Employee Transfer go to
+
+> Human Resource > Employee Transfer > New Employee Transfer
+
+Select Employee and add all details to be updated to Transfer Details table.
+
+<img class="screenshot" alt="Employee Transfer" src="{{docs_base_url}}/assets/img/human-resources/employee_transfer.png">
+
+Transfer document can be submitted on or after Transfer Date. Once submitted all the changes added to Transfer Details table will applied to Employee.
+
+<img class="screenshot" alt="Employee Transfer" src="{{docs_base_url}}/assets/img/human-resources/employee_transfer_1.png">
+
+> Note : If Create New Employee ID is checked, a new Employee will be created with property changes in Transfer Details table and old Employee will be marked as releived. Leave allocations for the new Employee has to be manually created from Leave Period.
diff --git a/erpnext/docs/user/manual/en/human-resources/index.txt b/erpnext/docs/user/manual/en/human-resources/index.txt
index 479e2dd..758cdaa 100644
--- a/erpnext/docs/user/manual/en/human-resources/index.txt
+++ b/erpnext/docs/user/manual/en/human-resources/index.txt
@@ -18,4 +18,6 @@
 daily-work-summary
 fleet-management
 loan-management
+employee-promotion
+employee-transfer
 articles
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index 0f11639..4cf28a1 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -156,6 +156,9 @@
 	def on_trash(self):
 		self.update_nsm_model()
 		delete_events(self.doctype, self.name)
+		if frappe.db.exists("Employee Transfer", {'new_employee_id': self.name, 'docstatus': 1}):
+			emp_transfer = frappe.get_doc("Employee Transfer", {'new_employee_id': self.name, 'docstatus': 1})
+			emp_transfer.db_set("new_employee_id", '')
 
 	def validate_preferred_email(self):
 		if self.prefered_contact_email and not self.get(scrub(self.prefered_contact_email)):
diff --git a/erpnext/hr/doctype/employee/employee_dashboard.py b/erpnext/hr/doctype/employee/employee_dashboard.py
index 0f95020..e1e1fce 100644
--- a/erpnext/hr/doctype/employee/employee_dashboard.py
+++ b/erpnext/hr/doctype/employee/employee_dashboard.py
@@ -25,6 +25,10 @@
 			{
 				'label': _('Evaluation'),
 				'items': ['Appraisal']
+			},
+			{
+				'label': _('Employee Lifecycle'),
+				'items': ['Employee Transfer', 'Employee Promotion']
 			}
 		]
 	}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.js b/erpnext/hr/doctype/employee_promotion/employee_promotion.js
index c1bb788..54e06f4 100644
--- a/erpnext/hr/doctype/employee_promotion/employee_promotion.js
+++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.js
@@ -1,6 +1,8 @@
 // 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
index a7d49bc..1e3081a 100644
--- a/erpnext/hr/doctype/employee_promotion/employee_promotion.json
+++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.json
@@ -83,6 +83,36 @@
    "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_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
    "fieldname": "promotion_date", 
    "fieldtype": "Date", 
    "hidden": 0, 
@@ -102,7 +132,7 @@
    "read_only": 0, 
    "remember_last_selected_value": 0, 
    "report_hide": 0, 
-   "reqd": 0, 
+   "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
    "translatable": 0, 
@@ -189,7 +219,7 @@
    "label": "Employee Promotion Detail", 
    "length": 0, 
    "no_copy": 0, 
-   "options": "Employee Promotion Detail", 
+   "options": "Employee Property History", 
    "permlevel": 0, 
    "precision": "", 
    "print_hide": 0, 
@@ -245,7 +275,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-04-14 15:42:49.452085", 
+ "modified": "2018-04-27 17:31:26.902394", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Employee Promotion", 
diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.py b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
index 564f1ad..5fcceed 100644
--- a/erpnext/hr/doctype/employee_promotion/employee_promotion.py
+++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
@@ -4,7 +4,27 @@
 
 from __future__ import unicode_literals
 import frappe
+from frappe import _
 from frappe.model.document import Document
+from frappe.utils import getdate
+from erpnext.hr.utils import update_employee
 
 class EmployeePromotion(Document):
-	pass
+	def validate(self):
+		if frappe.get_value("Employee", self.employee, "status") == "Left":
+			frappe.throw(_("Cannot promote Employee with status Left"))
+
+	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(employee, self.promotion_details)
+		employee.save()
+
+	def on_cancel(self):
+		employee = frappe.get_doc("Employee", self.employee)
+		employee = update_employee(employee, self.promotion_details, True)
+		employee.save()
diff --git a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py
index 8b668e7..420bbe6 100644
--- a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py
+++ b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py
@@ -5,6 +5,31 @@
 
 import frappe
 import unittest
+from frappe.utils import getdate, add_days
+from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee
 
 class TestEmployeePromotion(unittest.TestCase):
-	pass
+	def setUp(self):
+		self.employee = make_employee("employee@promotions.com")
+		frappe.db.sql("""delete from `tabEmployee Promotion`""")
+
+	def test_submit_before_promotion_date(self):
+		promotion_obj = frappe.get_doc({
+			"doctype": "Employee Promotion",
+			"employee": self.employee,
+			"promotion_details" :[
+				{
+				"property": "Designation",
+				"current": "Software Developer",
+				"new": "Project Manager",
+				"fieldname": "designation"
+				}
+			]
+		})
+		promotion_obj.promotion_date = add_days(getdate(), 1)
+		promotion_obj.save()
+		self.assertRaises(frappe.DocstatusTransitionError, promotion_obj.submit)
+		promotion = frappe.get_doc("Employee Promotion", promotion_obj.name)
+		promotion.promotion_date = getdate()
+		promotion.submit()
+		self.assertEqual(promotion.docstatus, 1)
diff --git a/erpnext/hr/doctype/employee_property_history/employee_property_history.json b/erpnext/hr/doctype/employee_property_history/employee_property_history.json
index 7a416b2..0a51579 100644
--- a/erpnext/hr/doctype/employee_property_history/employee_property_history.json
+++ b/erpnext/hr/doctype/employee_property_history/employee_property_history.json
@@ -34,13 +34,12 @@
    "precision": "", 
    "print_hide": 0, 
    "print_hide_if_no_value": 0, 
-   "read_only": 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
   }, 
   {
@@ -65,13 +64,12 @@
    "precision": "", 
    "print_hide": 0, 
    "print_hide_if_no_value": 0, 
-   "read_only": 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
   }, 
   {
@@ -96,13 +94,42 @@
    "precision": "", 
    "print_hide": 0, 
    "print_hide_if_no_value": 0, 
-   "read_only": 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_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
   }
  ], 
@@ -116,7 +143,7 @@
  "issingle": 0, 
  "istable": 1, 
  "max_attachments": 0, 
- "modified": "2018-04-14 11:44:36.458039", 
+ "modified": "2018-05-02 18:19:54.436391", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Employee Property History", 
diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.js b/erpnext/hr/doctype/employee_transfer/employee_transfer.js
index 1d694bd..af751a7 100644
--- a/erpnext/hr/doctype/employee_transfer/employee_transfer.js
+++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.js
@@ -1,6 +1,8 @@
 // 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
index 55fa073..ace9f93 100644
--- a/erpnext/hr/doctype/employee_transfer/employee_transfer.json
+++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.json
@@ -42,7 +42,6 @@
    "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -74,7 +73,6 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -102,10 +100,38 @@
    "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, 
-   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -137,7 +163,6 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -169,7 +194,6 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -200,7 +224,6 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -221,7 +244,7 @@
    "label": "Employee Transfer Detail", 
    "length": 0, 
    "no_copy": 0, 
-   "options": "Employee Transfer Detail", 
+   "options": "Employee Property History", 
    "permlevel": 0, 
    "precision": "", 
    "print_hide": 0, 
@@ -232,7 +255,6 @@
    "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -243,7 +265,7 @@
    "columns": 0, 
    "fieldname": "reallocate_leaves", 
    "fieldtype": "Check", 
-   "hidden": 0, 
+   "hidden": 1, 
    "ignore_user_permissions": 0, 
    "ignore_xss_filter": 0, 
    "in_filter": 0, 
@@ -263,7 +285,6 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0, 
    "unique": 0
   }, 
   {
@@ -294,7 +315,37 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 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, 
    "unique": 0
   }, 
   {
@@ -325,7 +376,6 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "translatable": 0, 
    "unique": 0
   }
  ], 
@@ -339,7 +389,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-04-14 15:42:31.098910", 
+ "modified": "2018-05-05 13:10:32.660537", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Employee Transfer", 
@@ -348,6 +398,7 @@
  "permissions": [
   {
    "amend": 0, 
+   "apply_user_permissions": 0, 
    "cancel": 0, 
    "create": 0, 
    "delete": 0, 
@@ -367,6 +418,7 @@
   }, 
   {
    "amend": 0, 
+   "apply_user_permissions": 0, 
    "cancel": 0, 
    "create": 1, 
    "delete": 0, 
@@ -386,6 +438,7 @@
   }, 
   {
    "amend": 1, 
+   "apply_user_permissions": 0, 
    "cancel": 1, 
    "create": 1, 
    "delete": 1, 
diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
index 96645c4..b58d334 100644
--- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
@@ -4,7 +4,63 @@
 
 from __future__ import unicode_literals
 import frappe
+from frappe import _
 from frappe.model.document import Document
+from frappe.utils import getdate
+from erpnext.hr.utils import update_employee
 
 class EmployeeTransfer(Document):
-	pass
+	def validate(self):
+		if frappe.get_value("Employee", self.employee, "status") == "Left":
+			frappe.throw(_("Cannot transfer Employee with status Left"))
+		if self.new_company and self.company == self.new_company:
+			frappe.throw_("New Company must be different from current company")
+
+	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(new_employee, self.transfer_details)
+			if self.new_company:
+				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(employee, self.transfer_details)
+			if self.new_company:
+				employee.company = self.new_company
+			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 <a href='#Form/Employee/{0}'>{0}</a>\
+					to cancel this document").format(self.new_employee_id))
+			#mark the employee as active
+			employee.status = "Active"
+			employee.relieving_date = ''
+			employee.save()
+		else:
+			employee = update_employee(employee, self.transfer_details, True)
+			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
index 049273e..3dae1a9 100644
--- a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py
@@ -5,6 +5,52 @@
 
 import frappe
 import unittest
+from frappe.utils import getdate, add_days
+from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee
 
 class TestEmployeeTransfer(unittest.TestCase):
-	pass
+	def setUp(self):
+		make_employee("employee2@transfers.com")
+		make_employee("employee3@transfers.com")
+		frappe.db.sql("""delete from `tabEmployee Transfer`""")
+
+	def test_submit_before_transfer_date(self):
+		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):
+		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")
diff --git a/erpnext/hr/employee_property_update.js b/erpnext/hr/employee_property_update.js
new file mode 100644
index 0000000..473916c
--- /dev/null
+++ b/erpnext/hr/employee_property_update.js
@@ -0,0 +1,147 @@
+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){
+			if(frm.doctype == "Employee Promotion"){
+				frm.doc.promotion_details = [];
+			}else if (frm.doctype == "Employee Transfer") {
+				frm.doc.transfer_details = [];
+			}
+		}
+	},
+	employee: function(frm) {
+		frm.add_fetch("employee", "company", "company");
+	},
+	refresh: function(frm) {
+		var table;
+		if(frm.doctype == "Employee Promotion"){
+			table = "promotion_details";
+		}else if (frm.doctype == "Employee Transfer") {
+			table = "transfer_details";
+		}
+		if(!table){return;}
+		cur_frm.fields_dict[table].grid.wrapper.find('.grid-add-row').hide();
+		cur_frm.fields_dict[table].grid.add_custom_button(__('Add Row'), () => {
+			if(!frm.doc.employee){
+				frappe.msgprint(__("Please select Employee"));
+				return;
+			}
+			frappe.call({
+				method: 'erpnext.hr.utils.get_employee_fields_label',
+				callback: function(r) {
+					if(r.message){
+						show_dialog(frm, table, r.message);
+					}
+				}
+			});
+		});
+	}
+});
+
+var show_dialog = function(frm, table, field_labels) {
+	var d = new frappe.ui.Dialog({
+		title: "Update Property",
+		fields: [
+			{fieldname: "property", label: __('Select Property'), fieldtype:"Select", options: field_labels},
+			{fieldname: "current", fieldtype: "Data", label:__('Current'), read_only: true},
+			{fieldname: "field_html", fieldtype: "HTML"}
+		],
+		primary_action_label: __('Add to Details'),
+		primary_action: () => {
+			d.get_primary_btn().attr('disabled', true);
+			if(d.data){
+				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.fields_dict.field_html.$wrapper.html("");
+					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 || ''
+		},
+		parent: d.fields_dict.field_html.wrapper,
+		only_input: false
+	});
+	dynamic_field.make_input();
+	$(dynamic_field.label_area).text(__("New"));
+	dynamic_field.$input.on("change", function(e) {
+		d.data.new = e.target.value;
+	}).on("awesomplete-close", function(e) {
+		d.data.new = e.target.value;
+	});
+};
+
+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);
+		d.fields_dict.field_html.$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/utils.py b/erpnext/hr/utils.py
index aa456aa..057f406 100644
--- a/erpnext/hr/utils.py
+++ b/erpnext/hr/utils.py
@@ -4,7 +4,48 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
+from frappe.utils import formatdate, format_datetime
+from frappe.utils import getdate, get_datetime
 
 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")
+
+@frappe.whitelist()
+def get_employee_fields_label():
+	fields = []
+	for df in frappe.get_meta("Employee").get("fields"):
+		if df.fieldtype in ["Data", "Date", "Datetime", "Float", "Int",
+		"Link", "Percent", "Select", "Small Text"] and df.fieldname not in ["lft", "rgt", "old_parent"]:
+			fields.append({"value": df.fieldname, "label": df.label})
+	return fields
+
+@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 update_employee(employee, details, cancel=False):
+	for item in details:
+		fieldtype = frappe.get_meta("Employee").get_field(item.fieldname).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)
+	return employee