From 71775790518bad69cb31ae0a4edf4ec482692068 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 2 Feb 2021 22:03:07 +0530 Subject: [PATCH] fix: Dependant sle logic fixes (#24489) * fix: Dependant sle logic fixes * fix: negative qty validation * fix: Travis fixes --- .../doctype/work_order/test_work_order.py | 4 +- .../united_states/test_united_states.py | 1 - .../setup/doctype/item_group/item_group.py | 2 - erpnext/shopping_cart/filters.py | 2 - erpnext/stock/doctype/bin/bin.py | 6 +-- .../repost_item_valuation.js | 2 +- erpnext/stock/stock_ledger.py | 48 +++++++------------ .../report/issue_analytics/issue_analytics.py | 3 +- .../issue_analytics/test_issue_analytics.py | 9 ++-- erpnext/www/all-products/index.py | 3 -- 10 files changed, 29 insertions(+), 51 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index e562d6c4bd..06a8e1987d 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -544,7 +544,7 @@ class TestWorkOrder(unittest.TestCase): expected_qty = {"_Test Item": 2, "_Test Item Home Desktop 100": 4} for row in ste3.items: self.assertEquals(row.qty, expected_qty.get(row.item_code)) - + ste_cancel_list.reverse() for ste_doc in ste_cancel_list: ste_doc.cancel() @@ -586,7 +586,7 @@ class TestWorkOrder(unittest.TestCase): for ste_row in ste2.items: if itemwise_qty.get(ste_row.item_code) and ste_row.s_warehouse: self.assertEquals(ste_row.qty, itemwise_qty.get(ste_row.item_code) / 2) - + ste_cancel_list.reverse() for ste_doc in ste_cancel_list: ste_doc.cancel() diff --git a/erpnext/regional/united_states/test_united_states.py b/erpnext/regional/united_states/test_united_states.py index ad95010a9a..513570ed6d 100644 --- a/erpnext/regional/united_states/test_united_states.py +++ b/erpnext/regional/united_states/test_united_states.py @@ -26,7 +26,6 @@ class TestUnitedStates(unittest.TestCase): make_payment_entry_to_irs_1099_supplier() filters = frappe._dict({"fiscal_year": "_Test Fiscal Year 2016", "company": "_Test Company 1"}) columns, data = execute_1099_report(filters) - print(columns, data) expected_row = {'supplier': '_US 1099 Test Supplier', 'supplier_group': 'Services', 'payments': 100.0, diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index e4cbf41f83..bff806d547 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -98,8 +98,6 @@ class ItemGroup(NestedSet, WebsiteGenerator): context.field_filters = filter_engine.get_field_filters() context.attribute_filters = filter_engine.get_attribute_fitlers() - print(context.field_filters, context.attribute_filters) - context.update({ "parents": get_parent_item_groups(self.parent_item_group), "title": self.name diff --git a/erpnext/shopping_cart/filters.py b/erpnext/shopping_cart/filters.py index e60364d92b..6c63d8759b 100644 --- a/erpnext/shopping_cart/filters.py +++ b/erpnext/shopping_cart/filters.py @@ -16,7 +16,6 @@ class ProductFiltersBuilder: def get_field_filters(self): filter_fields = [row.fieldname for row in self.doc.filter_fields] - print('FILTERS', self.doc.filter_fields) meta = frappe.get_meta('Item') fields = [df for df in meta.fields if df.fieldname in filter_fields] @@ -53,7 +52,6 @@ class ProductFiltersBuilder: def get_attribute_fitlers(self): attributes = [row.attribute for row in self.doc.filter_attributes] - print('ATTRIBUTES', attributes) attribute_docs = [ frappe.get_doc('Item Attribute', attribute) for attribute in attributes ] diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index ab19b77ad8..1088b4127d 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -17,7 +17,7 @@ class Bin(Document): '''Called from erpnext.stock.utils.update_bin''' self.update_qty(args) if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation": - from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle + from erpnext.stock.stock_ledger import update_entries_after, validate_negative_qty_in_future_sle if not args.get("posting_date"): args["posting_date"] = nowdate() @@ -37,8 +37,8 @@ class Bin(Document): "sle_id": args.name }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher) - # Update qty_after_transaction in future SLEs of this item and warehouse - update_qty_in_future_sle(args) + # Validate negative qty in future transactions + validate_negative_qty_in_future_sle(args) def update_qty(self, args): # update the stock values (for current quantities) diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js index e429cd5e30..b3e4286bcc 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js @@ -31,7 +31,7 @@ frappe.ui.form.on('Repost Item Valuation', { } }, refresh: function(frm) { - if (frm.doc.status == "Failed") { + if (frm.doc.status == "Failed" && frm.doc.docstatus==1) { frm.add_custom_button(__('Restart'), function () { frm.trigger("restart_reposting"); }).addClass("btn-primary"); diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 2b2a7a202d..46919c8c8c 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -62,7 +62,7 @@ def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False): sle.submit() return sle -def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=False, via_landed_cost_voucher=False): +def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=None, via_landed_cost_voucher=False): if not args and voucher_type and voucher_no: args = get_args_for_voucher(voucher_type, voucher_no) @@ -181,7 +181,7 @@ class update_entries_after(object): self.process_sle(sle) if sle.dependant_sle_voucher_detail_no: - self.get_dependent_entries_to_fix(entries_to_fix, sle) + entries_to_fix = self.get_dependent_entries_to_fix(entries_to_fix, sle) if self.exceptions: self.raise_exceptions() @@ -221,13 +221,15 @@ class update_entries_after(object): excluded_sle=sle.name) if not dependant_sle: - return + return entries_to_fix elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse: - return - elif dependant_sle.item_code != self.item_code \ - and (dependant_sle.item_code, dependant_sle.warehouse) not in self.new_items: - self.new_items[(dependant_sle.item_code, dependant_sle.warehouse)] = dependant_sle - return + return entries_to_fix + elif dependant_sle.item_code != self.item_code: + if (dependant_sle.item_code, dependant_sle.warehouse) not in self.new_items: + self.new_items[(dependant_sle.item_code, dependant_sle.warehouse)] = dependant_sle + return entries_to_fix + elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse in self.data: + return entries_to_fix self.initialize_previous_data(dependant_sle) @@ -236,7 +238,7 @@ class update_entries_after(object): future_sle_for_dependant = list(self.get_sle_after_datetime(args)) entries_to_fix.extend(future_sle_for_dependant) - entries_to_fix = sorted(entries_to_fix, key=lambda k: k['timestamp']) + return sorted(entries_to_fix, key=lambda k: k['timestamp']) def process_sle(self, sle): # previous sle data for this warehouse @@ -612,11 +614,11 @@ class update_entries_after(object): frappe.local.flags.currently_saving): msg = _("{0} units of {1} needed in {2} to complete this transaction.").format( - abs(deficiency), frappe.get_desk_link('Item', self.item_code), + abs(deficiency), frappe.get_desk_link('Item', exceptions[0]["item_code"]), frappe.get_desk_link('Warehouse', warehouse)) else: msg = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format( - abs(deficiency), frappe.get_desk_link('Item', self.item_code), + abs(deficiency), frappe.get_desk_link('Item', exceptions[0]["item_code"]), frappe.get_desk_link('Warehouse', warehouse), exceptions[0]["posting_date"], exceptions[0]["posting_time"], frappe.get_desk_link(exceptions[0]["voucher_type"], exceptions[0]["voucher_no"])) @@ -761,25 +763,6 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, return valuation_rate -def update_qty_in_future_sle(args, allow_negative_stock=None): - frappe.db.sql(""" - update `tabStock Ledger Entry` - set qty_after_transaction = qty_after_transaction + {qty} - where - item_code = %(item_code)s - and warehouse = %(warehouse)s - and voucher_no != %(voucher_no)s - and is_cancelled = 0 - and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s) - or ( - timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s) - and creation > %(creation)s - ) - ) - """.format(qty=args.actual_qty), args) - - validate_negative_qty_in_future_sle(args, allow_negative_stock) - def validate_negative_qty_in_future_sle(args, allow_negative_stock=None): allow_negative_stock = allow_negative_stock \ or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) @@ -808,6 +791,7 @@ def get_future_sle_with_negative_qty(args): and voucher_no != %(voucher_no)s and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s) and is_cancelled = 0 - and qty_after_transaction < 0 + and qty_after_transaction + {0} < 0 + order by timestamp(posting_date, posting_time) asc limit 1 - """, args, as_dict=1) \ No newline at end of file + """.format(args.actual_qty), args, as_dict=1) \ No newline at end of file diff --git a/erpnext/support/report/issue_analytics/issue_analytics.py b/erpnext/support/report/issue_analytics/issue_analytics.py index 0b629151a6..3fdb10ddf3 100644 --- a/erpnext/support/report/issue_analytics/issue_analytics.py +++ b/erpnext/support/report/issue_analytics/issue_analytics.py @@ -147,8 +147,7 @@ class IssueAnalytics(object): self.entries = frappe.db.get_all('Issue', fields=[self.field_map.get(self.filters.based_on), 'name', 'opening_date'], - filters=filters, - debug=1 + filters=filters ) def get_common_filters(self): diff --git a/erpnext/support/report/issue_analytics/test_issue_analytics.py b/erpnext/support/report/issue_analytics/test_issue_analytics.py index 432906db9b..77483198ec 100644 --- a/erpnext/support/report/issue_analytics/test_issue_analytics.py +++ b/erpnext/support/report/issue_analytics/test_issue_analytics.py @@ -17,9 +17,12 @@ class TestIssueAnalytics(unittest.TestCase): current_month_date = getdate() last_month_date = add_months(current_month_date, -1) - self.current_month = str(months[current_month_date.month - 1]).lower() + '_' + str(current_month_date.year) - self.last_month = str(months[last_month_date.month - 1]).lower() + '_' + str(last_month_date.year) - + self.current_month = str(months[current_month_date.month - 1]).lower() + self.last_month = str(months[last_month_date.month - 1]).lower() + if current_month_date.year != last_month_date.year: + self.current_month += '_' + str(current_month_date.year) + self.last_month += '_' + str(last_month_date.year) + def test_issue_analytics(self): create_service_level_agreements_for_issues() create_issue_types() diff --git a/erpnext/www/all-products/index.py b/erpnext/www/all-products/index.py index 0622270e05..fd6400f48c 100644 --- a/erpnext/www/all-products/index.py +++ b/erpnext/www/all-products/index.py @@ -12,7 +12,6 @@ def get_context(context): search = frappe.form_dict.search field_filters = frappe.parse_json(frappe.form_dict.field_filters) attribute_filters = frappe.parse_json(frappe.form_dict.attribute_filters) - print(field_filters, attribute_filters) start = frappe.parse_json(frappe.form_dict.start) else: search = field_filters = attribute_filters = None @@ -30,8 +29,6 @@ def get_context(context): context.field_filters = filter_engine.get_field_filters() context.attribute_filters = filter_engine.get_attribute_fitlers() - print(context.field_filters, context.attribute_filters) - context.product_settings = product_settings context.body_class = "product-page" context.page_length = product_settings.products_per_page or 20