Skip to content

feat(core): portal invitations, opening by the org admin and revocation (v0.6 U3) - #92

Merged
marchtian merged 1 commit into
mainfrom
claude/project-thread-0xnpwp
Nov 2, 2026
Merged

marchtian merged 1 commit into
mainfrom
claude/project-thread-0xnpwp

Conversation

@marchtian

@marchtian marchtian commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Requested by 田騏 · project thread

Closes #79 (v0.6 U3, overview #75). Depends on U1 #87 and U2 #88, both merged; rebased on GL-10 #89, #91 and U4 #90.

改了什么

新对象 srm_supplier_invitation 门户邀请(核心第 22 个对象,OWD private,门户不可见):品类经理、采购主管提出的门户开通需求。

  • supplier、contact 必填(建好后只读)。总监 2026-11-02 裁定:门户只能经联系人名册看到数据,没有供应商档案就无法隔离,所以新公司先登记为潜在供应商和联系人,再发邀请(FR-202 改写)。
  • 潜在 / 淘汰的供应商是准入邀请,categories 必填;准入中 / 合格 / 冻结的供应商只开通门户,不填品类;黑名单不能邀请。
  • portal_admin + admin_authorization(授权书,勾选时必填,GL-10 6.3.1 c)。
  • 状态 sent → accepted / expired / revoked、expires_on、portal_user、enabled_by/at、application:都只读,只由 flow 写。

