Nama ERP DocsNama ERP Docs
Home
Namasoft.com
Data Model
GitHub
Home
Namasoft.com
Data Model
GitHub
  • Home

    • Search
    • Guides

      • بعض المعلومات عن استعمال هذا الموقع
      • Utility Links
      • System Minimum Requirements
      • Nama ERP Installation Guide
      • Troubleshooting System Hanging or Unresponsiveness
      • Jasper Reports Complete Guide for Nama ERP
      • Keyboard Shortcuts
      • Layout Names:
      • List Views
      • Approvals System - Approval Definition Full Guide
      • Entity Flows
      • دليل استخدام أداة إنشاء التقارير ونماذج الطباعة
      • BI Module — Dashboards & Analytics
      • Nama ERP BI Module — Technical Reference
      • Virtual Entities — Reusable SQL Building Blocks for Reports & Dashboards
      • الفواتير والضرائب والخصومات
      • Supply Chain
      • Manufacturing
      • Tempo Language Manual
      • Sending Invoices and Documents to Customers
      • SMS and WhatsApp Configuration in Nama ERP
      • Two-Factor Authentication (2FA) Guide
      • Field Filter with Criteria
      • تعديل الترجمات في نظام نما
      • Criteria Based Validation
      • صيغ الحضور والانصراف
      • الربط مع ماكينات الحضور والانصراف
      • Nama ERP Notification System
      • Scheduled Tasks (Task Scheduler)
      • Nama Properties
      • تطبيق الهواتف المحمولة
      • Mobile QR Integrator Guide
      • أعدادات الحقول و الشاشات - Generic Reference Overrider User Guide
      • ReportWizard User Documentation (أداة إنشاء تقرير)
      • ORACLE JDBC Integration Connection in context.xml for integration purposes
      • Nama ERP REST API
      • Criteria from Text Parser (Text Criteria Guide)
      • ضوابط إنشاء طلبات تطوير من قبل أقسام الدعم الفني والتجهيز بالشركة
      • e-commerce integration (Magento)
      • .
    • Examples
    • Reprocessing Transactions
    • Frequently Asked Questions
    • AI Generated Entity Flows Documentation

      • Core
      • Accounting Module
      • AI Module
      • Contracting Module
      • CRM Module
      • EGTax Reader Module
      • Freight Management System Module
      • Hospital Management System Module
      • HR Module
      • e-commerce Integration Module
      • Manufacturing Module
      • POS Module
      • Real Estate Module
      • Service Center Module
      • Supply Chain Module
      • .
    • Release Notes

      • 2016
      • 2017
      • 2018
      • 2019
      • 2020
      • 2021
      • 2022
      • 2025
      • .
    • Video Tutorials

      • Supply Chain Videos
      • Report Wizard Videos
      • Human Resources Videos
      • .

BI Reference — EnhancedTable

Companion to bi-module-technical-reference.md. Load this when authoring a widget with type: "EnhancedTable" (or its pivot/cross-tab mode).

EnhancedTable is the JSON-driven table widget — every column declared in chartConfigJSON with its own formatting, renderer, conditional formatting, pinning, grouping, aggregation. Reuses the BI interaction machinery (clickEmitMapping, drillDownMapping, linkMappings, clickAction — main reference §4–5b) with an optional column field on each entry to scope to a specific cell. Classic Table widget remains; opt in via "type": "EnhancedTable".

1. chartConfigJSON structure

{
  "tableOptions": { },
  "columnGroups": [ ],
  "columns": [ ],
  "rowConditionalFormatting": { "cascade": false, "rules": [ ] },
  "clickEmitMapping":  [ ],
  "drillDownMapping":  [ ],
  "linkMappings":      [ ],
  "clickAction":       { }
}
KeyRequiredDescription
columnsYesOrdered column definitions (§3 below). Defines grid column order.
columnGroupsNoArray of {id, headerArTitle, headerEnTitle, marryChildren, openByDefault}. Columns reference via groupId.
tableOptionsNoGrid-level options (§2). Defaults are reasonable.
rowConditionalFormattingNoPer-row formatting (§5).
clickEmitMapping / drillDownMapping / linkMappings / clickActionNoPer-cell column field scopes to one column; onCellClick: true fires on left-click. Prefer per-entry onCellClick over widget-level clickAction for tables — see main reference §5a.1.

No dataMapping / echartOption — EnhancedTable does not use ECharts.

2. tableOptions

