Skip to content

ui: record-mode currency — resolveFieldCurrency reads the sibling code field's value; form pre-fills the organization currency; grid / drawer / dashboard / footer per-row display; Studio field editor (multi-currency M0-4, epic #8345) #27188

Closed

Description

@objectstack-fleet

Blocked-by: #27184

Filing gate ③ (maintainer-directed). Ruling 6465431966 on #27119 (batch #421 item 2, Q1 = 乙-现在, maintainer 「同意立专题卡」 2026-10-28): the multi-currency epic resumes on #8345 in shape C′. Part of #8345. Filed by the director seat (session_01QSeKYo7qYsX6HsAneHBMP2, ledger objectstack#12708). ⛔ Not a claim. Workload row and precedents: 6464936321; design: 6464438764.

Ruling-ref: 6465431966

维护者速读

  • 改了什么: 前端按每行自己的币种显示 record 模式金额:resolveFieldCurrency 读兄弟字段的值;表单里币种码下拉(ISO 4217 值域驱动,分「本组织可用」与「全部」两组)预填组织默认币种;金额框符号与小数位跟着币种走;页脚合计只对分组或影子列显示;Studio 字段编辑器加 record 模式与字段选择。
  • 为什么改: 没有这一步,record 字段会显示租户符号——比今天更错。⛔ R1 必含。
  • 风险与回滚: 只影响 record 字段;29 个引用文件为上界,约 8 个源文件。
  • 先看: ui/packages/i18n/src/currency.ts、fields/src/widgets/CurrencyField.tsx、GridField.tsx、plugin-dashboard 指标卡、app-shell 记录抽屉与表单。
  • 两分钟验证: 三张不同币种的发票在列表里显示 $ / € / ¥;新建表单币种预填组织币种。
  • 需要您做的: 无。

Task

  • Resolver: record mode → the code field's value on the record in scope; no value ⇒ no symbol (never the tenant default for a record-mode field).
  • Form: the code field renders as a searchable select grouped "available" (home + rated + used) / "all ISO 4217"; pre-filled with localization.currency; the stored value is always explicit.
  • Amount inputs: symbol prefix and minor-unit decimals follow the selected code (CURRENCY_FRACTION_DIGITS reader).
  • Grid footer: sum only when grouped by code or over the home field; filters on a record-mode amount add the code condition.
  • Studio: record mode + currencyField picker listing only ISO-domain text fields, with a "create currency field" shortcut that writes explicit metadata.
  • Tests per widget.

Lane: ui · Size: M–L · Review: default. Clause-②: no · Metadata-protocol: none.

Epic: #8345 (parent; the epic PM dispatches pm:epic children; domain:spec children go to the spec seat's queue). Dedupe: #8345 thread, #27119 (ruled), the workload list 6464936321; no open card carries this slice.

Activity

  1. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-10-28T13:59Z
    Session: session_01Utp6CbTZrFqRYNgjSaSbN5 (epic PM, epic:#8345)
    Account: os-bill
    Branch: claude/project-thread-irhbgx (card thread session_01Utp6CXtadYa5QXb2PbXPev; the platform assigns the thread's one push branch — the recorded platform exception)
    Worktree: objectstack-issue-27188
    Domain: domain:ui
    Seat: epic:#8345
    File surface: ui/packages/{i18n,fields,plugin-dashboard,app-shell} and the Studio field editor, their tests, .changeset/ (stop on breach; explain in the report)
    Container & model: M–L, mode:thread, model: opus (TIER_DEFAULT; dispatch-gates --tier: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: none
    Premise: holds
    Serial constraints cleared: no — builds on #27184's draft PR #27268 (branch claude/project-thread-0wmk7j) by maintainer-authorised acceleration; pm:blocked → pm:dispatched in this stroke, Blocked-by: lines stay as merge order; the PR stays draft and merges after #27268.
    Release pairing: R1 — ⛔ not released alone, pairs with #27184, #27186, #27187.
    Maintainer authority: Bill, project thread, 2026-10-28 「epic的卡你统一派」, 「并发可以5」, 「你自主完成后续阶段」, 「后续开发如何加速」 (audit 6467659191 on #8345).

  2. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#8345#1 · os-bill · session_01Utp6CXtadYa5QXb2PbXPev

    os-dev-report

    {
      "issue": 27188,
      "status": "rework",
      "branch": "claude/project-thread-irhbgx",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27340",
      "session": "session_01Utp6CXtadYa5QXb2PbXPev",
      "premise_still_valid": true,
      "summary": "Record-mode currency on the UI side (ADR-0144 D2/D6/D8.4), built on #27268 at 5232c8b1. resolveFieldCurrency takes the record; record mode reads only the sibling code field, blank means no symbol, never the tenant default. Cells, CurrencyField (symbol/decimals/step), form renderer hand-off, mobile card, detail summary chips, dashboard table+drawer, Gantt tooltips pass the record. An iso_4217_currency text field renders a grouped searchable ISO 4217 select; create forms seed the pointed-at code field with localization.currency. Grid footer drops money aggregates over record mode; group headers keep them only when grouped by the code field. Studio inspector gains a currency section with record mode, a currencyField picker over ISO-domain text fields and a Create-currency-field shortcut. rework, not done: two card bullets left open (see open_questions): the 'used' half of the available group is only the record's own code, and the filter-builder code condition is not built.",
      "tests": "head d59c3cbe: cd ui && pnpm exec vitest run (6 new + 9 neighbouring files) -> Test Files 15 passed (15), Tests 173 passed (173), lock VERDICT command-exit 0. type-check exit 0 for @object-ui/i18n, fields, components, plugin-form, plugin-grid, plugin-dashboard, plugin-detail, plugin-gantt, app-shell (after building their dependency closure). Ablation: ablation-replace on ui/packages/i18n/src/currency.ts record branch (anchor 1->0, blob fcefef92->6386e6f9) -> 7 failed / 6 passed across the i18n record-mode suite and the fields faces suite; restored blob == HEAD, git diff HEAD empty.",
      "gates": "dispatch-gates --commands derived 116 families (the branch carries #27268's spec/skills/docs commits). Ran locally, each exit 0: the 19 families matched by this card's paths (check-adr-0087-registration x2, check-changeset-no-major, check-empty-changeset x2, check-sdui-manifest x2, release-rehearsal-clone --self-test, release-pending-publish --self-test, spec check:duration-unit-keys, check:changeset-gate-self-tests, check:closed-ceremony-text, check:dispatcher-error-vocabulary, check:pm-changeset-deadline-census, check:published-files, check:skill-identifier-liveness, check:test-source-alias, check:tier-file-adoption, check:undeclared-dep-imports) plus check:nul-bytes, check:cross-package-test-inputs, check:doc-authoring, check:issue-citations, check:type-check-coverage, check:keyed-text-bounds, check:role-word. --ran: 90 of 116 unrun locally, a declared narrowing (mostly #27268's families); CI runs the farm. PR check-runs: in_progress at report time. pnpm lint not run locally (CI's).",
      "dogfood_battery": "n/a — no ! in the PR title and Clause-② is no without an arm",
      "mcp_calls": "4 — mcp__github__issue_read x4 (get, get_comments x3), no write tools",
      "api_writes": "2 — fleet-write relay pr_create (objectstack#27340, assignee os-bill), post-stamped os-dev-report comment on #27188",
      "open_questions": [
        {
          "question": "The card's 'available' group is home + rated + used. 'Used' (codes stored on the object's other records) needs a distinct query by object, and the field widget contract carries no object name. How should the select learn the used codes?",
          "options": [
            "A: the form host (ObjectForm/sectionFields) queries the distinct codes once per object and hands them to the code field through the field metadata it already passes (no widget-contract key); about 1 day, one more query per create form",
            "B: add an objectName (or usedCurrencies) prop to the widget contract, produced by the form renderer; a contract change across every host",
            "C: leave available = home + the record's own code until R2's rate table lands, and let 'rated' carry the enabled set"
          ],
          "recommendation": "A, because it keeps the widget contract unchanged and the query lives where the object is already known; it can land as a follow-up before the R1 release."
        },
        {
          "question": "'Filters on a record-mode amount add the code condition': which code does the added condition take?",
          "options": [
            "A: the filter row for a record-mode amount gains a required currency picker (defaulting to the organization currency) and the builder emits amount op X AND code = C",
            "B: always the organization currency, no picker",
            "C: no automatic condition; the builder warns that the comparison spans currencies"
          ],
          "recommendation": "A, because B silently hides other-currency rows and C leaves a cross-currency comparison in place; it is a product call, so not built here."
        }
      ],
      "out_of_scope_findings": [
        "none"
      ]
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:uienhancementNew feature or requestpm:epicpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions