feat: adjust purchase receipt valuation rate as per purchase invoice rate
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 21addab..28ed8b7 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -528,6 +528,32 @@
 		self.update_advance_tax_references()
 
 		self.process_common_party_accounting()
+		self.adjust_incoming_rate_of_purchase_receipt()
+
+	def adjust_incoming_rate_of_purchase_receipt(self):
+		if (
+			not frappe.db.get_single_value(
+				"Buying Settings", "adjust_incoming_rate_based_on_purchase_invoice_rate"
+			)
+			and self.is_subcontracted
+		):
+			return
+
+		purchase_receipts = []
+		for item in self.items:
+			if item.purchase_receipt and item.purchase_receipt not in purchase_receipts:
+				purchase_receipts.append(item.purchase_receipt)
+
+		for purchase_receipt in purchase_receipts:
+			doc = frappe.get_doc("Purchase Receipt", purchase_receipt)
+			doc.docstatus = 2
+			doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
+			doc.make_gl_entries_on_cancel()
+
+			doc.docstatus = 1
+			doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
+			doc.make_gl_entries()
+			doc.repost_future_sle_and_gle()
 
 	def make_gl_entries(self, gl_entries=None, from_repost=False):
 		if not gl_entries:
@@ -1423,6 +1449,7 @@
 			"Tax Withheld Vouchers",
 		)
 		self.update_advance_tax_references(cancel=1)
+		self.adjust_incoming_rate_of_purchase_receipt()
 
 	def update_project(self):
 		project_list = []
@@ -1485,11 +1512,17 @@
 		if po_details:
 			updated_pr += update_billed_amount_based_on_po(po_details, update_modified)
 
+		adjust_incoming_rate = frappe.db.get_single_value(
+			"Buying Settings", "adjust_incoming_rate_based_on_purchase_invoice_rate"
+		)
+
 		for pr in set(updated_pr):
 			from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage
 
 			pr_doc = frappe.get_doc("Purchase Receipt", pr)
-			update_billing_percentage(pr_doc, update_modified=update_modified)
+			update_billing_percentage(
+				pr_doc, update_modified=update_modified, adjust_incoming_rate=adjust_incoming_rate
+			)
 
 	def get_pr_details_billed_amt(self):
 		# Get billed amount based on purchase receipt item reference (pr_detail) in purchase invoice
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json
index 652dcf0..7bbe89a 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.json
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.json
@@ -18,6 +18,7 @@
   "pr_required",
   "column_break_12",
   "maintain_same_rate",
+  "adjust_incoming_rate_based_on_purchase_invoice_rate",
   "allow_multiple_items",
   "bill_for_rejected_quantity_in_purchase_invoice",
   "disable_last_purchase_rate",
@@ -147,6 +148,14 @@
    "fieldname": "show_pay_button",
    "fieldtype": "Check",
    "label": "Show Pay Button in Purchase Order Portal"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval: !doc.maintain_same_rate",
+   "description": "Users can enable the checkbox If they want to adjust the incoming rate (set using purchase receipt) based on the purchase invoice rate.",
+   "fieldname": "adjust_incoming_rate_based_on_purchase_invoice_rate",
+   "fieldtype": "Check",
+   "label": "Adjust Incoming Rate Based on Purchase Invoice Rate"
   }
  ],
  "icon": "fa fa-cog",
@@ -154,7 +163,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2023-02-15 14:42:10.200679",
+ "modified": "2023-02-20 14:25:58.544143",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Buying Settings",
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 4f7d9ad..603468d 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -265,7 +265,10 @@
 					) / qty_in_stock_uom
 				else:
 					item.valuation_rate = (
-						item.base_net_amount + item.item_tax_amount + flt(item.landed_cost_voucher_amount)
+						item.base_net_amount
+						+ item.item_tax_amount
+						+ flt(item.landed_cost_voucher_amount)
+						+ flt(item.get("adjust_incoming_rate"))
 					) / qty_in_stock_uom
 			else:
 				item.valuation_rate = 0.0
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index c8a4bd3..0f3224c 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -293,6 +293,7 @@
 			get_purchase_document_details,
 		)
 
