diff --git a/sf_trading/fixtures/custom_field.json b/sf_trading/fixtures/custom_field.json index a242f76..cd8a7ff 100644 --- a/sf_trading/fixtures/custom_field.json +++ b/sf_trading/fixtures/custom_field.json @@ -1,164 +1,116 @@ [ - { - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "columns": 0, - "default": null, - "depends_on": "eval:doc.is_internal_customer && doc.represents_company", - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Sales Invoice", - "fetch_from": null, - "fetch_if_empty": 0, - "fieldname": "inter_company_branch", - "fieldtype": "Link", - "hidden": 0, - "hide_border": 0, - "ignore_user_permissions": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "insert_after": "represents_company", - "is_system_generated": 0, - "label": "Inter Company Branch", - "module": "Sf Trading", - "name": "Sales Invoice-inter_company_branch", - "no_copy": 0, - "options": "Inter Company Branch", - "permlevel": 0, - "read_only": 0, - "reqd": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "columns": 0, - "default": "Credit", - "depends_on": null, - "description": "Cash = paid on or before invoice date; Credit = paid later. Set automatically.", - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Sales Invoice", - "fetch_from": null, - "fetch_if_empty": 0, - "fieldname": "custom_sale_type", - "fieldtype": "Select", - "hidden": 1, - "hide_border": 0, - "ignore_user_permissions": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "insert_after": "inter_company_branch", - "is_system_generated": 0, - "label": "Sale Type", - "module": "Sf Trading", - "name": "Sales Invoice-custom_sale_type", - "no_copy": 0, - "options": "Cash\nCredit", - "permlevel": 0, - "read_only": 1, - "reqd": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "columns": 0, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Customer", - "fetch_from": null, - "fetch_if_empty": 0, - "fieldname": "custom_commercial_registration_number", - "fieldtype": "Data", - "hidden": 0, - "hide_border": 0, - "hide_days": 0, - "hide_seconds": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "insert_after": "custom_vat_registration_number", - "is_system_generated": 0, - "is_virtual": 0, - "label": "Commercial Registration Number", - "length": 0, - "link_filters": null, - "mandatory_depends_on": null, - "modified": "2026-01-21 22:02:44.991168", - "module": null, - "name": "Customer-custom_commercial_registration_number", - "no_copy": 0, - "non_negative": 0, - "options": null, - "permlevel": 0, - "placeholder": null, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "read_only_depends_on": null, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "show_dashboard": 0, - "sort_options": 0, - "translatable": 0, - "unique": 0, - "width": null - }, - { - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "collapsible_depends_on": null, - "columns": 0, - "default": null, - "depends_on": null, - "description": "Shows Pending Approval when a linked Purchase Invoice is awaiting manager approval", - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Purchase Receipt", - "fetch_from": null, - "fetch_if_empty": 0, - "fieldname": "custom_billing_approval_status", - "fieldtype": "Data", - "hidden": 0, - "hide_border": 0, - "ignore_user_permissions": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "insert_after": "status", - "is_system_generated": 0, - "label": "Billing Approval Status", - "module": "Sf Trading", - "name": "Purchase Receipt-custom_billing_approval_status", - "no_copy": 0, - "options": null, - "permlevel": 0, - "read_only": 1, - "reqd": 0, - "translatable": 0, - "unique": 0 - } + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "Customer", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "custom_commercial_registration_number", + "fieldtype": "Data", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "custom_vat_registration_number", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Commercial Registration Number", + "length": 0, + "link_filters": null, + "mandatory_depends_on": null, + "modified": "2026-01-21 22:02:44.991168", + "module": null, + "name": "Customer-custom_commercial_registration_number", + "no_copy": 0, + "non_negative": 0, + "options": null, + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "show_dashboard": 0, + "sort_options": 0, + "translatable": 0, + "unique": 0, + "width": null + }, + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": "eval:doc.is_internal_customer && doc.represents_company", + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "Sales Invoice", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "inter_company_branch", + "fieldtype": "Link", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "represents_company", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Inter Company Branch", + "length": 0, + "link_filters": null, + "mandatory_depends_on": null, + "modified": "2026-05-04 17:38:13.257201", + "module": "Sf Trading", + "name": "Sales Invoice-inter_company_branch", + "no_copy": 0, + "non_negative": 0, + "options": "Inter Company Branch", + "permlevel": 0, + "placeholder": null, + "precision": null, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "show_dashboard": 0, + "sort_options": 0, + "translatable": 0, + "unique": 0, + "width": null + } ] \ No newline at end of file diff --git a/sf_trading/fixtures/property_setter.json b/sf_trading/fixtures/property_setter.json index 5570cf0..62db3bc 100644 --- a/sf_trading/fixtures/property_setter.json +++ b/sf_trading/fixtures/property_setter.json @@ -1,13 +1,18 @@ [ - { - "doc_type": "Sales Invoice Item", - "doctype": "Property Setter", - "doctype_or_field": "DocField", - "field_name": "barcode", - "module": "Sf Trading", - "name": "Sales Invoice Item-barcode-in_list_view", - "property": "in_list_view", - "property_type": "Check", - "value": "1" - } -] + { + "default_value": null, + "doc_type": "Sales Invoice Item", + "docstatus": 0, + "doctype": "Property Setter", + "doctype_or_field": "DocField", + "field_name": "barcode", + "is_system_generated": 0, + "modified": "2026-05-04 17:38:15.245578", + "module": "Sf Trading", + "name": "Sales Invoice Item-barcode-in_list_view", + "property": "in_list_view", + "property_type": "Check", + "row_name": null, + "value": "1" + } +] \ No newline at end of file diff --git a/sf_trading/fixtures/report.json b/sf_trading/fixtures/report.json new file mode 100644 index 0000000..3774fc2 --- /dev/null +++ b/sf_trading/fixtures/report.json @@ -0,0 +1,90 @@ +[ + { + "add_total_row": 1, + "add_translate_data": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "Yes", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2025-01-21 22:00:00", + "module": "Sf Trading", + "name": "DCR Detailed", + "prepared_report": 0, + "query": null, + "ref_doctype": "Sales Invoice", + "reference_report": null, + "report_name": "DCR Detailed", + "report_script": null, + "report_type": "Script Report", + "roles": [ + { + "parent": "DCR Detailed", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts Manager" + }, + { + "parent": "DCR Detailed", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "DCR Detailed", + "parentfield": "roles", + "parenttype": "Report", + "role": "System Manager" + } + ], + "timeout": 0 + }, + { + "add_total_row": 0, + "add_translate_data": 0, + "columns": [], + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "is_standard": "Yes", + "javascript": null, + "json": null, + "letter_head": null, + "modified": "2026-02-11 23:38:16.427918", + "module": "Accounts", + "name": "DCR Report", + "prepared_report": 0, + "query": null, + "ref_doctype": "GL Entry", + "reference_report": null, + "report_name": "DCR Report", + "report_script": null, + "report_type": "Script Report", + "roles": [ + { + "parent": "DCR Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts User" + }, + { + "parent": "DCR Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Accounts Manager" + }, + { + "parent": "DCR Report", + "parentfield": "roles", + "parenttype": "Report", + "role": "Auditor" + } + ], + "timeout": 0 + } +] \ No newline at end of file diff --git a/sf_trading/sf_trading/custom_search.py b/sf_trading/sf_trading/custom_search.py new file mode 100644 index 0000000..9a794c6 --- /dev/null +++ b/sf_trading/sf_trading/custom_search.py @@ -0,0 +1,64 @@ +import frappe +import re + +def natural_sort_key(item_name): + """Sort key that handles numeric parts correctly for size ordering.""" + parts = re.split(r'(\d+\.?\d*)', str(item_name)) + result = [] + for part in parts: + try: + result.append(float(part)) + except ValueError: + result.append(part.lower()) + return result + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def item_search_sorted(doctype, txt, searchfield, start, page_len, filters): + """Custom item search that sorts results by size (natural/numeric order).""" + + conditions = "" + if txt: + conditions = """ + AND ( + item.item_code LIKE %(txt)s + OR item.item_name LIKE %(txt)s + OR item.description LIKE %(txt)s + OR EXISTS ( + SELECT 1 FROM `tabItem Barcode` ib + WHERE ib.parent = item.item_code + AND ib.barcode LIKE %(txt)s + ) + ) + """ + + results = frappe.db.sql( + f""" + SELECT + item.item_code, + item.item_name, + item.item_group, + item.description, + item.stock_uom + FROM `tabItem` item + WHERE + item.disabled = 0 + AND item.has_variants = 0 + {conditions} + LIMIT %(page_len)s OFFSET %(start)s + """, + { + "txt": f"%{txt}%", + "start": start, + "page_len": page_len, + }, + as_dict=True + ) + + # Sort results by natural/numeric order of item_name + results.sort(key=lambda r: natural_sort_key(r.get("item_name", ""))) + + return [ + (r.item_code, r.item_name, r.item_group or "", r.description or "") + for r in results + ] \ No newline at end of file diff --git a/sf_trading/sf_trading/doctype/branch_configuration/branch_configuration.js b/sf_trading/sf_trading/doctype/branch_configuration/branch_configuration.js index 3ad9aa2..14272d6 100644 --- a/sf_trading/sf_trading/doctype/branch_configuration/branch_configuration.js +++ b/sf_trading/sf_trading/doctype/branch_configuration/branch_configuration.js @@ -1,3 +1,4 @@ + // Copyright (c) 2026, Enfono and contributors // For license information, please see license.txt diff --git a/sf_trading/sf_trading/doctype/branch_configuration/branch_configuration.py b/sf_trading/sf_trading/doctype/branch_configuration/branch_configuration.py index b405479..1505cd0 100644 --- a/sf_trading/sf_trading/doctype/branch_configuration/branch_configuration.py +++ b/sf_trading/sf_trading/doctype/branch_configuration/branch_configuration.py @@ -1,7 +1,8 @@ -# User permissions are created for company, branch, warehouse and cost center. +# User permissions are created for company, warehouse and cost center. # Company and first warehouse are set as is_default=1 so Frappe uses them as # the user's Session Defaults instead of falling back to global defaults. -# The "Branch User" role is auto-assigned to users added here. +# The role assigned in the User child table is auto-assigned to the user. +# A Module Profile matching the role is also applied automatically. import frappe from frappe.model.document import Document @@ -98,15 +99,12 @@ def create_permissions(self): if company_default_cc: create_permission(u.user, "Cost Center", company_default_cc, is_default=0) - # Auto-assign the selected role (Branch User, Warehouse User, or Stock User) + # Auto-assign the selected role selected_role = u.get("role") or BRANCH_USER_ROLE _assign_role(u.user, selected_role) - # Auto-set Module Profile to restrict sidebar modules - if selected_role == BRANCH_USER_ROLE: - _set_module_profile(u.user, "Branch User") - elif selected_role == "Damage User": - _set_module_profile(u.user, "Damage User") + # Auto-set Module Profile based on role + _set_module_profile_for_role(u.user, selected_role) def create_permission(user, allow, value, is_default=0): @@ -180,8 +178,7 @@ def _assign_role(user, role): if not role or not frappe.db.exists("Role", role): return - # Desk roles (Branch User / Stock User / Damage User / Stock Manager) require - # System User user_type; Website Users silently lose role assignments. + # Desk roles require System User user_type; Website Users silently lose role assignments. _ensure_system_user(user) # Check if role already assigned