diff --git a/.flake8 b/.flake8 index e783fbb..5400295 100644 --- a/.flake8 +++ b/.flake8 @@ -64,6 +64,7 @@ ignore = W391, W503, W504, + W604, E711, E129, F841, diff --git a/inventory_tools/inventory_tools/custom/operation.json b/inventory_tools/inventory_tools/custom/operation.json index f48d696..6c81292 100644 --- a/inventory_tools/inventory_tools/custom/operation.json +++ b/inventory_tools/inventory_tools/custom/operation.json @@ -44,7 +44,7 @@ "name": "Operation-alternative_workstations", "no_copy": 0, "non_negative": 0, - "options": "Alternative Workstations", + "options": "Alternative Workstation", "owner": "Administrator", "permlevel": 0, "precision": "", diff --git a/inventory_tools/inventory_tools/doctype/alternative_workstations/alternative_workstations.json b/inventory_tools/inventory_tools/doctype/alternative_workstation/alternative_workstation.json similarity index 94% rename from inventory_tools/inventory_tools/doctype/alternative_workstations/alternative_workstations.json rename to inventory_tools/inventory_tools/doctype/alternative_workstation/alternative_workstation.json index 470af37..a1ef665 100644 --- a/inventory_tools/inventory_tools/doctype/alternative_workstations/alternative_workstations.json +++ b/inventory_tools/inventory_tools/doctype/alternative_workstation/alternative_workstation.json @@ -23,7 +23,7 @@ "modified": "2024-02-01 07:04:30.059469", "modified_by": "Administrator", "module": "Inventory Tools", - "name": "Alternative Workstations", + "name": "Alternative Workstation", "owner": "Administrator", "permissions": [], "sort_field": "modified", diff --git a/inventory_tools/inventory_tools/doctype/alternative_workstations/alternative_workstations.py b/inventory_tools/inventory_tools/doctype/alternative_workstation/alternative_workstation.py similarity index 72% rename from inventory_tools/inventory_tools/doctype/alternative_workstations/alternative_workstations.py rename to inventory_tools/inventory_tools/doctype/alternative_workstation/alternative_workstation.py index c262a29..581d755 100644 --- a/inventory_tools/inventory_tools/doctype/alternative_workstations/alternative_workstations.py +++ b/inventory_tools/inventory_tools/doctype/alternative_workstation/alternative_workstation.py @@ -1,9 +1,9 @@ # Copyright (c) 2024, AgriTheory and contributors # For license information, please see license.txt -# import frappe +import frappe from frappe.model.document import Document -class AlternativeWorkstations(Document): +class AlternativeWorkstation(Document): pass diff --git a/inventory_tools/inventory_tools/doctype/inventory_tools_settings/inventory_tools_settings.json b/inventory_tools/inventory_tools/doctype/inventory_tools_settings/inventory_tools_settings.json index aa7da3a..9a9a449 100644 --- a/inventory_tools/inventory_tools/doctype/inventory_tools_settings/inventory_tools_settings.json +++ b/inventory_tools/inventory_tools/doctype/inventory_tools_settings/inventory_tools_settings.json @@ -25,7 +25,8 @@ "overproduction_percentage_for_work_order", "section_break_0", "update_warehouse_path", - "section_break_gzcbr", + "column_break_ddssn", + "allow_alternative_workstations", "uoms_section", "enforce_uoms" ], @@ -79,7 +80,8 @@ }, { "fieldname": "section_break_0", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Warehouses and Workstations" }, { "default": "0", @@ -87,11 +89,6 @@ "fieldtype": "Check", "label": "Update Warehouse Path" }, - { - "fieldname": "section_break_gzcbr", - "fieldtype": "Section Break", - "label": "Warehouses" - }, { "fieldname": "uoms_section", "fieldtype": "Section Break", @@ -146,11 +143,21 @@ "fieldtype": "Link", "label": "Aggregated Sales Warehouse", "options": "Warehouse" + }, + { + "fieldname": "column_break_ddssn", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "allow_alternative_workstations", + "fieldtype": "Check", + "label": "Allow Alternative Workstations" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-04-12 10:02:32.688310", + "modified": "2024-05-13 13:19:00.665445", "modified_by": "Administrator", "module": "Inventory Tools", "name": "Inventory Tools Settings", diff --git a/inventory_tools/inventory_tools/overrides/operation.py b/inventory_tools/inventory_tools/overrides/operation.py index d649d48..6d5c393 100644 --- a/inventory_tools/inventory_tools/overrides/operation.py +++ b/inventory_tools/inventory_tools/overrides/operation.py @@ -1,8 +1,8 @@ import frappe -def validate_alternative_workstation(self, method): +def validate_alternative_workstation(self, method=None): if self.workstation: for row in self.alternative_workstations: if row.workstation == self.workstation: - frappe.throw("Default Workstation should not be selected as alternative workstation") + frappe.throw(frappe._("Default Workstation should not be selected as alternative workstation")) diff --git a/inventory_tools/inventory_tools/overrides/purchase_invoice.py b/inventory_tools/inventory_tools/overrides/purchase_invoice.py index 87b8c15..200e89f 100644 --- a/inventory_tools/inventory_tools/overrides/purchase_invoice.py +++ b/inventory_tools/inventory_tools/overrides/purchase_invoice.py @@ -13,7 +13,7 @@ class InventoryToolsPurchaseInvoice(PurchaseInvoice): def validate_with_previous_doc(self): """ - HASH: 82d206b709ada758c277bc5a266dbc6ec10d00c8 + HASH: 014486de39dd7da6fe79bf803adcf1b66d890876 REPO: https://github.com/frappe/erpnext/ PATH: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py METHOD: validate_with_previous_doc diff --git a/inventory_tools/inventory_tools/overrides/stock_entry.py b/inventory_tools/inventory_tools/overrides/stock_entry.py index 7f5919d..e08791f 100644 --- a/inventory_tools/inventory_tools/overrides/stock_entry.py +++ b/inventory_tools/inventory_tools/overrides/stock_entry.py @@ -169,6 +169,7 @@ def get_pending_raw_materials(self, backflush_based_on=None): @frappe.whitelist() +@frappe.read_only() def get_production_item_if_work_orders_for_required_item_exists(stock_entry_name: str) -> str: stock_entry = frappe.get_doc("Stock Entry", stock_entry_name) diff --git a/inventory_tools/inventory_tools/overrides/workstation.py b/inventory_tools/inventory_tools/overrides/workstation.py index 2d3689f..1bce0f4 100644 --- a/inventory_tools/inventory_tools/overrides/workstation.py +++ b/inventory_tools/inventory_tools/overrides/workstation.py @@ -1,6 +1,6 @@ -import json - import frappe +from frappe.desk.reportview import execute +from frappe.desk.search import search_link """ This function fetch workstation of the document operation. @@ -25,38 +25,54 @@ @frappe.whitelist() +@frappe.read_only() @frappe.validate_and_sanitize_search_inputs def get_alternative_workstations(doctype, txt, searchfield, start, page_len, filters): + company = filters.get("company") or frappe.defaults.get_defaults().get("company") + if not frappe.get_cached_value( + "Inventory Tools Settings", company, "allow_alternative_workstations" + ): + filters.pop("operation") if "operation" in filters else True + filters.pop("company") if "company" in filters else True + return execute( + "Workstation", + filters=filters, + fields=[searchfield], + limit_start=start, + limit_page_length=page_len, + as_list=True, + ) + operation = filters.get("operation") if not operation: frappe.throw("Please select a Operation first.") - if txt: - searchfields = frappe.get_meta(doctype).get_search_fields() - searchfields = " or ".join(["ws." + field + f" LIKE '%{txt}%'" for field in searchfields]) - - conditions = "" - if txt and searchfields: - conditions = f"and ({searchfields})" + searchfields = list(reversed(frappe.get_meta(doctype).get_search_fields())) + select = ",\n".join([f"`tabWorkstation`.{field}" for field in searchfields]) + search_text = "AND `tabAlternative Workstation`.workstation LIKE %(txt)s" if txt else "" workstation = frappe.db.sql( - """ - Select aw.workstation, ws.workstation_type, ws.description - From `tabOperation` as op - Left Join `tabAlternative Workstations` as aw ON aw.parent = op.name - Left Join `tabWorkstation` as ws ON ws.name = aw.workstation - Where op.name = '{operation}' {conditions} - """.format( - conditions=conditions, operation=operation - ) + f""" + SELECT DISTINCT {select} + FROM `tabOperation`, `tabWorkstation`, `tabAlternative Workstation` + WHERE `tabWorkstation`.name = `tabAlternative Workstation`.workstation + AND `tabAlternative Workstation`.parent = %(operation)s + {search_text} + """, + {"operation": operation, "txt": f"%{txt}%"}, + as_list=True, ) - default_workstation = frappe.db.get_value("Operation", operation, "workstation") - flag = True - for row in workstation: - if row[0] == None: - workstation = ((default_workstation,),) - flag = False - if flag: - workstation += ((default_workstation,),) + default_workstation_name = frappe.db.get_value("Operation", operation, "workstation") + default_workstation_fields = frappe.db.get_values( + "Workstation", default_workstation_name, searchfields, as_dict=True + ) + if default_workstation_name not in [row[0] for row in workstation]: + _default = tuple( + [ + default_workstation_fields[0].name, + f"{frappe.bold('Default')} - {','.join([v for k, v in default_workstation_fields[0].items() if k != 'name'])}", + ] + ) + workstation.insert(0, _default) return workstation diff --git a/inventory_tools/inventory_tools/report/quotation_demand/quotation_demand.py b/inventory_tools/inventory_tools/report/quotation_demand/quotation_demand.py index 5b4bd74..325523d 100644 --- a/inventory_tools/inventory_tools/report/quotation_demand/quotation_demand.py +++ b/inventory_tools/inventory_tools/report/quotation_demand/quotation_demand.py @@ -162,7 +162,6 @@ def create(company, filters, rows): rows = [frappe._dict(r) for r in json.loads(rows)] if isinstance(rows, str) else rows if not rows: return - print(rows) counter = 0 settings = frappe.get_doc("Inventory Tools Settings", company) requesting_companies = list({row.company for row in rows}) diff --git a/inventory_tools/patches.txt b/inventory_tools/patches.txt index e69de29..97b629c 100644 --- a/inventory_tools/patches.txt +++ b/inventory_tools/patches.txt @@ -0,0 +1 @@ +inventory_tools.patches.rename_alternative_workstation # Tyler Matteson 5/13/24 \ No newline at end of file diff --git a/inventory_tools/patches/rename_alternative_workstation.py b/inventory_tools/patches/rename_alternative_workstation.py new file mode 100644 index 0000000..7ca7c6c --- /dev/null +++ b/inventory_tools/patches/rename_alternative_workstation.py @@ -0,0 +1,11 @@ +import frappe +from frappe.model.rename_doc import rename_doc + + +def execute(): + if frappe.db.exists("DocType", "Alternative Workstations"): + rename_doc( + "DocType", "Alternative Workstations", "Alternative Workstation", ignore_if_exists=True + ) + + frappe.reload_doc("inventory_tools", "doctype", "alternative_workstation", force=True) diff --git a/inventory_tools/public/js/job_card_custom.js b/inventory_tools/public/js/job_card_custom.js index b46df09..02749df 100644 --- a/inventory_tools/public/js/job_card_custom.js +++ b/inventory_tools/public/js/job_card_custom.js @@ -14,7 +14,8 @@ function set_workstation_query(frm) { return { query: 'inventory_tools.inventory_tools.overrides.workstation.get_alternative_workstations', filters: { - operation: doc.operation, + operation: frm.doc.operation, + company: frm.doc.company, }, } }) diff --git a/inventory_tools/public/js/work_order_custom.js b/inventory_tools/public/js/work_order_custom.js index 7a87ae6..0a26a8a 100644 --- a/inventory_tools/public/js/work_order_custom.js +++ b/inventory_tools/public/js/work_order_custom.js @@ -24,6 +24,7 @@ function get_workstations(frm) { query: 'inventory_tools.inventory_tools.overrides.workstation.get_alternative_workstations', filters: { operation: d.operation, + company: frm.doc.company, }, } }) diff --git a/inventory_tools/tests/fixtures.py b/inventory_tools/tests/fixtures.py index aead89c..927a115 100644 --- a/inventory_tools/tests/fixtures.py +++ b/inventory_tools/tests/fixtures.py @@ -488,6 +488,7 @@ "item_price": 0.02, "default_warehouse": "Storeroom - APC", "supplier": ["Freedom Provisions", "Unity Bakery Supply"], + "uom_conversion_detail": {"Box": 100}, }, { "item_code": "Salt", diff --git a/inventory_tools/tests/setup.py b/inventory_tools/tests/setup.py index f7dc0f2..09a6d15 100644 --- a/inventory_tools/tests/setup.py +++ b/inventory_tools/tests/setup.py @@ -622,7 +622,7 @@ def create_production_plan(settings, prod_plan_from_doc): job_card = frappe.get_doc("Job Card", job_card) job_card.time_logs[0].completed_qty = wo.qty job_card.save() - job_card.submit() + job_card.submit() # don't submit to test alternative workstations def create_fruit_material_request(settings): diff --git a/inventory_tools/tests/test_alternative_workstation.py b/inventory_tools/tests/test_alternative_workstation.py new file mode 100644 index 0000000..2b43bf9 --- /dev/null +++ b/inventory_tools/tests/test_alternative_workstation.py @@ -0,0 +1,37 @@ +import frappe +import pytest + + +@pytest.mark.order(45) +def test_alternative_workstation_query(): + # test default settings + frappe.call( + "frappe.desk.search.search_link", + **{ + "doctype": "Workstation", + "txt": "", + "reference_doctype": "Job Card", + }, + ) + assert len(frappe.response.results) == 16 # all workstations + + # test with inventory tools settings + inventory_tools_settings = frappe.get_doc( + "Inventory Tools Settings", frappe.defaults.get_defaults().get("company") + ) + inventory_tools_settings.allow_alternative_workstations = True + inventory_tools_settings.save() + frappe.call( + "frappe.desk.search.search_link", + **{ + "doctype": "Workstation", + "txt": "", + "query": "inventory_tools.inventory_tools.overrides.workstation.get_alternative_workstations", + "filters": {"operation": "Gather Pie Filling Ingredients"}, + "reference_doctype": "Job Card", + }, + ) + assert len(frappe.response.results) == 2 + assert frappe.response.results[0].get("value") == "Food Prep Table 1" # default returns first + assert "Default" in frappe.response.results[0].get("description") + assert frappe.response.results[1].get("value") == "Food Prep Table 2" diff --git a/inventory_tools/tests/test_uom.py b/inventory_tools/tests/test_uom.py index 32a9d0c..9c7e384 100644 --- a/inventory_tools/tests/test_uom.py +++ b/inventory_tools/tests/test_uom.py @@ -17,3 +17,27 @@ def test_uom_enforcement_validation(): so.save() assert "Invalid UOM" in exc_info.value.args[0] + + +@pytest.mark.order(41) +def test_uom_enforcement_query(): + inventory_tools_settings = frappe.get_doc( + "Inventory Tools Settings", frappe.defaults.get_defaults().get("company") + ) + inventory_tools_settings.enforce_uoms = True + inventory_tools_settings.save() + frappe.call( + "frappe.desk.search.search_link", + **{ + "doctype": "UOM", + "txt": "", + "query": "inventory_tools.inventory_tools.overrides.uom.uom_restricted_query", + "filters": {"parent": "Parchment Paper"}, + "reference_doctype": "Purchase Order Item", + }, + ) + assert len(frappe.response.results) == 2 + assert frappe.response.results[0].get("value") == "Nos" + assert frappe.response.results[0].get("description") == "1.0" + assert frappe.response.results[1].get("value") == "Box" + assert frappe.response.results[1].get("description") == "100.0"