Merge pull request #31943 from nabinhait/asset-repair

fix: gl entries for asset repair
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 986b700..132840e 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -1454,12 +1454,14 @@
 	return item
 
 
-def set_depreciation_settings_in_company():
-	company = frappe.get_doc("Company", "_Test Company")
-	company.accumulated_depreciation_account = "_Test Accumulated Depreciations - _TC"
-	company.depreciation_expense_account = "_Test Depreciations - _TC"
-	company.disposal_account = "_Test Gain/Loss on Asset Disposal - _TC"
-	company.depreciation_cost_center = "_Test Cost Center - _TC"
+def set_depreciation_settings_in_company(company=None):
+	if not company:
+		company = "_Test Company"
+	company = frappe.get_doc("Company", company)
+	company.accumulated_depreciation_account = "_Test Accumulated Depreciations - " + company.abbr
+	company.depreciation_expense_account = "_Test Depreciations - " + company.abbr
+	company.disposal_account = "_Test Gain/Loss on Asset Disposal - " + company.abbr
+	company.depreciation_cost_center = "Main - " + company.abbr
 	company.save()
 
 	# Enable booking asset depreciation entry automatically
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js
index f5e4e72..f9ed2cc 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.js
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.js
@@ -76,7 +76,7 @@
 			'warehouse': frm.doc.warehouse,
 			'qty': item.consumed_quantity,
 			'serial_no': item.serial_no,
-			'company': frm.doc.company
+			'company': frm.doc.company,
 		};
 
 		frappe.call({
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json
index ba31898..accb5bf 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.json
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.json
@@ -238,7 +238,6 @@
    "no_copy": 1
   },
   {
-   "depends_on": "eval:!doc.__islocal",
    "fieldname": "purchase_invoice",
    "fieldtype": "Link",
    "label": "Purchase Invoice",
@@ -257,6 +256,7 @@
    "fieldname": "stock_entry",
    "fieldtype": "Link",
    "label": "Stock Entry",
+   "no_copy": 1,
    "options": "Stock Entry",
    "read_only": 1
   }
@@ -264,10 +264,11 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-06-25 13:14:38.307723",
+ "modified": "2022-08-16 15:55:25.023471",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset Repair",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
@@ -303,6 +304,7 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "asset_name",
  "track_changes": 1,
  "track_seen": 1
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py
index 5bf6011..8758e9c 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.py
@@ -1,11 +1,11 @@
 # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
-
 import frappe
 from frappe import _
 from frappe.utils import add_months, cint, flt, getdate, time_diff_in_hours
 
+import erpnext
 from erpnext.accounts.general_ledger import make_gl_entries
 from erpnext.assets.doctype.asset.asset import get_asset_account
 from erpnext.controllers.accounts_controller import AccountsController
@@ -17,7 +17,7 @@
 		self.update_status()
 
 		if self.get("stock_items"):
-			self.set_total_value()
+			self.set_stock_items_cost()
 		self.calculate_total_repair_cost()
 
 	def update_status(self):
@@ -26,7 +26,7 @@
 		else:
 			self.asset_doc.set_status()
 
-	def set_total_value(self):
+	def set_stock_items_cost(self):
 		for item in self.get("stock_items"):
 			item.total_value = flt(item.valuation_rate) * flt(item.consumed_quantity)
 
@@ -66,6 +66,7 @@
 		if self.get("capitalize_repair_cost"):
 			self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
 			self.make_gl_entries(cancel=True)
+			self.db_set("stock_entry", None)
 			if (
 				frappe.db.get_value("Asset", self.asset, "calculate_depreciation")
 				and self.increase_in_asset_life
@@ -133,6 +134,7 @@
 					"qty": stock_item.consumed_quantity,
 					"basic_rate": stock_item.valuation_rate,
 					"serial_no": stock_item.serial_no,
+					"cost_center": self.cost_center,
 				},
 			)
 
@@ -142,72 +144,42 @@
 		self.db_set("stock_entry", stock_entry.name)
 
 	def increase_stock_quantity(self):
