From 16bc853f6abe484a0e99ba259bc9859699f54f1c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 12 May 2018 12:06:00 +0530 Subject: [PATCH] Reschedule for future depreciations and booked difference amount in accumulated depreciation account --- .../doctype/journal_entry/journal_entry.py | 14 ++- erpnext/assets/doctype/asset/asset.js | 6 ++ erpnext/assets/doctype/asset/asset.json | 32 ++++++- erpnext/assets/doctype/asset/asset.py | 38 ++++---- erpnext/assets/doctype/asset/depreciation.py | 10 +- .../asset_adjustment/asset_adjustment.js | 24 ++++- .../asset_adjustment/asset_adjustment.json | 93 +++++++++--------- .../asset_adjustment/asset_adjustment.py | 95 ++++++++++++++++++- erpnext/config/assets.py | 4 + erpnext/controllers/buying_controller.py | 4 +- 10 files changed, 247 insertions(+), 73 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 51308e5008..9bbd137159 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, nowdate +from frappe.utils import cstr, flt, fmt_money, formatdate, getdate, nowdate, cint from frappe import msgprint, _, scrub from erpnext.controllers.accounts_controller import AccountsController from erpnext.accounts.utils import get_balance_on, get_account_currency @@ -92,6 +92,7 @@ class JournalEntry(AccountsController): self.unlink_advance_entry_reference() self.unlink_asset_reference() self.unlink_inter_company_jv() + self.unlink_asset_adjustment_entry() def unlink_advance_entry_reference(self): for d in self.get("accounts"): @@ -109,9 +110,12 @@ class JournalEntry(AccountsController): for s in asset.get("schedules"): if s.journal_entry == self.name: s.db_set("journal_entry", None) - asset.value_after_depreciation += s.depreciation_amount - asset.db_set("value_after_depreciation", asset.value_after_depreciation) + idx = cint(s.finance_book_id) or 1 + finance_books = asset.get('finance_books')[idx - 1] + finance_books.value_after_depreciation += s.depreciation_amount + finance_books.db_update() + asset.set_status() def unlink_inter_company_jv(self): @@ -121,6 +125,10 @@ class JournalEntry(AccountsController): frappe.db.set_value("Journal Entry", self.name,\ "inter_company_journal_entry_reference", "") + def unlink_asset_adjustment_entry(self): + frappe.db.sql(""" update `tabAsset Adjustment` + set journal_entry = null where journal_entry = %s""", self.name) + def validate_party(self): for d in self.get("accounts"): account_type = frappe.db.get_value("Account", d.account, "account_type") diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 4daeae2880..c05667a767 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -80,6 +80,12 @@ frappe.ui.form.on('Asset', { frm.trigger("create_asset_maintenance"); }, __("Make")); } + if (frm.doc.status != 'Fully Depreciated') { + frm.add_custom_button(__("Asset Adjustment"), function() { + frm.trigger("create_asset_maintenance"); + }, __("Make")); + } + frm.page.set_inner_btn_group_as_primary(__("Make")); frm.trigger("setup_chart"); } diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 9a05cad65c..49a010d7d5 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -1600,6 +1600,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "purchase_receipt_amount", + "fieldtype": "Currency", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Purchase Receipt Amount", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1673,7 +1703,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-11 01:48:18.711485", + "modified": "2018-05-11 10:41:45.972686", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index bf70fbc17d..748849ecb2 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -63,9 +63,6 @@ class Asset(AccountsController): if not self.calculate_depreciation: return - self.value_after_depreciation = (flt(self.gross_purchase_amount) - - flt(self.opening_accumulated_depreciation)) - if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(nowdate()): frappe.throw(_("Available-for-use Date is entered as past date")) @@ -79,7 +76,9 @@ class Asset(AccountsController): for d in self.get('finance_books'): self.validate_asset_finance_books(d) - value_after_depreciation = flt(self.value_after_depreciation) + value_after_depreciation = (flt(self.gross_purchase_amount) - + flt(self.opening_accumulated_depreciation)) + d.value_after_depreciation = value_after_depreciation no_of_depreciations = cint(d.total_number_of_depreciations - 1) - cint(self.number_of_depreciations_booked) @@ -139,7 +138,8 @@ class Asset(AccountsController): depreciation_amount = days * rate_per_day from_date = schedule_date else: - depreciation_amount = self.get_depreciation_amount(value_after_depreciation,d) + depreciation_amount = self.get_depreciation_amount(value_after_depreciation, + d.total_number_of_depreciations, d) if depreciation_amount: value_after_depreciation -= flt(depreciation_amount) @@ -187,21 +187,24 @@ class Asset(AccountsController): frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date") .format(row.idx)) - def set_accumulated_depreciation(self): - value_after_depreciation = flt(self.value_after_depreciation) + def set_accumulated_depreciation(self, ignore_booked_entry = False): straight_line_idx = [d.idx for d in self.get("schedules") if d.depreciation_method == 'Straight Line'] finance_books = [] for i, d in enumerate(self.get("schedules")): + if ignore_booked_entry and d.journal_entry: + continue + if d.finance_book_id not in finance_books: accumulated_depreciation = flt(self.opening_accumulated_depreciation) + value_after_depreciation = flt(self.get_value_after_depreciation(d.finance_book_id)) finance_books.append(d.finance_book_id) depreciation_amount = flt(d.depreciation_amount, d.precision("depreciation_amount")) value_after_depreciation -= flt(depreciation_amount) if straight_line_idx and i == max(straight_line_idx) - 1: - book = self.get('finance_books')[d.finance_book_id - 1] + book = self.get('finance_books')[cint(d.finance_book_id) - 1] depreciation_amount += flt(value_after_depreciation - flt(book.expected_value_after_useful_life), d.precision("depreciation_amount")) @@ -210,10 +213,13 @@ class Asset(AccountsController): d.accumulated_depreciation_amount = flt(accumulated_depreciation, d.precision("accumulated_depreciation_amount")) - def get_depreciation_amount(self, depreciable_value, row): + def get_value_after_depreciation(self, idx): + return flt(self.get('finance_books')[cint(idx)-1].value_after_depreciation) + + def get_depreciation_amount(self, depreciable_value, total_number_of_depreciations, row): percentage_value = 100.0 if row.depreciation_method == 'Written Down Value' else 200.0 - factor = percentage_value / row.total_number_of_depreciations + factor = percentage_value / total_number_of_depreciations depreciation_amount = flt(depreciable_value * factor / 100, 0) value_after_depreciation = flt(depreciable_value) - depreciation_amount @@ -229,7 +235,7 @@ class Asset(AccountsController): prorata_temporis = 1 if row.depreciation_method in ("Straight Line", "Manual"): - depreciation_amount = (flt(self.value_after_depreciation) - + depreciation_amount = (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / (cint(row.total_number_of_depreciations) - cint(self.number_of_depreciations_booked)) * prorata_temporis else: @@ -306,7 +312,7 @@ class Asset(AccountsController): doc.submit() def make_gl_entries(self): - if self.purchase_receipt: + if self.purchase_receipt and self.purchase_receipt_amount: from erpnext.accounts.general_ledger import make_gl_entries gl_entries = [] @@ -320,8 +326,8 @@ class Asset(AccountsController): "against": fixed_aseet_account, "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "posting_date": self.available_for_use_date, - "credit": self.gross_purchase_amount, - "credit_in_account_currency": self.gross_purchase_amount + "credit": self.purchase_receipt_amount, + "credit_in_account_currency": self.purchase_receipt_amount })) gl_entries.append(self.get_gl_dict({ @@ -329,8 +335,8 @@ class Asset(AccountsController): "against": cwip_account, "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "posting_date": self.available_for_use_date, - "debit": self.gross_purchase_amount, - "debit_in_account_currency": self.gross_purchase_amount + "debit": self.purchase_receipt_amount, + "debit_in_account_currency": self.purchase_receipt_amount })) make_gl_entries(gl_entries) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 92a251e4fa..aacaef5414 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt, today, getdate +from frappe.utils import flt, today, getdate, cint def post_depreciation_entries(date=None): # Return if automatic booking of asset depreciation is disabled @@ -47,6 +47,7 @@ def make_depreciation_entry(asset_name, date=None): je.naming_series = depreciation_series je.posting_date = d.schedule_date je.company = asset.company + je.finance_book = d.finance_book je.remark = "Depreciation Entry against {0} worth {1}".format(asset_name, d.depreciation_amount) je.append("accounts", { @@ -68,9 +69,12 @@ def make_depreciation_entry(asset_name, date=None): je.submit() d.db_set("journal_entry", je.name) - asset.value_after_depreciation -= d.depreciation_amount + + idx = cint(d.finance_book_id) + finance_books = asset.get('finance_books')[idx - 1] + finance_books.value_after_depreciation -= d.depreciation_amount + finance_books.db_update() - asset.db_set("value_after_depreciation", asset.value_after_depreciation) asset.set_status() return asset diff --git a/erpnext/assets/doctype/asset_adjustment/asset_adjustment.js b/erpnext/assets/doctype/asset_adjustment/asset_adjustment.js index 0535743703..11c02e105f 100644 --- a/erpnext/assets/doctype/asset_adjustment/asset_adjustment.js +++ b/erpnext/assets/doctype/asset_adjustment/asset_adjustment.js @@ -2,7 +2,29 @@ // For license information, please see license.txt frappe.ui.form.on('Asset Adjustment', { - refresh: function(frm) { + asset: function(frm) { + frm.trigger("set_current_asset_value"); + }, + finance_book: function(frm) { + frm.trigger("set_current_asset_value"); + }, + + set_current_asset_value: function(frm) { + debugger + if (frm.doc.finance_book && frm.doc.asset) { + frm.call({ + method: "erpnext.assets.doctype.asset_adjustment.asset_adjustment.get_current_asset_value", + args: { + asset: frm.doc.asset, + finance_book: frm.doc.finance_book + }, + callback: function(r) { + if (r.message) { + frm.set_value('current_asset_value', r.message); + } + } + }); + } } }); diff --git a/erpnext/assets/doctype/asset_adjustment/asset_adjustment.json b/erpnext/assets/doctype/asset_adjustment/asset_adjustment.json index 8a0e95775a..faa36efe07 100644 --- a/erpnext/assets/doctype/asset_adjustment/asset_adjustment.json +++ b/erpnext/assets/doctype/asset_adjustment/asset_adjustment.json @@ -12,6 +12,37 @@ "editable_grid": 1, "engine": "InnoDB", "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -38,7 +69,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -100,37 +131,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "current_asset_value", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Current Asset Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -231,27 +232,26 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "company", - "fieldtype": "Link", + "fieldname": "current_asset_value", + "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, - "label": "Company", + "label": "Current Asset Value", "length": 0, "no_copy": 0, - "options": "Company", "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -281,7 +281,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -292,8 +292,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "accumulated_depreciation_account", - "fieldtype": "Link", + "fieldname": "difference_amount", + "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -301,15 +301,14 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Accumulated Depreciation Account", + "label": "Difference Amount", "length": 0, - "no_copy": 0, - "options": "Account", + "no_copy": 1, "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, @@ -358,7 +357,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-11 00:25:07.222408", + "modified": "2018-05-11 21:45:03.459696", "modified_by": "Administrator", "module": "Assets", "name": "Asset Adjustment", diff --git a/erpnext/assets/doctype/asset_adjustment/asset_adjustment.py b/erpnext/assets/doctype/asset_adjustment/asset_adjustment.py index 437f9bd902..6b4b752e7f 100644 --- a/erpnext/assets/doctype/asset_adjustment/asset_adjustment.py +++ b/erpnext/assets/doctype/asset_adjustment/asset_adjustment.py @@ -4,7 +4,100 @@ from __future__ import unicode_literals import frappe +from frappe import _ +from frappe.utils import flt, getdate, cint, date_diff +from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts from frappe.model.document import Document class AssetAdjustment(Document): - pass + def validate(self): + self.set_difference_amount() + self.set_current_asset_value() + + def on_submit(self): + self.make_depreciation_entry() + self.reschedule_depreciations() + + def on_cancel(self): + if self.journal_entry: + frappe.throw(_("Cancel the journal entry {0} first").format(self.journal_entry)) + + def set_difference_amount(self): + self.difference_amount = flt(self.current_asset_value - self.new_asset_value) + + def set_current_asset_value(self): + if not self.current_asset_value and self.asset and self.finance_book: + self.current_asset_value = get_current_asset_value(self.asset, self.finance_book) + + def make_depreciation_entry(self): + asset = frappe.get_doc("Asset", self.asset) + fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \ + get_depreciation_accounts(asset) + + depreciation_cost_center, depreciation_series = frappe.db.get_value("Company", asset.company, + ["depreciation_cost_center", "series_for_depreciation_entry"]) + + je = frappe.new_doc("Journal Entry") + je.voucher_type = "Depreciation Entry" + je.naming_series = depreciation_series + je.posting_date = self.date + je.company = self.company + je.remark = "Depreciation Entry against {0} worth {1}".format(self.asset, self.difference_amount) + + je.append("accounts", { + "account": accumulated_depreciation_account, + "credit_in_account_currency": self.difference_amount, + }) + + je.append("accounts", { + "account": depreciation_expense_account, + "debit_in_account_currency": self.difference_amount, + "cost_center": depreciation_cost_center + }) + + je.flags.ignore_permissions = True + je.submit() + + self.db_set("journal_entry", je.name) + + def reschedule_depreciations(self): + asset = frappe.get_doc('Asset', self.asset) + + for d in asset.finance_books: + d.value_after_depreciation = self.new_asset_value + + if d.depreciation_method in ("Straight Line", "Manual"): + end_date = max([s.schedule_date for s in asset.schedules if cint(s.finance_book_id) == d.idx]) + total_days = date_diff(end_date, self.date) + rate_per_day = flt(d.value_after_depreciation) / flt(total_days) + from_date = self.date + else: + no_of_depreciations = len([e.name for e in asset.schedules + if (cint(s.finance_book_id) == d.idx and not e.journal_entry)]) + + value_after_depreciation = d.value_after_depreciation + for data in asset.schedules: + if cint(data.finance_book_id) == d.idx and not data.journal_entry: + if d.depreciation_method in ("Straight Line", "Manual"): + days = date_diff(data.schedule_date, from_date) + depreciation_amount = days * rate_per_day + from_date = data.schedule_date + else: + depreciation_amount = asset.get_depreciation_amount(value_after_depreciation, + no_of_depreciations, d) + + if depreciation_amount: + value_after_depreciation -= flt(depreciation_amount) + data.depreciation_amount = depreciation_amount + + d.db_update() + + asset.set_accumulated_depreciation(ignore_booked_entry=True) + for asset_data in asset.schedules: + if not asset_data.journal_entry: + asset_data.db_update() + +@frappe.whitelist() +def get_current_asset_value(asset, finance_book): + return frappe.db.get_value('Asset Finance Book', + {'parent': asset, 'parenttype': 'Asset', 'finance_book': finance_book}, 'value_after_depreciation', debug=1) diff --git a/erpnext/config/assets.py b/erpnext/config/assets.py index 99a7a5c744..d52f1420cd 100644 --- a/erpnext/config/assets.py +++ b/erpnext/config/assets.py @@ -43,6 +43,10 @@ def get_data(): "type": "doctype", "name": "Asset Maintenance Log", }, + { + "type": "doctype", + "name": "Asset Adjustment", + }, { "type": "doctype", "name": "Asset Repair", diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index f33ed25a06..c4e9fdd61e 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -511,6 +511,7 @@ class BuyingController(StockController): item_data = frappe.db.get_value('Item', row.item_code, ['asset_naming_series', 'asset_category'], as_dict=1) + purchase_amount = flt(row.base_net_amount + row.item_tax_amount) asset = frappe.get_doc({ 'doctype': 'Asset', 'item_code': row.item_code, @@ -522,7 +523,8 @@ class BuyingController(StockController): 'company': self.company, 'purchase_date': self.posting_date, 'calculate_depreciation': 1, - 'gross_purchase_amount': flt(row.base_net_amount + row.item_tax_amount), + 'purchase_receipt_amount': purchase_amount, + 'gross_purchase_amount': purchase_amount, 'purchase_receipt': self.name if self.doctype == 'Purchase Receipt' else None, 'purchase_invoice': self.name if self.doctype == 'Purchase Invoice' else None })