agentsclimarketplace

Frappe fixtures

Skill prilk-consulting/frappe-agent-kit/skills/frappe-fixtures

Production-grade Frappe Framework & ERPNext skills, agents, and scaffolding commands for Claude Code

Install
npx -y skills add prilk-consulting/frappe-agent-kit --skill frappe-fixtures

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Frappe fixtures, workspaces, and sidebar setup. Use for exporting/syncing fixtures, Workspace + Workspace Sidebar + Desktop Icon configuration, nested-set fixture ordering, and per-environment ID pitfalls that break fresh-site migrations. Not for data-migration patches or patches.txt — use frappe-upgrade.

SKILL.md

6.4 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it

Fixtures, Workspaces & Sidebars

Usage

Use this skill when:

  • Exporting or syncing fixtures (bench export-fixtures, hooks fixtures list)
  • Setting up a Workspace, Workspace Sidebar, or Desktop Icon for an app
  • Debugging fresh-site bench migrate crashes caused by fixture ordering or per-environment links
  • Deciding between fixtures and install.py for custom fields

Fixture basics

# hooks.py
fixtures = [
    {"dt": "Custom Field", "filters": [["module", "=", "My Module"]]},
    {"dt": "Property Setter", "filters": [["module", "=", "My Module"]]},
    {"dt": "Role", "filters": [["name", "in", ["My Role"]]]},
]
bench --site <site> export-fixtures --app myapp     # DB → apps/myapp/myapp/fixtures/*.json
bench --site <site> migrate                          # JSON → DB (runs sync_fixtures)
bench --site <site> execute frappe.utils.fixtures.sync_fixtures --args "['myapp']"  # sync only
  • Always filter — an unfiltered {"dt": "Custom Field"} exports every custom field on the site, including other apps'. Each app owns only its own records.
  • Use or_filters when records can't be matched by one field: {"dt": "Wiki Document", "or_filters": [...]}.
  • Fixture sync is upsert-only: deleting a record from the JSON does not delete it from sites that already have it. Removals need a patch.
  • Re-export after every UI-side change, and diff the export before committingexport-fixtures rewrites whole files and happily captures unrelated records that match your filter.
  • For content managed live in the UI by end users (website pages, wiki content), consider NOT shipping fixtures at all — a fixture sync on deploy will overwrite their edits. Fixtures fit configuration, not user-owned content.

Workspace + Sidebar + Desktop Icon

CRITICAL naming rule

  • Workspace Sidebar name MUST differ from the module name
  • Workspace Sidebar module field MUST equal the module name (this prevents auto-gen)
  • Example: module="Logistics" → sidebar name="Logistics Hub", sidebar module="Logistics"

If name matches module, Frappe's auto_generate_sidebar_from_module() creates a cached auto-generated sidebar (top 3 DocTypes only) that overrides yours.

# hooks.py
fixtures = [
    {"dt": "Workspace", "filters": [["name", "in", ["MyWorkspace"]]]},
    {"dt": "Workspace Sidebar", "filters": [["name", "in", ["My Sidebar"]]]},
    {"dt": "Desktop Icon", "filters": [["name", "in", ["My Sidebar"]]]},
]
// fixtures/workspace_sidebar.json (minimal)
[{
  "doctype": "Workspace Sidebar", "name": "My Sidebar", "module": "MyModule",
  "title": "My Sidebar", "header_icon": "bot-message-square", "standard": 1,
  "items": [
    {"label": "Home", "link_to": "MyWorkspace", "link_type": "Workspace", "type": "Link"},
    {"label": "My DocType", "link_to": "My DocType", "link_type": "DocType", "type": "Link", "icon": "list"}
  ]
}]
  • Desktop Icon link_to and sidebar reference the sidebar name.
  • Icon SVG: 118x118px, colored background + white icon, in public/images/.
  • Sidebar item icons: pick from frappe/public/icons/lucide/icons.svg.
  • After syncing: bench --site <site> clear-cache (sidebars are cached).

Nested-set fixture ordering gotcha

bench export-fixtures writes records sorted alphabetically by name. For nested-set doctypes (tree doctypes: Item Group, Customer Group, Account, Wiki Document, BOM trees, …) this can put children before their parents in the JSON — random hash names sort unpredictably.

On a fresh site, bench migrate hits the child row first, calls update_add_node, looks up the parent's lft/rgt — and the parent doesn't exist yet:

File ".../frappe/utils/nestedset.py", line 77, in update_add_node
    left, right = frappe.db.get_value(doctype, {"name": parent}, ["lft", "rgt"], for_update=True)
TypeError: cannot unpack non-iterable NoneType object

Fix: topologically re-sort the JSON after every export so each record appears only after its parent. Run the bundled script (in this skill's scripts/ directory):

python3 scripts/sort_nested_fixtures.py apps/<app>/<app>/fixtures/<doctype>.json <parent_fieldname>
# e.g. python3 scripts/sort_nested_fixtures.py apps/myapp/myapp/fixtures/item_group.json parent_item_group

It re-sorts by tree depth in place (idempotent; exit 2 on parent cycles). Run it as a post-export step after every bench export-fixtures of a tree doctype. Fixture order is per-file; fixture files import alphabetically by filename, so make sure parent-doctype fixtures sort before child-doctype fixtures if they're linked.

This gotcha never shows on the dev site where the records already exist — only on fresh installs / first bench migrate of a downstream bench. Test fixtures on a throwaway site before shipping.

Per-environment ID gotcha

Fixtures that contain links to records that are generated per-environment (revision IDs, version rows, file attachments, auto-named children that aren't themselves exported) will migrate fine on the source site and crash on every other site with LinkValidationError: Could not find <DocType>.

Before committing an export, null out or strip any field whose value is an autoname/hash that the fixture set itself does not include. Typical offenders: "current revision" pointers, "latest version" links, cached file URLs pointing at /private/files/.

# post-export scrub example
for d in docs:
    d["main_revision"] = None   # per-environment pointer; rebuilt on first save

Custom fields: fixtures vs install.py

Two valid approaches — pick ONE per app, don't mix:

ApproachProCon
install.py + create_custom_fields() in after_install/after_migrateCode-reviewed dicts, no export step, idempotentFields not visible in fixture diffs
fixtures = [{"dt": "Custom Field", "filters": [["module", "=", "X"]]}]UI-first workflowExport captures noise; needs module set on every field

The install.py approach is generally preferred for apps that extend other apps' DocTypes — see the frappe-dev skill.

What ships with it: 1 file

1.9 KB alongside SKILL.md, 1 of them executable

scripts/

Keep looking

Skills are one crate of 326,970. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.