Skip to content

feat: surface NPD Tooling in the Project Connections tab - #10

Merged
Guru107 merged 5 commits into
developfrom
feature/tooling-connections
Jul 27, 2026
Merged

feat: surface NPD Tooling in the Project Connections tab#10
Guru107 merged 5 commits into
developfrom
feature/tooling-connections

Conversation

@Guru107

@Guru107 Guru107 commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Follow-up to #9 (merged). Adds the NPD Tooling connection to the Project form's Connections tab.

Approach

Adds a custom DocType Link on Project (the declarative, UI-visible Frappe mechanism for Connections) rather than a dashboard hook:

  • Created idempotently in install/after_install.py (add_project_tooling_connection), removed in before_uninstall.py (remove_project_tooling_connection). This matches the app's existing pattern — all its Project customizations are code in after_install.
  • NPD Tooling.project matches the Project dashboard's default fieldname, so the connection count resolves automatically.
  • Re-asserted by an after_migrate hook on every bench migrate, which is what actually makes the connection durable — see below.

Durability: why after_migrate and not a patch

custom=1 only protects a DocType Link from Customize Form rewrites. It does not survive a doctype re-import: whenever project.json changes (any ERPNext upgrade touching Project), Frappe reloads it via import_file.delete_old_docdelete_doc(..., for_reload=True)delete_from_table, which drops every DocType Link row parented to Project, custom ones included. Ordinary migrates skip the re-import on a hash match, which is why a naive "created → migrated → still there" check passes.

A one-shot patch cannot recover from this — once it is in the Patch Log it never runs again, so the connection would be gone permanently. install/after_migrate.py re-runs the idempotent helper after every migrate instead (it runs after doctype sync and after patches, so it sees the post-reload state).

Verified on a site whose Patch Log already contained the earlier one-shot patch: deleted the link, ran bench migrate, and the connection was re-created and present in get_meta("Project").get_dashboard_data().

Idempotency

The guard matches on parent + link_doctype only — it deliberately does not pin custom=1. A site that carries the same connection as a standard (custom=0) row via fixtures or export-customizations would otherwise get a second link and list NPD Tooling twice in Connections. Uninstall is the mirror image: it deletes only the custom=1 rows this app creates, leaving a standard link the app did not put there alone.

The link's identity is spelled once, as PROJECT_TOOLING_LINK in install/after_install.py, and shared by the install guard, the uninstall cleanup and the tests, so the three cannot drift apart.

Why not override_doctype_dashboards / export-customizations

  • The dashboard hook works but is code-only and heavier than a one-line connection.
  • bench export-customizations for Project bundles all of Project's standard links (Task, Timesheet, …) and every property setter on the site — not clean to ship. The programmatic custom link avoids that and stays consistent with the app.

Files

  • install/after_install.pyPROJECT_TOOLING_LINK + add_project_tooling_connection (idempotent), called from after_install
  • install/after_migrate.py — new; re-asserts the connection on every migrate
  • hooks.py — registers after_migrate; override_doctype_dashboards left commented (unused)
  • uninstall/before_uninstall.pyremove_project_tooling_connection removes the custom link on uninstall
  • tests/test_npd_tooling.py — asserts the link surfaces in Project's dashboard data + idempotency, and cleans up after itself so it leaves no DocType Link on the core Project doctype

No patch and no patches.txt change: the after_migrate hook covers existing sites, and fresh installs get the connection from after_install.

Verification

  • Connection present in frappe.get_meta("Project").get_dashboard_data().
  • Deleted the link on a site with the old patch already logged → bench migrate → connection restored (see Durability above).
  • Full app suite 72/72 on v15; pre-commit run --all-files → exit 0. (The earlier revision of this branch was also verified 72/72 on v16; the post-review state has only been re-run on v15.)

Note: on each site the connection appears once the app's hooks apply — bench --site <site> migrate, which runs patches and the after_migrate hook. bench migrate does not run after_install; only a fresh install-app does.

Guru107 and others added 5 commits July 27, 2026 11:39
Extend the Project form's dashboard via the override_doctype_dashboards
hook to add a 'Tooling' group linking NPD Tooling. NPD Tooling links to
Project through its 'project' field, matching the Project dashboard's
default fieldname, so the connection count resolves automatically. The
override is additive and idempotent. Verified on v15 and v16 (72/72).
Replace the override_doctype_dashboards hook with a custom DocType Link
on Project, created idempotently in after_install (and removed in
before_uninstall). This is the declarative, UI-visible Frappe mechanism
for Connections and matches the app's existing after_install pattern for
Project customizations.

The custom (custom=1) link is preserved across bench migrate (verified),
and its link_fieldname 'project' matches the Project dashboard's default
fieldname so the connection count resolves automatically. Removes
utils/dashboard_overrides.py and its hook; tests now assert the link
surfaces in Project's dashboard data and that creation is idempotent.
after_install adds the connection on fresh installs, but existing sites
that already applied the earlier tooling patch won't get it from that
(already-logged) patch. Add a dedicated patch so bench migrate ensures
the connection on existing installs. Reuses the idempotent helper;
verified on v15 and v16 (removed the link, migrated, patch re-added it).
Durability (spec review): custom=1 only protects a DocType Link from
Customize Form rewrites, not from a doctype re-import — when project.json
changes, Frappe reloads it via delete_doc(for_reload=True), which drops
every DocType Link row on Project, custom ones included. The one-shot
patch could not recover from that once logged. Replace it with an
after_migrate hook that re-asserts the connection on every migrate
(verified: deleted the link on a site whose Patch Log already contained
the old patch, migrated, connection restored and visible in Connections).

Idempotency guard: match on parent + link_doctype instead of pinning
custom=1, so a site carrying the same connection as a standard (custom=0)
row from fixtures no longer gets a duplicate listed in Connections.
Uninstall still deletes only the custom rows this app creates.

Standards review:
- Call add_project_tooling_connection() from after_install() directly
  rather than nesting it in create_tooling_setup(), so the already-shipped
  create_tooling_item_group patch keeps its original meaning; split the
  uninstall counterpart out as remove_project_tooling_connection(). Both
  docstrings are accurate again.
- Extract PROJECT_TOOLING_LINK as the single spelling of the connection's
  identity, shared by the install guard, uninstall and tests — the guard
  and the idempotency assertion can no longer disagree.
- Give TestProjectDashboardConnection setUp/tearDown so it stops leaving a
  DocType Link on the core Project doctype behind; move it to the end of
  the file and widen the module docstring to cover it.

Full app suite 72/72 on v15; pre-commit clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Frappe's Linters workflow runs the frappe-semgrep-rules, which block
frappe-manual-commit. The commit was redundant anyway: migrate invokes
after_migrate hooks from post_schema_updates, which is decorated @atomic
and already commits on success and rolls back if the phase raises — so
the manual commit was also partially defeating that rollback.

Verified the connection still survives without it: deleted the link,
ran bench migrate, confirmed the row and the Connections entry from a
fresh console session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Guru107
Guru107 merged commit 9dbecba into develop Jul 27, 2026
3 checks passed
@Guru107
Guru107 deleted the feature/tooling-connections branch July 27, 2026 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant