Merge pull request #26037 from deepeshgarg007/payment_entry_auto_tax_fixes

fix: Auto tax calculations in Payment Entry
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 5010fdc..114b7d2 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1937,69 +1937,53 @@
 		frappe.flags.country = country
 
 	def test_einvoice_json(self):
-		from erpnext.regional.india.e_invoice.utils import make_einvoice
+		from erpnext.regional.india.e_invoice.utils import make_einvoice, validate_totals
 
-		si = make_sales_invoice_for_ewaybill()
-		si.naming_series = 'INV-2020-.#####'
-		si.items = []
-		si.append("items", {
-			"item_code": "_Test Item",
-			"uom": "Nos",
-			"warehouse": "_Test Warehouse - _TC",
-			"qty": 2000,
-			"rate": 12,
-			"income_account": "Sales - _TC",
-			"expense_account": "Cost of Goods Sold - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-		})
-		si.append("items", {
-			"item_code": "_Test Item 2",
-			"uom": "Nos",
-			"warehouse": "_Test Warehouse - _TC",
-			"qty": 420,
-			"rate": 15,
-			"income_account": "Sales - _TC",
-			"expense_account": "Cost of Goods Sold - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-		})
+		si = get_sales_invoice_for_e_invoice()
 		si.discount_amount = 100
 		si.save()
 
 		einvoice = make_einvoice(si)
-
-		total_item_ass_value = 0
-		total_item_cgst_value = 0
-		total_item_sgst_value = 0
-		total_item_igst_value = 0
-		total_item_value = 0
-
-		for item in einvoice['ItemList']:
-			total_item_ass_value += item['AssAmt']
-			total_item_cgst_value += item['CgstAmt']
-			total_item_sgst_value += item['SgstAmt']
-			total_item_igst_value += item['IgstAmt']
-			total_item_value += item['TotItemVal']
-
-			self.assertTrue(item['AssAmt'], item['TotAmt'] - item['Discount'])
-			self.assertTrue(item['TotItemVal'], item['AssAmt'] + item['CgstAmt'] + item['SgstAmt'] + item['IgstAmt'])
-
-		value_details = einvoice['ValDtls']
-
-		self.assertEqual(einvoice['Version'], '1.1')
-		self.assertEqual(value_details['AssVal'], total_item_ass_value)
-		self.assertEqual(value_details['CgstVal'], total_item_cgst_value)
-		self.assertEqual(value_details['SgstVal'], total_item_sgst_value)
-		self.assertEqual(value_details['IgstVal'], total_item_igst_value)
-
-		calculated_invoice_value = \
-			value_details['AssVal'] + value_details['CgstVal'] \
-			+ value_details['SgstVal'] + value_details['IgstVal'] \
-			+ value_details['OthChrg'] - value_details['Discount']
-
-		self.assertTrue(value_details['TotInvVal'] - calculated_invoice_value < 0.1)
-
-		self.assertEqual(value_details['TotInvVal'], si.base_grand_total)
 		self.assertTrue(einvoice['EwbDtls'])
+		validate_totals(einvoice)
+
+		si.apply_discount_on = 'Net Total'
+		si.save()
+		einvoice = make_einvoice(si)
+		validate_totals(einvoice)
+
+		[d.set('included_in_print_rate', 1) for d in si.taxes]
+		si.save()
+		einvoice = make_einvoice(si)
+		validate_totals(einvoice)
+
+def get_sales_invoice_for_e_invoice():
+	si = make_sales_invoice_for_ewaybill()
+	si.naming_series = 'INV-2020-.#####'
+	si.items = []
+	si.append("items", {
+		"item_code": "_Test Item",
+		"uom": "Nos",
+		"warehouse": "_Test Warehouse - _TC",
+		"qty": 2000,
+		"rate": 12,
+		"income_account": "Sales - _TC",
+		"expense_account": "Cost of Goods Sold - _TC",
+		"cost_center": "_Test Cost Center - _TC",
+	})
+
+	si.append("items", {
+		"item_code": "_Test Item 2",
+		"uom": "Nos",
+		"warehouse": "_Test Warehouse - _TC",
+		"qty": 420,
+		"rate": 15,
+		"income_account": "Sales - _TC",
+		"expense_account": "Cost of Goods Sold - _TC",
+		"cost_center": "_Test Cost Center - _TC",
+	})
+
+	return si
 
 	def test_item_tax_net_range(self):
 		item = create_item("T Shirt")
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index e01cb6e..e025fc6 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -457,7 +457,7 @@
 					frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen)
 
 		elif party_type == "Employee":
-			if frappe.db.get_value("Employee", party_name, "status") == "Left":
+			if frappe.db.get_value("Employee", party_name, "status") != "Active":
 				frappe.msgprint(_("{0} {1} is not active").format(party_type, party_name), alert=True)
 
 def get_timeline_data(doctype, name):
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
index 11ec766..2448e17 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
@@ -30,42 +30,54 @@
 		po.reload()
 
 		po_data = [row for row in data if row.get('purchase_order') == po.name]
+		# Alphabetically sort to be certain of order
+		po_data = sorted(po_data, key = lambda i: i['rm_item_code'])
 
 		self.assertEqual(len(po_data), 2)
-		self.assertIn(po_data[0]['rm_item_code'], ['_Test Item', '_Test Item Home Desktop 100'])
-		self.assertIn(po_data[0]['p_qty'], [9, 18])
-		self.assertIn(po_data[0]['transferred_qty'], [1, 2])
+		self.assertEqual(po_data[0]['purchase_order'], po.name)
 
-		self.assertEqual(data[1]['purchase_order'], po.name)
-		self.assertIn(data[1]['rm_item_code'], ['_Test Item', '_Test Item Home Desktop 100'])
-		self.assertIn(data[1]['p_qty'], [9, 18])
-		self.assertIn(data[1]['transferred_qty'], [1, 2])
+		self.assertEqual(po_data[0]['rm_item_code'], '_Test Item')
+		self.assertEqual(po_data[0]['p_qty'], 8)
+		self.assertEqual(po_data[0]['transferred_qty'], 2)
+
+		self.assertEqual(po_data[1]['rm_item_code'], '_Test Item Home Desktop 100')
+		self.assertEqual(po_data[1]['p_qty'], 19)
+		self.assertEqual(po_data[1]['transferred_qty'], 1)
 
 		se.cancel()
 		po.cancel()
 
 def transfer_subcontracted_raw_materials(po):
+	# Order of supplied items fetched in PO is flaky
+	transfer_qty_map = {
+		'_Test Item': 2,
+		'_Test Item Home Desktop 100': 1
+	}
+
+	item_1 = po.supplied_items[0].rm_item_code
+	item_2 = po.supplied_items[1].rm_item_code
+
 	rm_item = [
 		{
 			'name': po.supplied_items[0].name,
-			'item_code': '_Test Item Home Desktop 100',
-			'rm_item_code': '_Test Item Home Desktop 100',
-			'item_name': '_Test Item Home Desktop 100',
-			'qty': 2,
+			'item_code': item_1,
+			'rm_item_code': item_1,
+			'item_name': item_1,
+			'qty': transfer_qty_map[item_1],
 			'warehouse': '_Test Warehouse - _TC',
 			'rate': 100,
-			'amount': 200,
+			'amount': 100 * transfer_qty_map[item_1],
 			'stock_uom': 'Nos'
 		},
 		{
 			'name': po.supplied_items[1].name,
-			'item_code': '_Test Item',
-			'rm_item_code': '_Test Item',
-			'item_name': '_Test Item',
-			'qty': 1,
+			'item_code': item_2,
+			'rm_item_code': item_2,
+			'item_name': item_2,
+			'qty': transfer_qty_map[item_2],
 			'warehouse': '_Test Warehouse - _TC',
 			'rate': 100,
-			'amount': 100,
+			'amount': 100 * transfer_qty_map[item_2],
 			'stock_uom': 'Nos'
 		}
 	]
diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json
index 5123d6a..5442ed5 100644
--- a/erpnext/hr/doctype/employee/employee.json
+++ b/erpnext/hr/doctype/employee/employee.json
@@ -207,7 +207,7 @@
    "label": "Status",
    "oldfieldname": "status",
    "oldfieldtype": "Select",
-   "options": "Active\nLeft",
+   "options": "Active\nInactive\nLeft",
    "reqd": 1,
    "search_index": 1
   },
@@ -813,7 +813,7 @@
  "idx": 24,
  "image_field": "image",
  "links": [],
- "modified": "2021-01-02 16:54:33.477439",
+ "modified": "2021-06-12 11:31:37.730760",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee",
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index ed7d588..bc56942 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -37,7 +37,7 @@
 
 	def validate(self):
 		from erpnext.controllers.status_updater import validate_status
-		validate_status(self.status, ["Active", "Temporary Leave", "Left"])
+		validate_status(self.status, ["Active", "Inactive", "Left"])
 
 		self.employee = self.name
 		self.set_employee_name()
@@ -478,7 +478,7 @@
 @frappe.whitelist()
 def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False):
 
-	filters = [['status', '!=', 'Left']]
+	filters = [['status', '=', 'Active']]
 	if company and company != 'All Companies':
 		filters.append(['company', '=', company])
 
diff --git a/erpnext/hr/doctype/employee/employee_list.js b/erpnext/hr/doctype/employee/employee_list.js
index 4483703..6679e31 100644
--- a/erpnext/hr/doctype/employee/employee_list.js
+++ b/erpnext/hr/doctype/employee/employee_list.js
@@ -3,7 +3,7 @@
 	filters: [["status","=", "Active"]],
 	get_indicator: function(doc) {
 		var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status];
-		indicator[1] = {"Active": "green", "Temporary Leave": "red", "Left": "gray"}[doc.status];
+		indicator[1] = {"Active": "green", "Inactive": "red", "Left": "gray"}[doc.status];
 		return indicator;
 	}
 };
diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.py b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
index 4994921..83fb235 100644
--- a/erpnext/hr/doctype/employee_promotion/employee_promotion.py
+++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
@@ -11,12 +11,12 @@
 
 class EmployeePromotion(Document):
 	def validate(self):
-		if frappe.get_value("Employee", self.employee, "status") == "Left":
-			frappe.throw(_("Cannot promote Employee with status Left"))
+		if frappe.get_value("Employee", self.employee, "status") != "Active":
+			frappe.throw(_("Cannot promote Employee with status Left or Inactive"))
 
 	def before_submit(self):
 		if getdate(self.promotion_date) > getdate():
-			frappe.throw(_("Employee Promotion cannot be submitted before Promotion Date "),
+			frappe.throw(_("Employee Promotion cannot be submitted before Promotion Date"),
 				frappe.DocstatusTransitionError)
 
 	def on_submit(self):
diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
index 3539970..6eec9fa 100644
--- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
@@ -11,12 +11,12 @@
 
 class EmployeeTransfer(Document):
 	def validate(self):
-		if frappe.get_value("Employee", self.employee, "status") == "Left":
-			frappe.throw(_("Cannot transfer Employee with status Left"))
+		if frappe.get_value("Employee", self.employee, "status") != "Active":
+			frappe.throw(_("Cannot transfer Employee with status Left or Inactive"))
 
 	def before_submit(self):
 		if getdate(self.transfer_date) > getdate():
-			frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date "),
+			frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date"),
 				frappe.DocstatusTransitionError)
 
 	def on_submit(self):
diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
index b8e56ae..049ea26 100644
--- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
+++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
@@ -10,8 +10,8 @@
 
 class RetentionBonus(Document):
 	def validate(self):
-		if frappe.get_value('Employee', self.employee, 'status') == 'Left':
-			frappe.throw(_('Cannot create Retention Bonus for left Employees'))
+		if frappe.get_value('Employee', self.employee, 'status') != 'Active':
+			frappe.throw(_('Cannot create Retention Bonus for Left or Inactive Employees'))
 		if getdate(self.bonus_payment_date) < getdate():
 			frappe.throw(_('Bonus Payment Date cannot be a past date'))
 
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index 0eaf790..11ebef7 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -38,7 +38,7 @@
 	einvoicing_eligible_from = frappe.db.get_single_value('E Invoice Settings', 'applicable_from') or '2021-04-01'
 	if getdate(doc.get('posting_date')) < getdate(einvoicing_eligible_from):
 		return False
-	
+
 	invalid_company = not frappe.db.get_value('E Invoice User', { 'company': doc.get('company') })
 	invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
 	company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
@@ -135,7 +135,7 @@
 
 def get_party_details(address_name, is_shipping_address=False):
 	addr = frappe.get_doc('Address', address_name)
-	
+
 	validate_address_fields(addr, is_shipping_address)
 
 	if addr.gst_state_number == 97:
