Merge pull request #33524 from rohitwaghchaure/revamp-process-loss-feature

refactor: revamp process loss feature & added tab breaks
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index f319003..45e04ee 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -378,7 +378,7 @@
 			return
 
 		# check if books nor frozen till endate:
-		if accounts_frozen_upto and (end_date) <= getdate(accounts_frozen_upto):
+		if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto):
 			end_date = get_last_day(add_days(accounts_frozen_upto, 1))
 
 		if via_journal_entry:
diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js
index ceba99a..71f2dcc 100644
--- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js
@@ -37,14 +37,11 @@
 
 	refresh: function(frm) {
 		frm.disable_save();
+		frm.add_custom_button(__('Get Payment Entries'), () =>
+			frm.trigger("get_payment_entries")
+		);
 
-		if (frm.doc.account && frm.doc.from_date && frm.doc.to_date) {
-			frm.add_custom_button(__('Get Payment Entries'), () =>
-				frm.trigger("get_payment_entries")
-			);
-
-			frm.change_custom_button_type('Get Payment Entries', null, 'primary');
-		}
+		frm.change_custom_button_type('Get Payment Entries', null, 'primary');
 	},
 
 	update_clearance_date: function(frm) {
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 40a4803..68ac982 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -589,15 +589,15 @@
 				d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
 		else:
 			for d in self.get("accounts"):
-				if flt(d.debit > 0):
+				if flt(d.debit) > 0:
 					accounts_debited.append(d.party or d.account)
 				if flt(d.credit) > 0:
 					accounts_credited.append(d.party or d.account)
 
 			for d in self.get("accounts"):
-				if flt(d.debit > 0):
+				if flt(d.debit) > 0:
 					d.against_account = ", ".join(list(set(accounts_credited)))
-				if flt(d.credit > 0):
+				if flt(d.credit) > 0:
 					d.against_account = ", ".join(list(set(accounts_debited)))
 
 	def validate_debit_credit_amount(self):
@@ -759,7 +759,7 @@
 					pay_to_recd_from = d.party
 
 				if pay_to_recd_from and pay_to_recd_from == d.party:
-					party_amount += d.debit_in_account_currency or d.credit_in_account_currency
+					party_amount += flt(d.debit_in_account_currency) or flt(d.credit_in_account_currency)
 					party_account_currency = d.account_currency
 
 			elif frappe.get_cached_value("Account", d.account, "account_type") in ["Bank", "Cash"]:
@@ -837,7 +837,7 @@
 			make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
 
 	@frappe.whitelist()
-	def get_balance(self):
+	def get_balance(self, difference_account=None):
 		if not self.get("accounts"):
 			msgprint(_("'Entries' cannot be empty"), raise_exception=True)
 		else:
@@ -852,7 +852,13 @@
 						blank_row = d
 
 				if not blank_row:
-					blank_row = self.append("accounts", {})
+					blank_row = self.append(
+						"accounts",
+						{
+							"account": difference_account,
+							"cost_center": erpnext.get_default_cost_center(self.company),
+						},
+					)
 
 				blank_row.exchange_rate = 1
 				if diff > 0:
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index 0b334ae..d986f32 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -170,7 +170,7 @@
 	}
 
 	reconcile() {
-		var show_dialog = this.frm.doc.allocation.filter(d => d.difference_amount && !d.difference_account);
+		var show_dialog = this.frm.doc.allocation.filter(d => d.difference_amount);
 
 		if (show_dialog && show_dialog.length) {
 
@@ -179,8 +179,12 @@
 				title: __("Select Difference Account"),
 				fields: [
 					{
-						fieldname: "allocation", fieldtype: "Table", label: __("Allocation"),
-						data: this.data, in_place_edit: true,
+						fieldname: "allocation",
+						fieldtype: "Table",
+						label: __("Allocation"),
+						data: this.data,
+						in_place_edit: true,
+						cannot_add_rows: true,
 						get_data: () => {
 							return this.data;
 						},
@@ -218,6 +222,10 @@
 							read_only: 1
 						}]
 					},
+					{
+						fieldtype: 'HTML',
+						options: "<b> New Journal Entry will be posted for the difference amount </b>"
+					}
 				],
 				primary_action: () => {
 					const args = dialog.get_values()["allocation"];
@@ -234,7 +242,7 @@
 			});
 
 			this.frm.doc.allocation.forEach(d => {
-				if (d.difference_amount && !d.difference_account) {
+				if (d.difference_amount) {
 					dialog.fields_dict.allocation.df.data.push({
 						'docname': d.name,
 						'reference_name': d.reference_name,
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index ff212f2..ac033f7 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -14,7 +14,6 @@
 	QueryPaymentLedger,
 	get_outstanding_invoices,
 	reconcile_against_document,
-	update_reference_in_payment_entry,
 )
 from erpnext.controllers.accounts_controller import get_advance_payment_entries
 
@@ -80,12 +79,13 @@
 			"t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1"
 		)
 
+		# nosemgrep
 		journal_entries = frappe.db.sql(
 			"""
 			select
 				"Journal Entry" as reference_type, t1.name as reference_name,
 				t1.posting_date, t1.remark as remarks, t2.name as reference_row,
-				{dr_or_cr} as amount, t2.is_advance,
+				{dr_or_cr} as amount, t2.is_advance, t2.exchange_rate,
 				t2.account_currency as currency
 			from
 				`tabJournal Entry` t1, `tabJournal Entry Account` t2
@@ -215,26 +215,26 @@
 			inv.currency = entry.get("currency")
 			inv.outstanding_amount = flt(entry.get("outstanding_amount"))
 
-	def get_difference_amount(self, allocated_entry):
-		if allocated_entry.get("reference_type") != "Payment Entry":
-			return
+	def get_difference_amount(self, payment_entry, invoice, allocated_amount):
+		difference_amount = 0
+		if invoice.get("exchange_rate") and payment_entry.get("exchange_rate", 1) != invoice.get(
+			"exchange_rate", 1
+		):
+			allocated_amount_in_ref_rate = payment_entry.get("exchange_rate", 1) * allocated_amount
+			allocated_amount_in_inv_rate = invoice.get("exchange_rate", 1) * allocated_amount
+			difference_amount = allocated_amount_in_ref_rate - allocated_amount_in_inv_rate
 
-		dr_or_cr = (
-			"credit_in_account_currency"
-			if erpnext.get_party_account_type(self.party_type) == "Receivable"
-			else "debit_in_account_currency"
-		)
-
-		row = self.get_payment_details(allocated_entry, dr_or_cr)
-
-		doc = frappe.get_doc(allocated_entry.reference_type, allocated_entry.reference_name)
-		update_reference_in_payment_entry(row, doc, do_not_save=True)
-
-		return doc.difference_amount
+		return difference_amount
 
 	@frappe.whitelist()
 	def allocate_entries(self, args):
 		self.validate_entries()
+
+		invoice_exchange_map = self.get_invoice_exchange_map(args.get("invoices"))
+		default_exchange_gain_loss_account = frappe.get_cached_value(
+			"Company", self.company, "exchange_gain_loss_account"
+		)
+
 		entries = []
 		for pay in args.get("payments"):
 			pay.update({"unreconciled_amount": pay.get("amount")})
@@ -248,7 +248,10 @@
 					inv["outstanding_amount"] = flt(inv.get("outstanding_amount")) - flt(pay.get("amount"))
 					pay["amount"] = 0
 
-				res.difference_amount = self.get_difference_amount(res)
+				inv["exchange_rate"] = invoice_exchange_map.get(inv.get("invoice_number"))
+				res.difference_amount = self.get_difference_amount(pay, inv, res["allocated_amount"])
+				res.difference_account = default_exchange_gain_loss_account
+				res.exchange_rate = inv.get("exchange_rate")
 
 				if pay.get("amount") == 0:
 					entries.append(res)
@@ -278,6 +281,7 @@
 				"amount": pay.get("amount"),
 				"allocated_amount": allocated_amount,
 				"difference_amount": pay.get("difference_amount"),
+				"currency": inv.get("currency"),
 			}
 		)
 
@@ -300,7 +304,11 @@
 				else:
 					reconciled_entry = entry_list
 
-				reconciled_entry.append(self.get_payment_details(row, dr_or_cr))
+				payment_details = self.get_payment_details(row, dr_or_cr)
+				reconciled_entry.append(payment_details)
+
+				if payment_details.difference_amount:
+					self.make_difference_entry(payment_details)
 
 		if entry_list:
 			reconcile_against_document(entry_list)
@@ -311,6 +319,56 @@
 		msgprint(_("Successfully Reconciled"))
 		self.get_unreconciled_entries()
 
+	def make_difference_entry(self, row):
+		journal_entry = frappe.new_doc("Journal Entry")
+		journal_entry.voucher_type = "Exchange Gain Or Loss"
+		journal_entry.company = self.company
+		journal_entry.posting_date = nowdate()
+		journal_entry.multi_currency = 1
+
+		party_account_currency = frappe.get_cached_value(
+			"Account", self.receivable_payable_account, "account_currency"
+		)
+		difference_account_currency = frappe.get_cached_value(
+			"Account", row.difference_account, "account_currency"
+		)
+
+		# Account Currency has balance
+		dr_or_cr = "debit" if self.party_type == "Customer" else "debit"
+		reverse_dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
+
+		journal_account = frappe._dict(
+			{
+				"account": self.receivable_payable_account,
+				"party_type": self.party_type,
+				"party": self.party,
+				"account_currency": party_account_currency,
+				"exchange_rate": 0,
+				"cost_center": erpnext.get_default_cost_center(self.company),
+				"reference_type": row.against_voucher_type,
+				"reference_name": row.against_voucher,
+				dr_or_cr: flt(row.difference_amount),
+				dr_or_cr + "_in_account_currency": 0,
+			}
+		)
+
+		journal_entry.append("accounts", journal_account)
+
+		journal_account = frappe._dict(
+			{
+				"account": row.difference_account,
+				"account_currency": difference_account_currency,
+				"exchange_rate": 1,
+				"cost_center": erpnext.get_default_cost_center(self.company),
+				reverse_dr_or_cr + "_in_account_currency": flt(row.difference_amount),
+			}
+		)
+
+		journal_entry.append("accounts", journal_account)
+
+		journal_entry.save()
+		journal_entry.submit()
+
 	def get_payment_details(self, row, dr_or_cr):
 		return frappe._dict(
 			{
@@ -320,6 +378,7 @@
 				"against_voucher_type": row.get("invoice_type"),
 				"against_voucher": row.get("invoice_number"),
 				"account": self.receivable_payable_account,
+				"exchange_rate": row.get("exchange_rate"),
 				"party_type": self.party_type,
 				"party": self.party,
 				"is_advance": row.get("is_advance"),
@@ -344,6 +403,41 @@
 		if not self.get("payments"):
 			frappe.throw(_("No records found in the Payments table"))
 
+	def get_invoice_exchange_map(self, invoices):
+		sales_invoices = [
+			d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Sales Invoice"
+		]
+		purchase_invoices = [
+			d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Purchase Invoice"
+		]
+		invoice_exchange_map = frappe._dict()
+
+		if sales_invoices:
+			sales_invoice_map = frappe._dict(
+				frappe.db.get_all(
+					"Sales Invoice",
+					filters={"name": ("in", sales_invoices)},
+					fields=["name", "conversion_rate"],
+					as_list=1,
+				)
+			)
+
+			invoice_exchange_map.update(sales_invoice_map)
+
+		if purchase_invoices:
+			purchase_invoice_map = frappe._dict(
+				frappe.db.get_all(
+					"Purchase Invoice",
+					filters={"name": ("in", purchase_invoices)},
+					fields=["name", "conversion_rate"],
+					as_list=1,
+				)
+			)
+
+			invoice_exchange_map.update(purchase_invoice_map)
+
+		return invoice_exchange_map
+
 	def validate_allocation(self):
 		unreconciled_invoices = frappe._dict()
 
diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
index 6030134..2ba90b4 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
@@ -6,7 +6,7 @@
 import frappe
 from frappe import qb
 from frappe.tests.utils import FrappeTestCase
-from frappe.utils import add_days, nowdate
+from frappe.utils import add_days, flt, nowdate
 
 from erpnext import get_default_cost_center
 from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
@@ -75,33 +75,11 @@
 		self.item = item if isinstance(item, str) else item.item_code
 
 	def create_customer(self):
-		if frappe.db.exists("Customer", "_Test PR Customer"):
-			self.customer = "_Test PR Customer"
-		else:
-			customer = frappe.new_doc("Customer")
-			customer.customer_name = "_Test PR Customer"
-			customer.type = "Individual"
-			customer.save()
-			self.customer = customer.name
-
-		if frappe.db.exists("Customer", "_Test PR Customer 2"):
-			self.customer2 = "_Test PR Customer 2"
-		else:
-			customer = frappe.new_doc("Customer")
-			customer.customer_name = "_Test PR Customer 2"
-			customer.type = "Individual"
-			customer.save()
-			self.customer2 = customer.name
-
-		if frappe.db.exists("Customer", "_Test PR Customer 3"):
-			self.customer3 = "_Test PR Customer 3"
-		else:
-			customer = frappe.new_doc("Customer")
-			customer.customer_name = "_Test PR Customer 3"
-			customer.type = "Individual"
-			customer.default_currency = "EUR"
-			customer.save()
-			self.customer3 = customer.name
+		self.customer = make_customer("_Test PR Customer")
+		self.customer2 = make_customer("_Test PR Customer 2")
+		self.customer3 = make_customer("_Test PR Customer 3", "EUR")
+		self.customer4 = make_customer("_Test PR Customer 4", "EUR")
+		self.customer5 = make_customer("_Test PR Customer 5", "EUR")
 
 	def create_account(self):
 		account_name = "Debtors EUR"
@@ -598,6 +576,156 @@
 		self.assertEqual(pr.payments[0].amount, amount)
 		self.assertEqual(pr.payments[0].currency, "EUR")
 
+	def test_difference_amount_via_journal_entry(self):
+		# Make Sale Invoice
+		si = self.create_sales_invoice(
+			qty=1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True
+		)
+		si.customer = self.customer4
+		si.currency = "EUR"
+		si.conversion_rate = 85
+		si.debit_to = self.debtors_eur
+		si.save().submit()
+
+		# Make payment using Journal Entry
+		je1 = self.create_journal_entry("HDFC - _PR", self.debtors_eur, 100, nowdate())
+		je1.multi_currency = 1
+		je1.accounts[0].exchange_rate = 1
+		je1.accounts[0].credit_in_account_currency = 0
+		je1.accounts[0].credit = 0
+		je1.accounts[0].debit_in_account_currency = 8000
+		je1.accounts[0].debit = 8000
+		je1.accounts[1].party_type = "Customer"
+		je1.accounts[1].party = self.customer4
+		je1.accounts[1].exchange_rate = 80
+		je1.accounts[1].credit_in_account_currency = 100
+		je1.accounts[1].credit = 8000
+		je1.accounts[1].debit_in_account_currency = 0
+		je1.accounts[1].debit = 0
+		je1.save()
+		je1.submit()
+
+		je2 = self.create_journal_entry("HDFC - _PR", self.debtors_eur, 200, nowdate())
+		je2.multi_currency = 1
+		je2.accounts[0].exchange_rate = 1
+		je2.accounts[0].credit_in_account_currency = 0
+		je2.accounts[0].credit = 0
+		je2.accounts[0].debit_in_account_currency = 16000
+		je2.accounts[0].debit = 16000
+		je2.accounts[1].party_type = "Customer"
+		je2.accounts[1].party = self.customer4
+		je2.accounts[1].exchange_rate = 80
+		je2.accounts[1].credit_in_account_currency = 200
+		je1.accounts[1].credit = 16000
+		je1.accounts[1].debit_in_account_currency = 0
+		je1.accounts[1].debit = 0
+		je2.save()
+		je2.submit()
+
+		pr = self.create_payment_reconciliation()
+		pr.party = self.customer4
+		pr.receivable_payable_account = self.debtors_eur
+		pr.get_unreconciled_entries()
+
+		self.assertEqual(len(pr.invoices), 1)
+		self.assertEqual(len(pr.payments), 2)
+
+		# Test exact payment allocation
+		invoices = [x.as_dict() for x in pr.invoices]
+		payments = [pr.payments[0].as_dict()]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+
+		self.assertEqual(pr.allocation[0].allocated_amount, 100)
+		self.assertEqual(pr.allocation[0].difference_amount, -500)
+
+		# Test partial payment allocation (with excess payment entry)
+		pr.set("allocation", [])
+		pr.get_unreconciled_entries()
+		invoices = [x.as_dict() for x in pr.invoices]
+		payments = [pr.payments[1].as_dict()]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+		pr.allocation[0].difference_account = "Exchange Gain/Loss - _PR"
+
+		self.assertEqual(pr.allocation[0].allocated_amount, 100)
+		self.assertEqual(pr.allocation[0].difference_amount, -500)
+
+		# Check if difference journal entry gets generated for difference amount after reconciliation
+		pr.reconcile()
+		total_debit_amount = frappe.db.get_all(
+			"Journal Entry Account",
+			{"account": self.debtors_eur, "docstatus": 1, "reference_name": si.name},
+			"sum(debit) as amount",
+			group_by="reference_name",
+		)[0].amount
+
+		self.assertEqual(flt(total_debit_amount, 2), -500)
+
+	def test_difference_amount_via_payment_entry(self):
+		# Make Sale Invoice
+		si = self.create_sales_invoice(
+			qty=1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True
+		)
+		si.customer = self.customer5
+		si.currency = "EUR"
+		si.conversion_rate = 85
+		si.debit_to = self.debtors_eur
+		si.save().submit()
+
+		# Make payment using Payment Entry
+		pe1 = create_payment_entry(
+			company=self.company,
+			payment_type="Receive",
+			party_type="Customer",
+			party=self.customer5,
+			paid_from=self.debtors_eur,
+			paid_to=self.bank,
+			paid_amount=100,
+		)
+
+		pe1.source_exchange_rate = 80
+		pe1.received_amount = 8000
+		pe1.save()
+		pe1.submit()
+
+		pe2 = create_payment_entry(
+			company=self.company,
+			payment_type="Receive",
+			party_type="Customer",
+			party=self.customer5,
+			paid_from=self.debtors_eur,
+			paid_to=self.bank,
+			paid_amount=200,
+		)
+
+		pe2.source_exchange_rate = 80
+		pe2.received_amount = 16000
+		pe2.save()
+		pe2.submit()
+
+		pr = self.create_payment_reconciliation()
+		pr.party = self.customer5
+		pr.receivable_payable_account = self.debtors_eur
+		pr.get_unreconciled_entries()
+
+		self.assertEqual(len(pr.invoices), 1)
+		self.assertEqual(len(pr.payments), 2)
+
+		invoices = [x.as_dict() for x in pr.invoices]
+		payments = [pr.payments[0].as_dict()]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+
+		self.assertEqual(pr.allocation[0].allocated_amount, 100)
+		self.assertEqual(pr.allocation[0].difference_amount, -500)
+
+		pr.set("allocation", [])
+		pr.get_unreconciled_entries()
+		invoices = [x.as_dict() for x in pr.invoices]
+		payments = [pr.payments[1].as_dict()]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+
+		self.assertEqual(pr.allocation[0].allocated_amount, 100)
+		self.assertEqual(pr.allocation[0].difference_amount, -500)
+
 	def test_differing_cost_center_on_invoice_and_payment(self):
 		"""
 		Cost Center filter should not affect outstanding amount calculation
@@ -618,3 +746,17 @@
 		# check PR tool output
 		self.assertEqual(len(pr.get("invoices")), 0)
 		self.assertEqual(len(pr.get("payments")), 0)
+
+
+def make_customer(customer_name, currency=None):
+	if not frappe.db.exists("Customer", customer_name):
+		customer = frappe.new_doc("Customer")
+		customer.customer_name = customer_name
+		customer.type = "Individual"
+
+		if currency:
+			customer.default_currency = currency
+		customer.save()
+		return customer.name
+	else:
+		return customer_name
diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
index 6a21692..0f7e47a 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
+++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
@@ -20,7 +20,9 @@
   "section_break_5",
   "difference_amount",
   "column_break_7",
-  "difference_account"
+  "difference_account",
+  "exchange_rate",
+  "currency"
  ],
  "fields": [
   {
@@ -37,7 +39,7 @@
    "fieldtype": "Currency",
    "in_list_view": 1,
    "label": "Allocated Amount",
-   "options": "Currency",
+   "options": "currency",
    "reqd": 1
   },
   {
@@ -112,7 +114,7 @@
    "fieldtype": "Currency",
    "hidden": 1,
    "label": "Unreconciled Amount",
-   "options": "Currency",
+   "options": "currency",
    "read_only": 1
   },
   {
@@ -120,7 +122,7 @@
    "fieldtype": "Currency",
    "hidden": 1,
    "label": "Amount",
-   "options": "Currency",
+   "options": "currency",
    "read_only": 1
   },
   {
@@ -129,11 +131,24 @@
    "hidden": 1,
    "label": "Reference Row",
    "read_only": 1
+  },
+  {
+   "fieldname": "currency",
+   "fieldtype": "Link",
+   "hidden": 1,
+   "label": "Currency",
+   "options": "Currency"
+  },
+  {
+   "fieldname": "exchange_rate",
+   "fieldtype": "Float",
+   "label": "Exchange Rate",
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-10-06 11:48:59.616562",
+ "modified": "2022-12-24 21:01:14.882747",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Reconciliation Allocation",
@@ -141,5 +156,6 @@
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json
index 00c9e12..c4dbd7e 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json
+++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json
@@ -11,7 +11,8 @@
   "col_break1",
   "amount",
   "outstanding_amount",
-  "currency"
+  "currency",
+  "exchange_rate"
  ],
  "fields": [
   {
@@ -62,11 +63,17 @@
    "hidden": 1,
    "label": "Currency",
    "options": "Currency"
+  },
+  {
+   "fieldname": "exchange_rate",
+   "fieldtype": "Float",
+   "hidden": 1,
+   "label": "Exchange Rate"
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-08-24 22:42:40.923179",
+ "modified": "2022-11-08 18:18:02.502149",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Reconciliation Invoice",
@@ -75,5 +82,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
index add07e8..d300ea9 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
+++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
@@ -15,7 +15,8 @@
   "difference_amount",
   "sec_break1",
   "remark",
-  "currency"
+  "currency",
+  "exchange_rate"
  ],
  "fields": [
   {
@@ -91,11 +92,17 @@
    "label": "Difference Amount",
    "options": "currency",
    "read_only": 1
+  },
+  {
+   "fieldname": "exchange_rate",
+   "fieldtype": "Float",
+   "hidden": 1,
+   "label": "Exchange Rate"
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-08-30 10:51:48.140062",
+ "modified": "2022-11-08 18:18:36.268760",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Reconciliation Payment",
@@ -103,5 +110,6 @@
  "permissions": [],
  "quick_entry": 1,
  "sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index e3531b0..fc23127 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -239,7 +239,7 @@
 	):
 		conditions.append("(posting_date >=%(from_date)s or is_opening = 'Yes')")
 
-	conditions.append("(posting_date <=%(to_date)s)")
+	conditions.append("(posting_date <=%(to_date)s or is_opening = 'Yes')")
 
 	if filters.get("project"):
 		conditions.append("project in %(project)s")
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index 8d67e30..335d92f 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -829,6 +829,9 @@
 					order_doctype: {
 						"doctype": "Stock Entry",
 						"field_map": {
+							"supplier": "supplier",
+							"supplier_name": "supplier_name",
+							"supplier_address": "supplier_address",
 							"to_warehouse": "supplier_warehouse",
 						},
 						"field_no_map": [field_no_map],