-		stock_entry = frappe.get_doc("Stock Entry", self.stock_entry)
-		stock_entry.flags.ignore_links = True
-		stock_entry.cancel()
+		if self.stock_entry:
+			stock_entry = frappe.get_doc("Stock Entry", self.stock_entry)
+			stock_entry.flags.ignore_links = True
+			stock_entry.cancel()
 
 	def make_gl_entries(self, cancel=False):
-		if flt(self.repair_cost) > 0:
+		if flt(self.total_repair_cost) > 0:
 			gl_entries = self.get_gl_entries()
 			make_gl_entries(gl_entries, cancel)
 
 	def get_gl_entries(self):
 		gl_entries = []
-		repair_and_maintenance_account = frappe.db.get_value(
-			"Company", self.company, "repair_and_maintenance_account"
-		)
+
 		fixed_asset_account = get_asset_account(
 			"fixed_asset_account", asset=self.asset, company=self.company
 		)
-		expense_account = (
+		self.get_gl_entries_for_repair_cost(gl_entries, fixed_asset_account)
+		self.get_gl_entries_for_consumed_items(gl_entries, fixed_asset_account)
+
+		return gl_entries
+
+	def get_gl_entries_for_repair_cost(self, gl_entries, fixed_asset_account):
+		if flt(self.repair_cost) <= 0:
+			return
+
+		pi_expense_account = (
 			frappe.get_doc("Purchase Invoice", self.purchase_invoice).items[0].expense_account
 		)
 
 		gl_entries.append(
 			self.get_gl_dict(
 				{
-					"account": expense_account,
-					"credit": self.repair_cost,
-					"credit_in_account_currency": self.repair_cost,
-					"against": repair_and_maintenance_account,
-					"voucher_type": self.doctype,
-					"voucher_no": self.name,
-					"cost_center": self.cost_center,
-					"posting_date": getdate(),
-					"company": self.company,
-				},
-				item=self,
-			)
-		)
-
-		if self.get("stock_consumption"):
-			# creating GL Entries for each row in Stock Items based on the Stock Entry created for it
-			stock_entry = frappe.get_doc("Stock Entry", self.stock_entry)
-			for item in stock_entry.items:
-				gl_entries.append(
-					self.get_gl_dict(
-						{
-							"account": item.expense_account,
-							"credit": item.amount,
-							"credit_in_account_currency": item.amount,
-							"against": repair_and_maintenance_account,
-							"voucher_type": self.doctype,
-							"voucher_no": self.name,
-							"cost_center": self.cost_center,
-							"posting_date": getdate(),
-							"company": self.company,
-						},
-						item=self,
-					)
-				)
-
-		gl_entries.append(
-			self.get_gl_dict(
-				{
 					"account": fixed_asset_account,
-					"debit": self.total_repair_cost,
-					"debit_in_account_currency": self.total_repair_cost,
-					"against": expense_account,
+					"debit": self.repair_cost,
+					"debit_in_account_currency": self.repair_cost,
+					"against": pi_expense_account,
 					"voucher_type": self.doctype,
 					"voucher_no": self.name,
 					"cost_center": self.cost_center,
@@ -220,7 +192,75 @@
 			)
 		)
 
-		return gl_entries
+		gl_entries.append(
+			self.get_gl_dict(
+				{
+					"account": pi_expense_account,
+					"credit": self.repair_cost,
+					"credit_in_account_currency": self.repair_cost,
+					"against": fixed_asset_account,
+					"voucher_type": self.doctype,
+					"voucher_no": self.name,
+					"cost_center": self.cost_center,
+					"posting_date": getdate(),
+					"company": self.company,
+				},
+				item=self,
+			)
+		)
+
+	def get_gl_entries_for_consumed_items(self, gl_entries, fixed_asset_account):
+		if not (self.get("stock_consumption") and self.get("stock_items")):
+			return
+
+		# creating GL Entries for each row in Stock Items based on the Stock Entry created for it
+		stock_entry = frappe.get_doc("Stock Entry", self.stock_entry)
+
+		default_expense_account = None
+		if not erpnext.is_perpetual_inventory_enabled(self.company):
+			default_expense_account = frappe.get_cached_value(
+				"Company", self.company, "default_expense_account"
+			)
+			if not default_expense_account:
+				frappe.throw(_("Please set default Expense Account in Company {0}").format(self.company))
+
+		for item in stock_entry.items:
+			if flt(item.amount) > 0:
+				gl_entries.append(
+					self.get_gl_dict(
+						{
+							"account": item.expense_account or default_expense_account,
+							"credit": item.amount,
+							"credit_in_account_currency": item.amount,
+							"against": fixed_asset_account,
+							"voucher_type": self.doctype,
+							"voucher_no": self.name,
+							"cost_center": self.cost_center,
+							"posting_date": getdate(),
+							"company": self.company,
+						},
+						item=self,
+					)
+				)
+
+				gl_entries.append(
+					self.get_gl_dict(
+						{
+							"account": fixed_asset_account,
+							"debit": item.amount,
+							"debit_in_account_currency": item.amount,
+							"against": item.expense_account or default_expense_account,
+							"voucher_type": self.doctype,
+							"voucher_no": self.name,
+							"cost_center": self.cost_center,
+							"posting_date": getdate(),
+							"against_voucher_type": "Stock Entry",
+							"against_voucher": self.stock_entry,
+							"company": self.company,
+						},
+						item=self,
+					)
+				)
 
 	def modify_depreciation_schedule(self):
 		for row in self.asset_doc.finance_books:
diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
index 4e7cf78..6e06f52 100644
--- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
@@ -6,6 +6,7 @@
 import frappe
 from frappe.utils import flt, nowdate
 
+from erpnext.assets.doctype.asset.asset import get_asset_account
 from erpnext.assets.doctype.asset.test_asset import (
 	create_asset,
 	create_asset_data,
@@ -125,10 +126,109 @@
 		asset_repair = create_asset_repair(capitalize_repair_cost=1, submit=1)
 		self.assertTrue(asset_repair.purchase_invoice)
 
-	def test_gl_entries(self):
-		asset_repair = create_asset_repair(capitalize_repair_cost=1, submit=1)
-		gl_entry = frappe.get_last_doc("GL Entry")
-		self.assertEqual(asset_repair.name, gl_entry.voucher_no)
+	def test_gl_entries_with_perpetual_inventory(self):
+		set_depreciation_settings_in_company(company="_Test Company with perpetual inventory")
+
+		asset_category = frappe.get_doc("Asset Category", "Computers")
+		asset_category.append(
+			"accounts",
+			{
+				"company_name": "_Test Company with perpetual inventory",
+				"fixed_asset_account": "_Test Fixed Asset - TCP1",
+				"accumulated_depreciation_account": "_Test Accumulated Depreciations - TCP1",
+				"depreciation_expense_account": "_Test Depreciations - TCP1",
+			},
+		)
+		asset_category.save()
+
+		asset_repair = create_asset_repair(
+			capitalize_repair_cost=1,
+			stock_consumption=1,
+			warehouse="Stores - TCP1",
+			company="_Test Company with perpetual inventory",
+			submit=1,
+		)
+
+		gl_entries = frappe.db.sql(
+			"""
+			select
+				account,
+				sum(debit) as debit,
+				sum(credit) as credit
+			from `tabGL Entry`
+			where
+				voucher_type='Asset Repair'
+				and voucher_no=%s
+			group by
+				account
+		""",
+			asset_repair.name,
+			as_dict=1,
+		)
+
+		self.assertTrue(gl_entries)
+
+		fixed_asset_account = get_asset_account(
+			"fixed_asset_account", asset=asset_repair.asset, company=asset_repair.company
+		)
+		pi_expense_account = (
+			frappe.get_doc("Purchase Invoice", asset_repair.purchase_invoice).items[0].expense_account
+		)
+		stock_entry_expense_account = (
+			frappe.get_doc("Stock Entry", asset_repair.stock_entry).get("items")[0].expense_account
+		)
+
+		expected_values = {
+			fixed_asset_account: [asset_repair.total_repair_cost, 0],
+			pi_expense_account: [0, asset_repair.repair_cost],
+			stock_entry_expense_account: [0, 100],
+		}
+
+		for d in gl_entries:
+			self.assertEqual(expected_values[d.account][0], d.debit)
+			self.assertEqual(expected_values[d.account][1], d.credit)
+
+	def test_gl_entries_with_periodical_inventory(self):
+		frappe.db.set_value(
+			"Company", "_Test Company", "default_expense_account", "Cost of Goods Sold - _TC"
+		)
+		asset_repair = create_asset_repair(
+			capitalize_repair_cost=1,
+			stock_consumption=1,
+			submit=1,
+		)
+
+		gl_entries = frappe.db.sql(
+			"""
+			select
+				account,
+				sum(debit) as debit,
+				sum(credit) as credit
+			from `tabGL Entry`
+			where
+				voucher_type='Asset Repair'
+				and voucher_no=%s
+			group by
+				account
+		""",
+			asset_repair.name,
+			as_dict=1,
+		)
+
+		self.assertTrue(gl_entries)
+
+		fixed_asset_account = get_asset_account(
+			"fixed_asset_account", asset=asset_repair.asset, company=asset_repair.company
+		)
+		default_expense_account = frappe.get_cached_value(
+			"Company", asset_repair.company, "default_expense_account"
+		)
+
+		expected_values = {fixed_asset_account: [1100, 0], default_expense_account: [0, 1100]}
+
+		for d in gl_entries:
+			self.assertEqual(expected_values[d.account][0], d.debit)
+			self.assertEqual(expected_values[d.account][1], d.credit)
 
 	def test_increase_in_asset_life(self):
 		asset = create_asset(calculate_depreciation=1, submit=1)
@@ -160,7 +260,7 @@
 	if args.asset:
 		asset = args.asset
 	else:
-		asset = create_asset(is_existing_asset=1, submit=1)
+		asset = create_asset(is_existing_asset=1, submit=1, company=args.company)
 	asset_repair = frappe.new_doc("Asset Repair")
 	asset_repair.update(
 		{
@@ -192,7 +292,7 @@
 
 	if args.submit:
 		asset_repair.repair_status = "Completed"
-		asset_repair.cost_center = "_Test Cost Center - _TC"
+		asset_repair.cost_center = frappe.db.get_value("Company", asset.company, "cost_center")
 
 		if args.stock_consumption:
 			stock_entry = frappe.get_doc(
@@ -204,6 +304,8 @@
 					"t_warehouse": asset_repair.warehouse,
 					"item_code": asset_repair.stock_items[0].item_code,
 					"qty": asset_repair.stock_items[0].consumed_quantity,
+					"basic_rate": args.rate if args.get("rate") is not None else 100,
+					"cost_center": asset_repair.cost_center,
 				},
 			)
 			stock_entry.submit()
@@ -213,7 +315,13 @@
 			asset_repair.repair_cost = 1000
 			if asset.calculate_depreciation:
 				asset_repair.increase_in_asset_life = 12
-			asset_repair.purchase_invoice = make_purchase_invoice().name
+			pi = make_purchase_invoice(
+				company=asset.company,
+				expense_account=frappe.db.get_value("Company", asset.company, "default_expense_account"),
+				cost_center=asset_repair.cost_center,
+				warehouse=asset_repair.warehouse,
+			)
+			asset_repair.purchase_invoice = pi.name
 
 		asset_repair.submit()
 	return asset_repair
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index f34ec56..f087d99 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -85,7 +85,6 @@
   "depreciation_expense_account",
   "series_for_depreciation_entry",
   "expenses_included_in_asset_valuation",
-  "repair_and_maintenance_account",
   "column_break_40",
   "disposal_account",
   "depreciation_cost_center",
@@ -234,7 +233,6 @@
    "label": "Default Warehouse for Sales Return",
    "options": "Warehouse"
   },
-
   {
    "fieldname": "country",
    "fieldtype": "Link",
@@ -679,12 +677,6 @@
    "label": "Fixed Asset Defaults"
   },
   {
-   "fieldname": "repair_and_maintenance_account",
-   "fieldtype": "Link",
-   "label": "Repair and Maintenance Account",
-   "options": "Account"
-  },
-  {
    "fieldname": "section_break_28",
    "fieldtype": "Section Break",
    "label": "Chart of Accounts"
@@ -709,7 +701,7 @@
  "image_field": "company_logo",
  "is_tree": 1,
  "links": [],
- "modified": "2022-06-30 18:03:18.701314",
+ "modified": "2022-08-16 16:09:02.327724",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Company",