@@ -188,11 +188,6 @@
 
 		item.qty = abs(item.qty)
 
-		if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount:
-			item.discount_amount = abs(item.base_amount - item.base_net_amount)
-		else:
-			item.discount_amount = 0
-
 		item.unit_rate = abs((abs(item.taxable_value) - item.discount_amount)/ item.qty)
 		item.gross_amount = abs(item.taxable_value) + item.discount_amount
 		item.taxable_value = abs(item.taxable_value)
@@ -254,18 +249,8 @@
 
 def get_invoice_value_details(invoice):
 	invoice_value_details = frappe._dict(dict())
-
-	if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount:
-		# Discount already applied on net total which means on items
-		invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
-		invoice_value_details.invoice_discount_amt = 0
-	elif invoice.apply_discount_on == 'Grand Total' and invoice.discount_amount:
-		invoice_value_details.invoice_discount_amt = invoice.base_discount_amount
-		invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
-	else:
-		invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
-		# since tax already considers discount amount
-		invoice_value_details.invoice_discount_amt = 0
+	invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
+	invoice_value_details.invoice_discount_amt = 0
 
 	invoice_value_details.round_off = invoice.base_rounding_adjustment
 	invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total)
@@ -287,8 +272,7 @@
 	considered_rows = []
 
 	for t in invoice.taxes:
-		tax_amount = t.base_tax_amount if (invoice.apply_discount_on == 'Grand Total' and invoice.discount_amount) \
-						else t.base_tax_amount_after_discount_amount
+		tax_amount = t.base_tax_amount_after_discount_amount
 		if t.account_head in gst_accounts_list:
 			if t.account_head in gst_accounts.cess_account:
 				# using after discount amt since item also uses after discount amt for cess calc
@@ -995,7 +979,7 @@
 		self.invoice.failure_description = self.get_failure_message(errors) if errors else ""
 		self.update_invoice()
 		frappe.db.commit()
-	
+
 	def get_failure_message(self, errors):
 		if isinstance(errors, list):
 			errors = ', '.join(errors)
@@ -1052,7 +1036,7 @@
 			_('{} e-invoices generated successfully').format(success),
 			title=_('Bulk E-Invoice Generation Complete')
 		)
-			
+
 	else:
 		enqueue_bulk_action(schedule_bulk_generate_irn, docnames=docnames)
 
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 075c698..a4466e7 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -817,12 +817,8 @@
 				considered_rows.append(prev_row_id)
 
 	for item in doc.get('items'):
-		if doc.apply_discount_on == 'Grand Total' and doc.discount_amount:
-			proportionate_value = item.base_amount if doc.base_total else item.qty
-			total_value = doc.base_total if doc.base_total else doc.total_qty
-		else:
-			proportionate_value = item.base_net_amount if doc.base_net_total else item.qty
-			total_value = doc.base_net_total if doc.base_net_total else doc.total_qty
+		proportionate_value = item.base_net_amount if doc.base_net_total else item.qty
+		total_value = doc.base_net_total if doc.base_net_total else doc.total_qty
 
 		applicable_charges = flt(flt(proportionate_value * (flt(additional_taxes) / flt(total_value)),
 			item.precision('taxable_value')))