Skip to content

The finance approver can open the contract it is asked to approve - #153

Merged
zhuangjianguo merged 1 commit into
mainfrom
claude/issue-134-finance-routed-read
Oct 26, 2026
Merged

zhuangjianguo merged 1 commit into
mainfrom
claude/issue-134-finance-routed-read

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #134

The finance half (ruling 6422705361, letter A). Rung 1 (the direct manager) stays open on the card, blocked by objectstack-ai/objectstack#26180; this PR adds only its README known-limitation line.

What changed

  • src/sharing/contract.sharing.ts: new criteria rule contract_finance_routed on clm_contract, condition record.route_finance == true, accessLevel: 'read', sharedWith: { type: 'position', value: CLM_POSITION.financeController } (clm_finance_controller). It mirrors contract_executive_routed / contract_gm_routed field for field: the same recipient kind (position), the same access level, and no status window.
  • README.md: the finance position row now says it reads every contract with route_finance, like the executive and GM rows. The rung-1 section gets the known limitation: on the open edition the direct manager decides from the request alone until plugin-approvals: the approver a step waits on gets no read of the record it is about — "无法打开此记录" on the request they must decide objectstack#26180 ships.

Why no status window. The executive and GM routed rules have none. The route_* flags are stamped at submission and re-evaluated on every resubmission (F2 contract_route writes all four, so a flag that stops matching is cleared). A status window would also have to be kept in step with the approval ladder, which the other two rules do not do. Finance's edit window does not change: it is still contract_finance_post_approval plus the clm_finance update row policy, both from approved on. One effect follows directly: finance now reads a routed contract from submitted / in_review on, before its rung opens. The executive and GM already read their routed contracts that way (measured below: 5 submitted, 8 in_review and 3 in_approval routed contracts became readable).

Measured (18.1.0, pnpm demo on a throwaway DB, port 3134, Chromium /opt/pw-browsers/chromium, locale zh-CN)

The personas were set up the README way: POST /api/v1/auth/admin/create-user for Business Requester 2, Executive 1 and Finance Controller 1; sys_user_position rows for clm_executive and clm_finance_controller; set-user-manager to make Business Requester 2 report to Executive 1; then each account's own change-password, because mustChangePassword is set. Business Requester 2 launched both orders through the 发起合同 wizard: type Order Form, counterparty picked, a one-page PDF uploaded as v1, 立即提交. Executive 1 approved rung 1 from 待我处理.

Step before (eace613) after (0843d78)
ORD 250,000 after 立即提交 in_approval, route_finance: true same
Finance 待我处理 lists the request (财务负责人 step) same
Finance opens it request page; 记录 ID card 「无法打开此记录」 the inbox opens the contract record page itself (/clm_contract/record/ID) with 通过 / 拒绝 / 转签 / 退回修改 / 退回补充 / 回复 and the badge 「审批中已锁定」
/_console/apps/clm/clm_contract/record/ID 「未找到记录」 opens
GET /api/v1/data/clm_contract/ID as finance 404 RECORD_NOT_FOUND 200
Tab 版本 not reachable v1 · Draft, file order-250k.pdf; the link opens the PDF in a new tab (Chromium PDF viewer renders the page); GET /api/v1/storage/files/ID → 200 application/pdf, 612 bytes, %PDF-1.4
Tab 审查与偏离 not reachable 审查 0 · 偏离 0 (the order skips legal review); GET clm_review / clm_deviation → 200
Tab 审批记录 not reachable 2 requests (Direct Manager 已批准, Finance Controller 待审批)
Finance edit while in_approval — no 编辑 button; the ⋯ menu holds only 添加到收藏 · 分享; PATCH payment_terms → 403 PERMISSION_DENIED 「您无权访问这条记录…」
Finance edit on a routed in_review contract (not approval-locked; seeded MSA-2026-0006) not visible PATCH payment_terms → 403 PERMISSION_DENIED (read-only share)
ORD 20,000 (route_finance: false, in_approval at rung 1) as finance 404 / 「未找到记录」 404 / 「未找到记录」; its version file GET /api/v1/storage/files/ID → 403
Contracts finance can list 82, all approved or later 98 = the same 82 (identical per-status counts) + 16 routed pre-approval (submitted 5 · in_review 8 · in_approval 3)
Post-approval access (seeded approved contract) PATCH payment_terms 200 · PATCH governing_law 403 Field write denied same
Finance approves ORD 250,000 — approved; then PATCH payment_terms 200, PATCH status 403 Field write denied