"tableOptions": {
  "pagination":         false,
  "pageSize":           25,
  "pageSizes":          [25, 50, 100, 250],
  "rowNumbers":         true,
  "enableRowGroup":     true,
  "enablePivot":        false,
  "pivotOpenByDefault": false,
  "grandTotalRow":      "bottom",
  "defaultColWidth":    null,
  "defaultFlex":        1,
  "defaultMinWidth":    130,
  "wrapText":           true,
  "autoHeight":         true,
  "enableRtl":          "auto",
  "cellSelection":      true
}
FieldDefaultNotes
paginationfalseOff by default — load-all-and-scroll. When on, AG Grid paginates the in-memory data (no server-side paging).
grandTotalRownull"top" / "bottom" / null. Computed client-side from visible rows. Only columns with aggFunc set show a value in the grand-total row — others render blank.
enableRowGroup / enablePivottrue / falseAG Grid row-grouping / pivot modes. Columns need rowGroup: true / pivot: true to be grouped/pivoted by default.

3. Column definition

{
  "id":            "amountCol",
  "field":         "netValue",
  "wizardFieldId": "price.netValue",
  "groupId":       "salesGroup",
  "headerArTitle": "الصافي",
  "headerEnTitle": "Net Value",
  "hide":          false,
  "width":         120,
  "minWidth":      80,
  "maxWidth":      240,
  "flex":          null,
  "pinned":        null,
  "sort":          null,
  "sortIndex":     null,
  "rowGroup":      false,
  "rowGroupIndex": null,
  "pivot":         false,
  "aggFunc":       "sum",
  "tooltipField":  null,
  "formatting":    { },
  "renderer":      { },
  "conditionalFormatting": { }
}
FieldRequiredDescription
idYesStable identifier. Used by mappings (column field), compareColumn, AG Grid column ID.
fieldOne ofSQL column name from widget's dataSource.
wizardFieldIdOne ofWizard field ID (wizard mode). Resolved via cached displayAlias. See bi-reference-wizard-mode.md.
groupIdNoReferences columnGroups[].id.
headerArTitle / headerEnTitleNoLocalized header. Falls back to id.
hide, width, minWidth, maxWidth, flexNoAG Grid layout.
pinnedNo"start" / "end" / null. Logical pinning that flips with reading direction.
sort, sortIndexNoInitial sort.
rowGroup, rowGroupIndex, pivot, aggFuncNoAggregation. aggFunc: "sum", "avg", "min", "max", "count", "first", "last".
tooltipFieldNoid of another column whose display value is this cell's tooltip.
formattingNo§4.1 below.
rendererNo§4.2 below. Defaults to text.
conditionalFormattingNo§5 below.

4. Formatting & renderers

4.1 formatting

Server-computed display string; client uses verbatim.

"formatting": {
  "type":              "currency",
  "decimals":          2,
  "thousandSeparator": true,
  "currencyCode":      "SAR",
  "currencySymbol":    "SAR",
  "currencyPlacement": "suffix",
  "dateFormat":        "yyyy-MM-dd",
  "percentScale":      "asIs",
  "nullDisplay":       "—",
  "prefix":            "",
  "suffix":            ""
}
typeOutputExtra options
textunchanged—
number1,234.50decimals, thousandSeparator
currency1,234.50 SARdecimals, currencySymbol, currencyPlacement (prefix/suffix)
percent45.00%decimals, percentScale (asIs: 45→45%; fraction: 0.45→45%)
date2026-04-19dateFormat (SimpleDateFormat, default yyyy-MM-dd)
datetime2026-04-19 14:30dateFormat (default yyyy-MM-dd HH:mm)
duration1:23:45seconds input

prefix/suffix wrap the formatted string. nullDisplay replaces empty values (default "").

Note: The currency-symbol field here is currencySymbol. The ECharts dataMapping.series[].format block uses currency instead — they are not interchangeable. See main reference §7.

4.2 renderer

"renderer": {
  "type": "text" | "html" | "badge" | "bar" | "sparkline" | "progress" | "icon",
  "badge":    { "shape": "pill" | "square", "variant": "solid" | "outline" },
  "bar":      { "min": 0, "max": 100, "color": "#4caf50" },
  "progress": { "min": 0, "max": 100 },
  "icon":     { "mapping": [ { "when": "Approved", "icon": "check_circle", "color": "#2e7d32" } ], "position": "prefix" | "replace" },
  "sparkline":{ "type": "line" | "column" | "area", "lineColor": "#2196F3", "fill": "rgba(33,150,243,0.2)" }
}
TypeVisualNotes
textPlain stringDefault. No renderer block needed.
htmlRaw HTML via v-htmlTrust model matches legacy Param_INHTML — no client sanitization.
badgePill/square with cell textbg/color come from conditionalFormatting; falls back to subtle blue. outline variant pairs with conditional bg for tinted-pill effect.
barHorizontal filled bar via agSparklineCellRendererValue scaled min→max. max is optional — when omitted, renderer auto-scales to the column's data max (the common pattern).
progressSame mechanism as bar, blue fillSemantically "progress toward target".
sparklineMini multi-point chartReads series from the column's own SQL field — CSV string 1,5,9,12 or JSON array [1,5,9,12].
iconCell value matched against mapping[].whenMatched entry renders an icon (replaces text when position: "replace").

