define-form.md 5.05 KB

How to define a form

A recipe for the metadata rows that produce a working module + form in BACK and FROUNT. Use this when you want to add a new screen without writing Java.

What you'll insert

Three rows, in order, in three tables. Plus optional rows for fields, permissions, and document numbering.

1. The module — gdsmodule

One row registers the module's existence. Required columns:

Column Value
sId unique ID — use the IdGen next-id helper, or any 32-char string not already taken
sName the URL fragment, e.g. /indexPage/commonList (use a shared page-template URL — see Slice 3)
sChinese / sEnglish / sBig5 display name in three languages
sParentId parent module's sId — places this module in the menu tree
sBrandsId / sSubsidiaryId tenant scope — should be your tenant's IDs (or '1111111111' if standard / system-level)
sVersionFlowId / sVersionFlowCode product-edition catalogue tags (look up in sisversionflow where populated); live menu visibility is still gated by the licence-derived module list
bVisible 1 to show in the menu
bInvalid 0 for active

Leave sSaveProName, sDeleteProName, sCalcProName, sProcName, sSaveProNameBefore empty unless you need custom CRUD (see the runtime reference).

2. The form-master — gdsconfigformmaster

One row per form per module. Required:

Column Value
sId unique form ID
sParentId the module's sId from step 1
sTbName the backing object's name — a real table, view, or proc
sType 'table', 'view', or 'proc'
sSqlStr the read SQL skeleton; usually SELECT ... FROM {sTbName} WHERE ...
sWhere default WHERE predicates (the runtime adds tenant filters automatically)
sOrder default ORDER BY
iPageSize rows per page in the grid
bGrd 1 if a grid layout, 0 if a single-record form
sBrandsId / sSubsidiaryId tenant scope

For a read-only report, use sType = 'view' and point sTbName at a viw_* view. The framework will not render save/delete buttons.

3. The fields — gdsconfigformslave, one row per field

Most-used columns (the table has 60+, most have sane defaults):

Column Value
sId unique field ID
sParentId the form's sId from step 2
iOrder sort order in the grid
sName the column name on the backing object
sChinese / sEnglish / sBig5 display label
sControlName UI control type — 文本框 (textbox), 下拉框 (dropdown), 日期 (date), 数字 (number), …
bVisible 1 to show
bNotEmpty 1 to require
bReadonly 1 for display-only
bCanInput 1 for editable
iColValue column-span in form layout
sDefault default value
sChineseDropDown dropdown SQL (if applicable) — SELECT sId AS sValue, sChinese AS sText FROM …
sActiveId / sActiveKey for popup-lookup controls — refers to another module

Optional layers

Document numbering — sysbillnosettings

If the module's records need auto-generated bill numbers (work-order numbers, quotation numbers, etc.), add a row here keyed by the form's sId. The runtime returns this in the getModelBysId response under billnosetting.

Permissions — gdsjurisdiction

See How to set permissions. One row per (module, button-or-data, role) tuple.

Tenant overrides

A tenant who wants a different view of this form inserts rows into gdsconfigformpersonalize (form-level override) or gdsconfigformcustomslave (field-level override). See Slice 4.

Verifying

Once all rows are inserted, the SPA's next request to getModelBysId/{your-module-sId} should return your form's metadata. Click the new sidebar item — the form should render. If it doesn't:

  1. Check bVisible = 1 on gdsmodule.
  2. Check that gdsroute has an entry for the URL (one is auto-added in most deployments).
  3. Check sBrandsId/sSubsidiaryId on every row match the user's tenant.
  4. Check that the form's sParentId matches the module's sId exactly — semantic FK, no cross-row check at write time.

Cache invalidation

After inserting, the runtime's cache holds the previous (empty) state until something clears it. When BACK saves the change, the save service synchronously calls BusinessCleanRedisData.delCleanRedisData*, which fires @CacheEvict on the relevant cache regions in CleanRedisServiceImpl. If you're inserting via raw SQL, no eviction runs — you'll need to either invoke BusinessCleanRedisDataImpl methods directly from inside the application, bounce the running services, or wait for the TTL to expire. (The JMS path with the similarly-named ConsumerChangeGdsModuleThread does base-data merging via stored proc, NOT cache invalidation despite the name — see Cache invalidation on metadata change.)