Rung 1 before the change (unchanged by this PR): Executive 1 opened the request and the 记录 ID card read 「无法打开此记录」 — the limitation the README line records.

The boot banner (both runs) shows no degraded / no such table. The 404 ERROR [SeedLoader] lines are the expected first-boot unresolved persona names that the demo's own note explains (inserted: 830). Browser console on the finance pages: one 404 resource load and three [object-ui] "predicate failed to evaluate" warnings for header actions. Both runs show the same lines; they are not changed here.

Gates (head 0843d78, all exit 0)

pnpm install                      exit=0   Done in 2s using pnpm v10.28.0
pnpm validate                     exit=0   ✓ Validation passed (1250ms)
pnpm lint                         exit=0   6 suggestion(s) (929ms) — no error or warning lines
pnpm typecheck                    exit=0   tsc --noEmit (no output)
pnpm lint:i18n-gate               exit=0   COVERAGE : 0 missing keys across 2 locale(s)
CLM_DEMO_SEED=1 pnpm validate     exit=0   ✓ Validation passed (1024ms)

The built artifact carries the rule: dist/objectstack.json contains "contract_finance_routed".

Proposed DESIGN.md §04 row (for the seat's governed PR #120; not touched here)

| contract_finance_routed | clm_contract | route_finance == true | position clm_finance_controller — read(审批中只读;编辑仍自 approved 起,§13 Q1) |

Acceptance notes

  • Child objects need no grant of their own. clm_contract_version / clm_review / clm_deviation are controlled_by_parent, and clm_finance already has object read on them. Through this rule the finance approver sees the versions (and the version PDF), reviews, deviations and approval records. The storage door follows the record: the file of a contract finance cannot read answers 403.
  • The barrel src/sharing/index.ts re-exports each rule by name. The new ContractFinanceRoutedSharingRule is not added there, because that file is outside the claimed file surface. It reaches the stack through ClmSharingRules, which is what objectstack.config.ts reads. Carrier: none — the seat can add the one export line if it wants the barrel complete.
  • On the read-only contract page, the 版本 and 审查与偏离 tabs still show 新建 buttons to finance. Finance holds no create on versions or deviations. Not exercised here; the buttons are the platform's related-list default. Noted, not filed.

🤖 Generated with Claude Code

https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn

The finance controller co-signs at in_approval whenever the matrix stamps
route_finance (F5), yet it reached contracts only from approved onward, so
the approval request it acts on read "无法打开此记录". Add the
contract_finance_routed criteria rule (route_finance == true, read, the
clm_finance_controller position), the same shape as the executive and
general-manager routed rules. The edit window is unchanged.

README: the finance position's row says what it reads, and the rung-1
section records that on the open edition the direct manager decides from
the request alone until objectstack-ai/objectstack#26180 ships.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 — ACCEPT · head 0843d78 · 2026-10-26T17:54Z

check reading
shape draft → main, Part of #134; mergeable clean
CI on the head both Validate check-runs success on 0843d78 = PR head.sha
scope 2 files: src/sharing/contract.sharing.ts (+25/−1, criteria rule contract_finance_routed, added to ClmSharingRules) and README.md (+14/−2, finance row and the rung-1 open-edition limitation). Within the claim's surface. Nothing governed
the ruling 6422705361 letter A, finance half — delivered: clm_contract where route_finance == true is read for position clm_finance_controller. Measured on the dev server with a real finance persona: before, 「无法打开此记录」 on the request and API 404; after, the routed contract opens read-only
seat call Q1 A — keep the executive/GM shape with no status window. F2 re-stamps all four route_* flags on every submission, so finance reads a routed contract from submitted on, the same as the other rungs. Editing still starts at approved (§13 Q1)
seat call Q2 A — keep the outcome-shaped title
observation src/sharing/index.ts does not re-export the individual rule constants; the rule ships through ClmSharingRules, as its siblings do. No card
not in this PR rung 1 (the direct manager) stays open on #134, Blocked-by objectstack-ai/objectstack#26180

Landing: under the maintainer's merge authorization (project thread: 「你可以自己审核的就自己合并」).


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants