feat(HR): Gratuity Payment
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 31a4c8a..df49667 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -242,7 +242,7 @@
 		elif self.party_type == "Supplier":
 			valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
 		elif self.party_type == "Employee":
-			valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance")
+			valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance", "Gratuity")
 		elif self.party_type == "Shareholder":
 			valid_reference_doctypes = ("Journal Entry")
 
@@ -604,7 +604,7 @@
 		if self.payment_type in ("Receive", "Pay") and self.party:
 			for d in self.get("references"):
 				if d.allocated_amount \
-					and d.reference_doctype in ("Sales Order", "Purchase Order", "Employee Advance"):
+					and d.reference_doctype in ("Sales Order", "Purchase Order", "Employee Advance", "Gratuity"):
 						frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
 
 	def update_expense_claim(self):
@@ -932,6 +932,8 @@
 			exchange_rate = ref_doc.get("exchange_rate")
 			if party_account_currency != ref_doc.currency:
 				total_amount = flt(total_amount) * flt(exchange_rate)
+		elif ref_doc.doctype == "Gratuity":
+				total_amount = ref_doc.amount
 		if not total_amount:
 			if party_account_currency == company_currency:
 				total_amount = ref_doc.base_grand_total
@@ -955,6 +957,8 @@
 				outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
 				if party_account_currency == company_currency:
 					exchange_rate = 1
+		elif reference_doctype == "Gratuity":
+			outstanding_amount = ref_doc.amount - flt(ref_doc.paid_amount)
 		else:
 			outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
 	else:
@@ -996,7 +1000,7 @@
 			total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
 	elif ref_doc.doctype == "Employee Advance":
 		total_amount, exchange_rate = get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc)
-		
+
 	if not total_amount:
 		total_amount, exchange_rate = get_total_amount_exchange_rate_base_on_currency(
 			party_account_currency, company_currency, ref_doc)
@@ -1032,7 +1036,7 @@
 
 def get_bill_no_and_update_amounts(reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency):
 	outstanding_amount, bill_no = None
-	if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
+if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
 		outstanding_amount = ref_doc.get("outstanding_amount")
 		bill_no = ref_doc.get("bill_no")
 	elif reference_doctype == "Expense Claim":
@@ -1160,7 +1164,7 @@
 		party_type = "Customer"
 	elif dt in ("Purchase Invoice", "Purchase Order"):
 		party_type = "Supplier"
-	elif dt in ("Expense Claim", "Employee Advance"):
+	elif dt in ("Expense Claim", "Employee Advance", "Gratuity"):
 		party_type = "Employee"
 	elif dt in ("Fees"):
 		party_type = "Student"
@@ -1177,6 +1181,8 @@
 		party_account = doc.advance_account
 	elif dt == "Expense Claim":
 		party_account = doc.payable_account
+	elif dt == "Gratuity":
+		party_account = doc.expense_account
 	else:
 		party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
 	return party_account
@@ -1222,6 +1228,9 @@
 	elif dt == "Dunning":
 		grand_total = doc.grand_total
 		outstanding_amount = doc.grand_total
+	elif dt == "Gratuity":
+		grand_total = doc.amount
+		outstanding_amount = flt(doc.amount) - flt(doc.paid_amount)
 	else:
 		if party_account_currency == doc.company_currency:
 			grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total)
diff --git a/erpnext/payroll/doctype/gratuity/gratuity.js b/erpnext/payroll/doctype/gratuity/gratuity.js
index cbf5119..d6e93af 100644
--- a/erpnext/payroll/doctype/gratuity/gratuity.js
+++ b/erpnext/payroll/doctype/gratuity/gratuity.js
@@ -5,7 +5,17 @@
 	refresh: function(frm){
 		if(frm.doc.docstatus === 1 && frm.doc.pay_via_salary_slip === 0 && frm.doc.status === "Unpaid") {
 			frm.add_custom_button(__("Make Payment Entry"), function() {
-				frm.trigger('make_payment_entry');
+				return frappe.call({
+					method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry',
+					args: {
+						"dt": cur_frm.doc.doctype,
+						"dn": cur_frm.doc.name
+					},
+					callback: function(r) {
+						var doclist = frappe.model.sync(r.message);
+						frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
+					}
+				});
 			});
 		}
 	},
@@ -17,6 +27,15 @@
 				}
 			};
 		});
+		frm.set_query("expense_account", function() {
+			return {
+				filters: {
+					"root_type": "Asset",
+					"is_group": 0,
+					"company": frm.doc.company
+				}
+			};
+		});
 	},
 	employee: function(frm) {
 		frm.events.calculate_work_experience_and_amount(frm);
@@ -38,9 +57,6 @@
 				frm.set_value("amount", r.message['amount']);
 			});
 		}
-	},
-	make_payment_entry: function(frm){
-		console.log("Hello");
 	}
 
 });
diff --git a/erpnext/payroll/doctype/gratuity/gratuity.json b/erpnext/payroll/doctype/gratuity/gratuity.json
index 8e7bb86..b8122df 100644
--- a/erpnext/payroll/doctype/gratuity/gratuity.json
+++ b/erpnext/payroll/doctype/gratuity/gratuity.json
@@ -24,6 +24,7 @@
   "column_break_15",
   "current_work_experience",
   "amount",
+  "paid_amount",
   "amended_from"
  ],
  "fields": [
@@ -77,7 +78,7 @@
    "default": "0",
    "fieldname": "amount",
    "fieldtype": "Currency",
-   "label": "Amount",
+   "label": "Total Amount",
    "read_only": 1,
    "reqd": 1
   },
@@ -164,11 +165,19 @@
    "fieldtype": "Date",
    "label": "Payroll Date",
    "mandatory_depends_on": "eval: doc.pay_via_salary_slip == 1"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.pay_via_salary_slip == 0",
+   "fieldname": "paid_amount",
+   "fieldtype": "Currency",
+   "label": "Paid Amount",
+   "read_only": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-08-06 15:51:16.047698",
+ "modified": "2020-08-14 11:59:15.499548",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Gratuity",
diff --git a/erpnext/payroll/doctype/gratuity/gratuity.py b/erpnext/payroll/doctype/gratuity/gratuity.py
index fe31f4d..23cc16b 100644
--- a/erpnext/payroll/doctype/gratuity/gratuity.py
+++ b/erpnext/payroll/doctype/gratuity/gratuity.py
@@ -6,13 +6,19 @@
 import frappe
 from frappe import _, bold
 from frappe.model.document import Document
+from frappe.utils import flt
+from math import floor
 
-from dateutil.relativedelta import relativedelta
-
+from frappe.utils import get_datetime
 class Gratuity(Document):
 	def validate(self):
 		calculate_work_experience_and_amount(self.employee, self.gratuity_rule)
 
+	def before_submit(self):
+		self.status = "Unpaid"
+		if self.pay_via_salary_slip:
+			self.status = "Paid"
+
 	def on_submit(self):
 		if self.pay_via_salary_slip:
 			additional_salary = frappe.new_doc('Additional Salary')
@@ -25,9 +31,27 @@
 			additional_salary.ref_doctype = self.doctype
 			additional_salary.ref_docname = self.name
 			additional_salary.submit()
-			self.status = "Paid"
-		else:
-			self.status = "Unpaid"
+
+
+	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"),
+				EmployeeAdvanceOverPayment)
+
+
+		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):
@@ -37,18 +61,29 @@
 	return {'current_work_experience': current_work_experience, "amount": gratuity_amount}
 
 def calculate_work_experience(employee, gratuity_rule):
+
+	total_working_days_per_year = frappe.db.get_value("Gratuity Rule", gratuity_rule, "total_working_days_per_year")
+
 	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(employee)))
 
-	time_difference = relativedelta(relieving_date, date_of_joining)
+	# time_difference = relativedelta(relieving_date, date_of_joining)
 	method = frappe.db.get_value("Gratuity Rule", gratuity_rule, "work_experience_calculation_function")
 
-	current_work_experience = time_difference.years
+	employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(date_of_joining)).days
+
+	# current_work_experience = time_difference.years
+
+	current_work_experience = employee_total_workings_days/total_working_days_per_year or 1
+
+	print("--->", current_work_experience)
 
 	if method == "Round off Work Experience":
-		if time_difference.months >= 6 and time_difference.days > 0:
-			current_work_experience += 1
+		current_work_experience = round(current_work_experience)
+	else:
+		current_work_experience = floor(current_work_experience)
+
 
 	return current_work_experience
 
@@ -61,41 +96,54 @@
 	total_applicable_components_amount = get_total_applicable_component_amount(employee, applicable_earnings_component, gratuity_rule)
 
 
-	fraction_to_be_paid = 0
 
+	calculate_gratuity_amount_based_on = frappe.db.get_value("Gratuity Rule", gratuity_rule, "calculate_gratuity_amount_based_on")
+
+	gratuity_amount = 0
+	fraction_to_be_paid = 0
+	year_left = experience
 	for slab in slabs:
-		if experience > slab.get("from", 0) and (slab.to == 0 or experience < slab.to):
-			fraction_to_be_paid = slab.fraction_of_applicable_earnings
-			if fraction_to_be_paid:
+		if calculate_gratuity_amount_based_on == "Single Slab":
+			if experience >= slab.get("from", 0) and (slab.to == 0 or experience <= slab.to):
+				gratuity_amount = total_applicable_components_amount * experience * slab.fraction_of_applicable_earnings
+				if slab.fraction_of_applicable_earnings:
+					break
+		elif calculate_gratuity_amount_based_on == "Sum of all previous slabs":
+			if slab.get("to") == 0 and slab.get("from") == 0:
+				gratuity_amount += year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings
 				break
 
-	gratuity_amount = total_applicable_components_amount * experience * fraction_to_be_paid
+			if experience > slab.get("to") and experience > slab.get("from"):
+				gratuity_amount += (slab.get("to") - slab.get("from")) * total_applicable_components_amount * slab.fraction_of_applicable_earnings
+				year_left -= (slab.get("to") - slab.get("from"))
+				print(experience, year_left)
+			elif slab.get("from") < experience < slab.get("to"):
+				print(year_left)
+				gratuity_amount += year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings
+
+
 
 	return gratuity_amount
 
 def 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")
-	if calculate_gratuity_amount_based_on == "Last Month Salary":
-		sal_slip  = get_last_salary_slip(employee)
+	sal_slip  = get_last_salary_slip(employee)
 
-		if not sal_slip:
-			frappe.throw(_("No Salary Slip is found for Employee: {0}").format(bold(employee)))
+	if not sal_slip:
+		frappe.throw(_("No Salary Slip is found for Employee: {0}").format(bold(employee)))
 
-		component_and_amounts = frappe.get_list("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
-	elif calculate_gratuity_amount_based_on == "Actual Salary":
-		pass
+	component_and_amounts = frappe.get_list("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
 
diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.json b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.json
index b5de281..40906fa 100644
--- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.json
+++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.json
@@ -8,6 +8,7 @@
  "field_order": [
   "applicable_earnings_component",
   "work_experience_calculation_function",
+  "total_working_days_per_year",
   "column_break_3",
   "disable",
   "calculate_gratuity_amount_based_on",
@@ -26,7 +27,7 @@
    "fieldtype": "Select",
    "in_list_view": 1,
    "label": "Calculate Gratuity Amount Based on",
-   "options": "Last Month Salary\nActual Salary",
+   "options": "Single Slab\nSum of all previous slabs",
    "reqd": 1
   },
   {
@@ -60,10 +61,15 @@
    "fieldtype": "Select",
    "label": "Work Experience Calculation method",
    "options": "Round off Work Experience\nTake Exact Completed Years"
+  },
+  {
+   "fieldname": "total_working_days_per_year",
+   "fieldtype": "Int",
+   "label": "Total Working Days per year"
   }
  ],
  "links": [],
- "modified": "2020-08-06 12:28:13.757792",
+ "modified": "2020-08-13 16:21:10.466739",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Gratuity Rule",