+		stock_rbnb = None
 		if erpnext.is_perpetual_inventory_enabled(self.company):
 			stock_rbnb = self.get_company_default("stock_received_but_not_billed")
 			landed_cost_entries = get_item_account_wise_additional_cost(self.name)
@@ -450,6 +451,21 @@
 								item=d,
 							)
 
+					if d.adjust_incoming_rate and stock_rbnb:
+						account_currency = get_account_currency(stock_rbnb)
+						self.add_gl_entry(
+							gl_entries=gl_entries,
+							account=stock_rbnb,
+							cost_center=d.cost_center,
+							debit=0.0,
+							credit=flt(d.adjust_incoming_rate),
+							remarks=_("Adjustment based on Purchase Invoice rate"),
+							against_account=warehouse_account_name,
+							account_currency=account_currency,
+							project=d.project,
+							item=d,
+						)
+
 					# sub-contracting warehouse
 					if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
 						self.add_gl_entry(
@@ -470,6 +486,7 @@
 						+ flt(d.landed_cost_voucher_amount)
 						+ flt(d.rm_supp_cost)
 						+ flt(d.item_tax_amount)
+						+ flt(d.adjust_incoming_rate)
 					)
 
 					divisional_loss = flt(
@@ -765,7 +782,7 @@
 			updated_pr += update_billed_amount_based_on_po(po_details, update_modified)
 
 		for pr in set(updated_pr):
-			pr_doc = self if (pr == self.name) else frappe.get_cached_doc("Purchase Receipt", pr)
+			pr_doc = self if (pr == self.name) else frappe.get_doc("Purchase Receipt", pr)
 			update_billing_percentage(pr_doc, update_modified=update_modified)
 
 		self.load_from_db()
@@ -881,7 +898,7 @@
 	return {d.po_detail: flt(d.billed_amt) for d in query}
 
 
-def update_billing_percentage(pr_doc, update_modified=True):
+def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate=False):
 	# Reload as billed amount was set in db directly
 	pr_doc.load_from_db()
 
@@ -897,6 +914,12 @@
 
 		total_amount += total_billable_amount
 		total_billed_amount += flt(item.billed_amt)
+		if adjust_incoming_rate:
+			adjusted_amt = 0.0
+			if item.billed_amt and item.amount:
+				adjusted_amt = flt(item.billed_amt) - flt(item.amount)
+
+			item.db_set("adjust_incoming_rate", adjusted_amt, update_modified=False)
 
 	percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6)
 	pr_doc.db_set("per_billed", percent_billed)
@@ -906,6 +929,26 @@
 		pr_doc.set_status(update=True)
 		pr_doc.notify_update()
 
+	if adjust_incoming_rate:
+		adjust_incoming_rate_for_pr(pr_doc)
+
+
+def adjust_incoming_rate_for_pr(doc):
+	doc.update_valuation_rate(reset_outgoing_rate=False)
+
+	for item in doc.get("items"):
+		item.db_update()
+
+	doc.docstatus = 2
+	doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
+	doc.make_gl_entries_on_cancel()
+
+	# update stock & gl entries for submit state of PR
+	doc.docstatus = 1
+	doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
+	doc.make_gl_entries()
+	doc.repost_future_sle_and_gle()
+
 
 def get_item_wise_returned_qty(pr_doc):
 	items = [d.name for d in pr_doc.items]
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index 7a350b9..e12c583 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -69,6 +69,7 @@
   "item_tax_amount",
   "rm_supp_cost",
   "landed_cost_voucher_amount",
+  "adjust_incoming_rate",
   "billed_amt",
   "warehouse_and_reference",
   "warehouse",
@@ -1007,12 +1008,20 @@
    "fieldtype": "Check",
    "label": "Has Item Scanned",
    "read_only": 1
+  },
+  {
+   "fieldname": "adjust_incoming_rate",
+   "fieldtype": "Currency",
+   "label": "Adjust Incoming Rate (Purchase Invoice)",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-01-18 15:48:58.114923",
+ "modified": "2023-02-28 12:05:59.732266",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt Item",