Flow

  • srm_invitation_send(新建时触发):复制邀请邮箱,写 14 天开通期限(示例值),通知组织管理员去成员页按 external 发邀请(flow 自己发不了,V3)。
  • srm_invitation_expire_sweep(每日 01:20,追赶式):过期的置为 expired。
  • srm_portal_enable(screen flow,runAs: 'user'):
    • 进门检查:操作人的组织角色是 owner / admin,邀请为 sent,供应商不在黑名单,联系人在职且未开通。
    • 所选用户必须是 external 成员,且没有登记在任何联系人上(一人一号)。
    • 任命 srm_portal_user(授权了门户管理员的再任命 srm_portal_admin),然后调用系统身份子 flow srm_portal_roster_write 写 portal_user。子 flow 写入前会再查一次一人一号。
  • 实测结果(18.1.0):组织管理员以 runAs 'user' 写字段级只读的 portal_user 时,数据层把这个字段剔掉,节点只给一条 warning,运行仍报成功(07 §6.3 PF19;PF18 编号已被 feat(core): let suppliers maintain their profile in the portal (v0.6 U4) #90 占用)。这是平台既定行为(objectstack#3407),不提 issue,总监已确认。runAs 'user' 的父 flow 调系统身份子 flow 可以写入;系统身份的 autolaunched flow,任何调用者(包括平台管理员)直接启动都会 403。
  • srm_application_submit(门户 screen flow,系统身份):按 feat(core): portal SCAR reply, claim confirmation and performance appeal (v0.6 U5) #91 的做法自查名册(load_membership:portal_user = 调用者、supplier = 页面记录、active),然后建申请:品类取自邀请,source = invited,并回填到邀请。门户没有准入申请的新建权,也没有品类的读权限。按钮按 feat(core): let suppliers maintain their profile in the portal (v0.6 U4) #90 的约定要求 srm_portal_access,内部员工看不到;直接触发时 flow 同样拒绝。
  • 回收:
    • 新增 srm_portal_contact_revoke:联系人停用时清掉 portal_user,这样日后改回在职也不会悄悄恢复门户访问;同时撤销该联系人的邀请并通知组织管理员。
    • srm_admission_rejection:清掉该供应商全部联系人的 portal_user、撤销邀请,有门户账号时通知组织管理员。
    • srm_disposition_apply(淘汰 / 黑名单):清名册的步骤不变,加上撤销邀请、通知组织管理员。

其他

  • srm_supplier_application.source(只读,internal / invited)。
  • 视图:门户邀请(待开通 / 已开通);联系人「门户账号」,季度复核用。
  • 导航:供应商 › 门户邀请 / 门户账号。
  • 权限:supplier_admin 可读建改(RCE),buyer 可读建(RC),srm_admin / executive / auditor 只读。
  • 英文翻译。

文档:01 FR-202、02 §4.2(邀请字段表、申请 source/supplier)、03 §3(开头、第 1/2/3/8 步,受邀申请的申请人通知发给 portal_user 而不是 created_by,由 U6 #82 实现)、03 §13、04 §3.3(开通第 0–4 步、回收表、季度复核)、05(导航、通知)、07 PF19、GL-10 6.6.1 a 和第 7 章(联系人停用时清除门户登记)。

怎么验证的

  • 新增 test/portal-invitation.test.ts,20 个用例,走真实的 invite-only + external 邀请 → 注册 → 接受路径:
    • 校验规则:品类必填 / 禁填、黑名单、联系人归属、授权书;品类经理改不了状态,也不能把邀请挪到别的联系人。
    • 开通:品类经理、普通员工、未入组织的用户、内部成员都被拒,并逐条断言拒绝原因;组织管理员开通后岗位、名册、邀请状态都对,用户下一次请求只看到本公司;授权了门户管理员的才任命门户管理员岗位;一人一号;子 flow 不能被直接启动。
    • PF19 平台探针。
    • 提交准入申请:别家的门户用户不论走按钮还是直接触发都被拒;本公司提交后来源为邀请、品类正确、回填到邀请,受理照常进入准入中;每张邀请只能提交一次;门户看不到邀请。
    • 回收:准入驳回、联系人停用(改回在职也不恢复)、淘汰(已开通和未开通的邀请都撤销),并检查组织管理员收到通知。
    • 过期扫描:只处理过期的 sent,重复运行不重复处理。
  • test/portal-isolation.test.ts:门户不可见对象加入 srm_supplier_invitation;feat(core): portal SCAR reply, claim confirmation and performance appeal (v0.6 U5) #91 的名册守卫加入 srm_application_submit(与 feat(core): let suppliers maintain their profile in the portal (v0.6 U4) #90 的 srm_change_submit、srm_application_resubmit 合并);两个「停用后改回在职」的用例按新行为改成先重新登记名册。
  • test/permissions.test.ts:4 个可见性快照加入门户邀请。
  • 本地全部通过:pnpm validate、pnpm lint、pnpm typecheck、pnpm i18n:check、pnpm test(341 个通过,1 个跳过;变基到 feat(core): let suppliers maintain their profile in the portal (v0.6 U4) #90 后重跑)、pnpm build,以及 governance/scripts/build.py --check(含 --packs bulk,hazmat,soe)。

留给后续


Generated by Claude Code

@marchtian marchtian self-assigned this Nov 2, 2026
…on (v0.6 U3)

New object srm_supplier_invitation (core 22): a category manager or buyer lead
records the portal request for a registered supplier and contact; an admission
invitation (potential / eliminated supplier) carries the applied categories, a
portal admin grant needs the authorization letter (GL-10 6.3.1 c). Status,
deadline and opening result are written by flows only.

- srm_invitation_send fills the email and the 14-day deadline and notifies the
  org admins, who invite the email as `external` on the members page (V3);
  srm_invitation_expire_sweep expires invitations past the deadline.
- srm_portal_enable (screen, runAs user): gate on the operator's org role,
  sent invitation, not blacklisted, active contact; the chosen user must be an
  external member not registered on any contact; assigns srm_portal_user (and
  srm_portal_admin) and calls the system sub-flow srm_portal_roster_write to
  write portal_user. Measured on 18.1.0: a runAs user write to the read-only
  portal_user is dropped with only a step warning (PF18, platform design), and
  a system autolaunched flow cannot be started directly by any caller.
- srm_application_submit (portal, system): checks the caller's roster row on
  the page's supplier like the membership key, creates the application with the
  invitation's categories and source = invited, and links it back.
- Revocation: contact deactivation clears portal_user and revokes the
  invitation (srm_portal_contact_revoke); admission rejection and
  eliminate / blacklist clear the supplier's roster and revoke invitations;
  org admins are notified to remove assignments and membership.
- Views: portal invitations (to open / opened) and portal accounts for the
  quarterly review; workbench navigation.

Docs per the director ruling of 2026-11-02: 01 FR-202, 02 invitation and
application source, 03 §3 / §13, 04 §3.3, 05, 07 PF18, GL-10 6.6.1 a and §7.

Closes #79

Claude-Session: https://claude.ai/code/session_017ihL2ejLARBQruoaFwM33H
Co-authored-by: Claude <noreply@anthropic.com>
@marchtian
marchtian force-pushed the claude/project-thread-0xnpwp branch from 72f4862 to 2cadb46 Compare November 2, 2026 06:41
@marchtian
marchtian merged commit 9497782 into main Nov 2, 2026
2 checks passed
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.

v0.6 U3:门户邀请 srm_supplier_invitation 与开通、回收

2 participants