Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Web app

Explanation

Metadata rendering

How the web app turns the server's metadata document into module tabs, lists, forms, record cards, trees, settings screens, and actions.

The web app has no screen code per entity. It reads the metadata document that every module publishes, prunes it for the signed-in user and the tenant, and hands each part to a generic engine. This page explains that path from GET /api/v1/_meta to a painted screen. The metadata classes themselves are listed on metadata model.

In short

  • loadMeta() loads /_meta, /_switches, and /_overlay once per session and keeps the result in memory (webapp/js/meta.js:16-47).
  • Three passes run before any screen reads the document: permission pruning, display-switch pruning, and the tenant field overlay (webapp/js/meta.js:29-38).
  • A module app turns each sidebar section into a tab by its view_type (webapp/js/module-app.js:101-139).
  • A list row opens a record card built from detail_view.tabs; a card's edit mode is the generic form built from form.field_groups (webapp/js/gen-list.js:816-844, webapp/js/gen-detail.js:63-139).
  • Labels resolve through resolveLabel, which picks the current language and falls back to English (webapp/js/meta.js:284-288).

How it works

From document to screen

The flowchart shows which engine renders which part of the metadata.

flowchart TD
  meta["GET /_meta"] --> load["loadMeta"]
  sw["GET /_switches"] --> load
  ov["GET /_overlay"] --> load
  load --> prune["pruneGatedFields and pruneSwitchedOff"]
  prune --> overlay["applyFieldOverlay"]
  overlay --> app["makeModuleScreen"]
  app -->|"view_type list or tabbed_list"| list["makeGenList"]
  app -->|"view_type tree"| tree["makeGenTree"]
  app -->|"view_type settings"| settings["makeGenSettings"]
  app -->|"view_type custom"| custom["getCustomWidget"]
  list -->|"row tap"| detail["makeGenDetail"]
  detail -->|"form tabs"| card["mountRecordForm"]
  card -->|"edit or create"| form["createGenForm"]
  list -->|"actions"| sheet["openActionSheet"]

The same content as steps:

  1. loadMeta() stores the parsed document in raw (webapp/js/meta.js:27-28).
  2. pruneGatedFields removes form fields and action body fields that name a permission the user does not hold (webapp/js/meta.js:53-74). The comment states that the API strips such values for everyone else, so the form would otherwise show an empty, unwritable box.
  3. pruneSwitchedOff removes fields, list columns, and sub-table columns whose switch is off for the tenant. An unknown switch counts as on (webapp/js/meta.js:80-105).
  4. applyFieldOverlay applies the tenant's field decisions: switched-off shipped fields leave the form and the list, tenant-defined fields join their form group with storage: "extra", optional list columns and filters are added, and closed fields are kept for read-only display (webapp/js/meta.js:117-231). See tenant fields.
  5. makeModuleScreen flattens the module's sidebar into tabs (webapp/js/module-app.js:93-139). See module apps and app scope.
  6. Each tab's render function paints into its own pane, so a late answer from a previous tab lands in a detached node (webapp/js/module-app.js:183-192).

Section types and their engines

SidebarSectionMeta.view_type Engine Uses in module metadata Source
list makeGenList 84 webapp/js/module-app.js:115-116
tabbed_list one makeGenList tab per entity_tabs entry 1 webapp/js/module-app.js:109-114
tree makeGenTree 1 webapp/js/module-app.js:117-118
settings makeGenSettings over tabs 9 webapp/js/module-app.js:119-125
custom the widget registered for custom_widget 29 webapp/js/module-app.js:126-137
detail none; the module app skips it 0 webapp/js/module-app.js:109-137

The usage counts were made with grep -rn 'view_type="<value>"' over src/cluster_erp/modules. A section with hidden_from_bar gets no tab; a section with required_permission gets no tab for a user without that code (webapp/js/module-app.js:105-108).

Entity parts and their engines

Metadata part Rendered by Page
ListViewMeta (columns, filters, search_enabled, paginated) makeGenList generic list
FormMeta and FieldGroupMeta createGenForm, makeGenFormView, mountRecordForm generic form
DetailViewMeta and DetailTabMeta makeGenDetail generic detail
TreeViewMeta makeGenTree generic tree and settings screens
ActionMeta openActionSheet, runEntityAction actions and action sheet
RecordLinkMeta the record card's link entries, raised as cluster:open-section generic form
FacetMeta facet rows on the party card generic detail
AvatarMeta avatar.js generic detail
HomeWidgetMeta the home screen widget registry home widgets
ModuleMeta.provides providerOf(capability) record documents, viewer, and share

Labels and titles

resolveLabel(label) returns the label in the current language, then the English label, then an empty string (webapp/js/meta.js:284-288). moduleLabel uses the first sidebar group label and falls back to the raw name in title case (webapp/js/meta.js:296-306). recordTitle(row, entityMeta) uses detail_view.title_field, then the first non-empty list column, then display_name, name, title, or code, and never an id (webapp/js/meta.js:392-414). langLabel(row) reads label_<lang> columns of reference rows with English and the code as fallbacks (webapp/js/meta.js:418-421).

Capability lookup

providerOf(capability) finds the module whose ModuleMeta.provides declares a capability, so generic code never names the module that offers it (webapp/js/meta.js:423-432). The record documents feature uses the capability record_documents (webapp/js/record-docs.js:23-37). tests/test_metadata_driven_rules.py:63 checks that this provider is declared by capability.

Overlay refresh

When a tenant field definition or setting changes, meta.js listens for the data-changed signal with entity type tenant_field_definition or tenant_field_setting and re-applies the overlay to a rebuilt baseline (webapp/js/meta.js:233-256). Open screens keep the metadata they were built with until they are opened again (webapp/js/meta.js:233-235).

Why it is built this way

  • The project's written engineering rules require that screen, form, list, and action behaviour comes from metadata. See metadata-driven UI.
  • The comment in webapp/js/meta.js:31-35 states that the baseline is rebuilt on demand instead of copied at boot because a copy of the 3 MB document cost about 100 ms on a desktop.
  • The server drops None values from the document. The comment in src/cluster_erp/server/app.py:1534-1537 (at commit 92f8051) states that nulls were half of the document and every client reads a missing key as not set.

Rules and boundaries

  • Generic engines name no business module. tests/test_architecture_boundaries.py:75 scans gen-list.js, gen-detail.js, gen-form.js, entity.js, and gen-settings.js.
  • The server checks at start-up that a sub_entity_table tab points at an entity with columns (src/cluster_erp/server/app.py:547-589 at commit 92f8051).
  • The metadata document is built once per server process. A metadata change takes effect after a restart (src/cluster_erp/server/app.py:1530-1543).

Interaction with other parts

  • The desktop client renders the same document; see the desktop section.
  • Permission codes that the client checks with hasPermission are seeded from the same metadata; see roles and permissions.

Limits and open points

  • EntityMeta.lock (RecordLockMeta) is not read by any file in webapp/js/; a search for .lock found no reader. The lock is enforced by the server's services.
  • A custom section whose widget is not registered is skipped with a console error instead of a broken tab (webapp/js/module-app.js:126-137).

Terms introduced

Term Code identifier Definition Turkish term Source
metadata store loadMeta The client-side copy of the server metadata that is loaded once, pruned, and served to the generic screens. metadata deposu webapp/js/meta.js:16
field overlay applyFieldOverlay The pass that lays the tenant's field decisions over the shipped forms and lists. alan katmanı webapp/js/meta.js:117
capability provider providerOf The lookup that finds the module offering a declared capability. yetenek sağlayıcısı webapp/js/meta.js:425

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13