5.7 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this app is
Frappe/ERPNext v15 app that does three things:
- Overrides the desk UI with a multi-theme visual system (CSS + JS bundle).
- Hides the Quality Management and Subcontracting modules app-wide (URL/API/bootinfo/permissions).
- Maintains a custom Taxes Az workspace on every migrate.
Installed into frappe-bench as a normal app. Python code is at jey_theme/ (Frappe app package). Frontend assets live at jey_theme/public/{css,js}.
Commands
Run from frappe-bench/ (the bench root), not this app dir.
bench build --app jey_theme— rebuild JS bundle after editingjey_theme/public/js/jey_theme.js.- CSS-only changes: no build needed, just hard-reload the browser.
bench migrate— runs theafter_migratehook that rebuilds the Taxes Az workspace fromsetup/taxes_workspace.py.bench execute jey_theme.setup.taxes_workspace.setup— re-run the workspace setup without a full migrate.bench restart— reload Python workers after editingaccess_control.pyorhooks.py.- No test suite, no linter config in this repo.
Architecture
Python: access control (jey_theme/access_control.py + hooks.py)
Goal: make blocked modules invisible and unreachable for every user, including Administrator.
BLOCKED_MODULES/BLOCKED_DOCTYPES_EXTRA— edit these frozensets to change what's blocked.- Three defense layers wired in
hooks.py:before_request— blocks/api/resource/<Doctype>,/app/<slug>, and form_dict-based calls at the HTTP boundary (catches Administrator, who bypasseshas_permission).has_permission = {"*": ...}— per-doc deny for non-admin users who have role access.extend_bootinfo— strips blocked items fromallowed_modules,modules,user.can_*,workspaces.pages,workspace_sidebar_item,module_wise_workspaces,app_data,desktop_icons, etc., so the client router/sidebar never sees them.
- Slug/doctype/workspace sets are cached on
frappe.localper-request. - When adding a new block vector: extend
extend_bootinforather than relying only onhas_permission, because the client has many independent boot-derived UIs.
Python: Taxes Az workspace (jey_theme/setup/taxes_workspace.py)
Idempotent setup run via after_migrate.
- Builds a
Workspacenamed Taxes Az with six cards (Declarations, Setup, Tools, E-Taxes Setup, E-Taxes Reference, E-Taxes Documents), aWorkspace Sidebarlisting every doctype, and aDesktop Iconthat routes via the sidebar. - The doctype list is a static snapshot in
DECLARATIONS_DOCTYPES/SETUP_DOCTYPES/TOOLS_DOCTYPES(taxes_az) andETAXES_SETUP_DOCTYPES/ETAXES_REFERENCE_DOCTYPES/ETAXES_DOCUMENT_DOCTYPES(invoice_az). Adding a DocType to either app does NOT auto-include it — append to the tuple and re-migrate. _cleanup_obsolete()removes legacy docs owned by this app (currently"Taxes"). Only touches records whereapp == "jey_theme"— never deletes ERPNext/other-app docs.
Frontend: multi-theme system
Bundle entry: jey_theme/public/js/jey_theme.js. CSS: jey_theme/public/css/{shared,theme_chrome,theme_modern}.css. All four are loaded via app_include_css / app_include_js in hooks.py for both desk and web contexts.
- Theme selector lives in
data-jey-themeon<html>, stored inlocalStorage("jey-theme"). Default:chrome. Set before first paint by an IIFE at the top ofjey_theme.js. - Themes supported: chrome (metallic rings), modern (frosted glass). No dark/light variants — each theme is self-contained.
- Theme-specific CSS MUST be wrapped in
[data-jey-theme="themename"]. Keyframes MUST be prefixedjey-<theme>-*to avoid cross-theme name collisions. - Shared CSS (checkboxes, icon base sizing, switcher UI, column-resize rules) lives in
shared.csswithout theme selectors.
Frontend: per-theme icons
THEME_ICONS[themeName]injey_theme.jsmaps icon keys to inline SVG path strings. Missing keys fall back toTHEME_ICONS.chrome, then_fallback.- JS replaces
<img class="app-icon">/.folder-iconwith inline<svg class="jey-icon">per current theme. window.jeyRebuildIcons()tears down and rebuilds all icon DOM — called on theme switch.- SVGs may carry a
cls(e.g.ico-buying) for shared CSS animations.
Frontend: early-paint tricks in jey_theme.js
The top IIFE runs before paint to avoid FOUC:
- Sets
data-jey-themeimmediately. - Defaults
container_fullwidth = "true"on fresh install. - Reads all
jey-col-widths:<Doctype>:<field>localStorage keys and injects a<style id="jey-col-resize-styles">with per-grid column widths, plus a MutationObserver that stampsdata-jey-gridon grids as they appear.
Keep this IIFE minimal and synchronous — anything slow here delays first paint.
Adding a new theme
- Create
jey_theme/public/css/theme_mytheme.csswith[data-jey-theme="mytheme"]selectors. Prefix keyframesjey-mytheme-*. - Add
THEME_ICONS.mytheme = { ... }injey_theme.js(only override keys you care about). - Add an entry to
JEY_THEMESarray in the switcher section ofjey_theme.js. - Add the CSS file to both
app_include_cssandweb_include_cssinhooks.py. bench build --app jey_theme.
Rules
- Themes can change anything (layout, positioning, colors, fonts, visibility, animations, DOM structure) — not just icons. Use JS (
jeyRebuildIcons/ theme-switch hooks) for DOM restructuring. - Never add
[data-theme="dark"]or light/dark variant rules — themes are self-contained (no inherited dark/light axis). - Don't touch ERPNext-owned records in setup scripts; gate deletes on
app == "jey_theme".