Make Journal entry from Invoice based on Payment Terms (due date) #11989 (#12047)

* split journal entry according to invoice due date

* fix bug where reference_due_dates options are not populated:
- after making JE, add options to _onload
- use __onload to add options

* cater for case where no payment_schedule
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index e25abfb..c92a728 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -61,6 +61,16 @@
 				}
 			}
 		});
+	},
+
+	accounts_on_form_rendered: function(frm) {
+		const options = frm.doc.__onload;
+
+		if (options && frm.cur_grid) {
+			frm.cur_grid.get_field("reference_due_date")
+				.df.options = options[frm.cur_grid.get_field('reference_name').value];
+			frm.cur_grid.refresh_field('reference_due_date');
+		}
 	}
 });
 
@@ -248,7 +258,6 @@
 
 		if (d.reference_type && d.reference_name && d.reference_due_date) {
 			if (in_list(["Sales Invoice", "Purchase Invoice"], d.reference_type)) {
-				console.log('cdt:', cdt, cdn);
 				frappe.model.set_value(cdt, cdn, 'debit_in_account_currency', '');
 				frappe.model.set_value(cdt, cdn, 'credit_in_account_currency', '');
 			}
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index a298ae3..0c3503b 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -3,7 +3,7 @@
 
 from __future__ import unicode_literals
 import frappe, erpnext, json
-from frappe.utils import cstr, flt, fmt_money, formatdate, getdate
+from frappe.utils import cstr, flt, fmt_money, formatdate, getdate, DATE_FORMAT
 from frappe import msgprint, _, scrub
 from erpnext.controllers.accounts_controller import AccountsController
 from erpnext.accounts.utils import get_balance_on, get_account_currency
@@ -647,9 +647,8 @@
 		party_type = "Supplier"
 		party_account = ref_doc.credit_to
 
-
-	if (dt=="Sales Invoice" and ref_doc.outstanding_amount > 0) \
-		or (dt=="Purchase Invoice" and ref_doc.outstanding_amount < 0):
+	if (dt == "Sales Invoice" and ref_doc.outstanding_amount > 0) \
+		or (dt == "Purchase Invoice" and ref_doc.outstanding_amount < 0):
 			amount_field_party = "credit_in_account_currency"
 			amount_field_bank = "debit_in_account_currency"
 	else:
@@ -670,6 +669,7 @@
 		"journal_entry": journal_entry
 	})
 
+
 def get_payment_entry(ref_doc, args):
 	cost_center = frappe.db.get_value("Company", ref_doc.company, "cost_center")
 	exchange_rate = 1
@@ -687,26 +687,58 @@
 		"remark": args.get("remarks")
 	})
 
