Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion data/samples/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 样例导入表格

这里的表格演示从 ERP 导出数据、用平台导入写入 SRM 的格式。全部是标明「示例」的虚构数据,
不对应任何真实企业或物料清单(AGENTS.md 红线 5)。CI 用它们验证导入可重复执行(`test/material.test.ts`)。
不对应任何真实企业或物料清单(AGENTS.md 红线 5)。CI 用它们验证导入可重复执行(`test/material.test.ts`、`test/import-idempotency.test.ts`)。

## 物料 `srm_material.csv`

Expand Down Expand Up @@ -37,3 +37,9 @@ POST /api/v1/data/srm_material/import
首次初始化时如果 ERP 的物料组能对上 SRM 品类,可以带上,值填品类名称。
- 同一张表格导入两遍,第二遍只更新、不新增,记录数与字段值不变。
- 未关联品类的物料在「物料 › 未关联品类」视图里,供采购员补齐。

## 存量供应商、联系人、证书

`srm_supplier.csv`、`srm_supplier_contact.csv`、`srm_supplier_certificate.csv` 是客户台账一次性迁移的样例,
表头用字段的中文标签。匹配规则、导入顺序、字段说明和常见错误见
[存量供应商导入说明](../../docs/implementation/legacy-supplier-import.md)。
7 changes: 7 additions & 0 deletions data/samples/srm_supplier.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
公司全称,简称,国家 / 地区,统一社会信用代码,注册号 / 税号,供应商类型,风险等级,纳税人类型,法定代表人,成立日期,存量导入
示例精密五金有限公司,示例五金,中国,91000000MA0LEGAC01,,生产商,低,一般纳税人,示例甲,2008-05-12,是
示例电子材料有限公司,示例电材,中国,91000000MA0LEGAC02,,生产商,中,一般纳税人,示例乙,2012-09-01,是
示例工业品商贸有限公司,示例工业品,中国,91000000MA0LEGAC03,,代理分销,低,小规模纳税人,示例丙,2016-03-20,是
示例物流服务有限公司,示例物流,中国,91000000MA0LEGAC04,,物流商,低,一般纳税人,示例丁,2010-11-11,是
Example Sensor Co. Ltd.,Example Sensor,日本,,JP0000000001,生产商,中,境外,,2001-04-01,是
Example Fasteners Inc.,Example Fasteners,美国,,US00-0000001,代理分销,低,境外,,1995-07-15,是
8 changes: 8 additions & 0 deletions data/samples/srm_supplier_certificate.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
供应商,资质类型,证书编号,发证机构,生效日期,有效期至,必备资质,存量导入
示例精密五金有限公司,营业执照,91000000MA0LEGAC01,示例市场监督管理局,2008-05-12,2058-05-11,是,是
示例精密五金有限公司,ISO 9001,EX-QMS-0001,示例认证中心,2024-01-01,2029-12-31,是,是
示例电子材料有限公司,营业执照,91000000MA0LEGAC02,示例市场监督管理局,2012-09-01,2042-08-31,是,是
示例电子材料有限公司,ISO 9001,EX-QMS-0002,示例认证中心,2021-01-01,2023-12-31,是,是
示例工业品商贸有限公司,营业执照,91000000MA0LEGAC03,示例市场监督管理局,2016-03-20,2046-03-19,是,是
示例物流服务有限公司,ISO 14001,EX-EMS-0001,示例认证中心,2019-06-01,2022-05-31,否,是
Example Sensor Co. Ltd.,ISO 9001,EX-QMS-JP01,Example Registrar,2023-04-01,2029-03-31,是,是
8 changes: 8 additions & 0 deletions data/samples/srm_supplier_contact.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
供应商,姓名,职能,手机,邮箱,主联系人
示例精密五金有限公司,示例联系人一,商务、主账号,13800000001,sales@wujin.example.com,是
示例精密五金有限公司,示例联系人二,质量,13800000002,quality@wujin.example.com,否
示例电子材料有限公司,示例联系人三,商务,13800000003,sales@dianzi.example.com,是
示例工业品商贸有限公司,示例联系人四,商务、财务,13800000004,contact@gongyepin.example.com,是
示例物流服务有限公司,示例联系人五,物流,13800000005,ops@wuliu.example.com,是
Example Sensor Co. Ltd.,Example Contact,商务,,sales@sensor.example.com,是
Example Fasteners Inc.,Example Buyer Desk,商务,,orders@fasteners.example.com,是
3 changes: 2 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
| [packs/](./packs/README.md) | 行业扩展包(设计保留,未排期):大宗物料包、危化品包、国企采购合规包,以及扩展包的组合与接入方式 | 产品、开发、实施 |
| [engineering/](./engineering/README.md) | 建模规范、分支与 PR、CI 门禁、测试策略、版本与升级 | 开发者、AI agent |
| [adr/](./adr/README.md) | 决策记录 | 全体 |
| [implementation/](./implementation/legacy-supplier-import.md) | 实施文档:存量供应商导入说明(v0.2);实施手册与上线检查清单随 v0.5 补充 | 实施顾问、管理员 |
| [../governance/](../governance/README.md) | 体系文件(管理制度)模板的结构、参数与生成方式 | 实施顾问、客户体系工程师 |
| [../AGENTS.md](../AGENTS.md) | 开发规则与红线 | 开发者、AI agent |

Expand All @@ -17,7 +18,7 @@
|---|---|---|
| `requirements/` | 需求记录 `NNNN-*.md`:客户需求提炼成通用表述后登记,写明落在核心、扩展包还是参数 | 收到第一个新需求时 |
| `integration/` | ERP 等外部系统的接口规格,每个数据流一份(I1–I15) | ERP 集成立项时([ADR-0008](./adr/0008-order-to-pay-stays-in-erp.md)) |
| `implementation/` | 实施手册(含客户层四种定制方式的边界)、数据导入模板、上线检查清单 | v0.5 试运行前 |
| `implementation/` 其余部分 | 实施手册(含客户层四种定制方式的边界)、上线检查清单 | v0.5 试运行前 |
| `reference/` | 由元数据自动生成的对象、字段、选项、权限清单(禁止手改) | 有代码之后 |
| `../content/docs/` | 对外的产品文档、用户手册、供应商操作指南 | 临近上线 |

Expand Down
18 changes: 13 additions & 5 deletions docs/design/02-domain-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,10 @@ erDiagram
| `quality_agreement_signed` / `integrity_agreement_signed` | boolean | 默认 false;协议生效时由合同 flow 置为 true |
| `erp_vendor_code` | text | ERP 供应商编码,唯一;预留,ERP 集成前空着或由采购员手工填写 |
| `portal_position` | text | 门户岗位标识,如 `srm_vendor_sup0000128`;开通门户时写入,只读。校验规则:非空时必须以 `srm_vendor_` 开头(没开通门户的供应商为空)。v0.6 加 |
| `legacy_imported` | boolean | 默认 false;存量导入的供应商为 true 并进入「存量导入、待复核」视图(v0.2) |
| `legacy_imported` | boolean | 默认 false;存量导入的供应商为 true(v0.2)。只能在新建时置为 true:`readonlyWhen: previous.legacy_imported != true`,已建档的供应商重复导入时该列被忽略;只有 `srm_admin` 可写 |
| `legacy_reviewed` | boolean | 默认 false;品类经理核对后经「确认复核」按钮置为 true。`legacy_imported && !legacy_reviewed` 的供应商列在「存量待复核」视图(v0.2) |
| `contact_count` | summary | `count` 联系人 |
| `primary_contact_count` | summary | `count` 联系人,条件 `is_primary == true`;联系人的主联系人守卫规则读它(v0.2) |
| `expired_certificate_count` | summary | `count` 资质证书,条件 `status == 'expired'` |

