• ErpClient.fieldOptions 调 /ai/fieldOptions(契约见 docs/erp-tasks-field-options.md):
    ERP 按其控件配置给候选,带行级数据权限、级联过滤(产品限定在已选客户名下)、
    联动回填映射——这三样本地直查库都没有。404/异常/mode=none 一律回落到
    FormResolverService.fkOptionPage,5 分钟退避避免反复撞未上线的端点。
    
    前端 FK 选择器改传 (formId, 字段, ctx):ctx = 表单上已绑定记录的 id,供级联过滤;
    ERP 报 need_context 时提示'请先选择客户'而不是给空列表。预览卡新增 formId 与
    fk 字段的 boundId,使修改单据时也有初始级联上下文。
    zichun authored
     
    Browse Code »
  • …expired key; preview save name-keyed + empty=abandon-change; previewId atomic claim; transactional batch queue + column truncation; digest zone token-budgeted; window-orphan tool_result trim; convId length cap; readFormData authoritative moduleId + /form/options permission gate (security audit); WRITE_CLAIM state-statement lookahead
    zichun authored
     
    Browse Code »

  • …p+CAS, tenant enforcement
    
    Audit findings 1-5 confirmed real; root causes were (a) identity self-reported by
    the client, (b) every failure path defaulting open.
    
    - ERP /ai/whoami (saas-8s+ @c8e0057 follow-up) resolves token → real user/tenant/type;
      AuthzService.resolveIdentity is now the single identity entry point. Client-supplied
      userid/brandsid/usertype removed from all request bodies and from chat.html.
    - dev-login is a real switch (erp.dev-login.enabled, default false) and its ERP creds
      no longer have built-in admin/666666 defaults; blank token in production → 401,
      never a silent fall back to the dev (sysadmin) account.
    - conversations/op/form endpoints require login; conversation ids are namespaced by
      user id and ownership-checked (403 otherwise); /op/pending no longer returns sPayload.
    - op confirm/cancel check the proposer, and confirm claims the draft via CAS so
      concurrent/repeat confirms cannot execute twice; bill numbers are regenerated at
      execution time instead of replaying the propose-time snapshot.
    - FK options take the tenant from the introspected identity and return empty rather
      than dropping the sBrandsId predicate.
    - secrets moved to env vars (DB_URL/DB_USERNAME/DB_PASSWORD/REDIS_*/LLM_*/ERP_BASEURL);
      allowMultiQueries=false. NOTE: the previously committed credentials must be rotated.
    - ids interpolated into ERP URLs are validated (safeId) to stop query/path injection.
    - update path rejects system columns, resolves FK names, and coerces by column type;
      numeric coercion now rejects unparseable input instead of writing 0/truncating, and
      proposal summaries show the value that will actually be written.
    - ResponseStatusException keeps its 401/403 status instead of being wrapped as 200.
    - anti-fabrication guard stays on when the intent gate itself fails.
    zichun authored
     
    Browse Code »
  • …r free-input flag, readFormData paging
    
    - POST /api/agent/form/submit: structured fields go straight to
      proposeWrite(action=create), no LLM re-encoding; ledger/state/memory recorded
    - GET /api/agent/form/options: {columns,rows,total,page,pageSize} with name search
    - askUser result carries allowFree=true (last option is always free text)
    - readFormData(page?) with page shown in output
    zichun authored
     
    Browse Code »

  • User: form rendered every field as a plain text box; no type (dropdown/date/number), and
    FK fields didn't pull options from their source tables.
    
    - FormCollect schema now carries per-field type: fkselect | select | number | date | text
      (+ options for select, fkTable for fkselect, hint for placeholder).
      Types inferred from column data type for generic forms; curated for quote.
    - New GET /api/agent/form/options?table=&q=&brandsid= -> FK options from the source table
      (elecustomer/eleproduct/elematerials...), whitelisted to tables that appear as sFkTable in the
      field dict (rejects arbitrary tables e.g. gdslogininfo), tenant-filtered + LIMIT.
    - chat.html renderFormCollect renders by type: fkselect = searchable <input list=datalist> that
      fetches options on focus/typing; select = <select>; number = <input type=number>; date picker; text.
    - Fix: tolerant label matching (strip parentheticals) so '多数量' maps whether or not the LLM keeps
      the '(逗号分隔)' hint -> multi-qty rows now reliably created.
    
    Verified via agent: quote form shows 客户/产品/物料=fkselect, 单位/印刷/颜色/单双面=select,
    尺寸/数量/单价/系数=number; options endpoint returns real names (客户 q=Little -> Little Antelope);
    non-FK table rejected; full write incl 多数量 (BJD202607086 -> 2000/4000/6000).
    zichun authored
     
    Browse Code »