-	party_row = je.append("accounts", {
-		"account": args.get("party_account"),
-		"party_type": args.get("party_type"),
-		"party": ref_doc.get(args.get("party_type").lower()),
-		"cost_center": cost_center,
-		"account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
-		"account_currency": args.get("party_account_currency") or \
-			get_account_currency(args.get("party_account")),
-		"balance": get_balance_on(args.get("party_account")),
-		"party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
-		"exchange_rate": exchange_rate,
-		args.get("amount_field_party"): args.get("amount"),
-		"is_advance": args.get("is_advance"),
-		"reference_type": ref_doc.doctype,
-		"reference_name": ref_doc.name
-	})
+	if not ref_doc.payment_schedule:
+		je.append("accounts", {
+			"account": args.get("party_account"),
+			"party_type": args.get("party_type"),
+			"party": ref_doc.get(args.get("party_type").lower()),
+			"cost_center": cost_center,
+			"account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
+			"account_currency": args.get("party_account_currency") or \
+								get_account_currency(args.get("party_account")),
+			"balance": get_balance_on(args.get("party_account")),
+			"party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
+			"exchange_rate": exchange_rate,
+			args.get("amount_field_party"): args.get("amount"),
+			"is_advance": args.get("is_advance"),
+			"reference_type": ref_doc.doctype,
+			"reference_name": ref_doc.name
+		})
+
+	else:
+		options_ref_name_list = [
+			d.due_date.strftime(DATE_FORMAT) for d in ref_doc.payment_schedule
+			if d.get('due_date')
+		]
+
+		for payment_term in ref_doc.payment_schedule:
+			je.append("accounts", {
+				"account": args.get("party_account"),
+				"party_type": args.get("party_type"),
+				"party": ref_doc.get(args.get("party_type").lower()),
+				"cost_center": cost_center,
+				"account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
+				"account_currency": args.get("party_account_currency") or \
+									get_account_currency(args.get("party_account")),
+				"balance": get_balance_on(args.get("party_account")),
+				"party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
+				"exchange_rate": exchange_rate,
+				args.get("amount_field_party"): payment_term.payment_amount,
+				"is_advance": args.get("is_advance"),
+				"reference_type": ref_doc.doctype,
+				"reference_name": ref_doc.name,
+				"reference_due_date": payment_term.due_date
+			})
+			je.set_onload(ref_doc.name, '\n'.join(options_ref_name_list))
+
+	# First multi currency check
+	for row in je.accounts:
+		if row.account_currency != ref_doc.company_currency:
+			je.multi_currency = 1
 
 	bank_row = je.append("accounts")
 
-	#make it bank_details
+	# Make it bank_details
 	bank_account = get_default_bank_cash_account(ref_doc.company, "Bank", account=args.get("bank_account"))
 	if bank_account:
 		bank_row.update(bank_account)
@@ -725,9 +757,9 @@
 	else:
 		bank_row.set(args.get("amount_field_bank"), amount * exchange_rate)
 
-	# set multi currency check
-	if party_row.account_currency != ref_doc.company_currency \
-		or (bank_row.account_currency and bank_row.account_currency != ref_doc.company_currency):
+	# Multi currency check again
+	if not je.multi_currency:
+		if bank_row.account_currency and bank_row.account_currency != ref_doc.company_currency:
 			je.multi_currency = 1
 
 	je.set_amounts_in_company_currency()
@@ -735,6 +767,7 @@
 
 	return je if args.get("journal_entry") else je.as_dict()
 
+
 @frappe.whitelist()
 def get_opening_accounts(company):
 	"""get all balance sheet accounts for opening entry"""
@@ -757,6 +790,7 @@
 		and jv.docstatus = 1 and jv.`{0}` like %s order by jv.name desc limit %s, %s""".format(frappe.db.escape(searchfield)),
 		(filters.get("account"), cstr(filters.get("party")), "%{0}%".format(txt), start, page_len))
 
+
 @frappe.whitelist()
 def get_outstanding(args):
 	if not frappe.has_permission("Account"):
@@ -809,6 +843,7 @@
 			"party": invoice.get(scrub(party_type))
 		}
 
+
 @frappe.whitelist()
 def get_party_account_and_balance(company, party_type, party):
 	if not frappe.has_permission("Account"):
@@ -826,6 +861,7 @@
 		"account_currency": frappe.db.get_value("Account", account, "account_currency")
 	}
 
+
 @frappe.whitelist()
 def get_account_balance_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None):
 	"""Returns dict of account balance and party type to be set in Journal Entry on selection of account."""
@@ -863,7 +899,7 @@
 
 	return grid_values
 
-# Added posting_date as one of the parameters of get_exchange_rate
+
 @frappe.whitelist()
 def get_exchange_rate(posting_date, account=None, account_currency=None, company=None,
 		reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None):
@@ -896,6 +932,7 @@
 	# don't return None or 0 as it is multipled with a value and that value could be lost
 	return exchange_rate or 1
 
+
 @frappe.whitelist()
 def get_average_exchange_rate(account):
 	exchange_rate = 0