From 0cf0ebf08b3a64da41addf0ccd9789bf4830eb8b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 26 Sep 2018 15:24:49 +0530 Subject: [PATCH 01/39] [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 352e6494f3e858c5e96c3b285020a64e4a972337 Mon Sep 17 00:00:00 2001 From: Shreya Shah Date: Wed, 26 Sep 2018 15:38:34 +0530 Subject: [PATCH 02/39] Contact and email fields in Payment Entry (#15490) * Add contact_person and contact_email fields to Payment Entry * Minor fix * Map contact details while making Payment Entry * Update payment_entry.py --- .../doctype/payment_entry/payment_entry.js | 57 +++++++++------ .../doctype/payment_entry/payment_entry.json | 70 ++++++++++++++++++- .../doctype/payment_entry/payment_entry.py | 8 +-- 3 files changed, 110 insertions(+), 25 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 9215e5f366..b0795caf23 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -23,7 +23,6 @@ frappe.ui.form.on('Payment Entry', { } } }); - frm.set_query("party_type", function() { return{ "filters": { @@ -31,7 +30,17 @@ frappe.ui.form.on('Payment Entry', { } } }); - + frm.set_query("contact_person", function() { + if (frm.doc.party) { + return { + query: 'frappe.contacts.doctype.contact.contact.contact_query', + filters: { + link_doctype: frm.doc.party_type, + link_name: frm.doc.party + } + }; + } + }); frm.set_query("paid_to", function() { var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; @@ -114,6 +123,11 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_dynamic_labels(frm); }, + contact_person: function(frm) { + frm.set_value("contact_email", ""); + erpnext.utils.get_contact_details(frm); + }, + hide_unhide_fields: function(frm) { var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: ""; @@ -146,7 +160,7 @@ frappe.ui.form.on('Payment Entry', { frm.toggle_display("set_exchange_gain_loss", (frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount && ((frm.doc.paid_from_account_currency != company_currency || - frm.doc.paid_to_account_currency != company_currency) && + frm.doc.paid_to_account_currency != company_currency) && frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency))); frm.refresh_fields(); @@ -208,7 +222,7 @@ frappe.ui.form.on('Payment Entry', { }); } else { if(frm.doc.party) { - frm.events.party(frm); + frm.events.party(frm); } if(frm.doc.mode_of_payment) { @@ -230,13 +244,16 @@ frappe.ui.form.on('Payment Entry', { }, party: function(frm) { + if (frm.doc.contact_email || frm.doc.contact_person) { + frm.set_value("contact_email", ""); + frm.set_value("contact_person", ""); + } if(frm.doc.payment_type && frm.doc.party_type && frm.doc.party) { if(!frm.doc.posting_date) { frappe.msgprint(__("Please select Posting Date before selecting Party")) frm.set_value("party", ""); return ; } - frm.set_party_account_based_on_party = true; return frappe.call({ @@ -302,7 +319,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate); } frm.set_value("received_amount", frm.doc.paid_amount); - + } else { frm.events.received_amount(frm); } @@ -350,7 +367,7 @@ frappe.ui.form.on('Payment Entry', { ]); } } - }); + }); } }, @@ -402,7 +419,7 @@ frappe.ui.form.on('Payment Entry', { } }) }, - + posting_date: function(frm) { frm.events.paid_from_account_currency(frm); }, @@ -415,7 +432,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate); frm.set_value("base_received_amount", frm.doc.base_paid_amount); } - + frm.events.set_unallocated_amount(frm); } @@ -425,17 +442,17 @@ frappe.ui.form.on('Payment Entry', { target_exchange_rate: function(frm) { frm.set_paid_amount_based_on_received_amount = true; - + if (frm.doc.received_amount) { frm.set_value("base_received_amount", flt(frm.doc.received_amount) * flt(frm.doc.target_exchange_rate)); - - if(!frm.doc.source_exchange_rate && + + if(!frm.doc.source_exchange_rate && (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) { frm.set_value("source_exchange_rate", frm.doc.target_exchange_rate); frm.set_value("base_paid_amount", frm.doc.base_received_amount); } - + frm.events.set_unallocated_amount(frm); } frm.set_paid_amount_based_on_received_amount = false; @@ -468,14 +485,14 @@ frappe.ui.form.on('Payment Entry', { frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount); else frm.events.set_unallocated_amount(frm); - + frm.set_paid_amount_based_on_received_amount = false; }, - + reset_received_amount: function(frm) { if(!frm.set_paid_amount_based_on_received_amount && (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) { - + frm.set_value("received_amount", frm.doc.paid_amount); if(frm.doc.source_exchange_rate) { @@ -483,7 +500,7 @@ frappe.ui.form.on('Payment Entry', { } frm.set_value("base_received_amount", frm.doc.base_paid_amount); } - + if(frm.doc.payment_type == "Receive") frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount); else @@ -549,7 +566,7 @@ frappe.ui.form.on('Payment Entry', { (frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") || (frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier") || (frm.doc.payment_type=="Pay" && frm.doc.party_type=="Employee") || - (frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student") + (frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student") ) { if(total_positive_outstanding > total_negative_outstanding) frm.set_value("paid_amount", @@ -694,7 +711,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_value("unallocated_amount", unallocated_amount); frm.trigger("set_difference_amount"); }, - + set_difference_amount: function(frm) { var difference_amount = 0; var base_unallocated_amount = flt(frm.doc.unallocated_amount) * @@ -753,7 +770,7 @@ frappe.ui.form.on('Payment Entry', { frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx])); return false; } - + if(frm.doc.party_type=="Employee" && !in_list(["Expense Claim", "Journal Entry"], row.reference_doctype) ) { diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 679c97fdd1..bc9062b96c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -376,6 +376,40 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "party", + "fieldname": "contact_person", + "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": "Contact", + "length": 0, + "no_copy": 0, + "options": "Contact", + "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, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -441,6 +475,40 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "contact_person", + "fieldname": "contact_email", + "fieldtype": "Data", + "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": "Email", + "length": 0, + "no_copy": 0, + "options": "Email", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "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, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -1972,7 +2040,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-09-11 15:44:28.647566", + "modified": "2018-09-25 14:38:48.312629", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 6a361a4bfa..aec3d1b014 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -299,12 +299,12 @@ class PaymentEntry(AccountsController): if self.payment_type == "Receive" \ and self.base_total_allocated_amount < self.base_received_amount + total_deductions \ and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate): - self.unallocated_amount = (self.base_received_amount + total_deductions - + self.unallocated_amount = (self.base_received_amount + total_deductions - self.base_total_allocated_amount) / self.source_exchange_rate elif self.payment_type == "Pay" \ and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \ and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate): - self.unallocated_amount = (self.base_paid_amount - (total_deductions + + self.unallocated_amount = (self.base_paid_amount - (total_deductions + self.base_total_allocated_amount)) / self.target_exchange_rate def set_difference_amount(self): @@ -790,7 +790,6 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre @frappe.whitelist() def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None): doc = frappe.get_doc(dt, dn) - if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0: frappe.throw(_("Can only make payment against unbilled {0}").format(dt)) @@ -877,7 +876,8 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= pe.mode_of_payment = doc.get("mode_of_payment") pe.party_type = party_type pe.party = doc.get(scrub(party_type)) - + pe.contact_person = doc.get("contact_person") + pe.contact_email = doc.get("contact_email") pe.ensure_supplier_is_not_blocked() pe.paid_from = party_account if payment_type=="Receive" else bank.account From e20b7cc47d63be838bac84dee60352db9b7c950c Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Wed, 26 Sep 2018 15:39:02 +0530 Subject: [PATCH 03/39] fix(python3): super() backwards compatibility fix (#15498) From c3772f1ac4c5c4df66da729d53a090935413dbe5 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 26 Sep 2018 15:55:43 +0530 Subject: [PATCH 04/39] minor fix (#15503) --- erpnext/selling/sales_common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index b7b186a8d6..c431df403a 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -264,7 +264,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ { row.incentives = flt( row.allocated_amount * row.commission_rate / 100.0, - precision("incentives", sales_person)); + precision("incentives", row)); } }, From 1e7c32b909cf99d7e46abb17624971979ee5ca23 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 26 Sep 2018 18:01:00 +0530 Subject: [PATCH 05/39] Payroll fixes and more (#15475) * fix(payroll): multiple minor fixes related to salary structure * Added GSTR-1 and GSTR-2 report in Accounts module page * delete asset movement records on cancellation of Purchase Receipt * Update consolidated_financial_statement.py * minor fix * minor fix * add filters on item prices report (#15495) --- .../consolidated_financial_statement.py | 3 +- erpnext/config/accounts.py | 10 ++++++ erpnext/controllers/buying_controller.py | 2 +- .../leave_application/leave_application.json | 35 ++++++++++++++++++- .../salary_component/salary_component.json | 9 ++--- .../doctype/salary_detail/salary_detail.json | 7 ++-- erpnext/hr/doctype/salary_slip/salary_slip.py | 4 +-- .../doctype/salary_slip/test_salary_slip.py | 29 +++++++++------ .../salary_structure/salary_structure.py | 15 +++++--- .../stock/report/item_prices/item_prices.js | 11 +++++- .../stock/report/item_prices/item_prices.py | 21 ++++++++--- 11 files changed, 113 insertions(+), 33 deletions(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index d517a56047..b6d4875e97 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -268,8 +268,7 @@ def get_companies(filters): return all_companies, companies def get_subsidiary_companies(company): - lft, rgt = frappe.get_cached_value('Company', - company, ["lft", "rgt"]) + lft, rgt = frappe.db.get_value('Company', company, ["lft", "rgt"]) return frappe.db.sql_list("""select name from `tabCompany` where lft >= {0} and rgt <= {1} order by lft, rgt""".format(lft, rgt)) diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index 660f78cc91..d9ffced547 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -235,6 +235,16 @@ def get_data(): "type": "doctype", "name": "GST HSN Code", }, + { + "type": "report", + "name": "GSTR-1", + "is_query_report": True + }, + { + "type": "report", + "name": "GSTR-2", + "is_query_report": True + }, { "type": "report", "name": "GST Sales Register", diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 3671481682..86fecdce45 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -661,7 +661,7 @@ class BuyingController(StockController): if self.doctype == 'Purchase Invoice' and not self.get('update_stock'): return - frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s and docstatus = 0", self.name) + frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s", self.name) frappe.db.sql("delete from `tabSerial No` where purchase_document_no=%s", self.name) def validate_schedule_date(self): diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json index 914dda21cb..f9d83a4244 100644 --- a/erpnext/hr/doctype/leave_application/leave_application.json +++ b/erpnext/hr/doctype/leave_application/leave_application.json @@ -651,6 +651,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "salary_slip", + "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": "Salary Slip", + "length": 0, + "no_copy": 0, + "options": "Salary Slip", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "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, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -916,7 +949,7 @@ "issingle": 0, "istable": 0, "max_attachments": 3, - "modified": "2018-08-21 14:44:42.766422", + "modified": "2018-09-21 15:53:11.935416", "modified_by": "Administrator", "module": "HR", "name": "Leave Application", diff --git a/erpnext/hr/doctype/salary_component/salary_component.json b/erpnext/hr/doctype/salary_component/salary_component.json index 4221916469..f7ce08c737 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.json +++ b/erpnext/hr/doctype/salary_component/salary_component.json @@ -770,7 +770,7 @@ "collapsible": 1, "collapsible_depends_on": "", "columns": 0, - "depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1 && doc.is_additional_component != 1", + "depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1", "fieldname": "condition_and_formula", "fieldtype": "Section Break", "hidden": 0, @@ -835,7 +835,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "default": "1", + "default": "", "fieldname": "amount_based_on_formula", "fieldtype": "Check", "hidden": 0, @@ -1003,7 +1003,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-07-02 16:55:44.467519", + "modified": "2018-09-20 16:44:58.876044", "modified_by": "Administrator", "module": "HR", "name": "Salary Component", @@ -1056,5 +1056,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 0, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.json b/erpnext/hr/doctype/salary_detail/salary_detail.json index ebec32b6f8..bc9812c34f 100644 --- a/erpnext/hr/doctype/salary_detail/salary_detail.json +++ b/erpnext/hr/doctype/salary_detail/salary_detail.json @@ -385,7 +385,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "default": "1", + "default": "0", "depends_on": "eval:doc.parenttype=='Salary Structure'", "fetch_from": "", "fieldname": "amount_based_on_formula", @@ -692,7 +692,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-07-04 16:28:32.314907", + "modified": "2018-09-20 16:59:33.622652", "modified_by": "Administrator", "module": "HR", "name": "Salary Detail", @@ -706,5 +706,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 0, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 2c2e4d4a02..85e372d184 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -384,8 +384,8 @@ class SalarySlip(TransactionBase): and t2.is_lwp = 1 and t1.docstatus = 1 and t1.employee = %(employee)s - and CASE WHEN t2.include_holiday != 1 THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date - WHEN t2.include_holiday THEN %(dt)s between from_date and to_date + and CASE WHEN t2.include_holiday != 1 THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date and ifnull(t1.salary_slip, '') = '' + WHEN t2.include_holiday THEN %(dt)s between from_date and to_date and ifnull(t1.salary_slip, '') = '' END """.format(holidays), {"employee": self.employee, "dt": dt}) if leave: diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index 144022b08e..73ab67dd6f 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -323,11 +323,12 @@ def make_salary_component(salary_components, test_tax): def get_salary_component_account(sal_comp): company = erpnext.get_default_company() sal_comp = frappe.get_doc("Salary Component", sal_comp) - sal_comp.append("accounts", { - "company": company, - "default_account": create_account(company) - }) - sal_comp.save() + if not sal_comp.get("accounts"): + sal_comp.append("accounts", { + "company": company, + "default_account": create_account(company) + }) + sal_comp.save() def create_account(company): salary_account = frappe.db.get_value("Account", "Salary - " + frappe.get_cached_value('Company', company, 'abbr')) @@ -347,7 +348,8 @@ def make_earning_salary_component(setup=False, test_tax=False): "abbr":'BS', "condition": 'base > 10000', "formula": 'base*.5', - "type": "Earning" + "type": "Earning", + "amount_based_on_formula": 1 }, { "salary_component": 'HRA', @@ -360,7 +362,8 @@ def make_earning_salary_component(setup=False, test_tax=False): "abbr":'SA', "condition": 'H < 10000', "formula": 'BS*.5', - "type": "Earning" + "type": "Earning", + "amount_based_on_formula": 1 }, { "salary_component": "Leave Encashment", @@ -401,7 +404,8 @@ def make_earning_salary_component(setup=False, test_tax=False): "abbr":'BS', "condition": 'base < 10000', "formula": 'base*.2', - "type": "Earning" + "type": "Earning", + "amount_based_on_formula": 1 }) return data @@ -412,13 +416,15 @@ def make_deduction_salary_component(setup=False, test_tax=False): "abbr":'PT', "condition": 'base > 10000', "formula": 'base*.1', - "type": "Deduction" + "type": "Deduction", + "amount_based_on_formula": 1 }, { "salary_component": 'TDS', "abbr":'T', "formula": 'base*.1', - "type": "Deduction" + "type": "Deduction", + "amount_based_on_formula": 1 } ] if not test_tax: @@ -427,7 +433,8 @@ def make_deduction_salary_component(setup=False, test_tax=False): "abbr":'T', "condition": 'employment_type=="Intern"', "formula": 'base*.1', - "type": "Deduction" + "type": "Deduction", + "amount_based_on_formula": 1 }) if setup or test_tax: make_salary_component(data, test_tax) diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py index 6b190db01f..a36d820f2c 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/salary_structure.py @@ -18,14 +18,21 @@ class SalaryStructure(Document): self.validate_max_benefits_with_flexi() def set_missing_values(self): - fields = ["depends_on_lwp", "variable_based_on_taxable_salary", "is_tax_applicable", "is_flexible_benefit"] + overwritten_fields = ["depends_on_lwp", "variable_based_on_taxable_salary", "is_tax_applicable", "is_flexible_benefit"] + overwritten_fields_if_missing = ["amount_based_on_formula", "formula", "amount"] for table in ["earnings", "deductions"]: for d in self.get(table): - component_default_value = frappe.db.get_value("Salary Component", str(d.salary_component), fields, as_dict=1) + component_default_value = frappe.db.get_value("Salary Component", str(d.salary_component), + overwritten_fields + overwritten_fields_if_missing, as_dict=1) if component_default_value: - for fieldname, value in iteritems(component_default_value): + for fieldname in overwritten_fields: + value = component_default_value.get(fieldname) if d.get(fieldname) != value: - d[fieldname] = value + d.set(fieldname, value) + + if not (d.get("amount") or d.get("formula")): + for fieldname in overwritten_fields_if_missing: + d.set(fieldname, component_default_value.get(fieldname)) def validate_amount(self): if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet: diff --git a/erpnext/stock/report/item_prices/item_prices.js b/erpnext/stock/report/item_prices/item_prices.js index 272f83e228..77bca4466d 100644 --- a/erpnext/stock/report/item_prices/item_prices.js +++ b/erpnext/stock/report/item_prices/item_prices.js @@ -3,6 +3,15 @@ frappe.query_reports["Item Prices"] = { "filters": [ - + { + "fieldname": "items", + "label": __("Items Filter"), + "fieldtype": "Select", + "options": "Enabled Items only\nDisabled Items only\nAll Items", + "default": "Enabled Items only", + "on_change": function(query_report) { + query_report.trigger_refresh(); + } + } ] } diff --git a/erpnext/stock/report/item_prices/item_prices.py b/erpnext/stock/report/item_prices/item_prices.py index 0375d8fabb..aa3ed92079 100644 --- a/erpnext/stock/report/item_prices/item_prices.py +++ b/erpnext/stock/report/item_prices/item_prices.py @@ -10,7 +10,8 @@ def execute(filters=None): if not filters: filters = {} columns = get_columns(filters) - item_map = get_item_details() + conditions = get_condition(filters) + item_map = get_item_details(conditions) pl = get_price_list() last_purchase_rate = get_last_purchase_rate() bom_rate = get_item_bom_rate() @@ -41,14 +42,14 @@ def get_columns(filters): return columns -def get_item_details(): +def get_item_details(conditions): """returns all items details""" item_map = {} for i in frappe.db.sql("""select name, item_group, item_name, description, - brand, stock_uom from tabItem - order by item_code, item_group""", as_dict=1): + brand, stock_uom from tabItem %s + order by item_code, item_group""" % (conditions), as_dict=1): item_map.setdefault(i.name, i) return item_map @@ -133,3 +134,15 @@ def get_valuation_rate(): item_val_rate_map.setdefault(d.item_code, d.val_rate) return item_val_rate_map + +def get_condition(filters): + """Get Filter Items""" + + if filters.get("items") == "Enabled Items only": + conditions = " where disabled=0 " + elif filters.get("items") == "Disabled Items only": + conditions = " where disabled=1 " + else: + conditions = "" + + return conditions From 183972f58f66ce7c3dac489595f524e31bfaa26a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 26 Sep 2018 18:34:07 +0530 Subject: [PATCH 06/39] fix(stock_entry): allow creating stock entry against work order even if item rate is zero --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 1a94c04ad2..eb817e1756 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -379,7 +379,7 @@ class StockEntry(StockController): def calculate_rate_and_amount(self, force=False, update_finished_item_rate=True, raise_error_if_no_rate=True): - self.set_basic_rate(force, update_finished_item_rate) + self.set_basic_rate(force, update_finished_item_rate, raise_error_if_no_rate) self.distribute_additional_costs() self.update_valuation_rate() self.set_total_incoming_outgoing_value() From c22ba2ec260d43896acab634a097ce98e73140e4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 26 Sep 2018 18:56:45 +0530 Subject: [PATCH 07/39] 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 08/39] 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 5c335550e3847568f4188c46e65ea6511e73076c Mon Sep 17 00:00:00 2001 From: Shreya Date: Thu, 27 Sep 2018 13:55:59 +0530 Subject: [PATCH 09/39] Fetch item_group's name and image on website if children --- erpnext/templates/includes/products_as_grid.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/templates/includes/products_as_grid.html b/erpnext/templates/includes/products_as_grid.html index 9908c8a247..8da82de72b 100644 --- a/erpnext/templates/includes/products_as_grid.html +++ b/erpnext/templates/includes/products_as_grid.html @@ -3,8 +3,8 @@
- {{ product_image_square(thumbnail or website_image) }} -
{{ item_name }}
+ {{ product_image_square(thumbnail or website_image or image) }} +
{{ item_name or name }}
{% if price_sales_uom %}
{{ price_sales_uom }}
({{ price_stock_uom }} / {{ stock_uom }})
From c2dd9ab77847bf745648b4236f7f7c681aaa05e3 Mon Sep 17 00:00:00 2001 From: Saif Date: Thu, 27 Sep 2018 15:18:04 +0500 Subject: [PATCH 10/39] Fix the problem that naming series would remove trailing '-' from series prefix if ##### was not entered (#15514) --- erpnext/setup/doctype/naming_series/naming_series.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/setup/doctype/naming_series/naming_series.py b/erpnext/setup/doctype/naming_series/naming_series.py index 4e08105a7d..2167b12e32 100644 --- a/erpnext/setup/doctype/naming_series/naming_series.py +++ b/erpnext/setup/doctype/naming_series/naming_series.py @@ -166,13 +166,12 @@ class NamingSeries(Document): def parse_naming_series(self): parts = self.prefix.split('.') - # If series contain date format like INV.YYYY.MM.##### - if len(parts) > 2: - del parts[-1] # Removed ### from the series - prefix = parse_naming_series(parts) - else: - prefix = parts[0] + # Remove ### from the end of series + if parts[-1] == "#" * len(parts[-1]): + del parts[-1] + + prefix = parse_naming_series(parts) return prefix def set_by_naming_series(doctype, fieldname, naming_series, hide_name_field=True): From ea0ef955397e5dae88497159a42f5bc8292d76c5 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 28 Sep 2018 17:05:55 +0530 Subject: [PATCH 11/39] [Fix] Consolidated Financial Statement report --- .../consolidated_financial_statement.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index b6d4875e97..2d134694f2 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -238,6 +238,9 @@ def accumulate_values_into_parents(accounts, accounts_by_name, companies): for d in reversed(accounts): if d.parent_account: account = d.parent_account.split('-')[0].strip() + if not accounts_by_name.get(account): + continue + for company in companies: accounts_by_name[account][company] = \ accounts_by_name[account].get(company, 0.0) + d.get(company, 0.0) From 6575e52942156a1b2dceaaf435fc1c61da7f34b6 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sun, 30 Sep 2018 21:08:46 +0530 Subject: [PATCH 12/39] fix: Refactor Review Area according to new Text Editor (#15536) - depends on https://github.com/frappe/frappe/pull/6159 --- .../public/js/hub/components/CommentInput.vue | 5 +- .../public/js/hub/components/ReviewArea.vue | 81 ++++++++++++++++--- 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/erpnext/public/js/hub/components/CommentInput.vue b/erpnext/public/js/hub/components/CommentInput.vue index cc430d0c28..31562c7a28 100644 --- a/erpnext/public/js/hub/components/CommentInput.vue +++ b/erpnext/public/js/hub/components/CommentInput.vue @@ -18,18 +18,19 @@ export default { }, methods: { make_input() { - this.message_input = new frappe.ui.CommentArea({ + this.message_input = frappe.ui.form.make_control({ parent: this.$refs['comment-input'], on_submit: (message) => { this.message_input.reset(); this.$emit('change', message); }, + only_input: true, no_wrapper: true }); }, submit_input() { if (!this.message_input) return; - const value = this.message_input.val(); + const value = this.message_input.get_value(); if (!value) return; this.message_input.submit(); } diff --git a/erpnext/public/js/hub/components/ReviewArea.vue b/erpnext/public/js/hub/components/ReviewArea.vue index 159fdd112f..51b9ab1502 100644 --- a/erpnext/public/js/hub/components/ReviewArea.vue +++ b/erpnext/public/js/hub/components/ReviewArea.vue @@ -1,6 +1,44 @@