Important: bar, progress, sparkline need AG Grid Enterprise's SparklinesModule (already registered in the Nama Vue shell).

5. Conditional formatting

Rules evaluated server-side; winning style baked into the wire payload — client does zero rule evaluation.

"conditionalFormatting": {
  "cascade": false,
  "rules": [
    { "when": { "type": "threshold", "op": ">=", "value": 1000 },
      "style": { "bg": "#e8f5e9", "color": "#1b5e20", "bold": true } },
    { "when": { "type": "threshold", "op": "<", "value": 0 },
      "style": { "bg": "#ffebee", "color": "#b71c1c", "bold": true } },
    { "when": { "type": "range", "min": 0, "max": 100, "maxExclusive": true },
      "style": { "bg": "#fff3e0" } },
    { "when": { "type": "enum", "values": ["Cancelled", "Rejected"] },
      "style": { "bg": "#eeeeee", "italic": true } },
    { "when": { "type": "compareColumn", "op": ">", "column": "budgetCol" },
      "style": { "bg": "#ffebee", "color": "#b71c1c" } },
    { "when": { "type": "isNull" },
      "style": { "italic": true, "color": "#9e9e9e" } }
  ]
}

Rule types:

typeOperandsComparison
thresholdop, valueNumeric (or date if column's formatting.type is date/datetime). Coerces both sides; rule misses if coercion fails — never falls through to string.
rangemin, max, minExclusive?, maxExclusive?Same coercion.
compareColumnop, column (another column's id)Compare to another cell on same row.
enumvalues[]Case-sensitive string equality on trimmed cell value.
isNull / isNotNull—Tests raw value before coercion.

Operators: >, >=, <, <=, =, !=.

cascade: false (default) → first match wins, evaluation stops. true → every match merges on top of previous (later keys override).

Traffic-light recipe: with cascade: false (default), order rules tightest first so the most-extreme condition wins:

"rules": [
  { "when": { "type": "threshold", "op": "<",  "value": 0   }, "style": { "bg": "#991b1b", "color": "#ffffff", "bold": true } },
  { "when": { "type": "threshold", "op": "<=", "value": 30  }, "style": { "bg": "#fecaca", "color": "#991b1b", "bold": true } },
  { "when": { "type": "threshold", "op": "<=", "value": 60  }, "style": { "bg": "#fde68a", "color": "#92400e" } },
  { "when": { "type": "threshold", "op": ">",  "value": 60  }, "style": { "bg": "#bfdbfe", "color": "#1e40af" } }
]

Style vocabulary (no raw CSS — small fixed set):

KeyCSS
bgbackground-color
colorcolor
boldfont-weight: 700
italicfont-style: italic
underlinetext-decoration: underline
borderborder (shorthand, trusted string)
aligntext-align (start/center/end)

5.1 rowConditionalFormatting

Same vocabulary, applied per row. Each rule must name the test column via when.column — there is no implicit "this cell". Winning style applied to whole row via AG Grid's getRowStyle.

"rowConditionalFormatting": {
  "cascade": false,
  "rules": [
    { "when": { "type": "threshold", "column": "marginCol", "op": "<", "value": 0 },
      "style": { "bg": "#ffebee" } },
    { "when": { "type": "enum", "column": "statusCol", "values": ["Cancelled"] },
      "style": { "bg": "#fafafa", "italic": true } }
  ]
}

6. Complete example

{
  "tableOptions": {
    "pagination": false, "rowNumbers": true,
    "enableRowGroup": true, "grandTotalRow": "bottom"
  },
  "columnGroups": [
    { "id": "customerGroup", "headerArTitle": "بيانات العميل", "headerEnTitle": "Customer", "marryChildren": true }
  ],
  "columns": [
    { "id": "invoiceCode",  "field": "invoiceCode",  "headerArTitle": "الفاتورة", "headerEnTitle": "Invoice", "pinned": "start", "width": 120 },
    { "id": "valueDate",    "field": "valueDate",    "headerArTitle": "التاريخ",  "headerEnTitle": "Date", "formatting": { "type": "date" }, "width": 110 },
    { "id": "customerName", "field": "customerName", "groupId": "customerGroup", "headerArTitle": "العميل", "headerEnTitle": "Customer" },
    { "id": "branchName",   "field": "branchName",   "groupId": "customerGroup", "headerArTitle": "الفرع",  "headerEnTitle": "Branch" },
    { "id": "qty", "field": "qty", "headerArTitle": "الكمية", "headerEnTitle": "Qty",
      "formatting": { "type": "number", "decimals": 0 },
      "renderer":   { "type": "progress", "progress": { "min": 0, "max": 50 } },
      "aggFunc": "sum", "width": 140 },
    { "id": "netValue", "field": "netValue", "headerArTitle": "الصافي", "headerEnTitle": "Net",
      "formatting": { "type": "currency", "decimals": 2, "currencySymbol": "SAR" },
      "aggFunc": "sum",
      "conditionalFormatting": { "rules": [
        { "when": { "type": "threshold", "op": ">=", "value": 10000 }, "style": { "bg": "#e8f5e9", "color": "#1b5e20", "bold": true } },
        { "when": { "type": "threshold", "op": "<",  "value": 0 },     "style": { "bg": "#ffebee", "color": "#b71c1c", "bold": true } }
      ] } },
    { "id": "tier", "field": "tier", "headerArTitle": "الفئة", "headerEnTitle": "Tier",
      "renderer": { "type": "badge", "badge": { "shape": "pill" } },
      "conditionalFormatting": { "rules": [
        { "when": { "type": "enum", "values": ["High"] },   "style": { "bg": "#2e7d32", "color": "#ffffff" } },
        { "when": { "type": "enum", "values": ["Medium"] }, "style": { "bg": "#f9a825", "color": "#ffffff" } },
        { "when": { "type": "enum", "values": ["Low"] },    "style": { "bg": "#9e9e9e", "color": "#ffffff" } }
      ] } }
  ],
  "rowConditionalFormatting": { "rules": [
    { "when": { "type": "threshold", "column": "netValue", "op": ">=", "value": 50000 }, "style": { "bg": "#fff8e1", "bold": true } }
  ] },
  "clickEmitMapping": [
    { "crossFilterCode": "customerFilter", "column": "customerName", "valueColumn": "customerName" }
  ]
}

7. Migration from Table

No automatic upgrade. Per-widget opt-in:

  1. Change type from Table to EnhancedTable.
  2. Open chart-config editor → Table Columns tab → click Generate Columns From Result Set.
  3. Customize formatting / renderers / conditional formatting.

Existing clickEmitMapping / drillDownMapping / linkMappings continue to work — column matches auto-generated column ids (which default to the SQL column name).

8. Pivot (cross-tab) layout

Pivot mode turns an EnhancedTable into a JasperReports-style cross-tab: row dimensions become row groups, column dimensions become nested column-group headers, measures fill cell intersections — with optional subtotals at each level and a grand total. The hand-authored columns block is ignored when pivot is set; the engine synthesizes columns + nested groups + row plan server-side.

8.1 Configuration

{
  "pivot": {
    "rowDimensions":      [ { "field": "sCode", "headerEnTitle": "Section" }, { "field": "iCode", "headerEnTitle": "Item" } ],
    "colDimensions":      [ { "field": "bCode", "headerEnTitle": "Branch" }, { "field": "wCode", "headerEnTitle": "Warehouse" } ],
    "measures": [
      { "field": "netQty",  "headerEnTitle": "Qty",  "aggFunc": "sum", "formatting": { "type": "number",   "decimals": 2 } },
      { "field": "netCost", "headerEnTitle": "Cost", "aggFunc": "sum", "formatting": { "type": "currency", "decimals": 2, "currencySymbol": "SAR" } }
    ],
    "useRowGrouping":     true,
    "rowSubtotals":       true,
    "colSubtotals":       true,
    "rowGrandTotal":      "bottom",
    "colGrandTotal":      "end",
    "subtotalLabelKey":   "biSubtotal",
    "grandTotalLabelKey": "biGrandTotal",
    "emptyCellAs":        null,
    "zeroAsEmpty":        true
  }
}
FieldDefaultNotes
rowDimensionsrequiredOrdered, outermost first. Each: field (raw SQL alias) or wizardFieldId, optional headers + formatting.
colDimensionsrequiredSame shape; ordering controls header nesting.
measuresrequiredEach: field/wizardFieldId, headers, aggFunc (default sum; v1 supports sum only), formatting, optional conditionalFormatting (applied to leaf, subtotal, and grand-total columns derived from this measure).
useRowGroupingtrueWhen true and ≥2 row dims, outer N-1 dims become AG Grid row groups; innermost is the leaf row. With one row dim, grouping is a no-op — backend falls back to flat rows + baked subtotal/grand-total rows.
rowSubtotalsfalsePer-group subtotal footer rows. With grouping active → AG Grid renders client-side. Without → backend bakes them in.
colSubtotalsfalseSubtotal columns at each non-leaf col-dim level. Always backend-rendered (AG Grid can't synthesize cross-tab columns).
rowGrandTotalnull"top" / "bottom" / null. With grouping: AG Grid grandTotalRow. Without: baked into result set.
colGrandTotalnull"start" / "end" / null. Logical — start = leading edge in either reading direction.
subtotalLabelKey / grandTotalLabelKeyhardcodedi18n keys; frontend resolves via Translator.translate(). Falls back to Subtotal / Grand Total (and Arabic).
emptyCellAsnullWhat no-data cells render: null (blank), 0, "-", etc.
zeroAsEmptyfalseMeasure aggregating to exactly 0 → render as emptyCellAs.

8.2 What the engine emits

Walks rows once, builds distinct row-tuple/col-tuple trees (alphabetically sorted per level), re-aggregates (rowTuple, colTuple) → measures via sum, emits:

  • Synthetic columns: M row-dim columns (pinned start) + leaf measure columns interleaved with col-subtotal columns (per non-leaf prefix) + col grand-total columns at chosen end. Stable IDs like pv:bCode=B1.wCode=W1:m=netQty.
  • Nested columnGroups with parentGroupId: one per non-leaf col-tuple prefix.
  • Synthetic rows: with grouping, only leaf data rows; without, leaf + subtotal + grand-total rows interleaved.
  • enhancedTableData.styles: total rows/cols tagged via t flag (0=grand, n>0=subtotal at depth n); default bold + tint applied. Explicit conditionalFormatting always wins.
  • agColumnCellDataTypes: every measure column tagged "number" so cell values flow as JSON numbers (required for aggFunc aggregations on group rows).

8.3 Wizard mode

In wizard mode (widget has wizardDataSource), use wizardFieldId instead of field on every dim/measure. WizardChartConfigRewriter resolves to cached SQL alias (displayAlias) before the engine runs.

8.4 Interaction skipping on totals

Subtotal and grand-total rows + columns are derived aggregates with no underlying source row, so click-emit / drill-down / link / drill-down-by all skip them. Detected via t flag on row data (_totalLevel) and column config (t). Cursor hint also disabled.

8.5 Coexistence with AG Grid client-side pivot

tableOptions.enablePivot (drag-pivot at runtime) is unrelated and stays untouched. Use it for free-form exploration; use the pivot block for author-defined cross-tab reports.

8.6 Complete pivot example

select w.code wCode, b.code bCode, i.code iCode, s.code sCode,
       sum(q.net) netQty, sum(q.netCost) netCost
from   ItemDimensionsQty q
left join InvItem      i on i.id = q.item_id
left join ItemSection  s on s.id = i.section_id
left join Warehouse    w on w.id = q.warehouse_id
left join Branch       b on b.id = w.branch_id
group  by w.code, b.code, i.code, s.code
{
  "pivot": {
    "rowDimensions": [
      { "field": "sCode", "headerEnTitle": "Section",   "headerArTitle": "القسم" },
      { "field": "iCode", "headerEnTitle": "Item",      "headerArTitle": "الصنف" }
    ],
    "colDimensions": [
      { "field": "bCode", "headerEnTitle": "Branch",    "headerArTitle": "الفرع" },
      { "field": "wCode", "headerEnTitle": "Warehouse", "headerArTitle": "المخزن" }
    ],
    "measures": [
      { "field": "netQty",  "headerEnTitle": "Qty",  "headerArTitle": "الكمية",  "aggFunc": "sum", "formatting": { "type": "number", "decimals": 0 } },
      { "field": "netCost", "headerEnTitle": "Cost", "headerArTitle": "التكلفة", "aggFunc": "sum",
        "formatting": { "type": "currency", "decimals": 2, "currencySymbol": "SAR" },
        "conditionalFormatting": { "rules": [
          { "when": { "type": "threshold", "op": ">=", "value": 1000000 }, "style": { "bold": true, "color": "#1b5e20" } }
        ] } }
    ],
    "useRowGrouping": true, "rowSubtotals": true, "colSubtotals": true,
    "rowGrandTotal": "bottom", "colGrandTotal": "end",
    "zeroAsEmpty": true, "emptyCellAs": "-"
  }
}
Edit On github
Last Updated:: 5/10/26, 3:37 AM