From a787ebb7325294eb95cca1c91b6257bd9cdab88f Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 23 Mar 2022 14:59:27 +0530 Subject: [PATCH 01/11] fix: Dont set `idx` while adding WO items to Stock Entry - `idx` must be computed by base document's `self.append()` function, so do not set it --- erpnext/stock/doctype/stock_entry/stock_entry.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 99cf4de5de..26d59fdab2 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1603,8 +1603,11 @@ class StockEntry(StockController): se_child.is_scrap_item = item_row.get("is_scrap_item", 0) se_child.is_process_loss = item_row.get("is_process_loss", 0) - for field in ["idx", "po_detail", "original_item", "expense_account", - "description", "item_name", "serial_no", "batch_no", "allow_zero_valuation_rate"]: + for field in [ + "po_detail", "original_item", "expense_account", + "description", "item_name", "serial_no", + "batch_no", "allow_zero_valuation_rate" + ]: if item_row.get(field): se_child.set(field, item_row.get(field)) From 639d380c1f333ba2162aacd62511d2593db156bc Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 28 Mar 2022 21:00:00 +0530 Subject: [PATCH 02/11] chore: Remove redundant idx query and value setting - idx can be removed from `select_columns` as it is already in the main query - setting idx to '' is not required as it is not used further --- erpnext/manufacturing/doctype/bom/bom.py | 4 ++-- erpnext/stock/doctype/stock_entry/stock_entry.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index bf29474c00..9b6cf46c11 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -1025,7 +1025,7 @@ def get_bom_items_as_dict( query = query.format( table="BOM Scrap Item", where_conditions="", - select_columns=", bom_item.idx, item.description, is_process_loss", + select_columns=", item.description, is_process_loss", is_stock_item=is_stock_item, qty_field="stock_qty", ) @@ -1038,7 +1038,7 @@ def get_bom_items_as_dict( is_stock_item=is_stock_item, qty_field="stock_qty" if fetch_qty_in_stock_uom else "qty", select_columns=""", bom_item.uom, bom_item.conversion_factor, bom_item.source_warehouse, - bom_item.idx, bom_item.operation, bom_item.include_item_in_manufacturing, bom_item.sourced_by_supplier, + bom_item.operation, bom_item.include_item_in_manufacturing, bom_item.sourced_by_supplier, bom_item.description, bom_item.base_rate as rate """, ) items = frappe.db.sql(query, {"qty": qty, "bom": bom, "company": company}, as_dict=True) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index eaf273051e..2545956b6b 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1382,7 +1382,6 @@ class StockEntry(StockController): if self.purpose != "Send to Subcontractor" and self.purpose in ["Manufacture", "Repack"]: scrap_item_dict = self.get_bom_scrap_material(self.fg_completed_qty) for item in scrap_item_dict.values(): - item.idx = "" if self.pro_doc and self.pro_doc.scrap_warehouse: item["to_warehouse"] = self.pro_doc.scrap_warehouse From c122882884d8a4d2f504eb53bef9003333bd393c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 29 Mar 2022 13:36:00 +0530 Subject: [PATCH 03/11] fix: credit limit validation in delivery note --- erpnext/stock/doctype/delivery_note/delivery_note.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 69e052bb81..0e68e85806 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -280,8 +280,11 @@ class DeliveryNote(SellingController): ) if bypass_credit_limit_check_at_sales_order: - validate_against_credit_limit = True - extra_amount = self.base_grand_total + for d in self.get("items"): + if not d.against_sales_invoice: + validate_against_credit_limit = True + extra_amount = self.base_grand_total + break else: for d in self.get("items"): if not (d.against_sales_order or d.against_sales_invoice): From fa3b953cf7ee9d4c5fa8330bb56499efa9339113 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 29 Mar 2022 13:54:14 +0530 Subject: [PATCH 04/11] test: idx mapping correctness --- .../doctype/work_order/test_work_order.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 3721704840..9209cbf755 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -1114,6 +1114,32 @@ class TestWorkOrder(FrappeTestCase): except frappe.MandatoryError: self.fail("Batch generation causing failing in Work Order") + @change_settings("Manufacturing Settings", {"backflush_raw_materials_based_on": "Material Transferred for Manufacture"}) + def test_manufacture_entry_mapped_idx_with_exploded_bom(self): + """Test if WO containing BOM with partial exploded items and scrap items, maps idx correctly.""" + test_stock_entry.make_stock_entry( + item_code="_Test Item", + target="_Test Warehouse - _TC", + basic_rate=5000.0, + qty=2, + ) + test_stock_entry.make_stock_entry( + item_code="_Test Item Home Desktop 100", + target="_Test Warehouse - _TC", + basic_rate=1000.0, + qty=2, + ) + + wo_order = make_wo_order_test_record( + qty=1, + use_multi_level_bom=1, + skip_transfer=1, + ) + + ste_manu = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 1)) + + for index, row in enumerate(ste_manu.get("items"), start=1): + self.assertEqual(index, row.idx) def update_job_card(job_card, jc_qty=None): employee = frappe.db.get_value("Employee", {"status": "Active"}, "name") From b12fe0f15bcc5d71c16d5bebc4d494518b6220b4 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 29 Mar 2022 13:54:26 +0530 Subject: [PATCH 05/11] fix: dont check for failed repost while freezing (#30472) [skip ci] --- erpnext/stock/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 741646dfff..4f1891fd75 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -526,7 +526,7 @@ def check_pending_reposting(posting_date: str, throw_error: bool = True) -> bool filters = { "docstatus": 1, - "status": ["in", ["Queued", "In Progress", "Failed"]], + "status": ["in", ["Queued", "In Progress"]], "posting_date": ["<=", posting_date], } From b80fac03afe99d3c0b9c0f09d6eb34573c5692cf Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 29 Mar 2022 17:19:22 +0530 Subject: [PATCH 06/11] fix: validate 0 transfer qty in stock entry (#30476) --- .../stock/doctype/stock_entry/stock_entry.py | 6 ++- .../doctype/stock_entry/test_stock_entry.py | 48 ++++--------------- 2 files changed, 13 insertions(+), 41 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index bc54f7f84e..c0a8e14dbf 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -225,12 +225,16 @@ class StockEntry(StockController): def set_transfer_qty(self): for item in self.get("items"): if not flt(item.qty): - frappe.throw(_("Row {0}: Qty is mandatory").format(item.idx)) + frappe.throw(_("Row {0}: Qty is mandatory").format(item.idx), title=_("Zero quantity")) if not flt(item.conversion_factor): frappe.throw(_("Row {0}: UOM Conversion Factor is mandatory").format(item.idx)) item.transfer_qty = flt( flt(item.qty) * flt(item.conversion_factor), self.precision("transfer_qty", item) ) + if not flt(item.transfer_qty): + frappe.throw( + _("Row {0}: Qty in Stock UOM can not be zero.").format(item.idx), title=_("Zero quantity") + ) def update_cost_in_project(self): if self.work_order and not frappe.db.get_value( diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index aeedcd1847..3ccd3420e3 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -51,7 +51,6 @@ class TestStockEntry(FrappeTestCase): def tearDown(self): frappe.db.rollback() frappe.set_user("Administrator") - frappe.db.set_value("Manufacturing Settings", None, "material_consumption", "0") def test_fifo(self): frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) @@ -767,13 +766,12 @@ class TestStockEntry(FrappeTestCase): fg_cost, flt(rm_cost + bom_operation_cost + work_order.additional_operating_cost, 2) ) + @change_settings("Manufacturing Settings", {"material_consumption": 1}) def test_work_order_manufacture_with_material_consumption(self): from erpnext.manufacturing.doctype.work_order.work_order import ( make_stock_entry as _make_stock_entry, ) - frappe.db.set_value("Manufacturing Settings", None, "material_consumption", "1") - bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item", "is_default": 1, "docstatus": 1}) work_order = frappe.new_doc("Work Order") @@ -983,43 +981,6 @@ class TestStockEntry(FrappeTestCase): repack.insert() self.assertRaises(frappe.ValidationError, repack.submit) - # def test_material_consumption(self): - # frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on", "BOM") - # frappe.db.set_value("Manufacturing Settings", None, "material_consumption", "0") - - # from erpnext.manufacturing.doctype.work_order.work_order \ - # import make_stock_entry as _make_stock_entry - # bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2", - # "is_default": 1, "docstatus": 1}) - - # work_order = frappe.new_doc("Work Order") - # work_order.update({ - # "company": "_Test Company", - # "fg_warehouse": "_Test Warehouse 1 - _TC", - # "production_item": "_Test FG Item 2", - # "bom_no": bom_no, - # "qty": 4.0, - # "stock_uom": "_Test UOM", - # "wip_warehouse": "_Test Warehouse - _TC", - # "additional_operating_cost": 1000, - # "use_multi_level_bom": 1 - # }) - # work_order.insert() - # work_order.submit() - - # make_stock_entry(item_code="_Test Serialized Item With Series", target="_Test Warehouse - _TC", qty=50, basic_rate=100) - # make_stock_entry(item_code="_Test Item 2", target="_Test Warehouse - _TC", qty=50, basic_rate=20) - - # item_quantity = { - # '_Test Item': 2.0, - # '_Test Item 2': 12.0, - # '_Test Serialized Item With Series': 6.0 - # } - - # stock_entry = frappe.get_doc(_make_stock_entry(work_order.name, "Material Consumption for Manufacture", 2)) - # for d in stock_entry.get('items'): - # self.assertEqual(item_quantity.get(d.item_code), d.qty) - def test_customer_provided_parts_se(self): create_item( "CUST-0987", is_customer_provided_item=1, customer="_Test Customer", is_purchase_item=0 @@ -1358,6 +1319,13 @@ class TestStockEntry(FrappeTestCase): issue.reload() # reload because reposting current voucher updates rate self.assertEqual(issue.value_difference, -30) + def test_transfer_qty_validation(self): + se = make_stock_entry(item_code="_Test Item", do_not_save=True, qty=0.001, rate=100) + se.items[0].uom = "Kg" + se.items[0].conversion_factor = 0.002 + + self.assertRaises(frappe.ValidationError, se.save) + def make_serialized_item(**args): args = frappe._dict(args) From b5ad626d23fc4246e5665b3df78d9eca181b0d4f Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 30 Mar 2022 10:22:05 +0530 Subject: [PATCH 07/11] fix: Linter --- erpnext/manufacturing/doctype/work_order/test_work_order.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 9209cbf755..8934f9c4e0 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -1114,7 +1114,10 @@ class TestWorkOrder(FrappeTestCase): except frappe.MandatoryError: self.fail("Batch generation causing failing in Work Order") - @change_settings("Manufacturing Settings", {"backflush_raw_materials_based_on": "Material Transferred for Manufacture"}) + @change_settings( + "Manufacturing Settings", + {"backflush_raw_materials_based_on": "Material Transferred for Manufacture"}, + ) def test_manufacture_entry_mapped_idx_with_exploded_bom(self): """Test if WO containing BOM with partial exploded items and scrap items, maps idx correctly.""" test_stock_entry.make_stock_entry( @@ -1141,6 +1144,7 @@ class TestWorkOrder(FrappeTestCase): for index, row in enumerate(ste_manu.get("items"), start=1): self.assertEqual(index, row.idx) + def update_job_card(job_card, jc_qty=None): employee = frappe.db.get_value("Employee", {"status": "Active"}, "name") job_card_doc = frappe.get_doc("Job Card", job_card) From fe962547301d617cd082e3fcd1c6013f5c6dcd20 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 30 Mar 2022 12:34:36 +0530 Subject: [PATCH 08/11] chore: skip style checks in sider [skip ci] --- .flake8 | 1 + 1 file changed, 1 insertion(+) diff --git a/.flake8 b/.flake8 index 4ff8840324..4b852abd7c 100644 --- a/.flake8 +++ b/.flake8 @@ -31,6 +31,7 @@ ignore = E124, # closing bracket, irritating while writing QB code E131, # continuation line unaligned for hanging indent E123, # closing bracket does not match indentation of opening bracket's line + E101, # ensured by use of black max-line-length = 200 exclude=.github/helper/semgrep_rules From 62c57199f2894cfe7ecd93c8d2ffffafa2bb7e8b Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 30 Mar 2022 12:50:37 +0530 Subject: [PATCH 09/11] chore: remove ui test config [skip ci] --- .github/workflows/ui-tests.yml | 117 --------------------------------- 1 file changed, 117 deletions(-) delete mode 100644 .github/workflows/ui-tests.yml diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml deleted file mode 100644 index ab6a53b5d9..0000000000 --- a/.github/workflows/ui-tests.yml +++ /dev/null @@ -1,117 +0,0 @@ -name: UI - -on: - pull_request: - paths-ignore: - - '**.md' - workflow_dispatch: - -concurrency: - group: ui-develop-${{ github.event.number }} - cancel-in-progress: true - -jobs: - test: - runs-on: ubuntu-latest - timeout-minutes: 60 - - strategy: - fail-fast: false - - name: UI Tests (Cypress) - - services: - mysql: - image: mariadb:10.3 - env: - MYSQL_ALLOW_EMPTY_PASSWORD: YES - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 - - steps: - - name: Clone - uses: actions/checkout@v2 - - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - - uses: actions/setup-node@v2 - with: - node-version: 14 - check-latest: true - - - name: Add to Hosts - run: | - echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts - - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - ${{ runner.os }}- - - - name: Cache node modules - uses: actions/cache@v2 - env: - cache-name: cache-node-modules - with: - path: ~/.npm - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - - uses: actions/cache@v2 - id: yarn-cache - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: Cache cypress binary - uses: actions/cache@v2 - with: - path: ~/.cache - key: ${{ runner.os }}-cypress- - restore-keys: | - ${{ runner.os }}-cypress- - ${{ runner.os }}- - - - name: Install - run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh - env: - DB: mariadb - TYPE: ui - - - name: Site Setup - run: cd ~/frappe-bench/ && bench --site test_site execute erpnext.setup.utils.before_tests - - - name: cypress pre-requisites - run: cd ~/frappe-bench/apps/frappe && yarn add cypress-file-upload@^5 @testing-library/cypress@^8 --no-lockfile - - - - name: Build Assets - run: cd ~/frappe-bench/ && bench build - env: - CI: Yes - - - name: UI Tests - run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests erpnext --headless - env: - CYPRESS_RECORD_KEY: 60a8e3bf-08f5-45b1-9269-2b207d7d30cd - - - name: Show bench console if tests failed - if: ${{ failure() }} - run: cat ~/frappe-bench/bench_run_logs.txt From 8ab226a2fc9056491b9abf59b44be46414bd7a89 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 30 Mar 2022 13:20:54 +0530 Subject: [PATCH 10/11] test: add class to uniquely id item dashboard (#30487) --- erpnext/stock/dashboard/item_dashboard.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/dashboard/item_dashboard.html b/erpnext/stock/dashboard/item_dashboard.html index 99698ba69a..b7a786e50f 100644 --- a/erpnext/stock/dashboard/item_dashboard.html +++ b/erpnext/stock/dashboard/item_dashboard.html @@ -1,4 +1,4 @@ -
+