**`srm_supplier_contact` 供应商联系人**(master_detail → 供应商;OWD `controlled_by_parent`)
Expand All @@ -214,7 +216,7 @@ erDiagram
| `contact_function` | multiselect | `admin` 主账号 / `sales` 商务 / `logistics` 物流 / `quality` 质量 / `finance` 财务(字段名不用 `role`,平台保留词) |
| `mobile` / `email` | phone / email | email 必填(门户邀请与通知用) |
| `portal_user` | user | 已开通门户账号时关联的平台用户;该用户同时被任命到供应商的门户岗位(见 [04 §3](./04-security-and-portal.md#3-供应商门户的数据隔离)) |
| `is_primary` | boolean | 默认 false;每家供应商至多一位主联系人:跨记录规则,由「设为主联系人」screen flow 以系统身份清掉其他联系人的 `is_primary`,不写成校验规则 |
| `is_primary` | boolean | 默认 false;每家供应商至多一位主联系人。守卫:校验规则在供应商 `primary_contact_count > 0` 时拒绝再设一位(插入,或更新时由否改是),导入同样适用;同一批写入里的两位拦不住(objectstack#25477),由「主联系人重复」视图兜底。更换主联系人由「设为主联系人」screen flow 以系统身份先清掉旧的再设新的 |
| `active` | boolean | 默认 true |

**`srm_supplier_bank_account` 银行账户**(master_detail → 供应商;v0.2)
Expand All @@ -237,11 +239,12 @@ erDiagram
|---|---|---|
| `cert_type` | select | `business_license` 营业执照 / `iso9001` / `iatf16949` / `iso14001` / `iso45001` / `production_license` 生产许可 / `ccc` 3C 认证 / `other` |
| `is_mandatory` | boolean | 默认 false;由 `srm_supplier_qualification_recalc` 在证书或品类资格写入时按该供应商全部品类的 `mandatory_cert_types` 重算,门户只读 |
| `cert_no` / `issuer` | text | |
| `cert_no` / `issuer` | text | 存量导入的证书 `cert_no` 必填(`requiredWhen: record.legacy_imported == true`),导入按「供应商 + 资质类型 + 证书编号」匹配 |
| `valid_from` / `valid_to` | date | `valid_to >= valid_from`(跨字段校验) |
| `attachment` | file | 扫描件,必填 |
| `attachment` | file | 扫描件;`requiredWhen: record.legacy_imported != true`——表格导入带不了文件,存量证书复核时补传 |
| `status` | select | `valid` 有效 / `expiring` 即将到期 / `expired` 已过期,由 flow 按 `valid_to` 维护,门户只读 |
| `verified` | boolean | 默认 false;SQE 经「核验证书」screen flow 置为 true;供应商修改编号、有效期或附件后由 flow 重置为 false,门户只读 |
| `legacy_imported` | boolean | 默认 false;存量导入的证书为 true,同供应商的同名字段(只在新建时可置为 true,只有 `srm_admin` 可写)(v0.2) |

**`srm_category` 采购品类**(OWD `public_read`):`code`、`name`、`parent`(`tree`,
上级品类)、`category_manager`(user)、`sqe`(user)、`is_direct`(boolean,直接
Expand Down Expand Up @@ -572,11 +575,16 @@ ERP 集成前为空)、`reviewer`(user)、`appeal_reason`、`appeal_result
|---|---|---|---|
| `potential` ★ | 潜在 | open | `qualifying`、`eliminated` |
| `qualifying` | 准入中(准入申请进入资料审核时) | open | `qualified`、`potential`(准入未通过) |
| `qualified` | 合格 | won | `frozen`、`eliminated`、`blacklisted` |
| `qualified` ★ | 合格(只有存量导入的供应商可以建档即合格) | won | `frozen`、`eliminated`、`blacklisted` |
| `frozen` | 冻结 | lost | `qualified`、`eliminated`、`blacklisted` |
| `eliminated` | 淘汰 | lost | `qualifying`(只能经新的准入申请重新走全流程,不能直接恢复)、`blacklisted` |
| `blacklisted` | 黑名单 | lost | `eliminated`(解除黑名单,需单独审批) |

**初始状态**:`status` 的默认值是 `legacy_imported ? 'qualified' : 'potential'`,导入表格不带状态列;
插入时的校验规则 `initial_status_by_source` 要求「合格」与「存量导入」同真同假,所以普通新建只能从
「潜在」开始准入。这样重复导入不会把冻结、淘汰的供应商改回合格(存量导入见
[导入说明](../implementation/legacy-supplier-import.md))。

「资质异常」不是状态,而是 `qualification_alert` 标志:它不改变供应商是否合格,
只阻止新的寻源邀请(BR-04),资质更新后自动解除。

Expand Down
5 changes: 3 additions & 2 deletions docs/design/04-security-and-portal.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ SRM 同时服务内部员工和互相竞争的供应商,权限设计的首要
| `srm_legal` | 合同读改(审批由 flow 授予) |
| `srm_executive` | 全部 SRM 对象只读 + 仪表盘;审批由 flow 授予,不需要额外写权限 |
| `srm_auditor` | 全部 SRM 对象只读(报价同样受封标约束),可读审批历史与字段历史,不可导出银行账号 |
| `srm_admin` | 品类、指标库、编码、通知模板的配置;物料导入与维护;不授予报价读权限 |
| `srm_admin` | 品类、指标库、编码、通知模板的配置;物料导入与维护;存量供应商、联系人、证书的导入(v0.2,唯一能写「存量导入」标记的权限集,[导入说明](../implementation/legacy-supplier-import.md));不授予报价读权限 |
| `srm_portal_supplier` | 供应商门户用户:见 §3.2 |
| `srm_portal_supplier_admin` | 在 `srm_portal_supplier` 之上:维护本公司联系人、银行账户,提交变更申请与绩效申诉;直接授予具体用户 |
| `srm_integration` | 预留(ERP 集成后):集成服务账号,物料、交付记录、订单镜像的写入;供应商仅可写 `erp_vendor_code`,价格记录只读;**不**授予报价单与银行账号 |
Expand All @@ -75,7 +75,8 @@ SRM 同时服务内部员工和互相竞争的供应商,权限设计的首要
### 2.3 对象权限矩阵(节选)

R 读 · C 建 · E 改 · D 删 · F 仅经 flow / screen flow 写入 · — 无权限。删除一律只给
`srm_admin`,业务上用状态(取消、作废)代替删除。
`srm_admin`,业务上用状态(取消、作废)代替删除。供应商主数据在集团内共享(§5),
`supplier_admin` 和 `srm_admin` 对供应商的编辑不限于自己建的记录(`modifyAllRecords`)。

| 对象 | base | sourcing | supplier_admin | buyer | quality | finance | portal |
|---|---|---|---|---|---|---|---|
Expand Down
4 changes: 2 additions & 2 deletions docs/design/05-experience-and-analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
采购协同工作台
├── 首页 我的待办 · 我负责品类的关键指标
├── 供应商
│ ├── 供应商 合格名录 / 全部 / 资质异常 / 存量待复核 / 按生命周期看板
│ ├── 供应商 合格名录 / 全部 / 资质异常 / 存量待复核 / 主联系人重复 / 按生命周期看板
│ ├── 准入申请 待我审核 / 进行中 / 已结束
│ ├── 资质证书 30 天内到期 / 已过期
│ ├── 资质证书 30 天内到期 / 已过期 / 存量导入缺扫描件
│ ├── 变更单 (v0.2)
│ ├── 处置单 (v0.3)
│ └── 门户邀请 (v0.6)
Expand Down
7 changes: 5 additions & 2 deletions docs/design/07-delivery-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ test/ # 仓库根目录
| R3 | 定时任务延迟或漏跑(objectstack#25442 待平台给出保证) | 中 | 中:提醒延迟 | 追赶式扫描(03 §2.3);强制规则不依赖计时器 |
| R4 | 每家供应商首次开通门户都要管理员新建岗位定义;flow 写任命也可能不被允许(objectstack#25443) | 高(建岗位必然手工) | 低:效率 | 开通流程写成管理员操作清单;v0.6 前验证 V2/V3;平台提供声明式成员集合后改用方案 B |
| R5 | 供应商不愿用门户,继续用邮件和微信 | 中 | 高:协同价值落空 | 试点选配合度高的供应商;邮件只给链接不给内容;在采购合同中约定门户为正式渠道 |
| R6 | 存量供应商数据质量差(重复、缺信用代码) | 高 | 中 | v0.2 导入前专项清洗;导入时按信用代码查重;标记「存量导入」待复核 |
| R6 | 存量供应商数据质量差(重复、缺信用代码) | 高 | 中 | v0.2 导入前专项清洗;导入时按信用代码(境外按国家 / 地区 + 注册号)匹配,可重复执行;标记「存量导入」进「存量待复核」视图([导入说明](../implementation/legacy-supplier-import.md)) |
| R7 | ERP 集成推迟后,交付数据靠每月导入,客户不按时导出则绩效断档 | 中 | 中 | 导入模板与操作清单写进实施手册;考评 flow 发现本期无交付记录时标记「数据不全」而不是给零分 |
| R8 | 每版对象与 flow 较多,回归成本上升 | 中 | 中 | 审批矩阵、导入幂等、关键 flow 的自动化测试进 CI(docs/engineering §3) |
| R9 | 考评在 flow 里批量处理,供应商多时运行时间长 | 低 | 中 | 按法人或品类错峰拆分;规模继续增长时评估服务端函数 |
Expand Down Expand Up @@ -205,7 +205,7 @@ test/ # 仓库根目录
| 唯一约束 | ✅ | 信用代码、`country` + `registration_no` | 空值安全的组合唯一(ADR-0120) | 2026-10-23 确认 |
| 报表(含矩阵) | ✅ | 比价矩阵(v0.7) | — | — |
| 仪表盘 | ✅ ⚠️ | 各类看板 | 求和 / 平均只能落在存储数值字段;多值字段不能做维度 | 2026-10-23 确认 |
| 平台导入(表格) | ✅ | 物料、存量供应商、交付记录 | 按外部编号匹配,可重复执行 | v0.2 用样例表格验证 |
| 平台导入(表格) | ✅ ⚠️ | 物料、存量供应商、交付记录 | 按外部编号匹配,可重复执行;只在新建时写入的列(如供应商初始状态)用字段默认值和 `readonlyWhen` 表达(PF8);一次导入只能一组匹配字段,境内 / 境外供应商分两遍(PF9);同一批里的跨行规则拦不住(PF10) | 2026-10-23 样例表格在 CI 跑两遍通过([导入说明](../implementation/legacy-supplier-import.md)) |
| 批量 API `upsert`、`http` 节点、webhook | ✅ ⚠️ | ERP 集成(预留) | 业务字段记同步状态 + 追赶式重推(06 §1.1) | ERP 立项时验证 |
| 通知(`notify`) | ⚠️ | 全部通知 | 只能发给组织内用户,外部联系人的邮件待平台(PF3 objectstack#25444) | 2026-10-23 |
| 字段历史、审批历史 | ✅ | 审计 | — | 2026-10-23 确认 |
Expand Down Expand Up @@ -235,6 +235,9 @@ test/ # 仓库根目录
| PF5 | objectstack#25446 禁止自审开关 | 职责分离 | `expression` 改派 |
| PF6 | objectstack#25233 门户用户可列出全组织用户 | 门户隔离 | v0.6 阻塞;见 D8 |
| PF7 | objectstack#25447 ADR-0138 D5(多组织环境下公开表单绑定到指定组织) | 多法人客户的自助注册 | 邀请注册;多法人按 ADR-0006 单组织时不受影响 |
| PF8 | objectstack#25475 导入时「只在新建时写入」的列 | 存量导入不回退状态 | `status` 默认值按 `legacy_imported` 计算、`legacy_imported` 用 `readonlyWhen` 锁定(02 §4.1) |
| PF9 | objectstack#25476 导入按多组匹配字段依次匹配 | 境内 / 境外供应商同表导入 | 同一张表导两遍,各自跳过匹配字段为空的行 |
| PF10 | objectstack#25477 条件唯一(每家至多一位主联系人)在批量写入中生效 | 联系人导入 | 汇总字段 + 校验规则守卫单条写入;「主联系人重复」视图兜底同批写入 |

---

Expand Down
4 changes: 3 additions & 1 deletion docs/engineering/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
| 字段名不含 `role` | `role` 是平台保留禁用词;联系人的角色字段叫 `contact_function` | objectstack ADR-0090 D3 |
| `stageField` 不用共享选项列表 | 状态字段的每个选项必须带 `kind`,只能内联定义;其他可被扩展的枚举才做成 picklist | 迁移 `stage-field-option-kind-required` |
| 唯一约束按组织作用域、空值安全 | 声明索引用 `unique: 'organization'`;允许多条空值的组合唯一(如境外供应商的 `country` + `registration_no`)用平台的空值安全写法 | objectstack ADR-0120 |
| 跨记录规则不写校验规则 | 「每家供应商至多一位主联系人」这类规则由 screen flow 以系统身份处理,不写成单条校验 | 审核 F-29 |
| 跨记录规则不写校验规则 | 「每家供应商至多一位主联系人」这类规则由 screen flow 以系统身份处理,不写成单条校验;校验规则只经父记录的带条件汇总做守卫(拒绝第二位),同一批写入拦不住(objectstack#25477) | 审核 F-29 |
| 表达式默认值先判 `has()` | 默认值按字段声明顺序计算,读后声明字段时该字段可能还没取默认值,写成 `has(record.x) && record.x == …` | v0.2 存量导入 |
| 平台限制提需求 | 遇到平台做不到的,先查重再在 objectstack 提 issue,PR 里链接;变通做法写明是临时措施,并登记到 07 §6.3 | 2026-10-23 用户要求 |
| 外部单号字段预留、空着 | `erp_vendor_code`、`erp_material_no` 等字段保留在对象上,ERP 集成前不写 | [ADR-0008](../adr/0008-order-to-pay-stays-in-erp.md) |
| 中国特有字段单独分组 | 统一社会信用代码、纳税人类型等放在「工商与税务」分组,便于将来拆中国本地化包 | [ADR-0009](../adr/0009-core-first-pack-criteria-and-customer-layer.md) |

Expand Down
Loading
Loading