Merge branch 'develop' into fix_asset_scrap_and_sale_dep
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 94db9a9..afd5a59 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -22,9 +22,12 @@
from erpnext.accounts.party import get_due_date, get_party_account, get_party_details
from erpnext.accounts.utils import get_account_currency
from erpnext.assets.doctype.asset.depreciation import (
+ depreciate_asset,
get_disposal_account_and_cost_center,
get_gl_entries_on_asset_disposal,
get_gl_entries_on_asset_regain,
+ reset_depreciation_schedule,
+ reverse_depreciation_entry_made_after_disposal,
)
from erpnext.controllers.accounts_controller import validate_account_head
from erpnext.controllers.selling_controller import SellingController
@@ -1086,18 +1089,20 @@
asset.db_set("disposal_date", None)
if asset.calculate_depreciation:
- self.reverse_depreciation_entry_made_after_disposal(asset)
- self.reset_depreciation_schedule(asset)
+ posting_date = frappe.db.get_value("Sales Invoice", self.return_against, "posting_date")
+ reverse_depreciation_entry_made_after_disposal(asset, posting_date)
+ reset_depreciation_schedule(asset, self.posting_date)
else:
+ if asset.calculate_depreciation:
+ depreciate_asset(asset, self.posting_date)
+ asset.reload()
+
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(
asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name")
)
asset.db_set("disposal_date", self.posting_date)
- if asset.calculate_depreciation:
- self.depreciate_asset(asset)
-
for gle in fixed_asset_gl_entries:
gle["against"] = self.customer
gl_entries.append(self.get_gl_dict(gle, item=item))
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index a4bb960..b6e86cd 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -4,11 +4,12 @@
import frappe
from frappe import _
-from frappe.utils import cint, flt, getdate, today
+from frappe.utils import add_months, cint, flt, getdate, nowdate, today
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_checks_for_pl_and_bs_accounts,
)
+from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
def post_depreciation_entries(date=None, commit=True):
@@ -196,6 +197,11 @@
_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status)
)
+ date = today()
+
+ depreciate_asset(asset, date)
+ asset.reload()
+
depreciation_series = frappe.get_cached_value(
"Company", asset.company, "series_for_depreciation_entry"
)
@@ -203,7 +209,7 @@
je = frappe.new_doc("Journal Entry")
je.voucher_type = "Journal Entry"
je.naming_series = depreciation_series
- je.posting_date = today()
+ je.posting_date = date
je.company = asset.company
je.remark = "Scrap Entry for asset {0}".format(asset_name)
@@ -214,7 +220,7 @@
je.flags.ignore_permissions = True
je.submit()
- frappe.db.set_value("Asset", asset_name, "disposal_date", today())
+ frappe.db.set_value("Asset", asset_name, "disposal_date", date)
frappe.db.set_value("Asset", asset_name, "journal_entry_for_scrap", je.name)
asset.set_status("Scrapped")
@@ -225,6 +231,9 @@
def restore_asset(asset_name):
asset = frappe.get_doc("Asset", asset_name)
+ reverse_depreciation_entry_made_after_disposal(asset, asset.disposal_date)
+ reset_depreciation_schedule(asset, asset.disposal_date)
+
je = asset.journal_entry_for_scrap
asset.db_set("disposal_date", None)
@@ -234,6 +243,89 @@
asset.set_status()
+def depreciate_asset(asset, date):
+ asset.flags.ignore_validate_update_after_submit = True
+ asset.prepare_depreciation_data(date_of_disposal=date)
+ asset.save()
+
+ make_depreciation_entry(asset.name, date)
+
+
+def reset_depreciation_schedule(asset, date):
+ asset.flags.ignore_validate_update_after_submit = True
+
+ # recreate original depreciation schedule of the asset
+ asset.prepare_depreciation_data(date_of_return=date)
+
+ modify_depreciation_schedule_for_asset_repairs(asset)
+ asset.save()
+
+
+def modify_depreciation_schedule_for_asset_repairs(asset):
+ asset_repairs = frappe.get_all(
+ "Asset Repair", filters={"asset": asset.name}, fields=["name", "increase_in_asset_life"]
+ )
+
+ for repair in asset_repairs:
+ if repair.increase_in_asset_life:
+ asset_repair = frappe.get_doc("Asset Repair", repair.name)
+ asset_repair.modify_depreciation_schedule()
+ asset.prepare_depreciation_data()
+
+
+def reverse_depreciation_entry_made_after_disposal(asset, date):
+ row = -1
+ finance_book = asset.get("schedules")[0].get("finance_book")
+ for schedule in asset.get("schedules"):
+ if schedule.finance_book != finance_book:
+ row = 0
+ finance_book = schedule.finance_book
+ else:
+ row += 1
+
+ if schedule.schedule_date == date:
+ if not disposal_was_made_on_original_schedule_date(
+ asset, schedule, row, date
+ ) or disposal_happens_in_the_future(date):
+
+ reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
+ reverse_journal_entry.posting_date = nowdate()
+ frappe.flags.is_reverse_depr_entry = True
+ reverse_journal_entry.submit()
+
+ frappe.flags.is_reverse_depr_entry = False
+ asset.flags.ignore_validate_update_after_submit = True
+ schedule.journal_entry = None
+ depreciation_amount = get_depreciation_amount_in_je(reverse_journal_entry)
+ asset.finance_books[0].value_after_depreciation += depreciation_amount
+ asset.save()
+
+
+def get_depreciation_amount_in_je(journal_entry):
+ if journal_entry.accounts[0].debit_in_account_currency:
+ return journal_entry.accounts[0].debit_in_account_currency
+ else:
+ return journal_entry.accounts[0].credit_in_account_currency
+
+
+# if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
+def disposal_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_disposal):
+ for finance_book in asset.get("finance_books"):
+ if schedule.finance_book == finance_book.finance_book:
+ orginal_schedule_date = add_months(
+ finance_book.depreciation_start_date, row * cint(finance_book.frequency_of_depreciation)
+ )
+
+ if orginal_schedule_date == posting_date_of_disposal:
+ return True
+ return False
+
+
+def disposal_happens_in_the_future(posting_date_of_disposal):
+ if posting_date_of_disposal > getdate():
+ return True
+
+ return False
def get_gl_entries_on_asset_regain(
asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index e7af9bd..f72b524 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -4,7 +4,16 @@
import unittest
import frappe
-from frappe.utils import add_days, add_months, cstr, flt, get_last_day, getdate, nowdate
+from frappe.utils import (
+ add_days,
+ add_months,
+ cstr,
+ flt,
+ get_first_day,
+ get_last_day,
+ getdate,
+ nowdate,
+)
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.assets.doctype.asset.asset import make_sales_invoice, split_asset
@@ -178,28 +187,48 @@
self.assertEqual(doc.items[0].is_fixed_asset, 1)
def test_scrap_asset(self):
+ date = nowdate()
+ purchase_date = add_months(get_first_day(date), -2)
+
asset = create_asset(
calculate_depreciation=1,
- available_for_use_date="2020-01-01",
- purchase_date="2020-01-01",
+ available_for_use_date=purchase_date,
+ purchase_date=purchase_date,
expected_value_after_useful_life=10000,
total_number_of_depreciations=10,
frequency_of_depreciation=1,
submit=1,
)
- post_depreciation_entries(date=add_months("2020-01-01", 4))
+ post_depreciation_entries(date=add_months(purchase_date, 2))
+ asset.load_from_db()
+
+ accumulated_depr_amount = flt(
+ asset.gross_purchase_amount - asset.finance_books[0].value_after_depreciation,
+ asset.precision("gross_purchase_amount"),
+ )
+ self.assertEquals(accumulated_depr_amount, 18000.0)
scrap_asset(asset.name)
-
asset.load_from_db()
+
+ accumulated_depr_amount = flt(
+ asset.gross_purchase_amount - asset.finance_books[0].value_after_depreciation,
+ asset.precision("gross_purchase_amount"),
+ )
+ pro_rata_amount, _, _ = asset.get_pro_rata_amt(
+ asset.finance_books[0], 9000, add_months(get_last_day(purchase_date), 1), date
+ )
+ pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount"))
+ self.assertEquals(accumulated_depr_amount, 18000.00 + pro_rata_amount)
+
self.assertEqual(asset.status, "Scrapped")
self.assertTrue(asset.journal_entry_for_scrap)
expected_gle = (
- ("_Test Accumulated Depreciations - _TC", 36000.0, 0.0),
+ ("_Test Accumulated Depreciations - _TC", 18000.0 + pro_rata_amount, 0.0),
("_Test Fixed Asset - _TC", 0.0, 100000.0),
- ("_Test Gain/Loss on Asset Disposal - _TC", 64000.0, 0.0),
+ ("_Test Gain/Loss on Asset Disposal - _TC", 82000.0 - pro_rata_amount, 0.0),
)
gle = frappe.db.sql(
@@ -216,19 +245,27 @@
self.assertFalse(asset.journal_entry_for_scrap)
self.assertEqual(asset.status, "Partially Depreciated")
+ accumulated_depr_amount = flt(
+ asset.gross_purchase_amount - asset.finance_books[0].value_after_depreciation,
+ asset.precision("gross_purchase_amount"),
+ )
+ self.assertEquals(accumulated_depr_amount, 18000.0)
+
def test_gle_made_by_asset_sale(self):
+ date = nowdate()
+ purchase_date = add_months(get_first_day(date), -2)
+
asset = create_asset(
calculate_depreciation=1,
- available_for_use_date="2020-06-06",
- purchase_date="2020-01-01",
+ available_for_use_date=purchase_date,
+ purchase_date=purchase_date,
expected_value_after_useful_life=10000,
- total_number_of_depreciations=3,
- frequency_of_depreciation=10,
- depreciation_start_date="2020-12-31",
+ total_number_of_depreciations=10,
+ frequency_of_depreciation=1,
submit=1,
)
- post_depreciation_entries(date="2021-01-01")
+ post_depreciation_entries(date=add_months(purchase_date, 2))
si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company")
si.customer = "_Test Customer"
@@ -239,10 +276,15 @@
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
+ pro_rata_amount, _, _ = asset.get_pro_rata_amt(
+ asset.finance_books[0], 9000, add_months(get_last_day(purchase_date), 1), date
+ )
+ pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount"))
+
expected_gle = (
- ("_Test Accumulated Depreciations - _TC", 20490.2, 0.0),
+ ("_Test Accumulated Depreciations - _TC", 18000.0 + pro_rata_amount, 0.0),
("_Test Fixed Asset - _TC", 0.0, 100000.0),
- ("_Test Gain/Loss on Asset Disposal - _TC", 54509.8, 0.0),
+ ("_Test Gain/Loss on Asset Disposal - _TC", 57000.0 - pro_rata_amount, 0.0),
("Debtors - _TC", 25000.0, 0.0),
)
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index 93194c0..08355f0 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -12,8 +12,11 @@
import erpnext
from erpnext.assets.doctype.asset.depreciation import (
+ depreciate_asset,
get_gl_entries_on_asset_disposal,
get_value_after_depreciation_on_disposal_date,
+ reset_depreciation_schedule,
+ reverse_depreciation_entry_made_after_disposal,
)
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import (
@@ -424,7 +427,7 @@
asset = self.get_asset(item)
if asset.calculate_depreciation:
- self.depreciate_asset(asset)
+ depreciate_asset(asset, self.posting_date)
asset.reload()
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(
@@ -520,8 +523,8 @@
self.set_consumed_asset_status(asset)
if asset.calculate_depreciation:
- self.reverse_depreciation_entry_made_after_disposal(asset)
- self.reset_depreciation_schedule(asset)
+ reverse_depreciation_entry_made_after_disposal(asset, self.posting_date)
+ reset_depreciation_schedule(asset, self.posting_date)
def get_asset(self, item):
asset = frappe.get_doc("Asset", item.asset)
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 8686cb5..22291a3 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -38,7 +38,6 @@
validate_party_frozen_disabled,
)
from erpnext.accounts.utils import get_account_currency, get_fiscal_years, validate_fiscal_year
-from erpnext.assets.doctype.asset.depreciation import make_depreciation_entry
from erpnext.buying.utils import update_last_purchase_rate
from erpnext.controllers.print_settings import (
set_print_templates_for_item_table,
@@ -1891,88 +1890,6 @@
_("Select finance book for the item {0} at row {1}").format(item.item_code, item.idx)
)
- def depreciate_asset(self, asset):
- asset.flags.ignore_validate_update_after_submit = True
- asset.prepare_depreciation_data(date_of_disposal=self.posting_date)
- asset.save()
-
- make_depreciation_entry(asset.name, self.posting_date)
-
- def reset_depreciation_schedule(self, asset):
- asset.flags.ignore_validate_update_after_submit = True
-
- # recreate original depreciation schedule of the asset
- asset.prepare_depreciation_data(date_of_return=self.posting_date)
-
- self.modify_depreciation_schedule_for_asset_repairs(asset)
- asset.save()
-
- def modify_depreciation_schedule_for_asset_repairs(self, asset):
- asset_repairs = frappe.get_all(
- "Asset Repair", filters={"asset": asset.name}, fields=["name", "increase_in_asset_life"]
- )
-
- for repair in asset_repairs:
- if repair.increase_in_asset_life:
- asset_repair = frappe.get_doc("Asset Repair", repair.name)
- asset_repair.modify_depreciation_schedule()
- asset.prepare_depreciation_data()
-
- def reverse_depreciation_entry_made_after_disposal(self, asset):
- from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
-
- posting_date_of_original_disposal = self.get_posting_date_of_disposal_entry()
-
- row = -1
- finance_book = asset.get("schedules")[0].get("finance_book")
- for schedule in asset.get("schedules"):
- if schedule.finance_book != finance_book:
- row = 0
- finance_book = schedule.finance_book
- else:
- row += 1
-
- if schedule.schedule_date == posting_date_of_original_disposal:
- if not self.disposal_was_made_on_original_schedule_date(
- asset, schedule, row, posting_date_of_original_disposal
- ) or self.disposal_happens_in_the_future(posting_date_of_original_disposal):
-
- reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
- reverse_journal_entry.posting_date = nowdate()
- frappe.flags.is_reverse_depr_entry = True
- reverse_journal_entry.submit()
-
- frappe.flags.is_reverse_depr_entry = False
- asset.flags.ignore_validate_update_after_submit = True
- schedule.journal_entry = None
- asset.save()
-
- def get_posting_date_of_disposal_entry(self):
- if self.doctype == "Sales Invoice" and self.return_against:
- return frappe.db.get_value("Sales Invoice", self.return_against, "posting_date")
- else:
- return self.posting_date
-
- # if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
- def disposal_was_made_on_original_schedule_date(
- self, asset, schedule, row, posting_date_of_disposal
- ):
- for finance_book in asset.get("finance_books"):
- if schedule.finance_book == finance_book.finance_book:
- orginal_schedule_date = add_months(
- finance_book.depreciation_start_date, row * cint(finance_book.frequency_of_depreciation)
- )
-
- if orginal_schedule_date == posting_date_of_disposal:
- return True
- return False
-
- def disposal_happens_in_the_future(self, posting_date_of_disposal):
- if posting_date_of_disposal > getdate():
- return True
-
- return False
-
@frappe.whitelist()
def get_tax_rate(account_head):