Skip to content

[18.0][ADD] web_diagram_builder: build dependency diagrams from any many2one field#3538

Open
mmaanneell wants to merge 156 commits into
OCA:18.0from
mmaanneell:18.0_migrate_raphael_to_cytoscape
Open

[18.0][ADD] web_diagram_builder: build dependency diagrams from any many2one field#3538
mmaanneell wants to merge 156 commits into
OCA:18.0from
mmaanneell:18.0_migrate_raphael_to_cytoscape

Conversation

@mmaanneell
Copy link
Copy Markdown

Summary

This PR adds web_diagram_builder, a new module that lets users build and visualize interactive hierarchical diagrams from any Odoo model containing a recursive many2one field — without writing a single line of code.

Typical use cases: contacts and sub-contacts (res.partner / parent_id), product categories, departments, stock locations, or any other self-referencing model.

This PR also includes the migration of web_diagram's renderer from Raphael.js to Cytoscape.js (with dagre layout), along with a navigation help popup and several bug fixes for Odoo 18 compatibility.

Related to: #3489


Features

Feature Description
Interactive diagram Cytoscape.js renderer with dagre hierarchical layout, zoom/pan/drag
Export PNG Full-resolution export of the complete diagram
Find path Shortest path between two nodes (LCA algorithm), displayed as colored bubbles
CSV import/export Export diagram config as CSV, re-import on another instance
In-app tutorial Step-by-step guide accessible from the form view ("How does it work?" button)
Bilingual help Navigation tips popup in EN and FR (auto-detected from user language)
Auto-refresh Optional cron-based automatic recompute for diagrams with auto_refresh = True
Templates Predefined templates for common models (contacts, product categories, etc.)
Translations Full French (fr_CA) translation included

Tests

12 Python unit tests covering: BFS traversal, root node detection, max_nodes limit, domain filtering, recompute, last_computed timestamp, node/link counts, action_open_diagram, action_compute_all, and link signal labels.


Dependencies

  • web_diagram (this repository) — provides the diagram view rendered by Cytoscape.js

Checklist

  • Module follows OCA structure (readme/, pyproject.toml, license headers)
  • author includes Odoo Community Association (OCA)
  • development_status: Beta
  • maintainers set
  • Python unit tests included
  • French (fr_CA) translations included
  • No breaking changes to existing modules

This module was developed during an internship at TechnoLibre as the continuation of the work described in #3489.

yajo and others added 30 commits November 14, 2025 11:07
This module prefers kanban sub-form views when using a tablet, or any other
device with a touch screen, no matter its size.

@moduon MT-4472
Signed-off-by yajo
Signed-off-by legalsylvain
Signed-off-by ivs-cetmix
Signed-off-by pedrobaeza
Signed-off-by legalsylvain
Currently translated at 100.0% (21 of 21 strings)

Translation: web-18.0/web-18.0-web_pivot_computed_measure
Translate-URL: https://translation.odoo-community.org/projects/web-18-0/web-18-0-web_pivot_computed_measure/it/
Currently translated at 100.0% (8 of 8 strings)

Translation: web-18.0/web-18.0-web_widget_x2many_2d_matrix
Translate-URL: https://translation.odoo-community.org/projects/web-18-0/web-18-0-web_widget_x2many_2d_matrix/it/
mathben and others added 25 commits March 27, 2026 03:00
…nates

Applying position:absolute directly to the Raphael SVG element breaks its
internal coordinate system and makes nodes invisible.  The root cause is
that height="100%" on the SVG cannot resolve against a flex-grown parent.

Instead, wrap .o_diagram in a new .o_diagram_canvas div that is the flex
item (flex:1 1 auto; position:relative).  .o_diagram itself is then
position:absolute with inset:0, giving it a definite pixel height that
the SVG's height="100%" resolves against correctly.

Generated by Claude Code 2.1.85 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
… + 0px SVG

Root cause: new Raphael(div, "100%", "100%") in an off-screen div with no
explicit dimensions resolves "100%" to 0px.  Raphael always sets
overflow:hidden on the SVG (inline style), so all content drawn at y>0 is
clipped by the 0px viewport and never rendered visibly.

