FINPROD ACCTH 启用付款批次并收口代账交接
这个提交包含在:
@@ -95,6 +95,7 @@ const baseUrl = process.env.LIVE_SMOKE_BASE_URL?.trim() || "https://oa.hyinda.co
|
|||||||
const reportDir = process.env.P2_FIN_LIVE_REPORT_DIR?.trim() ||
|
const reportDir = process.env.P2_FIN_LIVE_REPORT_DIR?.trim() ||
|
||||||
path.resolve(process.cwd(), "..", "artifacts/qa-flow/p2-fin-live-local");
|
path.resolve(process.cwd(), "..", "artifacts/qa-flow/p2-fin-live-local");
|
||||||
const summaryPath = path.join(reportDir, "live-result.json");
|
const summaryPath = path.join(reportDir, "live-result.json");
|
||||||
|
const finalFlagState = process.env.P2_FIN_LIVE_FINAL_STATE?.trim() === "force_on" ? "force_on" : "force_off";
|
||||||
|
|
||||||
function nowMarker() {
|
function nowMarker() {
|
||||||
return `${markerPrefix}-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
return `${markerPrefix}-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
||||||
@@ -363,11 +364,11 @@ async function expectAuditAction(
|
|||||||
.toBe(true);
|
.toBe(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
test.describe("P2-FIN-LIVE 公网付款批次短窗口", () => {
|
test.describe("公网付款批次受控验收", () => {
|
||||||
test.skip(process.env.E2E_MODE !== "live", "仅在 live 模式下执行");
|
test.skip(process.env.E2E_MODE !== "live", "仅在 live 模式下执行");
|
||||||
test.describe.configure({ mode: "serial" });
|
test.describe.configure({ mode: "serial" });
|
||||||
|
|
||||||
test("公网付款批次 flag-on 闭环后强制回滚到 flag-off", async ({}, testInfo) => {
|
test("公网付款批次 flag-on 闭环后按 final mode 收口", async ({}, testInfo) => {
|
||||||
test.setTimeout(420_000);
|
test.setTimeout(420_000);
|
||||||
if (process.env.P2_FIN_LIVE_APPROVED !== "1") {
|
if (process.env.P2_FIN_LIVE_APPROVED !== "1") {
|
||||||
throw new Error("P2_FIN_LIVE_APPROVED=1 is required for public write test");
|
throw new Error("P2_FIN_LIVE_APPROVED=1 is required for public write test");
|
||||||
@@ -377,12 +378,14 @@ test.describe("P2-FIN-LIVE 公网付款批次短窗口", () => {
|
|||||||
const marker = nowMarker();
|
const marker = nowMarker();
|
||||||
const summary: LiveSummary = {
|
const summary: LiveSummary = {
|
||||||
issueId,
|
issueId,
|
||||||
mode: "p2-fin-live",
|
mode: finalFlagState === "force_on" ? "fin-prod-enable" : "p2-fin-live",
|
||||||
status: "RUNNING",
|
status: "RUNNING",
|
||||||
baseUrl,
|
baseUrl,
|
||||||
marker,
|
marker,
|
||||||
startedAt: new Date().toISOString(),
|
startedAt: new Date().toISOString(),
|
||||||
stages: {},
|
stages: {
|
||||||
|
finalFlagState,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const clients: AuthenticatedApiClient[] = [];
|
const clients: AuthenticatedApiClient[] = [];
|
||||||
@@ -543,13 +546,29 @@ test.describe("P2-FIN-LIVE 公网付款批次短窗口", () => {
|
|||||||
} finally {
|
} finally {
|
||||||
if (admin) {
|
if (admin) {
|
||||||
try {
|
try {
|
||||||
const rolledBack = await forcePaymentBatchOff(admin.api, `${issueId} rollback ${marker}`);
|
if (finalFlagState === "force_on" && !caughtError) {
|
||||||
summary.stages.rollback = { paymentFlagAfterRollback: snapshotFlag(rolledBack) };
|
const paymentFlag = await loadFlag(admin.api, "payment_batch_v1");
|
||||||
expect(rolledBack.effectiveEnabled).toBe(false);
|
const finalPaymentFlag =
|
||||||
|
paymentFlag.overrideMode === "force_on" && paymentFlag.effectiveEnabled
|
||||||
|
? paymentFlag
|
||||||
|
: await updateFlag(admin.api, "payment_batch_v1", "force_on", `${issueId} keep force_on ${marker}`);
|
||||||
|
const finalErpFlag = await loadFlag(admin.api, "erp_sync");
|
||||||
|
summary.stages.finalState = {
|
||||||
|
paymentFlag: snapshotFlag(finalPaymentFlag),
|
||||||
|
erpSyncEffectiveEnabled: finalErpFlag.effectiveEnabled,
|
||||||
|
};
|
||||||
|
expect(finalPaymentFlag.effectiveEnabled).toBe(true);
|
||||||
|
expect(finalPaymentFlag.overrideMode).toBe("force_on");
|
||||||
|
expect(finalErpFlag.effectiveEnabled).toBe(false);
|
||||||
|
} else {
|
||||||
|
const rolledBack = await forcePaymentBatchOff(admin.api, `${issueId} rollback ${marker}`);
|
||||||
|
summary.stages.rollback = { paymentFlagAfterRollback: snapshotFlag(rolledBack) };
|
||||||
|
expect(rolledBack.effectiveEnabled).toBe(false);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
rollbackError = error;
|
rollbackError = error;
|
||||||
summary.status = "FAIL";
|
summary.status = "FAIL";
|
||||||
summary.stages.rollback = { error: sanitizeError(error) };
|
summary.stages.finalState = { error: sanitizeError(error) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ DCL7 已删除本文件中 R70-R91 的长流水历史,避免旧环境、旧开
|
|||||||
- 2026-05-17 PRELAUNCH 已按“系统尚未正式使用”的新前提完成正式启用前公网预回归:公网 `/ready`、public bundle、live smoke、PDF live OCR、远端 Caddy/compose/migration 只读基线、隔离核心业务流、付款批次公网 PRELAUNCH 窗口、公网移动矩阵和企业微信 UA/渲染矩阵均形成证据;公网测试数据均带 `PRELAUNCH` marker。最终 `payment_batch_v1=flag_force_off`,未部署、不接真实支付、不写 ERP 实时凭证、不开放图片 OCR。浏览器全链路 `all` 中报销 PDF 因样票重复记可解释 `SKIP`,采购分流程功能报告通过但手工 live spec 存在长等待/退出码稳定性缺口,后续作为 QA 自动化维护项。
|
- 2026-05-17 PRELAUNCH 已按“系统尚未正式使用”的新前提完成正式启用前公网预回归:公网 `/ready`、public bundle、live smoke、PDF live OCR、远端 Caddy/compose/migration 只读基线、隔离核心业务流、付款批次公网 PRELAUNCH 窗口、公网移动矩阵和企业微信 UA/渲染矩阵均形成证据;公网测试数据均带 `PRELAUNCH` marker。最终 `payment_batch_v1=flag_force_off`,未部署、不接真实支付、不写 ERP 实时凭证、不开放图片 OCR。浏览器全链路 `all` 中报销 PDF 因样票重复记可解释 `SKIP`,采购分流程功能报告通过但手工 live spec 存在长等待/退出码稳定性缺口,后续作为 QA 自动化维护项。
|
||||||
- 2026-05-17 CASHIER-VOUCHER-V1 已按“无 ERP/独立财务系统、只有代账”的新业务前提完成正式启用前财务闭环补强:新增付款批次银行回单附件、复核后补凭证、银行渠道执行前回单阻断和 PC 付款批次详情操作区;业务口径为 `wangzhiling` 出纳执行、`wangshunli` 管理员复核,后续正式流程是 OA 内审批/台账/付款批次 + 银行对公付款回单 + 月度代账交接。CASHV 代码已由 `LAUNCH-DEPLOY-FINANCE` 部署到公网,不接真实支付、不写 ERP、不开放图片 OCR、不导入银行流水;下一步需另建或延续 `FIN-PROD-ENABLE` 和 `ACCOUNTING-HANDOFF-V1` 合同。
|
- 2026-05-17 CASHIER-VOUCHER-V1 已按“无 ERP/独立财务系统、只有代账”的新业务前提完成正式启用前财务闭环补强:新增付款批次银行回单附件、复核后补凭证、银行渠道执行前回单阻断和 PC 付款批次详情操作区;业务口径为 `wangzhiling` 出纳执行、`wangshunli` 管理员复核,后续正式流程是 OA 内审批/台账/付款批次 + 银行对公付款回单 + 月度代账交接。CASHV 代码已由 `LAUNCH-DEPLOY-FINANCE` 部署到公网,不接真实支付、不写 ERP、不开放图片 OCR、不导入银行流水;下一步需另建或延续 `FIN-PROD-ENABLE` 和 `ACCOUNTING-HANDOFF-V1` 合同。
|
||||||
- 2026-05-17 LAUNCH-DEPLOY-FINANCE 已按用户要求完成“本地全流程/页面门禁 -> 部署 -> 公网复检”:本地基础代码门禁、业务流、PC/移动/企业微信页面矩阵均通过;部署 commit `ab5ab4d` 到 `https://oa.hyinda.com`,`SYNC_ENV=0` 未覆盖远端 env,新增回单附件 migration 已应用;公网 readiness、PDF live OCR、移动/企业微信矩阵和 CASHV 短窗口均通过。CASHV 公网测试报销 `BX-20260517-0025` 完成无回单执行阻断、上传/下载回单、付款执行、AP 导出和审计核验;最终 `payment_batch_v1=flag_force_off`,正式常开仍需 `FIN-PROD-ENABLE`。
|
- 2026-05-17 LAUNCH-DEPLOY-FINANCE 已按用户要求完成“本地全流程/页面门禁 -> 部署 -> 公网复检”:本地基础代码门禁、业务流、PC/移动/企业微信页面矩阵均通过;部署 commit `ab5ab4d` 到 `https://oa.hyinda.com`,`SYNC_ENV=0` 未覆盖远端 env,新增回单附件 migration 已应用;公网 readiness、PDF live OCR、移动/企业微信矩阵和 CASHV 短窗口均通过。CASHV 公网测试报销 `BX-20260517-0025` 完成无回单执行阻断、上传/下载回单、付款执行、AP 导出和审计核验;最终 `payment_batch_v1=flag_force_off`,正式常开仍需 `FIN-PROD-ENABLE`。
|
||||||
|
- 2026-05-17 FIN-PROD-ENABLE 已完成公网付款批次正式常开:未重新部署业务代码;新增 QA-only 正式启用 runner 后,公网测试报销 `BX-20260517-0026` 完成审批、付款批次创建/复核、无回单执行阻断、回单上传/下载、付款执行、报销 `paid`、票据 `archived`、AP 台账 `paid`、AP 导出和审计核验;公网移动矩阵 6 passed、企业微信渲染矩阵 8 passed。最终 `payment_batch_v1=force_on`,`erp_sync=force_off`;不接真实支付、不接 ERP、不开放图片 OCR、不导入银行流水。
|
||||||
|
- 2026-05-17 ACCOUNTING-HANDOFF-V1 已固化月度代账交接 SOP:OA 侧导出 AP 台账、报销、票据中心、付款批次/回单附件清单和审计日志;银行流水和银行回单原件由 `wangzhiling` 从银行渠道导出,交给代账人工核对。公网只读/导出核对显示付款批次清单、回单附件列表/下载、AP 导出、报销导出、票据导出和审计日志导出均返回 200;不开发银行流水导入或一键 zip 包。
|
||||||
|
|
||||||
## 关键证据索引
|
## 关键证据索引
|
||||||
|
|
||||||
@@ -65,6 +67,8 @@ DCL7 已删除本文件中 R70-R91 的长流水历史,避免旧环境、旧开
|
|||||||
| PRELAUNCH | 已收口:正式启用前公网预回归完成;公网 readiness/PDF live OCR、远端只读基线、隔离核心业务流、PRELAUNCH 付款批次窗口、公网移动和企业微信渲染矩阵通过;最终 `payment_batch_v1=flag_force_off` | `issues/2026-05-17_00-46-49-prelaunch-production-readiness.csv`、`artifacts/qa-flow/p2-fin-live-20260517-014144/live-result.json`、`artifacts/qa-flow/p2-safe-regression-20260517-014436/summary.json`、`artifacts/prelaunch/browser-business/report.md`、`artifacts/prelaunch/browser-business-purchase-rerun/report.md` |
|
| PRELAUNCH | 已收口:正式启用前公网预回归完成;公网 readiness/PDF live OCR、远端只读基线、隔离核心业务流、PRELAUNCH 付款批次窗口、公网移动和企业微信渲染矩阵通过;最终 `payment_batch_v1=flag_force_off` | `issues/2026-05-17_00-46-49-prelaunch-production-readiness.csv`、`artifacts/qa-flow/p2-fin-live-20260517-014144/live-result.json`、`artifacts/qa-flow/p2-safe-regression-20260517-014436/summary.json`、`artifacts/prelaunch/browser-business/report.md`、`artifacts/prelaunch/browser-business-purchase-rerun/report.md` |
|
||||||
| CASHIER-VOUCHER-V1 | 已收口:正式启用前财务闭环与银行回单附件完成;后端 focused 回归 12 tests passed,前端 typecheck/build 通过,本地隔离 reimbursement 业务域 10 passed/1 skipped,governance 业务域 9 passed;已由 LDF 部署 | `issues/2026-05-17_09-04-37-cashier-payment-voucher-v1.csv`、`artifacts/qa-flow/business-flow-20260517-091904/summary.txt`、`artifacts/qa-flow/business-flow-20260517-092824/summary.txt` |
|
| CASHIER-VOUCHER-V1 | 已收口:正式启用前财务闭环与银行回单附件完成;后端 focused 回归 12 tests passed,前端 typecheck/build 通过,本地隔离 reimbursement 业务域 10 passed/1 skipped,governance 业务域 9 passed;已由 LDF 部署 | `issues/2026-05-17_09-04-37-cashier-payment-voucher-v1.csv`、`artifacts/qa-flow/business-flow-20260517-091904/summary.txt`、`artifacts/qa-flow/business-flow-20260517-092824/summary.txt` |
|
||||||
| LAUNCH-DEPLOY-FINANCE | 已收口:CASHV 部署和公网复检完成;本地全流程/页面门禁通过,公网 readiness/PDF live OCR、移动/企业微信矩阵和 CASHV 短窗口通过;最终 `payment_batch_v1=flag_force_off` | `issues/2026-05-17_09-38-55-launch-deploy-finance.csv`、`artifacts/qa-flow/business-flow-20260517-094407/summary.txt`、`artifacts/qa-flow/live-smoke-readiness-20260517-102904/summary.json`、`artifacts/qa-flow/p2-fin-live-20260517-103450/live-result.json` |
|
| LAUNCH-DEPLOY-FINANCE | 已收口:CASHV 部署和公网复检完成;本地全流程/页面门禁通过,公网 readiness/PDF live OCR、移动/企业微信矩阵和 CASHV 短窗口通过;最终 `payment_batch_v1=flag_force_off` | `issues/2026-05-17_09-38-55-launch-deploy-finance.csv`、`artifacts/qa-flow/business-flow-20260517-094407/summary.txt`、`artifacts/qa-flow/live-smoke-readiness-20260517-102904/summary.json`、`artifacts/qa-flow/p2-fin-live-20260517-103450/live-result.json` |
|
||||||
|
| FIN-PROD-ENABLE | 已收口:公网付款批次正式启用完成;测试报销 `BX-20260517-0026` 付款闭环到 paid,票据 archived,AP 台账 paid,导出和审计核验通过;公网移动矩阵 6 passed,企业微信渲染矩阵 8 passed;最终 `payment_batch_v1=force_on`、`erp_sync=force_off` | `issues/2026-05-17_11-40-47-fin-prod-enable-payment-batch.csv`、`artifacts/qa-flow/fin-prod-enable-20260517-114731/summary.json`、`artifacts/qa-flow/fin-prod-enable-20260517-114731/live-result.json` |
|
||||||
|
| ACCOUNTING-HANDOFF-V1 | 已收口:月度代账交接 SOP 固化;OA 侧 AP/报销/票据/付款批次/回单附件/审计资料清单可用,公网导出和回单下载状态核对 200;银行流水和回单原件由出纳线下导出给代账 | `issues/2026-05-17_11-40-48-accounting-handoff-v1.csv`、`docs/管理员手册.md` |
|
||||||
|
|
||||||
## 当前门禁
|
## 当前门禁
|
||||||
|
|
||||||
@@ -91,6 +95,8 @@ DCL7 已删除本文件中 R70-R91 的长流水历史,避免旧环境、旧开
|
|||||||
| PRELAUNCH 正式启用前预回归 | 通过,含 QA 稳定性观察项,未部署 | 公网 live readiness PASS,远端 Caddy active、compose db/server Up、Prisma migration up to date;脱敏数据清点 activeEmployees=12、activeWecomBindings=5、pendingWorkflowTasks=7;`run-p2-fin-live-window.sh` PRELAUNCH PASS,测试报销 `BX-20260517-0024` 付款闭环 paid、票据 archived、AP 台账 paid;`run-p2-safe-regression.sh` PASS。浏览器 `all` 长链路中 PDF 样票重复和采购超时按证据分流:采购分流程报告 PASS,本地隔离 procurement PASS,记录为手工 live spec 稳定性维护项,不作为业务 P0/P1 阻断。 |
|
| PRELAUNCH 正式启用前预回归 | 通过,含 QA 稳定性观察项,未部署 | 公网 live readiness PASS,远端 Caddy active、compose db/server Up、Prisma migration up to date;脱敏数据清点 activeEmployees=12、activeWecomBindings=5、pendingWorkflowTasks=7;`run-p2-fin-live-window.sh` PRELAUNCH PASS,测试报销 `BX-20260517-0024` 付款闭环 paid、票据 archived、AP 台账 paid;`run-p2-safe-regression.sh` PASS。浏览器 `all` 长链路中 PDF 样票重复和采购超时按证据分流:采购分流程报告 PASS,本地隔离 procurement PASS,记录为手工 live spec 稳定性维护项,不作为业务 P0/P1 阻断。 |
|
||||||
| CASHIER-VOUCHER-V1 财务闭环与银行回单附件 | 通过,已部署 | 新增付款批次回单附件模型/API/PC 操作区,银行渠道执行付款前必须上传回单,复核后仍可补凭证号/付款日期/付款渠道;`prisma validate`、`node --check`、后端 focused 单测、前端 typecheck/build、隔离 reimbursement 业务流和 governance 权限直连业务流均通过;已由 LDF 部署并完成公网 CASHV 短窗口复检。正式启用前仍需设置 `payment_batch_v1=force_on` 并完成代账交接 SOP。 |
|
| CASHIER-VOUCHER-V1 财务闭环与银行回单附件 | 通过,已部署 | 新增付款批次回单附件模型/API/PC 操作区,银行渠道执行付款前必须上传回单,复核后仍可补凭证号/付款日期/付款渠道;`prisma validate`、`node --check`、后端 focused 单测、前端 typecheck/build、隔离 reimbursement 业务流和 governance 权限直连业务流均通过;已由 LDF 部署并完成公网 CASHV 短窗口复检。正式启用前仍需设置 `payment_batch_v1=force_on` 并完成代账交接 SOP。 |
|
||||||
| LAUNCH-DEPLOY-FINANCE 部署与公网复检 | 通过,最终关闭付款批次 | 本地业务流 auth/reimbursement/approval/leave/procurement/governance 通过,本地 PC/移动/企业微信矩阵通过;部署 commit `ab5ab4d`,`SYNC_ENV=0`,新增 migration `20260517090437_cashier_payment_receipts` 已应用,公网 readiness/PDF live OCR 通过,公网移动/企微矩阵通过;CASHV 短窗口验证无回单执行 409、回单上传/下载、付款 executed、报销 paid、票据 archived、AP 台账 paid、AP 导出 200 和审计可查;最终 `payment_batch_v1=flag_force_off`。 |
|
| LAUNCH-DEPLOY-FINANCE 部署与公网复检 | 通过,最终关闭付款批次 | 本地业务流 auth/reimbursement/approval/leave/procurement/governance 通过,本地 PC/移动/企业微信矩阵通过;部署 commit `ab5ab4d`,`SYNC_ENV=0`,新增 migration `20260517090437_cashier_payment_receipts` 已应用,公网 readiness/PDF live OCR 通过,公网移动/企微矩阵通过;CASHV 短窗口验证无回单执行 409、回单上传/下载、付款 executed、报销 paid、票据 archived、AP 台账 paid、AP 导出 200 和审计可查;最终 `payment_batch_v1=flag_force_off`。 |
|
||||||
|
| FIN-PROD-ENABLE 付款批次正式常开 | 通过,未重新部署,最终开启付款批次 | 新增 QA-only `run-fin-prod-enable-window.sh` 和 Playwright force-on 模式;`client` typecheck/build 通过;公网 pre flag-off PASS 后完成 FINPROD 付款闭环,`BX-20260517-0026` paid、票据 archived、AP 台账 paid、无回单执行 409、回单下载 200、AP 导出 200、关键审计可查;公网移动矩阵 6 passed、企业微信渲染矩阵 8 passed;最终 `payment_batch_v1=force_on`、`erp_sync=force_off`。 |
|
||||||
|
| ACCOUNTING-HANDOFF-V1 代账交接 | 通过,无需开发一键包 | 现有能力核对:付款批次清单 200,回单附件列表 200,回单下载 200,AP 导出 200,报销导出 200,票据中心导出 200,审计日志导出 200;`docs/管理员手册.md` 已补月度交接 SOP。AP 台账不是银行流水,银行流水和回单原件由出纳从银行侧导出,不导入 OA。 |
|
||||||
|
|
||||||
## R7EP / OPS8 / QAF8 / UX8 / P2T 预检推进
|
## R7EP / OPS8 / QAF8 / UX8 / P2T 预检推进
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -50,6 +50,8 @@
|
|||||||
| `PRELAUNCH` | `issues/2026-05-17_00-46-49-prelaunch-production-readiness.csv` | `EVIDENCE` | 正式启用前公网预回归已收口;公网 readiness、远端只读基线、隔离核心业务流、付款批次 PRELAUNCH 窗口、移动/企业微信矩阵和 Go/No-Go 摘要完成;最终 `payment_batch_v1=flag_force_off`,未部署 |
|
| `PRELAUNCH` | `issues/2026-05-17_00-46-49-prelaunch-production-readiness.csv` | `EVIDENCE` | 正式启用前公网预回归已收口;公网 readiness、远端只读基线、隔离核心业务流、付款批次 PRELAUNCH 窗口、移动/企业微信矩阵和 Go/No-Go 摘要完成;最终 `payment_batch_v1=flag_force_off`,未部署 |
|
||||||
| `CASHIER-VOUCHER-V1` | `issues/2026-05-17_09-04-37-cashier-payment-voucher-v1.csv` | `EVIDENCE` | 正式启用前财务闭环与银行回单附件已收口;新增付款批次银行回单附件、复核后补凭证和银行渠道执行前回单阻断;已由 `LAUNCH-DEPLOY-FINANCE` 部署到公网 |
|
| `CASHIER-VOUCHER-V1` | `issues/2026-05-17_09-04-37-cashier-payment-voucher-v1.csv` | `EVIDENCE` | 正式启用前财务闭环与银行回单附件已收口;新增付款批次银行回单附件、复核后补凭证和银行渠道执行前回单阻断;已由 `LAUNCH-DEPLOY-FINANCE` 部署到公网 |
|
||||||
| `LAUNCH-DEPLOY-FINANCE` | `issues/2026-05-17_09-38-55-launch-deploy-finance.csv` | `EVIDENCE` | CASHV 部署与公网复检已收口;本地全流程/页面门禁、受控部署、migration 应用、公网 readiness、PDF live OCR、移动/企微矩阵和 CASHV 短窗口均通过;最终 `payment_batch_v1=flag_force_off` |
|
| `LAUNCH-DEPLOY-FINANCE` | `issues/2026-05-17_09-38-55-launch-deploy-finance.csv` | `EVIDENCE` | CASHV 部署与公网复检已收口;本地全流程/页面门禁、受控部署、migration 应用、公网 readiness、PDF live OCR、移动/企微矩阵和 CASHV 短窗口均通过;最终 `payment_batch_v1=flag_force_off` |
|
||||||
|
| `FIN-PROD-ENABLE` | `issues/2026-05-17_11-40-47-fin-prod-enable-payment-batch.csv` | `EVIDENCE` | 公网付款批次正式常开已收口;测试报销 `BX-20260517-0026` 完成付款闭环、AP 导出、审计和移动/企微矩阵,最终 `payment_batch_v1=force_on`、`erp_sync=force_off` |
|
||||||
|
| `ACCOUNTING-HANDOFF-V1` | `issues/2026-05-17_11-40-48-accounting-handoff-v1.csv` | `EVIDENCE` | 月度代账交接 SOP 已收口;OA 侧导出和回单附件清单可用,银行流水和回单原件由出纳从银行侧导出,不导入 OA |
|
||||||
|
|
||||||
旧 Issue CSV 保留为历史审计证据:不删除、不批量回写、不移动到 `docs/archive/` 或其他归档目录。新连续合同如替代旧合同,只在新合同 `Notes` 或文档索引中说明 supersedes。
|
旧 Issue CSV 保留为历史审计证据:不删除、不批量回写、不移动到 `docs/archive/` 或其他归档目录。新连续合同如替代旧合同,只在新合同 `Notes` 或文档索引中说明 supersedes。
|
||||||
|
|
||||||
@@ -57,8 +59,8 @@
|
|||||||
|
|
||||||
| Doc_ID | Path | Category | Authority | Status | Owner | Related_Issues | Evidence | Supersedes | Last_Audited | Audit_Method | Risk | Notes |
|
| Doc_ID | Path | Category | Authority | Status | Owner | Related_Issues | Evidence | Supersedes | Last_Audited | Audit_Method | Risk | Notes |
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
| `IDX` | `docs/文档索引.md` | 入口索引 | 最高文档入口 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO,P2AF,P2-FIN-LIVE-DECISION,P2-FIN-LIVE,PRELAUNCH,CASHIER-VOUCHER-V1,LAUNCH-DEPLOY-FINANCE` | `rg` | none | 2026-05-17 | `node scripts/qa/audit-docs.mjs --check-registry` | medium | 判断当前该读什么 |
|
| `IDX` | `docs/文档索引.md` | 入口索引 | 最高文档入口 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO,P2AF,P2-FIN-LIVE-DECISION,P2-FIN-LIVE,PRELAUNCH,CASHIER-VOUCHER-V1,LAUNCH-DEPLOY-FINANCE,FIN-PROD-ENABLE,ACCOUNTING-HANDOFF-V1` | `rg` | none | 2026-05-17 | `node scripts/qa/audit-docs.mjs --check-registry` | medium | 判断当前该读什么 |
|
||||||
| `REG` | `docs/文档事实源状态矩阵.md` | 文档台账 | 文档分类和状态事实源 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO,P2AF,P2-FIN-LIVE-DECISION,P2-FIN-LIVE,PRELAUNCH,CASHIER-VOUCHER-V1,LAUNCH-DEPLOY-FINANCE` | `audit-docs` | none | 2026-05-17 | `audit-docs --check-registry` | medium | 判断文档权威性 |
|
| `REG` | `docs/文档事实源状态矩阵.md` | 文档台账 | 文档分类和状态事实源 | `CURRENT` | product/dev | `GOV8,QAOPS9,P2D,P2-ORG,P2-FIN-DECISION,P2-FIN-V1,P2-OCR-SAMPLE,P2Q,P2AUTO,P2AF,P2-FIN-LIVE-DECISION,P2-FIN-LIVE,PRELAUNCH,CASHIER-VOUCHER-V1,LAUNCH-DEPLOY-FINANCE,FIN-PROD-ENABLE,ACCOUNTING-HANDOFF-V1` | `audit-docs` | none | 2026-05-17 | `audit-docs --check-registry` | medium | 判断文档权威性 |
|
||||||
| `PRD-MVP` | `docs/海音达OA-MVP需求规格说明书.md` | 需求事实源 | PRD 主事实源 | `CURRENT` | product | `REQ8,P2T,GOV8` | Issue CSV | `docs/requirements-specification.md` | 2026-05-13 | `rg` | high | 定义做什么、不做什么和验收口径 |
|
| `PRD-MVP` | `docs/海音达OA-MVP需求规格说明书.md` | 需求事实源 | PRD 主事实源 | `CURRENT` | product | `REQ8,P2T,GOV8` | Issue CSV | `docs/requirements-specification.md` | 2026-05-13 | `rg` | high | 定义做什么、不做什么和验收口径 |
|
||||||
| `PRODUCT-SCOPE` | `docs/产品口径.md` | 需求事实源 | 产品边界事实源 | `CURRENT` | product | `REQ8,P2T` | `rg` | none | 2026-05-13 | `rg` | high | 记录已拍板产品边界 |
|
| `PRODUCT-SCOPE` | `docs/产品口径.md` | 需求事实源 | 产品边界事实源 | `CURRENT` | product | `REQ8,P2T` | `rg` | none | 2026-05-13 | `rg` | high | 记录已拍板产品边界 |
|
||||||
| `DESIGN-UI` | `docs/产品设计规则.md` | 设计事实源 | PC/移动/企业微信设计事实源 | `CURRENT` | design/frontend | `MIA7,MIF7,GOV8,DG8,DG9` | Playwright/视觉审查/DG8/DG9 | none | 2026-05-13 | `rg` | high | 页面改动必须读取;DG8 后飞书为主 UI/布局参考;DG9 后按页面分层和样板选型 |
|
| `DESIGN-UI` | `docs/产品设计规则.md` | 设计事实源 | PC/移动/企业微信设计事实源 | `CURRENT` | design/frontend | `MIA7,MIF7,GOV8,DG8,DG9` | Playwright/视觉审查/DG8/DG9 | none | 2026-05-13 | `rg` | high | 页面改动必须读取;DG8 后飞书为主 UI/布局参考;DG9 后按页面分层和样板选型 |
|
||||||
@@ -163,4 +165,4 @@
|
|||||||
|
|
||||||
- `ME6-3` 代表性企业微信真机门禁已关闭。
|
- `ME6-3` 代表性企业微信真机门禁已关闭。
|
||||||
- MIF7-1..7 与 MIF7T 已完成本地整改、公网部署、公网匿名复检和带凭据 live smoke/OCR 复检。
|
- MIF7-1..7 与 MIF7T 已完成本地整改、公网部署、公网匿名复检和带凭据 live smoke/OCR 复检。
|
||||||
- R7E 真实扩大试运行阶段验收、P2 排序、R7E 收口、QAOPS8 上线硬化阶段收口、QAOPS9 上线硬化连续合同、PRELAUNCH 正式启用前公网预回归、CASHIER-VOUCHER-V1 财务闭环与银行回单附件以及 LAUNCH-DEPLOY-FINANCE 部署复检均已完成,当前 P0/P1 阻断为 0;P2D 已完成决策分流,P2-ORG 只读巡检已完成且无需写修复合同;P2-FIN-LIVE、PRELAUNCH 和 LDF 均验证付款批次公网短窗口并回到 `payment_batch_v1=flag_force_off`;后续正式常开付款批次和代账交接 SOP 必须另建或延续对应连续合同。
|
- R7E 真实扩大试运行阶段验收、P2 排序、R7E 收口、QAOPS8 上线硬化阶段收口、QAOPS9 上线硬化连续合同、PRELAUNCH 正式启用前公网预回归、CASHIER-VOUCHER-V1 财务闭环与银行回单附件、LAUNCH-DEPLOY-FINANCE 部署复检、FIN-PROD-ENABLE 正式常开和 ACCOUNTING-HANDOFF-V1 代账交接 SOP 均已完成,当前 P0/P1 阻断为 0;P2D 已完成决策分流,P2-ORG 只读巡检已完成且无需写修复合同;当前公网 `payment_batch_v1=force_on`,`erp_sync=force_off`,正式支付接口、ERP、图片 OCR 和银行流水导入继续冻结。
|
||||||
|
|||||||
+5
-1
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
## 最近事实源链
|
## 最近事实源链
|
||||||
|
|
||||||
R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> MIF7/MIF7T(公网部署与带凭据公网复检已收口) -> DG8 -> DG9 -> R7E -> FBA -> QAOPS9/P2D -> P2-ORG -> P2-FIN-DECISION -> P2-FIN-V1 -> P2-OCR-SAMPLE -> P2Q -> P2AUTO -> P2AF -> P2-FIN-LIVE-DECISION -> P2-FIN-LIVE -> PRELAUNCH -> CASHIER-VOUCHER-V1 -> LAUNCH-DEPLOY-FINANCE。
|
R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> MIF7/MIF7T(公网部署与带凭据公网复检已收口) -> DG8 -> DG9 -> R7E -> FBA -> QAOPS9/P2D -> P2-ORG -> P2-FIN-DECISION -> P2-FIN-V1 -> P2-OCR-SAMPLE -> P2Q -> P2AUTO -> P2AF -> P2-FIN-LIVE-DECISION -> P2-FIN-LIVE -> PRELAUNCH -> CASHIER-VOUCHER-V1 -> LAUNCH-DEPLOY-FINANCE -> FIN-PROD-ENABLE -> ACCOUNTING-HANDOFF-V1。
|
||||||
|
|
||||||
- R6、PC6、PA6、FL6、AP6、RI6、LP6 已完成并部署。
|
- R6、PC6、PA6、FL6、AP6、RI6、LP6 已完成并部署。
|
||||||
- ME6 自动化、公网视觉矩阵和 `ME6-3` 代表性企业微信真机门禁已完成。
|
- ME6 自动化、公网视觉矩阵和 `ME6-3` 代表性企业微信真机门禁已完成。
|
||||||
@@ -44,6 +44,8 @@ R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> M
|
|||||||
- PRELAUNCH 是未正式使用前的正式启用准备合同:公网作为准生产预启用环境承载带 `PRELAUNCH` marker 的测试数据和自动化回归;公网 readiness、远端只读基线、隔离核心业务流、付款批次 PRELAUNCH 窗口、移动/企业微信自动化和 Go/No-Go 摘要已完成。最终 `payment_batch_v1=flag_force_off`,未部署、不接真实支付、不写 ERP 实时凭证、不开放图片 OCR;采购公网手工 live spec 长等待/退出码问题作为 QA 自动化稳定性维护项。
|
- PRELAUNCH 是未正式使用前的正式启用准备合同:公网作为准生产预启用环境承载带 `PRELAUNCH` marker 的测试数据和自动化回归;公网 readiness、远端只读基线、隔离核心业务流、付款批次 PRELAUNCH 窗口、移动/企业微信自动化和 Go/No-Go 摘要已完成。最终 `payment_batch_v1=flag_force_off`,未部署、不接真实支付、不写 ERP 实时凭证、不开放图片 OCR;采购公网手工 live spec 长等待/退出码问题作为 QA 自动化稳定性维护项。
|
||||||
- CASHIER-VOUCHER-V1 是正式启用前财务闭环与代账交接第一轮实现合同:基于“无 ERP/独立财务系统、只有代账”的业务事实,将付款闭环固定为 OA 内审批/台账/付款批次 + 银行对公付款回单附件 + 月度代账交接;已新增批次级银行回单附件模型/API/PC 操作区、复核后补凭证和银行渠道执行前回单阻断,代码已由 `LAUNCH-DEPLOY-FINANCE` 部署到公网,不接真实支付、不写 ERP、不开放图片 OCR、不导入银行流水。
|
- CASHIER-VOUCHER-V1 是正式启用前财务闭环与代账交接第一轮实现合同:基于“无 ERP/独立财务系统、只有代账”的业务事实,将付款闭环固定为 OA 内审批/台账/付款批次 + 银行对公付款回单附件 + 月度代账交接;已新增批次级银行回单附件模型/API/PC 操作区、复核后补凭证和银行渠道执行前回单阻断,代码已由 `LAUNCH-DEPLOY-FINANCE` 部署到公网,不接真实支付、不写 ERP、不开放图片 OCR、不导入银行流水。
|
||||||
- LAUNCH-DEPLOY-FINANCE 是 CASHIER-VOUCHER-V1 后的部署与公网复检合同:先完成本地基础门禁、业务流、PC/移动/企业微信页面矩阵,再以 `SYNC_ENV=0` 部署到 `https://oa.hyinda.com`;新增银行回单附件 migration 已应用,公网 readiness、PDF live OCR、移动/企业微信矩阵和 CASHV 短窗口均通过,最终 `payment_batch_v1=flag_force_off`,正式常开仍需 `FIN-PROD-ENABLE`。
|
- LAUNCH-DEPLOY-FINANCE 是 CASHIER-VOUCHER-V1 后的部署与公网复检合同:先完成本地基础门禁、业务流、PC/移动/企业微信页面矩阵,再以 `SYNC_ENV=0` 部署到 `https://oa.hyinda.com`;新增银行回单附件 migration 已应用,公网 readiness、PDF live OCR、移动/企业微信矩阵和 CASHV 短窗口均通过,最终 `payment_batch_v1=flag_force_off`,正式常开仍需 `FIN-PROD-ENABLE`。
|
||||||
|
- FIN-PROD-ENABLE 是公网付款批次正式常开合同:未重新部署业务代码;新增 QA-only 正式启用 runner 后完成公网正式验收,测试报销 `BX-20260517-0026` 完成审批、付款批次创建/复核、无回单执行阻断、回单上传/下载、付款执行、AP 导出和审计核验;公网移动矩阵 6 passed、企业微信渲染矩阵 8 passed;最终 `payment_batch_v1=force_on`,`erp_sync=force_off`。
|
||||||
|
- ACCOUNTING-HANDOFF-V1 是月度代账交接 SOP 合同:公司无 ERP/独立财务系统,OA 侧交接资料固定为 AP 台账、报销导出、票据中心导出、付款批次/回单附件清单和审计日志;银行侧流水和回单原件由出纳从银行渠道导出交给代账人工核对,不导入 OA,不开发一键 zip 包。
|
||||||
|
|
||||||
## 当前活跃治理流
|
## 当前活跃治理流
|
||||||
|
|
||||||
@@ -74,6 +76,8 @@ R6 -> PC6 -> PA6 -> FL6 -> AP6 -> RI6 -> LP6 -> ME6/QG6 -> DG7/MRF7 -> MIA7 -> M
|
|||||||
| `PRELAUNCH` | `issues/2026-05-17_00-46-49-prelaunch-production-readiness.csv` | 已收口:正式启用前公网预回归完成,最终 `payment_batch_v1=flag_force_off` | 公网环境基线、远端只读状态、PRELAUNCH 测试数据、核心业务自动化、付款批次公网窗口、移动/企业微信矩阵、启用范围和 Go/No-Go 摘要 | 不部署、不执行生产恢复、不重置数据库、不打印远端 env、不接真实支付、不写 ERP 实时凭证、不开放图片 OCR |
|
| `PRELAUNCH` | `issues/2026-05-17_00-46-49-prelaunch-production-readiness.csv` | 已收口:正式启用前公网预回归完成,最终 `payment_batch_v1=flag_force_off` | 公网环境基线、远端只读状态、PRELAUNCH 测试数据、核心业务自动化、付款批次公网窗口、移动/企业微信矩阵、启用范围和 Go/No-Go 摘要 | 不部署、不执行生产恢复、不重置数据库、不打印远端 env、不接真实支付、不写 ERP 实时凭证、不开放图片 OCR |
|
||||||
| `CASHIER-VOUCHER-V1` | `issues/2026-05-17_09-04-37-cashier-payment-voucher-v1.csv` | 已收口:正式启用前财务闭环与银行回单附件完成,已由 LDF 部署 | 付款批次银行回单附件、复核后补凭证、银行渠道执行前回单阻断、管理员复核审计、出纳执行、代账交接前置 | 不接真实支付、不接 ERP、不开放图片 OCR、不导入银行流水 |
|
| `CASHIER-VOUCHER-V1` | `issues/2026-05-17_09-04-37-cashier-payment-voucher-v1.csv` | 已收口:正式启用前财务闭环与银行回单附件完成,已由 LDF 部署 | 付款批次银行回单附件、复核后补凭证、银行渠道执行前回单阻断、管理员复核审计、出纳执行、代账交接前置 | 不接真实支付、不接 ERP、不开放图片 OCR、不导入银行流水 |
|
||||||
| `LAUNCH-DEPLOY-FINANCE` | `issues/2026-05-17_09-38-55-launch-deploy-finance.csv` | 已收口:CASHV 部署与公网复检完成,最终 `payment_batch_v1=flag_force_off` | 本地全流程/页面门禁、受控部署、migration 应用、公网 readiness、PDF live OCR、移动/企微矩阵、CASHV 公网短窗口 | 不正式常开付款批次、不接真实支付、不接 ERP、不开放图片 OCR、不重置数据库 |
|
| `LAUNCH-DEPLOY-FINANCE` | `issues/2026-05-17_09-38-55-launch-deploy-finance.csv` | 已收口:CASHV 部署与公网复检完成,最终 `payment_batch_v1=flag_force_off` | 本地全流程/页面门禁、受控部署、migration 应用、公网 readiness、PDF live OCR、移动/企微矩阵、CASHV 公网短窗口 | 不正式常开付款批次、不接真实支付、不接 ERP、不开放图片 OCR、不重置数据库 |
|
||||||
|
| `FIN-PROD-ENABLE` | `issues/2026-05-17_11-40-47-fin-prod-enable-payment-batch.csv` | 已收口:公网付款批次正式启用完成,最终 `payment_batch_v1=force_on` | 公网正式启用 runner、测试报销 `BX-20260517-0026`、银行回单附件、AP 导出、审计、移动/企微矩阵、最终 flag-on 证据 | 不重新部署、不接真实支付、不接 ERP、不开放图片 OCR、不导入银行流水 |
|
||||||
|
| `ACCOUNTING-HANDOFF-V1` | `issues/2026-05-17_11-40-48-accounting-handoff-v1.csv` | 已收口:月度代账交接 SOP 固化 | OA 侧 AP/报销/票据/付款批次/回单附件/审计资料清单,银行侧流水和回单原件由出纳线下导出,代账人工核对 | 不开发银行流水导入、不开发一键 zip 包、不保存真实银行资料到仓库 |
|
||||||
|
|
||||||
旧 Issue CSV 不删除、不批量回写、不移动到归档目录。若新治理流替代旧合同,只在新 CSV 的 `Notes` 或本索引说明 supersedes,旧文件保留历史审计语义。
|
旧 Issue CSV 不删除、不批量回写、不移动到归档目录。若新治理流替代旧合同,只在新 CSV 的 `Notes` 或本索引说明 supersedes,旧文件保留历史审计语义。
|
||||||
|
|
||||||
|
|||||||
+43
-8
@@ -1,6 +1,6 @@
|
|||||||
# hyb-oa 管理员手册(10 人以下团队版)
|
# hyb-oa 管理员手册(10 人以下团队版)
|
||||||
|
|
||||||
**当前状态(2026-05-13 / DCL8)**:R6 已通过,PC6、PA6、FL6、AP6、RI6、LP6、ME6、QG6、DG7/MRF7、MIA7、MIF7/MIF7T 和 MIF7TD 已完成相应收口;`ME6-3` 代表性企业微信真机门禁已关闭。R7 未真实启动,R7/OPS7/DOC7 仍只是准备材料,不代表已启动或已上线;下一步先做 R7E 自动门禁和参与人/窗口确认。
|
**当前状态(2026-05-17 / FINPROD)**:R7E、PRELAUNCH、CASHIER-VOUCHER-V1 和 LAUNCH-DEPLOY-FINANCE 均已收口;银行回单附件能力已部署到公网。FIN-PROD-ENABLE 已完成公网正式启用验收,`payment_batch_v1=force_on`,`erp_sync=force_off`。公司当前无 ERP/独立财务系统,财务闭环以 OA 内审批、台账、付款批次、银行对公付款回单和月度代账交接为准。
|
||||||
|
|
||||||
## 1. 适用范围
|
## 1. 适用范围
|
||||||
- 适用于 `small_team_internal` 工作区模式。
|
- 适用于 `small_team_internal` 工作区模式。
|
||||||
@@ -15,16 +15,16 @@
|
|||||||
## 3. 首次上线最小配置
|
## 3. 首次上线最小配置
|
||||||
1. 进入 `工作区设置`,确认产品模式为 `small_team_internal`。
|
1. 进入 `工作区设置`,确认产品模式为 `small_team_internal`。
|
||||||
2. 首批主路径优先保留:`Portal`、`审批待办`、`报销`、`票据/OCR`、`请假`、`项目`、`采购/费用`、`合同/附件`。
|
2. 首批主路径优先保留:`Portal`、`审批待办`、`报销`、`票据/OCR`、`请假`、`项目`、`采购/费用`、`合同/附件`。
|
||||||
3. `团队状态`、`周报`、`待办`、`公告`、`人员`、`资产` 可按真实试运行需要开启;`预算`、`支付批次`、`AP 运营`、`ERP`、`AI`、完整差旅能力默认作为管理员备用或历史能力,不作为当前 MVP 首批主线。
|
3. `团队状态`、`周报`、`待办`、`公告`、`人员`、`资产` 可按真实试运行需要开启;`支付批次` 和 `AP 运营` 已作为财务闭环正式能力面向出纳、财务和管理员开放,普通员工不显示该入口;`预算`、`ERP`、`AI`、完整差旅能力仍作为备用或历史能力。
|
||||||
4. 在 `用户管理` 中按推荐预设配置:老板 / 管理员 / 主管 / 员工。
|
4. 在 `用户管理` 中按推荐预设配置:老板 / 管理员 / 主管 / 员工。
|
||||||
- 正式审批组织口径:`majianyong` 配置为员工可见直属主管和 `manager`;`wangshunli` 配置为后台公司负责人 `ceo + admin`,不配置为普通 `manager`。
|
- 正式审批组织口径:`majianyong` 配置为员工可见直属主管和 `manager`;`wangshunli` 配置为后台公司负责人 `ceo + admin`,不配置为普通 `manager`。
|
||||||
- 普通员工和专用测试员工的直属主管配置为 `majianyong`;`majianyong` 的直属领导配置为 `wangshunli`。
|
- 普通员工和专用测试员工的直属主管配置为 `majianyong`;`majianyong` 的直属领导配置为 `wangshunli`。
|
||||||
- `admin` 只代表系统管理权限,不代表业务审批权;需要业务审批时必须配置 `manager`、`ceo`、`finance`、`hr` 等正式业务角色。
|
- `admin` 只代表系统管理权限,不代表业务审批权;需要业务审批时必须配置 `manager`、`ceo`、`finance`、`hr` 等正式业务角色。
|
||||||
- 缺直属主管、主管停用、主管链断裂或审批节点解析到申请人本人时,应先修正组织或审批规则,不要用管理员账号代批。
|
- 缺直属主管、主管停用、主管链断裂或审批节点解析到申请人本人时,应先修正组织或审批规则,不要用管理员账号代批。
|
||||||
5. 检查功能开关:
|
5. 检查功能开关:
|
||||||
- 建议开启:`portal_home_v1`、企业微信入口相关开关、报销/OCR、审批待办、请假和项目基础能力。
|
- 建议开启:`portal_home_v1`、企业微信入口相关开关、报销/OCR、审批待办、请假、项目基础能力和 `payment_batch_v1`。
|
||||||
- 按需开启:`announcements_v1`、`presence_board_v1`、`weekly_report_v1`、`employee_lifecycle_v1`、`todo_board_v1`、`global_search_v1`。
|
- 按需开启:`announcements_v1`、`presence_board_v1`、`weekly_report_v1`、`employee_lifecycle_v1`、`todo_board_v1`、`global_search_v1`。
|
||||||
- 建议保留备用:`payment_batch_v1`、`cutover_console_v1`、预算/AP/ERP/AI 相关开关。
|
- 建议保留备用:`cutover_console_v1`、预算、ERP、AI 和完整差旅相关开关。
|
||||||
|
|
||||||
### 3.1 ORG8 组织账号与角色边界
|
### 3.1 ORG8 组织账号与角色边界
|
||||||
|
|
||||||
@@ -163,6 +163,38 @@ WHERE e.status = 'active'
|
|||||||
ORDER BY e.employee_no;
|
ORDER BY e.employee_no;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 3.2 正式财务闭环与代账交接
|
||||||
|
|
||||||
|
公司当前没有 ERP 或独立财务系统,OA 不生成 ERP 实时凭证,也不接真实支付接口。正式财务闭环如下:
|
||||||
|
|
||||||
|
1. 员工在 OA 提交报销,发票/OCR 正式入口保持 PDF-only,图片只作为普通附件。
|
||||||
|
2. 直属主管、财务或负责人按审批规则在 OA 内完成审批。
|
||||||
|
3. `wangzhiling` 作为出纳,在付款批次中创建待付款批次,按批次准备对公银行付款。
|
||||||
|
4. `wangshunli` 以管理员身份复核付款批次;管理员复核是当前明确业务选择,系统保留 maker-checker 和审计记录。
|
||||||
|
5. 出纳在 OA 外通过网银或银行渠道付款,然后在付款批次详情上传银行回单附件,补齐凭证号、付款日期和付款渠道。
|
||||||
|
6. 出纳执行 OA 已付款标记;系统将报销单置为 `paid`,票据归档为 `archived`,AP 台账变为已付款状态。
|
||||||
|
|
||||||
|
银行渠道付款必须至少有一份银行回单附件后才能在 OA 内执行付款标记。回单附件支持 PDF、JPG、PNG、WEBP,作为批次级付款证据保存;导出和报告中不复制回单原件内容。
|
||||||
|
|
||||||
|
月度代账交接按以下清单执行:
|
||||||
|
|
||||||
|
| 资料 | 来源 | 负责人 | 说明 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| AP 台账导出 | OA `AP 运营` | 出纳/财务 | 用于核对已付款、待付款和异常项;AP 台账不是银行流水 |
|
||||||
|
| 报销导出 | OA 报销导出 | 出纳/财务 | 按月导出,必要时筛选已通过或已付款状态 |
|
||||||
|
| 票据中心导出 | OA 票据/OCR | 出纳/财务 | 用于核对 PDF 发票识别、绑定和归档状态 |
|
||||||
|
| 付款批次清单 | OA `付款批次` | 出纳/财务 | 记录批次、复核、执行和对应报销单 |
|
||||||
|
| 银行回单附件清单 | OA 付款批次详情 | 出纳 | 只交接清单和必要回单原件,不把真实回单提交到代码仓库 |
|
||||||
|
| 审计日志导出 | OA 审计日志 | 管理员 | 覆盖导出、付款批次创建、复核、回单上传和执行动作 |
|
||||||
|
| 银行流水和回单原件 | 网银或银行渠道 | 出纳 | 由出纳从银行侧导出,交给代账人工核对;不导入 OA |
|
||||||
|
|
||||||
|
交接建议:
|
||||||
|
|
||||||
|
- 每月固定一个交接目录或压缩包,由出纳线下维护,命名建议为 `YYYY-MM-海音达-代账交接`。
|
||||||
|
- OA 导出文件、银行流水、银行回单原件和异常说明分目录保存;仓库只记录流程和脱敏证据,不保存正式财务资料。
|
||||||
|
- 代账如发现金额、收款方、日期或票据状态不一致,先回查 OA 报销单、付款批次、银行回单和审计日志,再决定是否由出纳补充说明。
|
||||||
|
- 若代账方后续要求一键交接包,再另建开发合同,明确包内文件、权限、脱敏、审计和下载回滚规则。
|
||||||
|
|
||||||
## 4. 人员生命周期管理
|
## 4. 人员生命周期管理
|
||||||
### 4.1 入职
|
### 4.1 入职
|
||||||
1. 在 `人员管理` 创建员工账号。
|
1. 在 `人员管理` 创建员工账号。
|
||||||
@@ -205,20 +237,23 @@ ORDER BY e.employee_no;
|
|||||||
|
|
||||||
## 8. 备用模块治理
|
## 8. 备用模块治理
|
||||||
- 采购/费用、合同/附件和项目轻管理属于 P1 轻量能力,可在 P0 审批、报销/OCR、请假和企业微信入口稳定后逐步开放。
|
- 采购/费用、合同/附件和项目轻管理属于 P1 轻量能力,可在 P0 审批、报销/OCR、请假和企业微信入口稳定后逐步开放。
|
||||||
- 预算、支付批次、AP 运营、ERP、AI、完整差旅、资产深度管理等保留代码和历史数据,但不作为当前 MVP 首批主路径。
|
- 支付批次和 AP 运营已作为正式财务闭环开放给出纳、财务和管理员;普通员工仍不显示财务运营入口。
|
||||||
|
- 预算、ERP、AI、完整差旅、资产深度管理等保留代码和历史数据,但不作为当前 MVP 首批主路径。
|
||||||
- 若短期完全不用,保持管理员可见即可,不必删除。
|
- 若短期完全不用,保持管理员可见即可,不必删除。
|
||||||
- 只有出现真实场景时,再恢复到主导航或单独扩展。
|
- 只有出现真实场景时,再恢复到主导航或单独扩展。
|
||||||
|
|
||||||
## 9. 推荐上线顺序
|
## 9. 推荐上线顺序
|
||||||
1. 先完成账号、角色、部门、企业微信自建应用入口和账号密码回退路径。
|
1. 先完成账号、角色、部门、企业微信自建应用入口和账号密码回退路径。
|
||||||
2. 再启用 `OA 统一审批中心 + 报销/发票/OCR/附件`,跑通提交、审批、退回、查重、付款和导出闭环。
|
2. 再启用 `OA 统一审批中心 + 报销/发票/OCR/附件`,跑通提交、审批、退回、查重、付款和导出闭环。
|
||||||
3. 同步启用 `请假/补卡/外出/加班`,明确企业微信打卡只是原始采集,正式考勤解释回到 OA 审批结果。
|
3. 同步启用付款批次、AP 台账和银行回单附件,付款动作仍由出纳在 OA 外通过银行渠道完成。
|
||||||
4. 在 P0 稳定后开放 P1:`采购/费用审批`、`合同/附件归档`、`项目轻管理`。
|
4. 同步启用 `请假/补卡/外出/加班`,明确企业微信打卡只是原始采集,正式考勤解释回到 OA 审批结果。
|
||||||
5. 最后按真实需要评估周报、团队状态、公告、人员生命周期、资产、预算、支付、ERP、AI 和完整差旅。
|
5. 在 P0 稳定后开放 P1:`采购/费用审批`、`合同/附件归档`、`项目轻管理`。
|
||||||
|
6. 最后按真实需要评估周报、团队状态、公告、人员生命周期、资产、预算、ERP、AI 和完整差旅。
|
||||||
|
|
||||||
## 10. 试点验收口径
|
## 10. 试点验收口径
|
||||||
- 全员都能账号密码登录;已绑定员工能从企业微信 `海音达工作台` 进入移动工作台。
|
- 全员都能账号密码登录;已绑定员工能从企业微信 `海音达工作台` 进入移动工作台。
|
||||||
- 至少 1 笔报销完成:上传发票或附件 -> OCR/查重 -> 提交 -> OA 审批 -> 财务复核/付款标记 -> 导出。
|
- 至少 1 笔报销完成:上传发票或附件 -> OCR/查重 -> 提交 -> OA 审批 -> 财务复核/付款标记 -> 导出。
|
||||||
|
- 至少 1 个付款批次完成:出纳创建 -> 管理员复核 -> 出纳上传银行回单 -> 执行 OA 已付款标记 -> AP 台账和审计可查。
|
||||||
- 至少 1 条请假或补卡完成:员工提交 -> OA 审批 -> 团队状态或考勤解释口径不破坏。
|
- 至少 1 条请假或补卡完成:员工提交 -> OA 审批 -> 团队状态或考勤解释口径不破坏。
|
||||||
- 至少 1 个审批待办从企业微信提醒 deeplink 回到 OA 页面处理。
|
- 至少 1 个审批待办从企业微信提醒 deeplink 回到 OA 页面处理。
|
||||||
- P1 试点时,至少 1 条采购/费用申请、1 条合同台账、1 个项目关联事项可创建、审批或归档。
|
- P1 试点时,至少 1 条采购/费用申请、1 条合同台账、1 个项目关联事项可创建、审批或归档。
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
"ID","Title","Description","Acceptance","Test_Method","Tools","Dev_Status","Review1_Status","Regression_Status","Files","Dependencies","Notes"
|
||||||
|
"FINPROD-0","FIN-PROD-ENABLE 合同建档","建立正式启用公网付款批次合同;基线为 426a968,CASHIER-VOUCHER-V1 已部署,目标是正式设置 payment_batch_v1=force_on 并完成 OA 内付款闭环验收。","1) CSV 校验通过;2) 本轮不新增 API/schema/UI;3) 使用公网现有测试账号和授权 PDF 样票;4) wangzhiling 作为出纳/付款执行口径,wangshunli 作为管理员复核和回滚决策人口径;5) 不接真实支付、不接 ERP、不开放图片 OCR、不导入银行流水、不重置数据库、不打印 secret。","受影响流程:CASHV 部署完成 -> FIN-PROD-ENABLE 正式常开准备;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-17_11-40-47-fin-prod-enable-payment-batch.csv;人工核对本轮边界和用户批准。","python3|manual","DONE","DONE","DONE","issues/2026-05-17_11-40-47-fin-prod-enable-payment-batch.csv","[]","完成:CSV 初始校验通过;用户已批准正式启用付款批次;本轮不新增业务 API/schema/UI,不接真实支付,不接 ERP,不开放图片 OCR;失败回滚路径为 payment_batch_v1 force_off。"
|
||||||
|
"FINPROD-1","公网正式启用前置门禁","启用前确认公网健康、live smoke/PDF OCR 凭据可用、当前 payment_batch_v1 为关闭态、文档登记无新增阻断,且工作区没有未解释变更。","1) check-live-smoke-env --strict 通过;2) check-payment-batch-flag-off --json 通过或明确当前已关闭;3) audit-docs 仅允许既有 warning;4) git diff --check 通过;5) 不打印 env、cookie、PDF 路径或导出内容。","受影响流程:公网登录/凭据 -> 付款批次 flag-off 只读门禁 -> 文档门禁;验证步骤:set -a && source ~/.hyb-oa/live-smoke.env && set +a;LIVE_SMOKE_BASE_URL=https://oa.hyinda.com node scripts/qa/check-live-smoke-env.mjs --strict;LIVE_SMOKE_BASE_URL=https://oa.hyinda.com node scripts/qa/check-payment-batch-flag-off.mjs --json;node scripts/qa/audit-docs.mjs --check-registry;git diff --check。","node|bash|git","DONE","DONE","DONE","scripts/qa/check-live-smoke-env.mjs|scripts/qa/check-payment-batch-flag-off.mjs|docs","[""FINPROD-0""]","完成:check-live-smoke-env --strict PASS,publicReady/liveSmokeReady/liveOcrReady=true;pre flag-off PASS,finance/employee/admin 均为 409 payment_batch_v1_disabled / flag_force_off;audit-docs errors=0 warnings=1 为既有未登记文档 warning;git diff --check PASS。"
|
||||||
|
"FINPROD-2","正式启用 QA runner 准备","复用或新增 QA-only runner,使公网付款批次验收通过后可保持 payment_batch_v1=force_on;默认失败时仍恢复 force_off;不改业务运行时代码。","1) runner 必须要求 P2_FIN_LIVE_APPROVED=1 和 FIN_PROD_ENABLE_APPROVED=1;2) PASS 时最终检查 payment_batch_v1=force_on;3) FAIL 时强制恢复 force_off;4) erp_sync 全程 force_off;5) runner 报告只记录脱敏 ID、状态、HTTP 结果和审计动作。","受影响流程:公网付款批次正式启用自动化 -> 成功保持开启/失败回滚;验证步骤:node --check scripts/qa/check-payment-batch-flag-off.mjs;bash -n scripts/qa/run-fin-prod-enable-window.sh;cd client && npm run typecheck && npm run build;如修改 Playwright spec,执行对应 typecheck。","node|bash|npm|playwright","DONE","DONE","DONE","scripts/qa/run-fin-prod-enable-window.sh|client/tests/e2e/manual/p2-fin-live-payment-batch.spec.ts","[""FINPROD-1""]","完成:新增 QA-only run-fin-prod-enable-window.sh;p2-fin-live spec 增加显式 force_on final mode,默认短窗口仍 force_off;bash -n PASS,node --check check-payment-batch-flag-off PASS,client typecheck/build PASS;未改业务运行时代码,未重新部署。"
|
||||||
|
"FINPROD-3","公网付款批次正式闭环验收","在公网正式开启 payment_batch_v1 后,用带 FINPROD marker 的报销完成审批、出纳建批次、管理员/复核人复核、出纳上传银行回单、执行 OA 已付款标记。","1) 新建或选用专用测试报销并审批到 approved;2) payment_batch_v1 更新为 force_on 且 erp_sync 仍为 force_off;3) finance/cashier 可创建和执行付款批次;4) 管理员/复核人复核审计可查;5) 银行渠道无回单执行被 409 阻断;6) 上传回单后执行成功,报销 paid、票据 archived、AP 台账 paid。","受影响流程:报销提交 -> 审批 -> PDF 发票绑定 -> 付款批次创建 -> 复核 -> 上传银行回单 -> 执行 OA 已付款;验证步骤:FIN_PROD_ENABLE_APPROVED=1 P2_FIN_LIVE_APPROVED=1 QA_ISSUE_ID=FINPROD P2_FIN_LIVE_MARKER_PREFIX=FINPROD LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-fin-prod-enable-window.sh。","bash|playwright","DONE","DONE","DONE","scripts/qa/run-fin-prod-enable-window.sh|client/tests/e2e/manual/p2-fin-live-payment-batch.spec.ts|artifacts/qa-flow","[""FINPROD-2""]","完成:报告 artifacts/qa-flow/fin-prod-enable-20260517-114731;测试报销 BX-20260517-0026 审批后进入付款批次;无回单执行 409;上传/下载银行回单后执行成功;报销 paid、票据 archived、AP 台账 paid;真实银行付款仍在 OA 外。"
|
||||||
|
"FINPROD-4","权限、AP 导出与审计核验","核验付款批次常开后,员工、审批人和 admin-only 不越权;AP 导出成功;关键审计动作可查且不打印导出内容。","1) employee/manager 创建付款批次被拒绝;2) admin-only 若非 wildcard 应拒绝创建/执行,若为 wildcard 记录 SKIP_WILDCARD_SUPERADMIN;3) AP 导出返回 200;4) FEATURE_FLAG_UPDATED、PAYMENT_BATCH_CREATED、PAYMENT_BATCH_REVIEWED、PAYMENT_BATCH_RECEIPT_UPLOADED、PAYMENT_BATCH_EXECUTED、REIMBURSEMENT_PAYMENT_MARKED、EXPORT_AP_OPERATIONS 审计可查;5) 报告不包含导出明细。","受影响流程:权限边界 -> AP 导出 -> 审计追踪;验证步骤:同 FINPROD-3 runner 内置权限、导出和审计断言;必要时用 admin 只读审计列表核验动作,不下载或打印完整导出内容。","playwright|manual","DONE","DONE","DONE","client/tests/e2e/manual/p2-fin-live-payment-batch.spec.ts|server/src/routes/exports.js|server/src/routes/audit.js","[""FINPROD-3""]","完成:employee/manager 创建付款批次均 403;admin 为 wildcard superadmin,admin-only 负例按 SKIP_WILDCARD_SUPERADMIN 记录;AP 导出 200;FEATURE_FLAG_UPDATED、PAYMENT_BATCH_CREATED/REVIEWED/RECEIPT_UPLOADED/EXECUTED、REIMBURSEMENT_PAYMENT_MARKED、EXPORT_AP_OPERATIONS 审计断言通过。"
|
||||||
|
"FINPROD-5","公网移动与企业微信自动化复检","付款批次正式启用后复跑公网移动和企业微信 UA/渲染矩阵,确认常开状态不影响移动入口和查看体验。","1) dg7-mobile-information-dedupe 通过;2) r124-mobile-entry-matrix 通过;3) r6-wecom-mobile-rendering-hardening 通过;4) 真机仅保留入口/免登/查看确认,不在手机执行付款动作。","受影响流程:公网移动首页/业务入口/通知/企业微信 UA 查看;验证步骤:cd client && E2E_BASE_URL=https://oa.hyinda.com npx playwright test tests/e2e/dg7-mobile-information-dedupe.spec.ts tests/e2e/r124-mobile-entry-matrix.spec.ts tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts --workers=1 --reporter=list。","playwright","DONE","DONE","DONE","client/tests/e2e/dg7-mobile-information-dedupe.spec.ts|client/tests/e2e/r124-mobile-entry-matrix.spec.ts|client/tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts","[""FINPROD-3""]","完成:公网移动矩阵 6 passed;企业微信渲染矩阵 8 passed;自动化覆盖 320/360/375/390/393/414/430,真机仍仅作为后续入口/免登/查看差异确认。"
|
||||||
|
"FINPROD-6","最终 flag 状态确认","验收通过后保持 payment_batch_v1=force_on;若 FINPROD-3/4/5 任一阻断,则恢复 force_off 并记录原因;erp_sync 必须保持 force_off。","1) PASS 时 payment_batch_v1 effectiveEnabled=true 且 overrideMode=force_on;2) FAIL 时 payment_batch_v1 恢复 force_off;3) erp_sync effectiveEnabled=false;4) 记录最终状态、报告目录和测试报销 formNumber;5) 不停留在未记录状态。","受影响流程:付款批次正式常开决策 -> 回滚或保持开启;验证步骤:runner 输出 live-result.json;必要时 admin 只读 GET /api/system/feature-flags 核对 payment_batch_v1 与 erp_sync;不打印 token/env。","node|playwright|manual","DONE","DONE","DONE","scripts/qa/run-fin-prod-enable-window.sh|artifacts/qa-flow","[""FINPROD-4"",""FINPROD-5""]","完成:final-force-on-check PASS;最终 payment_batch_v1 override=force_on effective=true reason=flag_force_on_global;erp_sync override=force_off effective=false reason=flag_force_off;报告 artifacts/qa-flow/fin-prod-enable-20260517-114731。"
|
||||||
|
"FINPROD-7","收口回填与提交","回填 FINPROD CSV、文档索引、事实源矩阵和人工试运行报告,提交中文 commit。","1) CSV 状态按证据回填;2) docs/文档索引.md、docs/文档事实源状态矩阵.md、docs/trialrun/人工试运行报告.md 更新;3) validate、audit-docs、rg、git diff --check 通过;4) 中文 commit 引用 FINPROD;5) 最终说明公网 URL、部署状态、/ready、最终 flag 状态。","流程级验证:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-17_11-40-47-fin-prod-enable-payment-batch.csv;node scripts/qa/audit-docs.mjs --check-registry;rg -n 'FINPROD|FIN-PROD-ENABLE|payment_batch_v1|银行回单|EXPORT_AP_OPERATIONS|PAYMENT_BATCH' docs issues scripts client/tests;git diff --check;git status --short。","python3|node|rg|git","DONE","DONE","DONE","issues/2026-05-17_11-40-47-fin-prod-enable-payment-batch.csv|docs/文档索引.md|docs/文档事实源状态矩阵.md|docs/trialrun/人工试运行报告.md","[""FINPROD-6""]","完成:FINPROD CSV/docs 已回填;validate PASS,audit-docs errors=0 warnings=1(既有 unregistered docs),rg 关键词核对完成,git diff --check PASS;公网 https://oa.hyinda.com 未重新部署业务代码,最终 payment_batch_v1=force_on、erp_sync=force_off。"
|
||||||
|
@@ -0,0 +1,7 @@
|
|||||||
|
"ID","Title","Description","Acceptance","Test_Method","Tools","Dev_Status","Review1_Status","Regression_Status","Files","Dependencies","Notes"
|
||||||
|
"ACCTH-0","ACCOUNTING-HANDOFF-V1 合同建档","建立月度代账交接 SOP 合同;业务口径为 OA 内审批/台账/付款批次 + 银行对公付款回单 + 月度代账人工交接,不做银行流水自动导入。","1) CSV 校验通过;2) 不开发银行流水导入、不接 ERP、不接真实支付接口;3) OA 侧固定交接资料范围;4) 银行侧原始流水和回单由出纳从银行渠道导出;5) 不提交真实银行回单或隐私导出内容。","受影响流程:FIN-PROD-ENABLE -> 月度 AP/报销/票据/付款批次/审计资料整理 -> 代账人工核对;验证步骤:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-17_11-40-48-accounting-handoff-v1.csv;人工核对交接边界。","python3|manual","DONE","DONE","DONE","issues/2026-05-17_11-40-48-accounting-handoff-v1.csv","[]","完成:CSV 初始校验通过;边界固定为月度人工代账交接,不开发银行流水导入,不接 ERP/真实支付,不保存真实银行回单或隐私导出内容到仓库。"
|
||||||
|
"ACCTH-1","现有导出与回单附件能力只读核对","核对 AP 台账导出、报销导出、票据中心导出、付款批次清单、银行回单附件清单和审计日志导出的现有能力与权限。","1) AP 台账导出存在且 FINPROD 验证 200;2) 报销、票据、审计导出能力存在或记录缺口;3) 付款批次回单附件可按批次列表/下载;4) 不打印导出明细、不提交真实附件;5) 缺口仅记录,不顺手开发。","受影响流程:月度代账资料准备 -> OA 导出/清单 -> 银行侧资料人工核对;验证步骤:rg -n 'exports/(ap-operations|reimbursements|invoice|audit)|receipt-attachments|payment-batches' server/src/routes client/src docs;必要时用公网测试数据只读确认回单附件列表和导出 API 状态。","rg|manual|node","DONE","DONE","DONE","server/src/routes/exports.js|server/src/routes/paymentBatches.js|client/src|docs","[""ACCTH-0""]","完成:公网状态核对仅输出状态和字节数,不打印内容;付款批次清单 200,回单附件列表 200,回单下载 200,AP 导出 200,报销导出 200,票据中心导出 200,审计日志导出 200。"
|
||||||
|
"ACCTH-2","月度代账交接 SOP 回写","在既有文档中补充月度代账交接 SOP:出纳准备银行侧流水/回单原件,OA 导出 AP/报销/票据/付款批次/回单附件清单/审计日志,代账人工核对。","1) 优先更新既有文档,不新建综合规划文档;2) 写清月度资料、责任人、时间点、命名规则、隐私边界和异常处理;3) 明确 AP 台账不是银行流水;4) 明确不导入银行流水;5) 图片 OCR、ERP、真实支付接口仍冻结。","受影响流程:月末出纳整理 -> 代账接收 -> 人工核对 -> 异常回查 OA;验证步骤:审阅 docs/管理员手册.md 或现有运营手册;更新 docs/trialrun/人工试运行报告.md、docs/文档索引.md、docs/文档事实源状态矩阵.md 的状态摘要;git diff --check。","manual|git","DONE","DONE","DONE","docs/管理员手册.md|docs/trialrun/人工试运行报告.md|docs/文档索引.md|docs/文档事实源状态矩阵.md","[""ACCTH-1""]","完成:docs/管理员手册.md 新增正式财务闭环与代账交接 SOP;索引、事实源矩阵和人工试运行报告已同步 FINPROD/ACCTH 状态;明确 AP 台账不是银行流水,银行流水不导入 OA,ERP/真实支付/图片 OCR 继续冻结。"
|
||||||
|
"ACCTH-3","代账交接样例清单","基于 FINPROD 或预启用测试数据生成一组脱敏样例清单,证明资料项能从 OA 和银行侧分工获得。","1) 样例只包含月份、资料项、来源、负责人、状态、脱敏 ID 或数量;2) 不包含真实银行账号、完整票据号码、税号、手机号、导出明细或回单原件;3) 记录 FINPROD 测试报销/付款批次的脱敏 formNumber 或报告路径;4) 标明银行侧资料由出纳线下导出。","受影响流程:测试月份资料核对 -> 代账交接清单 -> 异常项回查;验证步骤:阅读 FINPROD live-result.json 和既有 LDF/PRELAUNCH 报告,只摘录 formNumber、状态和报告路径;不得复制导出内容。","manual|node","DONE","DONE","DONE","artifacts/qa-flow|docs/trialrun/人工试运行报告.md","[""ACCTH-2""]","完成:样例使用 FINPROD 脱敏证据,formNumber=BX-20260517-0026,报告 artifacts/qa-flow/fin-prod-enable-20260517-114731;只记录状态和报告路径,不复制导出内容或银行回单原件。"
|
||||||
|
"ACCTH-4","一键交接包决策","判断现有导出和 SOP 是否足够支撑代账交接;若足够则不开发;若需要一键 zip,另建实现合同。","1) 结论为 NO_BUILD_NEEDED 或 GO_TO_PACKAGE_CONTRACT;2) 若 NO_BUILD_NEEDED,说明现有导出+银行侧资料足够;3) 若 GO_TO_PACKAGE_CONTRACT,列出包内文件、权限、脱敏、审计和下载回滚要求;4) 不在本合同临时开发 zip 功能。","受影响流程:月度代账交接效率评估 -> 是否需要一键包;验证步骤:结合 ACCTH-1/2/3 结论人工判断并写入 CSV Notes。","manual","DONE","DONE","DONE","issues/2026-05-17_11-40-48-accounting-handoff-v1.csv","[""ACCTH-3""]","完成:结论 NO_BUILD_NEEDED;现有 OA 导出 + 付款批次回单附件 + 银行侧出纳线下资料足够支撑代账人工核对;一键 zip 交接包不在本轮开发,如代账方提出固定格式再另建合同。"
|
||||||
|
"ACCTH-5","收口回填与提交","回填 ACCTH CSV、索引、事实源矩阵和人工试运行报告;与 FINPROD 一并或分批提交中文 commit。","1) CSV 状态按证据回填;2) validate、audit-docs、rg、git diff --check 通过;3) 文档明确代账交接边界和冻结项;4) 中文 commit 引用 ACCTH;5) 给出后续 Go/No-Go 摘要。","流程级验证:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-17_11-40-48-accounting-handoff-v1.csv;node scripts/qa/audit-docs.mjs --check-registry;rg -n 'ACCTH|ACCOUNTING-HANDOFF|代账|银行回单|AP 台账|银行流水|payment_batch_v1' docs issues;git diff --check;git status --short。","python3|node|rg|git","DONE","DONE","DONE","issues/2026-05-17_11-40-48-accounting-handoff-v1.csv|docs","[""ACCTH-4""]","完成:ACCTH CSV/docs 已回填;validate PASS,audit-docs errors=0 warnings=1(既有 unregistered docs),rg 关键词核对完成,git diff --check PASS;结论 NO_BUILD_NEEDED,正式银行付款制度不由系统自动执行。"
|
||||||
|
@@ -0,0 +1,293 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -u -o pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
CLIENT_DIR="$ROOT_DIR/client"
|
||||||
|
PUBLIC_BASE_URL="${LIVE_SMOKE_BASE_URL:-https://oa.hyinda.com}"
|
||||||
|
REPORT_DIR="${FIN_PROD_ENABLE_REPORT_DIR:-$ROOT_DIR/artifacts/qa-flow/fin-prod-enable-$(date +%Y%m%d-%H%M%S)}"
|
||||||
|
SUMMARY_TXT="$REPORT_DIR/summary.txt"
|
||||||
|
STATUS=0
|
||||||
|
|
||||||
|
mkdir -p "$REPORT_DIR"
|
||||||
|
: >"$SUMMARY_TXT"
|
||||||
|
|
||||||
|
load_default_env() {
|
||||||
|
local env_file="${FIN_PROD_ENABLE_ENV_FILE:-$HOME/.hyb-oa/live-smoke.env}"
|
||||||
|
if [[ "${FIN_PROD_ENABLE_SKIP_ENV_FILE:-0}" == "1" || ! -f "$env_file" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$env_file"
|
||||||
|
set +a
|
||||||
|
PUBLIC_BASE_URL="${LIVE_SMOKE_BASE_URL:-$PUBLIC_BASE_URL}"
|
||||||
|
echo "[fin-prod-enable] loaded masked env file" | tee -a "$SUMMARY_TXT"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_step() {
|
||||||
|
local name="$1"
|
||||||
|
shift
|
||||||
|
local log_file="$REPORT_DIR/${name}.log"
|
||||||
|
|
||||||
|
echo ">>> [fin-prod-enable] $name" | tee -a "$SUMMARY_TXT"
|
||||||
|
if "$@" > >(tee "$log_file") 2>&1; then
|
||||||
|
echo "[fin-prod-enable][PASS] $name" | tee -a "$SUMMARY_TXT"
|
||||||
|
else
|
||||||
|
local code="$?"
|
||||||
|
echo "[fin-prod-enable][FAIL] $name exit=$code" | tee -a "$SUMMARY_TXT"
|
||||||
|
STATUS=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_shell_step() {
|
||||||
|
local name="$1"
|
||||||
|
local command="$2"
|
||||||
|
run_step "$name" bash -lc "$command"
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch_feature_flags_node() {
|
||||||
|
local expected="$1"
|
||||||
|
EXPECTED_PAYMENT_BATCH_STATE="$expected" node <<'NODE'
|
||||||
|
const expected = process.env.EXPECTED_PAYMENT_BATCH_STATE;
|
||||||
|
const baseUrl = (process.env.LIVE_SMOKE_BASE_URL || "https://oa.hyinda.com").replace(/\/$/, "");
|
||||||
|
const identifier = process.env.LIVE_SMOKE_ADMIN_IDENTIFIER?.trim();
|
||||||
|
const password = process.env.LIVE_SMOKE_ADMIN_PASSWORD?.trim();
|
||||||
|
|
||||||
|
if (!identifier || !password) {
|
||||||
|
console.error("[fin-prod-enable][flag-check] missing admin credentials");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchJson(url, init = {}) {
|
||||||
|
const response = await fetch(url, {
|
||||||
|
...init,
|
||||||
|
signal: AbortSignal.timeout(20_000),
|
||||||
|
});
|
||||||
|
const text = await response.text();
|
||||||
|
let json = null;
|
||||||
|
try {
|
||||||
|
json = text ? JSON.parse(text) : null;
|
||||||
|
} catch {
|
||||||
|
json = null;
|
||||||
|
}
|
||||||
|
return { response, json };
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorSummary(json, status) {
|
||||||
|
const error = json?.error ?? {};
|
||||||
|
return `${status} ${error.code ?? ""} ${error.message ?? ""}`.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
const login = await fetchJson(`${baseUrl}/api/auth/login`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"x-device-fingerprint": "fin-prod-enable-flag-check",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ identifier, password }),
|
||||||
|
});
|
||||||
|
if (!login.response.ok || !login.json?.data?.accessToken) {
|
||||||
|
console.error(`[fin-prod-enable][flag-check] login failed: ${errorSummary(login.json, login.response.status)}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = login.json.data.accessToken;
|
||||||
|
const list = await fetchJson(`${baseUrl}/api/system/feature-flags`, {
|
||||||
|
headers: { authorization: `Bearer ${token}`, accept: "application/json" },
|
||||||
|
});
|
||||||
|
if (!list.response.ok) {
|
||||||
|
console.error(`[fin-prod-enable][flag-check] list flags failed: ${errorSummary(list.json, list.response.status)}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = list.json?.data?.items ?? [];
|
||||||
|
const payment = items.find((item) => item.flagKey === "payment_batch_v1");
|
||||||
|
const erp = items.find((item) => item.flagKey === "erp_sync");
|
||||||
|
if (!payment || !erp) {
|
||||||
|
console.error("[fin-prod-enable][flag-check] missing payment_batch_v1 or erp_sync");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[fin-prod-enable][flag-check] payment_batch_v1 override=${payment.overrideMode} effective=${payment.effectiveEnabled} reason=${payment.effectiveReason}`);
|
||||||
|
console.log(`[fin-prod-enable][flag-check] erp_sync override=${erp.overrideMode} effective=${erp.effectiveEnabled} reason=${erp.effectiveReason}`);
|
||||||
|
|
||||||
|
const paymentOn = payment.overrideMode === "force_on" && payment.effectiveEnabled === true;
|
||||||
|
const paymentOff = payment.overrideMode === "force_off" && payment.effectiveEnabled === false;
|
||||||
|
const erpOff = erp.effectiveEnabled === false;
|
||||||
|
if (expected === "force_on" && (!paymentOn || !erpOff)) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (expected === "force_off" && (!paymentOff || !erpOff)) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
NODE
|
||||||
|
}
|
||||||
|
|
||||||
|
force_payment_batch_flag_off() {
|
||||||
|
node <<'NODE'
|
||||||
|
const baseUrl = (process.env.LIVE_SMOKE_BASE_URL || "https://oa.hyinda.com").replace(/\/$/, "");
|
||||||
|
const identifier = process.env.LIVE_SMOKE_ADMIN_IDENTIFIER?.trim();
|
||||||
|
const password = process.env.LIVE_SMOKE_ADMIN_PASSWORD?.trim();
|
||||||
|
const issueId = process.env.QA_ISSUE_ID?.trim() || "FINPROD";
|
||||||
|
|
||||||
|
if (!identifier || !password) {
|
||||||
|
console.error("[fin-prod-enable][rollback] missing admin credentials");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchJson(url, init = {}) {
|
||||||
|
const response = await fetch(url, {
|
||||||
|
...init,
|
||||||
|
signal: AbortSignal.timeout(20_000),
|
||||||
|
});
|
||||||
|
const text = await response.text();
|
||||||
|
let json = null;
|
||||||
|
try {
|
||||||
|
json = text ? JSON.parse(text) : null;
|
||||||
|
} catch {
|
||||||
|
json = null;
|
||||||
|
}
|
||||||
|
return { response, json };
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorSummary(json, status) {
|
||||||
|
const error = json?.error ?? {};
|
||||||
|
return `${status} ${error.code ?? ""} ${error.message ?? ""}`.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
const login = await fetchJson(`${baseUrl}/api/auth/login`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"x-device-fingerprint": "fin-prod-enable-rollback",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ identifier, password }),
|
||||||
|
});
|
||||||
|
if (!login.response.ok || !login.json?.data?.accessToken) {
|
||||||
|
console.error(`[fin-prod-enable][rollback] login failed: ${errorSummary(login.json, login.response.status)}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = login.json.data.accessToken;
|
||||||
|
const list = await fetchJson(`${baseUrl}/api/system/feature-flags`, {
|
||||||
|
headers: { authorization: `Bearer ${token}`, accept: "application/json" },
|
||||||
|
});
|
||||||
|
if (!list.response.ok) {
|
||||||
|
console.error(`[fin-prod-enable][rollback] list flags failed: ${errorSummary(list.json, list.response.status)}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const flag = list.json?.data?.items?.find((item) => item.flagKey === "payment_batch_v1");
|
||||||
|
if (!flag) {
|
||||||
|
console.error("[fin-prod-enable][rollback] missing payment_batch_v1");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flag.overrideMode === "force_off" && flag.effectiveEnabled === false) {
|
||||||
|
console.log("[fin-prod-enable][rollback] payment_batch_v1 already force_off");
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await fetchJson(`${baseUrl}/api/system/feature-flags/payment_batch_v1`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${token}`,
|
||||||
|
"content-type": "application/json",
|
||||||
|
accept: "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
overrideMode: "force_off",
|
||||||
|
rolloutType: "global",
|
||||||
|
departmentIds: [],
|
||||||
|
note: `${issueId} failed final rollback`,
|
||||||
|
expectedVersion: flag.version,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (!updated.response.ok) {
|
||||||
|
console.error(`[fin-prod-enable][rollback] update failed: ${errorSummary(updated.json, updated.response.status)}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const item = updated.json?.data?.item;
|
||||||
|
console.log(`[fin-prod-enable][rollback] payment_batch_v1 override=${item?.overrideMode ?? "unknown"} effective=${item?.effectiveEnabled ?? "unknown"}`);
|
||||||
|
NODE
|
||||||
|
}
|
||||||
|
|
||||||
|
write_summary_json() {
|
||||||
|
local qa_status="PASS"
|
||||||
|
local notes="FINPROD 正式启用:公网付款批次验收通过后保持 payment_batch_v1=force_on;失败时回滚 force_off;不接真实支付、不接 ERP、不开放图片 OCR。"
|
||||||
|
if [[ "$STATUS" -ne 0 ]]; then
|
||||||
|
qa_status="FAIL"
|
||||||
|
notes="FINPROD 正式启用失败或门禁失败:已尝试将 payment_batch_v1 回滚到 force_off;不接真实支付、不接 ERP、不开放图片 OCR。"
|
||||||
|
fi
|
||||||
|
|
||||||
|
QA_REPORT_DIR="$REPORT_DIR" \
|
||||||
|
QA_ISSUE_ID="${QA_ISSUE_ID:-FINPROD}" \
|
||||||
|
QA_BASE_URL="$PUBLIC_BASE_URL" \
|
||||||
|
QA_MODE="fin-prod-enable" \
|
||||||
|
QA_STATUS="$qa_status" \
|
||||||
|
QA_SUMMARY_NOTES="$notes" \
|
||||||
|
node "$ROOT_DIR/scripts/qa/write-qa-summary.mjs" >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
if [[ "${P2_FIN_LIVE_APPROVED:-0}" == "1" && "${FIN_PROD_ENABLE_APPROVED:-0}" == "1" ]]; then
|
||||||
|
if [[ "$STATUS" -eq 0 ]]; then
|
||||||
|
echo ">>> [fin-prod-enable] final-force-on-check" | tee -a "$SUMMARY_TXT"
|
||||||
|
if LIVE_SMOKE_BASE_URL="$PUBLIC_BASE_URL" fetch_feature_flags_node "force_on" > >(tee "$REPORT_DIR/final-force-on-check.log") 2>&1; then
|
||||||
|
echo "[fin-prod-enable][PASS] final-force-on-check" | tee -a "$SUMMARY_TXT"
|
||||||
|
else
|
||||||
|
local code="$?"
|
||||||
|
echo "[fin-prod-enable][FAIL] final-force-on-check exit=$code" | tee -a "$SUMMARY_TXT"
|
||||||
|
STATUS=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$STATUS" -ne 0 ]]; then
|
||||||
|
echo ">>> [fin-prod-enable] failure-force-flag-off" | tee -a "$SUMMARY_TXT"
|
||||||
|
if force_payment_batch_flag_off > >(tee "$REPORT_DIR/failure-force-flag-off.log") 2>&1; then
|
||||||
|
echo "[fin-prod-enable][PASS] failure-force-flag-off" | tee -a "$SUMMARY_TXT"
|
||||||
|
else
|
||||||
|
local code="$?"
|
||||||
|
echo "[fin-prod-enable][FAIL] failure-force-flag-off exit=$code" | tee -a "$SUMMARY_TXT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ">>> [fin-prod-enable] final-flag-off-check" | tee -a "$SUMMARY_TXT"
|
||||||
|
if LIVE_SMOKE_BASE_URL="$PUBLIC_BASE_URL" fetch_feature_flags_node "force_off" > >(tee "$REPORT_DIR/final-flag-off-check.log") 2>&1; then
|
||||||
|
echo "[fin-prod-enable][PASS] final-flag-off-check" | tee -a "$SUMMARY_TXT"
|
||||||
|
else
|
||||||
|
local code="$?"
|
||||||
|
echo "[fin-prod-enable][FAIL] final-flag-off-check exit=$code" | tee -a "$SUMMARY_TXT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
write_summary_json
|
||||||
|
echo "[fin-prod-enable] report=$REPORT_DIR" | tee -a "$SUMMARY_TXT"
|
||||||
|
exit "$STATUS"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap finish EXIT
|
||||||
|
|
||||||
|
load_default_env
|
||||||
|
|
||||||
|
if [[ "${P2_FIN_LIVE_APPROVED:-0}" != "1" || "${FIN_PROD_ENABLE_APPROVED:-0}" != "1" ]]; then
|
||||||
|
echo "[fin-prod-enable][ERROR] P2_FIN_LIVE_APPROVED=1 and FIN_PROD_ENABLE_APPROVED=1 are required for public write enablement" | tee -a "$SUMMARY_TXT"
|
||||||
|
STATUS=2
|
||||||
|
exit "$STATUS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_shell_step "live-env-strict" "LIVE_SMOKE_BASE_URL='$PUBLIC_BASE_URL' node '$ROOT_DIR/scripts/qa/check-live-smoke-env.mjs' --strict"
|
||||||
|
[[ "$STATUS" -eq 0 ]] || exit "$STATUS"
|
||||||
|
|
||||||
|
run_shell_step "pre-flag-off-check" "LIVE_SMOKE_BASE_URL='$PUBLIC_BASE_URL' node '$ROOT_DIR/scripts/qa/check-payment-batch-flag-off.mjs' --json"
|
||||||
|
[[ "$STATUS" -eq 0 ]] || exit "$STATUS"
|
||||||
|
|
||||||
|
run_shell_step "fin-prod-enable-spec" "cd '$CLIENT_DIR' && PLAYWRIGHT_MANUAL=1 E2E_MODE=live P2_FIN_LIVE_APPROVED=1 P2_FIN_LIVE_FINAL_STATE=force_on QA_ISSUE_ID='${QA_ISSUE_ID:-FINPROD}' P2_FIN_LIVE_MARKER_PREFIX='${P2_FIN_LIVE_MARKER_PREFIX:-${QA_ISSUE_ID:-FINPROD}}' P2_FIN_LIVE_REPORT_DIR='$REPORT_DIR' LIVE_SMOKE_BASE_URL='$PUBLIC_BASE_URL' npx playwright test tests/e2e/manual/p2-fin-live-payment-batch.spec.ts --workers=1 --reporter=list"
|
||||||
|
|
||||||
|
if [[ "${FIN_PROD_ENABLE_SKIP_MOBILE:-0}" == "1" ]]; then
|
||||||
|
echo "[fin-prod-enable][SKIP] mobile-wecom FIN_PROD_ENABLE_SKIP_MOBILE=1" | tee -a "$SUMMARY_TXT"
|
||||||
|
else
|
||||||
|
run_shell_step "public-mobile-matrix" "cd '$CLIENT_DIR' && E2E_BASE_URL='$PUBLIC_BASE_URL' npx playwright test tests/e2e/dg7-mobile-information-dedupe.spec.ts tests/e2e/r124-mobile-entry-matrix.spec.ts --workers=1 --reporter=list"
|
||||||
|
run_shell_step "wecom-rendering-matrix" "cd '$CLIENT_DIR' && E2E_BASE_URL='$PUBLIC_BASE_URL' npx playwright test tests/e2e/r6-wecom-mobile-rendering-hardening.spec.ts --workers=1 --reporter=list"
|
||||||
|
fi
|
||||||
在新工单中引用
屏蔽一个用户