From 0cf0ebf08b3a64da41addf0ccd9789bf4830eb8b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 26 Sep 2018 15:24:49 +0530 Subject: [PATCH 1/8] [Refactored] Asset Depreciation Ledger report based on GL entries (#15415) * [Refactored] Asset Depreciation Ledger report is based on GL entries * Provision to make manual JV from the asset if Calculate Depreciation is disabled --- .../asset_depreciation_ledger.py | 92 ++++++++++++------- erpnext/assets/doctype/asset/asset.js | 22 +++++ erpnext/assets/doctype/asset/asset.py | 31 +++++++ erpnext/assets/doctype/asset/depreciation.py | 6 +- 4 files changed, 116 insertions(+), 35 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py index 5497384233..0e2742d8b7 100644 --- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py +++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py @@ -3,47 +3,75 @@ from __future__ import unicode_literals import frappe +from frappe.utils import flt from frappe import _ def execute(filters=None): columns, data = get_columns(), get_data(filters) return columns, data - + def get_data(filters): - data = frappe.db.sql(""" - select - a.name as asset, a.asset_category, a.status, - a.depreciation_method, a.purchase_date, a.gross_purchase_amount, - ds.schedule_date as depreciation_date, ds.depreciation_amount, - ds.accumulated_depreciation_amount, - (a.gross_purchase_amount - ds.accumulated_depreciation_amount) as amount_after_depreciation, - ds.journal_entry as depreciation_entry - from - `tabAsset` a, `tabDepreciation Schedule` ds - where - a.name = ds.parent - and a.docstatus=1 - and ifnull(ds.journal_entry, '') != '' - and ds.schedule_date between %(from_date)s and %(to_date)s - and a.company = %(company)s - {conditions} - order by - a.name asc, ds.schedule_date asc - """.format(conditions=get_filter_conditions(filters)), filters, as_dict=1) - - return data - -def get_filter_conditions(filters): - conditions = "" - + data = [] + depreciation_accounts = frappe.db.sql_list(""" select name from tabAccount + where ifnull(account_type, '') = 'Depreciation' """) + + filters_data = [["company", "=", filters.get('company')], + ["posting_date", ">=", filters.get('from_date')], + ["posting_date", "<=", filters.get('to_date')], + ["against_voucher_type", "=", "Asset"], + ["account", "in", depreciation_accounts]] + if filters.get("asset"): - conditions += " and a.name = %(asset)s" - + filters_data.append(["against_voucher", "=", filters.get("asset")]) + if filters.get("asset_category"): - conditions += " and a.asset_category = %(asset_category)s" - - return conditions + assets = frappe.db.sql_list("""select name from tabAsset + where asset_category = %s and docstatus=1""", filters.get("asset_category")) + + filters_data.append(["against_voucher", "in", assets]) + + gl_entries = frappe.get_all('GL Entry', + filters= filters_data, + fields = ["against_voucher", "debit_in_account_currency as debit", "voucher_no", "posting_date"], + order_by= "against_voucher, posting_date") + + if not gl_entries: + return data + + assets = [d.against_voucher for d in gl_entries] + assets_details = get_assets_details(assets) + for d in gl_entries: + asset_data = assets_details.get(d.against_voucher) + if not asset_data.get("accumulated_depreciation_amount"): + asset_data.accumulated_depreciation_amount = d.debit + else: + asset_data.accumulated_depreciation_amount += d.debit + + row = frappe._dict(asset_data) + row.update({ + "depreciation_amount": d.debit, + "depreciation_date": d.posting_date, + "amount_after_depreciation": (flt(row.gross_purchase_amount) - + flt(row.accumulated_depreciation_amount)), + "depreciation_entry": d.voucher_no + }) + + data.append(row) + + return data + +def get_assets_details(assets): + assets_details = {} + + fields = ["name as asset", "gross_purchase_amount", + "asset_category", "status", "depreciation_method", "purchase_date"] + + for d in frappe.get_all("Asset", fields = fields, filters = {'name': ('in', assets)}): + assets_details.setdefault(d.asset, d) + + return assets_details + def get_columns(): return [ { diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 01577eba77..875df59681 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -67,11 +67,33 @@ frappe.ui.form.on('Asset', { frm.trigger("create_asset_maintenance"); }, __("Make")); } + + if (!frm.doc.calculate_depreciation) { + frm.add_custom_button(__("Depreciation Entry"), function() { + frm.trigger("make_journal_entry"); + }, __("Make")); + } + frm.page.set_inner_btn_group_as_primary(__("Make")); frm.trigger("setup_chart"); } }, + make_journal_entry: function(frm) { + frappe.call({ + method: "erpnext.assets.doctype.asset.asset.make_journal_entry", + args: { + asset_name: frm.doc.name + }, + callback: function(r) { + if (r.message) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + } + }) + }, + setup_chart: function(frm) { var x_intervals = [frm.doc.purchase_date]; var asset_values = [frm.doc.gross_purchase_amount]; diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 8bba0b6936..7fa5810d34 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -283,3 +283,34 @@ def get_item_details(item_code): }) return ret + +@frappe.whitelist() +def make_journal_entry(asset_name): + asset = frappe.get_doc("Asset", asset_name) + 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"]) + depreciation_cost_center = asset.cost_center or depreciation_cost_center + + je = frappe.new_doc("Journal Entry") + je.voucher_type = "Depreciation Entry" + je.naming_series = depreciation_series + je.company = asset.company + je.remark = "Depreciation Entry against asset {0}".format(asset_name) + + je.append("accounts", { + "account": depreciation_expense_account, + "reference_type": "Asset", + "reference_name": asset.name, + "cost_center": depreciation_cost_center + }) + + je.append("accounts", { + "account": accumulated_depreciation_account, + "reference_type": "Asset", + "reference_name": asset.name + }) + + return je \ No newline at end of file diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index acd5892cd6..89d3808e6a 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -5,13 +5,13 @@ 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 - if not frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically"): + if not cint(frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")): return - + if not date: date = today() for asset in get_depreciable_assets(date): From c22ba2ec260d43896acab634a097ce98e73140e4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 26 Sep 2018 18:56:45 +0530 Subject: [PATCH 2/8] fix(sales return): validation message fix --- erpnext/controllers/sales_and_purchase_return.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 5e95b9efb4..8d6d07013a 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -139,7 +139,7 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items): .format(args.item_code), StockOverReturnError) elif abs(current_stock_qty) > max_returnable_qty: frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}") - .format(args.idx, reference_qty, args.item_code), StockOverReturnError) + .format(args.idx, max_returnable_qty, args.item_code), StockOverReturnError) def get_ref_item_dict(valid_items, ref_item_row): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos From 7d6d678e8d96ecbc4adc7aadfddc7ae859d3ccef Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 26 Sep 2018 19:04:11 +0530 Subject: [PATCH 3/8] purchase receipt return entry in dashboard --- .../doctype/purchase_receipt/purchase_receipt_dashboard.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py index 9ade1afd8a..7311025ab2 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py @@ -6,7 +6,8 @@ def get_data(): 'non_standard_fieldnames': { 'Purchase Invoice': 'purchase_receipt', 'Landed Cost Voucher': 'receipt_document', - 'Subscription': 'reference_document' + 'Subscription': 'reference_document', + 'Purchase Receipt': 'return_against' }, 'internal_links': { 'Purchase Order': ['items', 'purchase_order'], @@ -24,7 +25,7 @@ def get_data(): }, { 'label': _('Returns'), - 'items': ['Stock Entry'] + 'items': ['Purchase Receipt'] }, { 'label': _('Subscription'), From 8fbf8566182ccce05aa0ca0678e8acc85ee0f1a0 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 1 Oct 2018 12:00:45 +0530 Subject: [PATCH 4/8] [Fix] Salary slip --- erpnext/hr/doctype/salary_slip/salary_slip.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.js b/erpnext/hr/doctype/salary_slip/salary_slip.js index afb7f1a287..8cecbaa7b0 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.js +++ b/erpnext/hr/doctype/salary_slip/salary_slip.js @@ -113,16 +113,14 @@ frappe.ui.form.on('Salary Slip Timesheet', { // Get leave details //--------------------------------------------------------------------- var get_emp_and_leave_details = function(doc, dt, dn) { - if(!doc.start_date){ - return frappe.call({ - method: 'get_emp_and_leave_details', - doc: locals[dt][dn], - callback: function(r, rt) { - cur_frm.refresh(); - calculate_all(doc, dt, dn); - } - }); - } + return frappe.call({ + method: 'get_emp_and_leave_details', + doc: locals[dt][dn], + callback: function(r, rt) { + cur_frm.refresh(); + calculate_all(doc, dt, dn); + } + }); } cur_frm.cscript.employee = function(doc,dt,dn){ From c7f8b82fff5eece20cfe434c1f3ba05ac8e40318 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 27 Sep 2018 19:38:48 +0530 Subject: [PATCH 5/8] [Fix] Attendance tool --- .../hr/doctype/upload_attendance/upload_attendance.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py index 78d5aee81d..c295d4643e 100644 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py +++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cstr, add_days, date_diff +from frappe.utils import cstr, add_days, date_diff, getdate from frappe import _ from frappe.utils.csvutils import UnicodeWriter from frappe.model.document import Document @@ -48,8 +48,9 @@ def add_data(w, args): for employee in employees: existing_attendance = {} if existing_attendance_records \ - and tuple([date, employee.name]) in existing_attendance_records: - existing_attendance = existing_attendance_records[tuple([date, employee.name])] + and tuple([getdate(date), employee.name]) in existing_attendance_records: + existing_attendance = existing_attendance_records[tuple([getdate(date), employee.name])] + row = [ existing_attendance and existing_attendance.name or "", employee.name, employee.employee_name, date, @@ -114,6 +115,7 @@ def upload(): if not row: continue row_idx = i + 5 d = frappe._dict(zip(columns, row)) + d["doctype"] = "Attendance" if d.name: d["docstatus"] = frappe.db.get_value("Attendance", d.name, "docstatus") @@ -121,6 +123,8 @@ def upload(): try: check_record(d) ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True)) + except AttributeError: + pass except Exception as e: error = True ret.append('Error for row (#%d) %s : %s' % (row_idx, From a5576f5b214ad7b4772ecfba6e4264b57c9ec914 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 3 Oct 2018 10:39:50 +0530 Subject: [PATCH 6/8] [Fix] Stock difference between gl entry and stock ledger entry booked in stock adjustment (#15374) --- .../purchase_invoice/purchase_invoice.py | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 21b71ff37c..8e2fd82f1d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -388,16 +388,20 @@ class PurchaseInvoice(BuyingController): expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") warehouse_account = get_warehouse_account_map() + voucher_wise_stock_value = {} + if self.update_stock: + for d in frappe.get_all('Stock Ledger Entry', + fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}): + voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference) + for item in self.get("items"): if flt(item.base_net_amount): account_currency = get_account_currency(item.expense_account) if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items: - val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9 - # warehouse account - warehouse_debit_amount = flt(flt(item.valuation_rate, val_rate_db_precision) - * flt(item.qty) * flt(item.conversion_factor), item.precision("base_net_amount")) + warehouse_debit_amount = self.make_stock_adjustment_entry(gl_entries, + item, voucher_wise_stock_value, account_currency) gl_entries.append( self.get_gl_dict({ @@ -469,6 +473,36 @@ class PurchaseInvoice(BuyingController): self.negative_expense_to_be_booked += flt(item.item_tax_amount, \ item.precision("item_tax_amount")) + def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value, account_currency): + net_amt_precision = item.precision("base_net_amount") + val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9 + + warehouse_debit_amount = flt(flt(item.valuation_rate, val_rate_db_precision) + * flt(item.qty) * flt(item.conversion_factor), net_amt_precision) + + # Stock ledger value is not matching with the warehouse amount + if (self.update_stock and voucher_wise_stock_value.get(item.name) and + warehouse_debit_amount != flt(voucher_wise_stock_value.get(item.name), net_amt_precision)): + + stock_adjustment_account = self.get_company_default("stock_adjustment_account") + stock_amount = flt(voucher_wise_stock_value.get(item.name), net_amt_precision) + stock_adjustment_amt = warehouse_debit_amount - stock_amount + + gl_entries.append( + self.get_gl_dict({ + "account": stock_adjustment_account, + "against": item.expense_account, + "debit": stock_adjustment_amt, + "remarks": self.get("remarks") or _("Stock Adjustment"), + "cost_center": item.cost_center, + "project": item.project + }, account_currency) + ) + + warehouse_debit_amount = stock_amount + + return warehouse_debit_amount + def make_tax_gl_entries(self, gl_entries): # tax table gl entries valuation_tax = {} From d1b87ba41c4ed4b1442cb99306d3f94b6d6a8ce1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 3 Oct 2018 16:29:43 +0530 Subject: [PATCH 7/8] Book cost of goods sold instead of stock adjustment --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 8e2fd82f1d..5d11797a5c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -484,13 +484,13 @@ class PurchaseInvoice(BuyingController): if (self.update_stock and voucher_wise_stock_value.get(item.name) and warehouse_debit_amount != flt(voucher_wise_stock_value.get(item.name), net_amt_precision)): - stock_adjustment_account = self.get_company_default("stock_adjustment_account") + cost_of_goods_sold_account = self.get_company_default("default_expense_account") stock_amount = flt(voucher_wise_stock_value.get(item.name), net_amt_precision) stock_adjustment_amt = warehouse_debit_amount - stock_amount gl_entries.append( self.get_gl_dict({ - "account": stock_adjustment_account, + "account": cost_of_goods_sold_account, "against": item.expense_account, "debit": stock_adjustment_amt, "remarks": self.get("remarks") or _("Stock Adjustment"), From 79dc8ac9cc435d2caabaff03091c7fd293a2c63d Mon Sep 17 00:00:00 2001 From: Ameya Shenoy Date: Thu, 4 Oct 2018 09:11:50 +0000 Subject: [PATCH 8/8] bumped to version 10.1.55 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 68f1fa749b..246b8dd3f3 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '10.1.54' +__version__ = '10.1.55' def get_default_company(user=None): '''Get default company for user'''