Fix: read container.offsetWidth/offsetHeight before creating the Raphael
paper, passing real pixel values so the coordinate system and overflow
clipping match the visible area.  Fall back to 800x600 if the container
has not yet been laid out (rare, but avoids a blank diagram on first mount).

Also change .o_diagram overflow from hidden to auto so that if the diagram
is larger than the viewport the user can scroll, consistent with the
original Odoo 13 behaviour.

Generated by Claude Code 2.1.85 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
Odoo 18 reuses the controller instance when navigating within the same
action, so onWillStart does not re-run. Add onWillUpdateProps to reload
fresh data on every re-activation. Also wire up Pager's updateTotal prop
so clicking the total triggers a manual refresh (replaces oe_pager_refresh).

Generated by Claude Code 2.1.85 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
The off-screen div approach created a fixed-pixel SVG that did not fill
the container, making nodes invisible when the container had different
dimensions. Rendering Raphael directly in .o_diagram (which has an
explicit height via position:absolute+inset:0 inside .o_diagram_canvas)
lets height="100%" on the SVG resolve to the full available space and
overflow:hidden clip at the correct boundary.

Generated by Claude Code 2.1.85 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
…iagrams

Adds a new Odoo 18 application that builds interactive dependency diagrams
from any recursive many2one field (e.g. res.partner / parent_id).

Models:
- web.diagram.builder        — main config: model, recursive field, domain
- web.diagram.builder.template — reusable presets (Partner Hierarchy etc.)
- web.diagram.builder.node   — computed nodes with flow_start for layout
- web.diagram.builder.link   — computed edges (parent → child direction)

BFS traversal follows the recursive field upward from the filtered
records to discover all ancestors, then creates nodes and links.  The
diagram view reuses web_diagram's graph_get layout engine.  An hourly
cron (inactive by default) can refresh all builders automatically.

Generated by Claude Code 2.1.85 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
numbercall was removed from ir.cron in Odoo 17+.

Generated by Claude Code 2.1.85 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
… elements

Odoo 18 validates <field> children of <node> and <arrow> against the
parent diagram model before _postprocess_tag_arrow/node can re-scope
them to the sub-model, causing a false validation error. Field children
are not needed for the diagram to work — the controller reads fields via
arch attributes (source, destination, label), not from field elements.

Generated by Claude Code 2.1.85 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
height="100%" on the SVG never resolves inside Odoo 18's Layout because
no CSS ancestor provides an explicit height. Use getBoundingClientRect()
to measure how much viewport space remains below the container and pass
that as an explicit pixel height to Raphael. Remove the o_diagram_canvas
wrapper and the position:absolute CSS chain that were added as workarounds
but caused nodes to be invisible.

