Skip to content

3. The equipment register entity

The entity

spk-assembly/metadata/entities/oeq_equipment.json (real file):

json
{
  "entity": {
    "name": "oeq_equipment",
    "tableName": "oeq_equipment",
    "label": "{\"en\":\"Equipment\"}",
    "category": "office-equipment",
    "icon": "devices",
    "color": "#2563EB",
    "pkStrategy": "identity"
  },
  "fields": [
    { "fieldName": "asset_tag", "label": "\"Asset Tag\"", "dataType": "text", "nullable": false, "required": true, "isUnique": true, "isIndexed": true, "isDisplayField": true, "displayOrder": 1 },
    { "fieldName": "name", "label": "\"Name\"", "dataType": "text", "nullable": false, "required": true, "displayOrder": 2 },
    { "fieldName": "category_id", "label": "\"Category Id\"", "dataType": "long", "nullable": true, "required": false, "isIndexed": true, "displayOrder": 3 },
    { "fieldName": "category_name", "label": "\"Category\"", "dataType": "text", "nullable": true, "required": false, "displayOrder": 4 },
    { "fieldName": "serial_number", "label": "\"Serial Number\"", "dataType": "text", "nullable": true, "required": false, "displayOrder": 5 },
    { "fieldName": "status", "label": "\"Status\"", "dataType": "enum", "nullable": false, "required": true, "isIndexed": true, "defaultValue": "AVAILABLE", "typeParams": "{\"enumValues\":[\"AVAILABLE\",\"ASSIGNED\",\"MAINTENANCE\",\"RETIRED\"]}", "displayOrder": 6 },
    { "fieldName": "purchase_date", "label": "\"Purchase Date\"", "dataType": "date", "nullable": true, "required": false, "displayOrder": 7 },
    { "fieldName": "warranty_expiry_date", "label": "\"Warranty Expiry Date\"", "dataType": "date", "nullable": true, "required": false, "displayOrder": 8 },
    { "fieldName": "notes", "label": "\"Notes\"", "dataType": "text", "nullable": true, "required": false, "displayOrder": 9 }
  ]
}

Design notes

  • category_id (long) + category_name (text) — we denormalize the category name so the grid can show it without a join. The create dialog writes both. (The alternative — a reference flag — needs a registered provider; chapter 5 uses that for the employee field, where one exists.)
  • status enum with AVAILABLE as default.
  • asset_tag is isDisplayField and isUnique.

The provider

Every entity you want a grid over needs a data provider. spk-assembly/metadata/provider/oeq-equipment-provider.json (real file):

json
{
  "name": "oeq-equipment-provider",
  "description": "Data Provider for oeq_equipment (grid + CRUD).",
  "definition": {
    "kind": "rest",
    "connectionRef": "self",
    "basePath": "/api/v1/entities/oeq_equipment/records",
    "supports": ["search", "get", "create", "update"]
  },
  "metadata": {}, "modules": []
}

The category lookup plumbing

The register page's create dialog will have a category picker. That needs a Data View producing { value, label } pairs and a search Data Service over it.

spk-assembly/metadata/data_view/oeq-category-select-options-view.json (real file) — selects t.id as value and a concatenated category_code - category_name as label.

spk-assembly/metadata/data_service/oeq-category-search.json (real file):

json
{
  "name": "oeq-category-search",
  "description": "Typeahead category search for the category lookup field.",
  "definition": {
    "operation": "search",
    "source": { "kind": "dataView", "dataViewName": "oeq-category-select-options-view" },
    "filters": [ { "field": "t.category_name", "operator": "contains", "value": "${param.search}" } ],
    "parameters": [ { "name": "search", "type": "string" } ]
  },
  "metadata": {}, "modules": []
}

The KPI Data Services

The register page shows Total / Available / Assigned. Three count services plus a composite:

  • oeq-equipment-count-total.jsoncount, no filter
  • oeq-equipment-count-available.jsoncount, filters: [{ "field": "status", "operator": "eq", "value": "AVAILABLE" }]
  • oeq-equipment-count-assigned.jsoncount, filter status = ASSIGNED
  • oeq-equipment-kpis.jsoncomposite with three steps total/available/assigned

(all four real files)

Verify

bash
node developer-docs/examples/test-examples.mjs

validates every one of these files against its schema. After publishing:

bash
$ erp api post "/api/v1/data-services/oeq-equipment-kpis/execute" --body "{}"
{ "results": { "total": { "value": 0 }, "available": { "value": 0 }, "assigned": { "value": 0 } } }

That results.<step>.value shape is what the page will bind.

Next: 4. The equipment register page