From 987408f2030cf68c5c1556fa02b4e73df4ab49c0 Mon Sep 17 00:00:00 2001 From: sokumon Date: Fri, 28 Aug 2026 01:27:52 +0530 Subject: [PATCH] chore: some more fixtures --- .../accounts/sidebar/accounts/accounts.json | 24 +-- erpnext/banking/__init__.py | 0 erpnext/dock/erpnext/erpnext.json | 65 +++--- .../quality.json} | 4 +- erpnext/tests/test_sidebar_fixtures.py | 185 ------------------ 5 files changed, 50 insertions(+), 228 deletions(-) delete mode 100644 erpnext/banking/__init__.py rename erpnext/quality_management/sidebar/{quality_management/quality_management.json => quality/quality.json} (98%) delete mode 100644 erpnext/tests/test_sidebar_fixtures.py diff --git a/erpnext/accounts/sidebar/accounts/accounts.json b/erpnext/accounts/sidebar/accounts/accounts.json index cd37e3f3982..328df8bce39 100644 --- a/erpnext/accounts/sidebar/accounts/accounts.json +++ b/erpnext/accounts/sidebar/accounts/accounts.json @@ -864,7 +864,8 @@ "open_in_new_tab": 0, "route_options": "{\"is_return\": 1}", "show_arrow": 0, - "type": "Link" + "type": "Link", + "filters": "{\"is_return\": 1}" }, { "added": 0, @@ -940,7 +941,8 @@ "open_in_new_tab": 0, "route_options": "{\"is_return\": 1}", "show_arrow": 0, - "type": "Link" + "type": "Link", + "filters": "{\"is_return\": 1}" }, { "added": 0, @@ -1152,22 +1154,6 @@ "show_arrow": 0, "type": "Link" }, - { - "added": 0, - "child": 1, - "collapsible": 1, - "hidden": 0, - "icon": "settings", - "indent": 0, - "is_default_module": 0, - "keep_closed": 0, - "label": "Settings", - "link_to": "Accounts Settings", - "link_type": "DocType", - "open_in_new_tab": 0, - "show_arrow": 0, - "type": "Link" - }, { "added": 0, "child": 0, @@ -1891,7 +1877,7 @@ "type": "Link" } ], - "modified": "2026-08-26 16:36:51.109018", + "modified": "2026-08-28 12:00:00.000000", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts", diff --git a/erpnext/banking/__init__.py b/erpnext/banking/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/dock/erpnext/erpnext.json b/erpnext/dock/erpnext/erpnext.json index d76af5a1dea..107843ad2fe 100644 --- a/erpnext/dock/erpnext/erpnext.json +++ b/erpnext/dock/erpnext/erpnext.json @@ -9,151 +9,172 @@ "added": 0, "hidden": 0, "icon": "landmark", - "sidebar": "Accounts", + "link_to": "Accounts", + "link_type": "Sidebar", "title": "Accounts" }, { "added": 0, "hidden": 0, "icon": "handshake", - "sidebar": "CRM", + "link_to": "CRM", + "link_type": "Sidebar", "title": "CRM" }, { "added": 0, "hidden": 0, "icon": "shopping-cart", - "sidebar": "Buying", + "link_to": "Buying", + "link_type": "Sidebar", "title": "Buying" }, { "added": 0, "hidden": 0, "icon": "folder-kanban", - "sidebar": "Projects", + "link_to": "Projects", + "link_type": "Sidebar", "title": "Projects" }, { "added": 0, "hidden": 0, "icon": "store", - "sidebar": "Selling", + "link_to": "Selling", + "link_type": "Sidebar", "title": "Selling" }, { "added": 0, "hidden": 0, "icon": "sliders-horizontal", - "sidebar": "Setup", + "link_to": "Setup", + "link_type": "Sidebar", "title": "Setup" }, { "added": 0, "hidden": 0, "icon": "building-2", - "sidebar": "Manufacturing", + "link_to": "Manufacturing", + "link_type": "Sidebar", "title": "Manufacturing" }, { "added": 0, "hidden": 0, "icon": "package", - "sidebar": "Stock", + "link_to": "Stock", + "link_type": "Sidebar", "title": "Stock" }, { "added": 0, "hidden": 0, "icon": "headset", - "sidebar": "Support", + "link_to": "Support", + "link_type": "Sidebar", "title": "Support" }, { "added": 0, "hidden": 0, "icon": "pocket-knife", - "sidebar": "Utilities", + "link_to": "Utilities", + "link_type": "Sidebar", "title": "Utilities" }, { "added": 0, "hidden": 0, "icon": "archive", - "sidebar": "Assets", + "link_to": "Assets", + "link_type": "Sidebar", "title": "Assets" }, { "added": 0, "hidden": 0, "icon": "panels-top-left", - "sidebar": "Portal", + "link_to": "Portal", + "link_type": "Sidebar", "title": "Portal" }, { "added": 0, "hidden": 0, "icon": "wrench", - "sidebar": "Maintenance", + "link_to": "Maintenance", + "link_type": "Sidebar", "title": "Maintenance" }, { "added": 0, "hidden": 0, "icon": "globe", - "sidebar": "Regional", + "link_to": "Regional", + "link_type": "Sidebar", "title": "Regional" }, { "added": 0, "hidden": 0, "icon": "plug", - "sidebar": "ERPNext Integrations", + "link_to": "ERPNext Integrations", + "link_type": "Sidebar", "title": "Integrations" }, { "added": 0, "hidden": 0, "icon": "shield-check", - "sidebar": "Quality Management", + "link_to": "Quality", + "link_type": "Sidebar", "title": "Quality" }, { "added": 0, "hidden": 0, "icon": "messages-square", - "sidebar": "Communication", + "link_to": "Communication", + "link_type": "Sidebar", "title": "Communication" }, { "added": 0, "hidden": 0, "icon": "phone", - "sidebar": "Telephony", + "link_to": "Telephony", + "link_type": "Sidebar", "title": "Telephony" }, { "added": 0, "hidden": 0, "icon": "layers", - "sidebar": "Bulk Transaction", + "link_to": "Bulk Transaction", + "link_type": "Sidebar", "title": "Bulk Transaction" }, { "added": 0, "hidden": 0, "icon": "factory", - "sidebar": "Subcontracting", + "link_to": "Subcontracting", + "link_type": "Sidebar", "title": "Subcontracting" }, { "added": 0, "hidden": 0, "icon": "file-code", - "sidebar": "EDI", + "link_to": "EDI", + "link_type": "Sidebar", "title": "EDI" } ], - "modified": "2026-08-26 23:30:00.000000", + "modified": "2026-08-28 12:00:00.000000", "modified_by": "Administrator", "name": "erpnext", "owner": "Administrator", diff --git a/erpnext/quality_management/sidebar/quality_management/quality_management.json b/erpnext/quality_management/sidebar/quality/quality.json similarity index 98% rename from erpnext/quality_management/sidebar/quality_management/quality_management.json rename to erpnext/quality_management/sidebar/quality/quality.json index d5ee5f472fd..30a883aa249 100644 --- a/erpnext/quality_management/sidebar/quality_management/quality_management.json +++ b/erpnext/quality_management/sidebar/quality/quality.json @@ -195,10 +195,10 @@ "type": "Link" } ], - "modified": "2026-08-16 00:00:00.000000", + "modified": "2026-08-28 12:00:00.000000", "modified_by": "Administrator", "module": "Quality Management", - "name": "Quality Management", + "name": "Quality", "owner": "Administrator", "sequence_id": 0.0, "standard": 1, diff --git a/erpnext/tests/test_sidebar_fixtures.py b/erpnext/tests/test_sidebar_fixtures.py deleted file mode 100644 index eff541117f0..00000000000 --- a/erpnext/tests/test_sidebar_fixtures.py +++ /dev/null @@ -1,185 +0,0 @@ -# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -"""erpnext ships its navigation as `Sidebar` fixtures, one per module that has an arrangement. - -The framework renamed `Module Sidebar` to `Sidebar` and moved an app's fixtures from -`/module_sidebar/` to `/sidebar/`. An app that has not followed is not broken -- -its folder is simply never walked, and each of its modules falls back to a base computed from its -own contents -- so nothing here fails loudly if the conversion is half done. That is exactly why -it is asserted: the failure mode is erpnext's curated navigation quietly reverting to generated. - -Two facts, and they are different questions: - -- `TestTheFixturesAreWhereMigrateLooks` is about the *files*. Import finds them, and orphan - removal derives the same record name from the path that the file declares -- a mismatch there - makes migrate delete the very rows it just imported. -- `TestTheModulesResolveToTheirShippedArrangement` is about *navigation*, asked through the - resolver seam rather than through any payload key. It is what says the files actually took. -""" - -import json -import os - -import frappe -from frappe.desk.doctype.sidebar.convert_fixtures import export_path -from frappe.desk.doctype.sidebar.sidebar import resolve_sidebar -from frappe.model.sync import create_entity_file_map, get_doc_files - -from erpnext.tests.utils import ERPNextTestSuite - -#: Every module that ships a `Sidebar`. `Banking` is deliberately absent -- it is new, has no -#: arrangement yet, and a module with no fixture is not baseless: it is served by a computed base -#: built from its own contents. -#: -#: Eleven of these are real arrangements, and ten are not, and the difference is worth knowing -#: before reading a failure. `Accounts` (124 items), `Selling` (62), `Stock` (56) and the rest of -#: the large modules carry navigation somebody arranged. The ten small ones -- `Bulk Transaction`, -#: `Communication`, `EDI`, `ERPNext Integrations`, `Maintenance`, `Portal`, `Regional`, -#: `Subcontracting`, `Telephony`, `Utilities` -- are a computed base that was materialized and -#: frozen, legible in the rows: `icon: settings` on exactly the doctypes with "settings" in the -#: name, and the `Reports` section label as a code constant out of `generate_items`. -#: -#: For those ten the header icon is the only authored part, which makes it the assertion that -#: separates shipped from computed for every module here: `build_computed_base` hands out -#: `hammer`, and not one of these twenty-one says `hammer`. -SIDEBAR_MODULES = [ - "Accounts", - "Assets", - "Bulk Transaction", - "Buying", - "CRM", - "Communication", - "EDI", - "ERPNext Integrations", - "Maintenance", - "Manufacturing", - "Portal", - "Projects", - "Quality Management", - "Regional", - "Selling", - "Setup", - "Stock", - "Subcontracting", - "Support", - "Telephony", - "Utilities", -] - -#: `Portal` ships a fixture with an empty item list, so it has no arrangement to resolve to. -#: `get_sidebar_bases` fills an empty document's rows from the computed base and keeps only what -#: the document says about *itself* -- title, icon, app -- so every resolution fact below would be -#: a fact about the framework's fallback rather than about erpnext's authoring. It is named here, -#: not filtered out by a rule, so that a fixture which lost its items has to be excluded by hand. -MODULES_WITH_SHIPPED_ITEMS = [module for module in SIDEBAR_MODULES if module != "Portal"] - - -def shipped(module: str) -> dict: - """The fixture as it sits in the app folder, before any site has seen it.""" - with open(export_path(module)) as f: - return json.load(f) - - -class TestTheFixturesAreWhereMigrateLooks(ERPNextTestSuite): - def test_every_authoring_module_ships_one(self): - """Named individually rather than globbed: a fixture that stopped being exported would - pass a test that only checks the files it can find.""" - for module in SIDEBAR_MODULES: - with self.subTest(module=module): - self.assertTrue(os.path.exists(export_path(module))) - - def test_they_declare_the_renamed_doctype(self): - """A fixture still naming `Module Sidebar` would import against a doctype the site no - longer has -- which is why the walk skips the old folder rather than failing on it.""" - for module in SIDEBAR_MODULES: - with self.subTest(module=module): - self.assertEqual(shipped(module)["doctype"], "Sidebar") - - def test_the_module_walk_picks_them_up(self): - """`get_doc_files` is what migrate imports from. It only opens folders named by - `IMPORTABLE_DOCTYPES`, so this is the fact that the folder rename landed.""" - for module in SIDEBAR_MODULES: - with self.subTest(module=module): - module_path = frappe.get_module_path(module) - self.assertIn(export_path(module), get_doc_files(files=[], start_path=module_path)) - - def test_record_name_and_filename_agree(self): - """Orphan removal maps a file to a record by reading the `name` out of it and looking for - that record. Standard rows whose file it cannot find are deleted, so a fixture whose name - and path disagree is imported and then reaped on the same migrate.""" - known = create_entity_file_map(["Sidebar"])["Sidebar"] - - for module in SIDEBAR_MODULES: - with self.subTest(module=module): - self.assertEqual(shipped(module)["name"], module) - self.assertEqual(known.get(module), export_path(module)) - - def test_they_store_no_item_key(self): - """A base row's identity is derived from its own columns, so `Sidebar.clear_stored_keys` - nulls `key` on the way in and `no_nulls=True` drops it on the way back out. A fixture - still carrying one disagrees with what a developer-mode re-export would write, which is - how a diff nobody authored appears -- and frappe's own eleven shipped fixtures carry none. - - Only `key`. A Check field valued `0` is not null, so `is_default_module` does survive an - export and every one of frappe's fixtures ships it; dropping it here would be the same - divergence in the other direction.""" - for module in SIDEBAR_MODULES: - with self.subTest(module=module): - for item in shipped(module)["items"]: - self.assertNotIn("key", item) - - def test_portal_ships_no_arrangement_of_its_own(self): - """Named rather than left implicit, because it is why `Portal` is absent from every - resolution fact below. An empty document is not a hidden module: `get_sidebar_bases` fills - its rows from the computed base and keeps only what it says about itself, so what this - fixture contributes is its icon.""" - self.assertEqual(shipped("Portal")["items"], []) - self.assertTrue(shipped("Portal")["header_icon"]) - self.assertNotIn("Portal", MODULES_WITH_SHIPPED_ITEMS) - - -class TestTheModulesResolveToTheirShippedArrangement(ERPNextTestSuite): - """The point of the whole exercise: what a person's navigation resolves to. - - Asserted as Administrator, who is filtered out of nothing and carries no customization, so the - resolution is the shipped arrangement itself rather than one reader's view of it. That a - *restricted* reader sees less is the framework's fact and is asserted there. - """ - - def test_a_module_resolves_to_the_items_its_fixture_ships(self): - """Labels in order, which is the whole of what the fixture authored. A computed base would - still resolve to something -- these modules have doctypes -- so "resolves to anything at - all" is not the fact worth asserting.""" - for module in MODULES_WITH_SHIPPED_ITEMS: - with self.subTest(module=module): - resolved = resolve_sidebar(module, "Administrator") - - self.assertIsNotNone(resolved) - self.assertEqual( - [item["label"] for item in resolved.items], - [item["label"] for item in shipped(module)["items"]], - ) - - def test_the_label_and_icon_are_the_fixture_s(self): - """`resolve_sidebar` answers `None` for a scope that resolves to nothing, and that is the - failure this file exists to catch -- so it is asserted against rather than skipped over. - Ten fixtures failing to import would otherwise pass this test in silence.""" - for module in MODULES_WITH_SHIPPED_ITEMS: - with self.subTest(module=module): - resolved = resolve_sidebar(module, "Administrator") - self.assertIsNotNone(resolved) - - fixture = shipped(module) - self.assertEqual(resolved.label, fixture["title"]) - self.assertEqual(resolved.header_icon, fixture["header_icon"]) - - def test_a_module_opens_on_its_own_navigation(self): - """Landing is derived from the resolved entries, so a module falling back to a computed - base would land somewhere the fixture never named.""" - for module in MODULES_WITH_SHIPPED_ITEMS: - with self.subTest(module=module): - resolved = resolve_sidebar(module, "Administrator") - - self.assertIsNotNone(resolved) - self.assertIsNotNone(resolved.landing)