Generated by Claude Code 2.1.85 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
Apply a Bootstrap 5 / Odoo 18 inspired colour palette and card look:
- Nodes: rounded corners (r=8), light border (#CED4DA), near-black text,
  soft blue-grey fill (#EEF0F4) instead of medium grey
- Selected state: Bootstrap primary blue (#0D6EFD), thicker stroke
- Edges: softer blue-grey (#8C96A5), explicit fill:none on path
- Typography: Helvetica Neue / Arial stack on all labels
- Close button / connector: muted grey tones

Two new style keys consumed by graph.js: node_border_radius, font_family.

Generated by Claude Code 2.1.85 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
…builder

node_count/node_ids and link_count/link_ids shared the same string label,
triggering Odoo's duplicate-label warning. Renamed to Node Count / Link Count.

Generated by Claude Code 2.1.85 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
When a start node has no outgoing transitions (isolated root with
flow_start=True but no children), make_acyclic returns [] and
tree_list[start] is empty. A previous start whose own tree has only
one edge trivially passes the same-tree subset check (empty [1:] slice),
so roots becomes non-empty and the code tries tree_list[start][0][1]
on the empty list — IndexError.

Guard the roots block with an additional check that tree_list[self.start]
is non-empty; isolated roots keep their position from tree_order() and
simply skip the multi-root alignment step.

Generated by Claude Code 2.1.85 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
Raphael sets overflow:hidden on its SVG element, clipping any node
placed beyond the initial paper height. When many nodes are present,
the layout algorithm places them at large y-coordinates (140px × rank)
that exceed the viewport-based height (~600px), making all but the
first few nodes invisible. Derive the required height from the actual
node coordinates before creating the Raphael paper so all nodes fit.

Generated by Claude Code 2.1.85 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
…n pass

Odoo 18 introduced a separate _validate_view pass that iterates all
children unconditionally. Without _validate_tag_node and _validate_tag_arrow,
field children inside <node> and <arrow> tags were validated against
the diagram model instead of their respective object models, causing
'Field does not exist in model' errors on install.
Recursive dependency diagram builder for any many2one field.
Includes node/link generation, template system, diagram and form views,
UX improvements (View Diagram button, help texts, success notification),
and bug fixes (domain, readonly, create=False on diagram).

Co-Authored-By: Manel Guechetouli <manelguechetouli@gmail.com>
Generated by Claude Code model claude-sonnet-4-6
Add import wizard (CSV → diagrams), import result/report models,
multi-diagram ZIP export via Action menu, and dedicated node/link form views.

Co-Authored-By: Manel Guechetouli <manelguechetouli@gmail.com>
Generated by Claude Code model claude-sonnet-4-6
Add fr.po with translations for UI labels, action names, selection
field values, and empty-state messages.

Co-Authored-By: Manel Guechetouli <manelguechetouli@gmail.com>
Generated by Claude Code model claude-sonnet-4-6
… menu)

Add bilingual help wizard accessible from the form stat button (saved
records) and from Configuration → Quick Guide menu (always accessible).

Co-Authored-By: Manel Guechetouli <manelguechetouli@gmail.com>
Generated by Claude Code model claude-sonnet-4-6
Add a wizard accessible from the Nodes tab that finds and visualizes
the path between any two nodes in the hierarchy using LCA algorithm.
Shows text path and colored bubble visualization in a popup.

Co-Authored-By: Manel Guechetouli <manelguechetouli@gmail.com>
Generated by Claude Code model claude-sonnet-4-6
Co-Authored-By: Manel Guechetouli <manelguechetouli@gmail.com>
Generated by Claude Code model claude-sonnet-4-6
Add overrides for <node>, <arrow> and <label> tags so Odoo 18 does not
reject them as invalid form elements. Includes unit tests.

Co-Authored-By: Manel Guechetouli <manelguechetouli@gmail.com>
Generated by Claude Code model claude-sonnet-4-6
…name as title)

Co-Authored-By: Manel Guechetouli <manelguechetouli@gmail.com>
Generated by Claude Code model claude-sonnet-4-6
Replace Raphael.js with Cytoscape.js + dagre layout for better
performance and UX on large diagrams (300+ nodes). Adds zoom/pan,
hierarchical dagre layout, and PNG export. Compact layout algorithm
replaced by dagre client-side layout.

Co-Authored-By: Manel Guechetouli <manelguechetouli@gmail.com>
Generated by Claude Code model claude-sonnet-4-6
Add a Help button in the diagram toolbar that opens a FormViewDialog
with navigation tips (mouse, keyboard, other). Content and title adapt
to the user's language (FR/EN). Add French translations for the
diagram view labels in web_diagram_builder.

Co-Authored-By: Manel Guechetouli <manelguechetouli@gmail.com>
Generated by Claude Code model claude-sonnet-4-6
Add OCA-required fields (author, website, maintainers, development_status),
pyproject.toml, readme/ fragments, and copyright header on __init__.py.
Remove invalid 'test' key from manifest.

Co-Authored-By: Manel Guechetouli <manelguechetouli@gmail.com>
Generated by Claude Code claude-sonnet-4-6
@mmaanneell
Copy link
Copy Markdown
Author

/rebuild

… key

Ajout de author, website, summary conformes OCA.
Suppression de la clé 'test' dépréciée et du doublon cytoscape-dagre dans assets.

Co-Authored-By: Manel Guechetouli <manelguechetouli@gmail.com>
Generated by Claude Code claude-sonnet-4-6
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.