From d62de6c8e56cff0693ef607ce47ce94651f94c89 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 9 May 2019 18:50:14 +0530 Subject: [PATCH 1/8] fix(hr): Use event_status instead of status Training Feedback DocType has event_status field (not status) This was broken since PR #10379, PR #17197 made this failure explicit. --- erpnext/hr/doctype/training_feedback/training_feedback.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.py b/erpnext/hr/doctype/training_feedback/training_feedback.py index b47e2b4be88..1a334507917 100644 --- a/erpnext/hr/doctype/training_feedback/training_feedback.py +++ b/erpnext/hr/doctype/training_feedback/training_feedback.py @@ -15,11 +15,11 @@ class TrainingFeedback(Document): def on_submit(self): training_event = frappe.get_doc("Training Event", self.training_event) - status = None + event_status = None for e in training_event.employees: if e.employee == self.employee: - status = 'Feedback Submitted' + event_status = 'Feedback Submitted' break - if status: - frappe.db.set_value("Training Event", self.training_event, "status", status) + if event_status: + frappe.db.set_value("Training Event", self.training_event, "event_status", event_status) From 510dc60bf0875e6595403195b0b0029de965fbea Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 16 May 2019 17:28:39 +0530 Subject: [PATCH 2/8] fix: GL Entry for opening stock reconciliation --- erpnext/controllers/stock_controller.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index af689808c07..7d92bdda49b 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -80,6 +80,7 @@ class StockController(AccountsController): "cost_center": item_row.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", "debit": flt(sle.stock_value_difference, 2), + "is_opening": item_row.get("is_opening"), }, warehouse_account[sle.warehouse]["account_currency"])) # to target warehouse / expense account @@ -89,7 +90,8 @@ class StockController(AccountsController): "cost_center": item_row.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", "credit": flt(sle.stock_value_difference, 2), - "project": item_row.get("project") or self.get("project") + "project": item_row.get("project") or self.get("project"), + "is_opening": item_row.get("is_opening") })) elif sle.warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(sle.warehouse) @@ -123,8 +125,17 @@ class StockController(AccountsController): def get_voucher_details(self, default_expense_account, default_cost_center, sle_map): if self.doctype == "Stock Reconciliation": - return [frappe._dict({ "name": voucher_detail_no, "expense_account": default_expense_account, - "cost_center": default_cost_center }) for voucher_detail_no, sle in sle_map.items()] + reconciliation_purpose = frappe.db.get_value(self.doctype, self.name, "purpose") + is_opening = "Yes" if reconciliation_purpose == "Opening Stock" else "No" + details = [] + for voucher_detail_no, sle in sle_map.items(): + details.append(frappe._dict({ + "name": voucher_detail_no, + "expense_account": default_expense_account, + "cost_center": default_cost_center, + "is_opening": is_opening + })) + return details else: details = self.get("items") From 090219814e3cd63d7acdd87efcf4a85d3bdf2f56 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 16 May 2019 19:17:02 +0530 Subject: [PATCH 3/8] fix: BOM Item rate based on uom conversion factor and exchange rate --- erpnext/manufacturing/doctype/bom/bom.js | 7 ++- erpnext/manufacturing/doctype/bom/bom.py | 13 +++--- erpnext/manufacturing/doctype/bom/test_bom.py | 45 ++++++++++++++----- erpnext/stock/get_item_details.py | 29 ++++++------ 4 files changed, 62 insertions(+), 32 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 3c35e6cc5e1..9248ac0fe85 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -205,7 +205,12 @@ var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) { 'item_code': d.item_code, 'bom_no': d.bom_no != null ? d.bom_no: '', "scrap_items": scrap_items, - 'qty': d.qty + 'qty': d.qty, + "stock_qty": d.stock_qty, + "include_item_in_manufacturing": d.include_item_in_manufacturing, + "uom": d.uom, + "stock_uom": d.stock_uom, + "conversion_factor": d.conversion_factor }, callback: function(r) { d = locals[cdt][cdn]; diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index b04ba7c4fb0..97eb0e752af 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -170,13 +170,14 @@ class BOM(WebsiteGenerator): rate = self.get_valuation_rate(arg) elif arg: if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom: - rate = self.get_bom_unitcost(arg['bom_no']) + rate = self.get_bom_unitcost(arg['bom_no']) * (arg.get("conversion_factor") or 1) else: if self.rm_cost_as_per == 'Valuation Rate': - rate = self.get_valuation_rate(arg) + rate = self.get_valuation_rate(arg) * (arg.get("conversion_factor") or 1) elif self.rm_cost_as_per == 'Last Purchase Rate': - rate = arg.get('last_purchase_rate') \ - or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate") + rate = (arg.get('last_purchase_rate') \ + or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")) \ + * (arg.get("conversion_factor") or 1) elif self.rm_cost_as_per == "Price List": if not self.buying_price_list: frappe.throw(_("Please select Price List")) @@ -189,7 +190,7 @@ class BOM(WebsiteGenerator): "transaction_type": "buying", "company": self.company, "currency": self.currency, - "conversion_rate": self.conversion_rate or 1, + "conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function "conversion_factor": arg.get("conversion_factor") or 1, "plc_conversion_rate": 1, "ignore_party": True @@ -207,7 +208,7 @@ class BOM(WebsiteGenerator): frappe.msgprint(_("{0} not found for item {1}") .format(self.rm_cost_as_per, arg["item_code"]), alert=True) - return flt(rate) + return flt(rate) / (self.conversion_rate or 1) def update_cost(self, update_parent=True, from_child_bom=False, save=True): if self.docstatus == 2: diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index 1129025a694..e230e598489 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -9,6 +9,7 @@ from frappe.utils import cstr from frappe.test_runner import make_test_records from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost +from six import string_types test_records = frappe.get_test_records('BOM') @@ -63,16 +64,8 @@ class TestBOM(unittest.TestCase): and item_code='_Test Item 2' and docstatus=1 and parenttype='BOM'""") rm_rate = rm_rate[0][0] if rm_rate else 0 - # update valuation rate of item '_Test Item 2' - warehouse_list = frappe.db.sql_list("""select warehouse from `tabBin` - where item_code='_Test Item 2' and actual_qty > 0""") - - if not warehouse_list: - warehouse_list.append("_Test Warehouse - _TC") - - for warehouse in warehouse_list: - create_stock_reconciliation(item_code="_Test Item 2", warehouse=warehouse, - qty=200, rate=rm_rate + 10) + # Reset item valuation rate + reset_item_valuation_rate(item_code='_Test Item 2', qty=200, rate=rm_rate + 10) # update cost of all BOMs based on latest valuation rate update_cost() @@ -96,7 +89,7 @@ class TestBOM(unittest.TestCase): self.assertEqual(bom.base_raw_material_cost, 480000) self.assertEqual(bom.base_total_cost, 486000) - def test_bom_cost_multi_uom_multi_currency(self): + def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self): frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependant", 1) for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)): frappe.db.sql("delete from `tabItem Price` where price_list='_Test Price List' and item_code=%s", @@ -131,5 +124,35 @@ class TestBOM(unittest.TestCase): self.assertEqual(bom.base_raw_material_cost, 27000) self.assertEqual(bom.base_total_cost, 33000) + def test_bom_cost_multi_uom_based_on_valuation_rate(self): + bom = frappe.copy_doc(test_records[2]) + bom.set_rate_of_sub_assembly_item_based_on_bom = 0 + bom.rm_cost_as_per = "Valuation Rate" + bom.items[0].uom = "_Test UOM 1" + bom.items[0].conversion_factor = 6 + bom.insert() + + reset_item_valuation_rate(item_code='_Test Item', qty=200, rate=200) + + bom.update_cost() + + self.assertEqual(bom.items[0].rate, 20) + def get_default_bom(item_code="_Test FG Item 2"): return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1}) + +def reset_item_valuation_rate(item_code, warehouse_list=None, qty=None, rate=None): + if warehouse_list and isinstance(warehouse_list, string_types): + warehouse_list = [warehouse_list] + + if not warehouse_list: + warehouse_list = frappe.db.sql_list(""" + select warehouse from `tabBin` + where item_code=%s and actual_qty > 0 + """, item_code) + + if not warehouse_list: + warehouse_list.append("_Test Warehouse - _TC") + + for warehouse in warehouse_list: + create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=qty, rate=rate) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index cb2c62e7b0a..fd11a0bfb5a 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -390,7 +390,7 @@ def get_price_list_rate(args, item_doc, out): pl_details = get_price_list_currency_and_exchange_rate(args) args.update(pl_details) validate_price_list(args) - if meta.get_field("currency") and args.price_list: + if meta.get_field("currency"): validate_conversion_rate(args, meta) price_list_rate = get_price_list_rate_for(args, item_doc.name) or 0 @@ -567,21 +567,22 @@ def validate_conversion_rate(args, meta): get_field_precision(meta.get_field("conversion_rate"), frappe._dict({"fields": args}))) - if (not args.plc_conversion_rate - and args.price_list_currency==frappe.db.get_value("Price List", args.price_list, "currency", cache=True)): - args.plc_conversion_rate = 1.0 + if args.price_list: + if (not args.plc_conversion_rate + and args.price_list_currency==frappe.db.get_value("Price List", args.price_list, "currency", cache=True)): + args.plc_conversion_rate = 1.0 - # validate price list currency conversion rate - if not args.get("price_list_currency"): - throw(_("Price List Currency not selected")) - else: - validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate, - meta.get_label("plc_conversion_rate"), args.company) + # validate price list currency conversion rate + if not args.get("price_list_currency"): + throw(_("Price List Currency not selected")) + else: + validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate, + meta.get_label("plc_conversion_rate"), args.company) - if meta.get_field("plc_conversion_rate"): - args.plc_conversion_rate = flt(args.plc_conversion_rate, - get_field_precision(meta.get_field("plc_conversion_rate"), - frappe._dict({"fields": args}))) + if meta.get_field("plc_conversion_rate"): + args.plc_conversion_rate = flt(args.plc_conversion_rate, + get_field_precision(meta.get_field("plc_conversion_rate"), + frappe._dict({"fields": args}))) def get_party_item_code(args, item_doc, out): if args.transaction_type=="selling" and args.customer: From 19901c14c929c76859bb59068dc5a527594b5eca Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 17 May 2019 14:30:45 +0530 Subject: [PATCH 4/8] fix: Removed unused variable --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 7d92bdda49b..9a490a7edb0 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -128,7 +128,7 @@ class StockController(AccountsController): reconciliation_purpose = frappe.db.get_value(self.doctype, self.name, "purpose") is_opening = "Yes" if reconciliation_purpose == "Opening Stock" else "No" details = [] - for voucher_detail_no, sle in sle_map.items(): + for voucher_detail_no in sle_map: details.append(frappe._dict({ "name": voucher_detail_no, "expense_account": default_expense_account, From 09536f402e46dc023126726325c945628ad016b3 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 20 May 2019 17:01:22 +0530 Subject: [PATCH 5/8] fix: payment reconciliation not update the status of the expense claim --- erpnext/accounts/utils.py | 3 +++ .../expense_claim/expense_claim_dashboard.py | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index e9ae6ee21ac..120bfe2489d 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -333,6 +333,9 @@ def reconcile_against_document(args): doc = frappe.get_doc(d.voucher_type, d.voucher_no) doc.make_gl_entries(cancel = 0, adv_adj =1) + if d.voucher_type == 'Payment Entry': + doc.update_expense_claim() + def check_if_advance_entry_modified(args): """ check if there is already a voucher reference diff --git a/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py b/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py new file mode 100644 index 00000000000..d27245f944e --- /dev/null +++ b/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'reference_name', + 'transactions': [ + { + 'label': _('Payment'), + 'items': ['Payment Entry'] + }, + ] + } \ No newline at end of file From 3a949bb298f65636734db4eb3b05e06925faa777 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 21 May 2019 01:46:55 +0530 Subject: [PATCH 6/8] fix: item group not disaplying in the website if shopping cart is disabled --- erpnext/setup/doctype/item_group/item_group.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 7118a1517c7..4107f854b38 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -70,7 +70,7 @@ class ItemGroup(NestedSet, WebsiteGenerator): limit=context.page_length + 1, search=frappe.form_dict.get("search")), "parents": get_parent_item_groups(self.parent_item_group), "title": self.name, - "products_as_list": cint(frappe.db.get_single_value('Website Settings', 'products_as_list')) + "products_as_list": cint(frappe.db.get_single_value('Products Settings', 'products_as_list')) }) if self.slideshow: @@ -114,8 +114,9 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non data = frappe.db.sql(query, {"product_group": product_group,"search": search, "today": nowdate()}, as_dict=1) data = adjust_qty_for_expired_items(data) - for item in data: - set_product_info_for_website(item) + if cint(frappe.db.get_single_value("Shopping Cart Settings", "enabled")): + for item in data: + set_product_info_for_website(item) return [get_item_for_list_in_html(r) for r in data] From e802bdd1869b804229bbcdb0bb4402637edb3f29 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 21 May 2019 10:33:39 +0530 Subject: [PATCH 7/8] fix: while making the item, default warehouse not set even if the stock settings has the warehouse --- erpnext/stock/doctype/item/item.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 8f93c3f7ff2..16fd811fee3 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -691,7 +691,18 @@ class Item(WebsiteGenerator): 'income_account': item.income_account }) else: - self.append("item_defaults", {"company": frappe.defaults.get_defaults().company}) + warehouse = '' + defaults = frappe.defaults.get_defaults() or {} + + # To check default warehouse is belong to the default company + if defaults.get("default_warehouse") and frappe.db.exists("Warehouse", + {'name': defaults.default_warehouse, 'company': defaults.company}): + warehouse = defaults.default_warehouse + + self.append("item_defaults", { + "company": defaults.get("company"), + "default_warehouse": warehouse + }) def update_variants(self): if self.flags.dont_update_variants or \ From 58c4cfc0d74766917522ce81836337b2f5ff6ef1 Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Tue, 21 May 2019 14:31:08 +0550 Subject: [PATCH 8/8] bumped to version 11.1.31 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index f1e06ef8227..03186ff8e18 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__ = '11.1.30' +__version__ = '11.1.31' def get_default_company(user=None): '''Get default company for user'''