diff --git a/AGENTS.md b/AGENTS.md index 2e51930..e8d2eab 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -39,25 +39,25 @@ ## 模块快速查询 -退货预报与签收结果:`src/modules/overseas-warehouse/views/ReturnOrdersView.vue`、`api/returnOrders.ts`;菜单仍名为“退货换标”。列表路由 `return-relabel`、独立新建路由 `return-relabel/create` 与详情路由均复用该页面,并分别由 `createPage/detailPage` 明确模式;新建页直接填写海外仓、联系人、客户参考号及 SKU 预报明细,取消或提交后返回列表,不再由 `WarehouseServiceView.vue` 或列表内弹框承载。客户退货库存/箱号共用 `views/ReturnInventoryView.vue`、`api/returnInventory.ts`,`return-cartons` 传 `cartonOnly=true`。Backend 为 Lastleg `returnreceipt/`,退货单签收完成不等于换标或退货出库完成;后续换标、出库须按各自业务状态核对。 +本表只保留首个入口;完整 API、字段、状态与恢复规则见 [按需业务索引](docs/business-index.md)。从本应用根执行 `rg -n -F '任务词或文件名' docs/business-index.md` 读取命中条款;涉及业务行为变更时必须核对对应细则,纯定位不全文加载。保留所有适用的最近模块指引。 -先从 `src/modules/registry.ts` 确认模块,再按目标模块的 `routes.ts → views/ → api/、store/、contracts.ts 或 public.ts` 查询。页面是否正式开放以 `routes.ts` 的 `release` 为准,不从任务或进度文档推断。 +表中路径均相对本应用根目录。 -| 业务 | 模块入口 | 联查模块或服务端 | -|---|---|---| -| 登录、注册与会话 | `src/modules/auth/`、`src/api/session.ts`、`src/api/http.ts` | Backend `auth/`、`iam/`;正常轮换的统一 Access/Refresh Token 会话持久化,“记住账号”只记登录标识;访问令牌失效但刷新令牌仍在时先恢复会话,公开认证接口不得携带旧 Token | -| 企业资料、成员与角色 | `src/modules/tenant-management/`、`src/modules/account/` | Backend `iam/`、`auth/`、`file/`;公司联系电话/邮箱是企业资料,不作为登录标识;成员登录手机号可选,修改时同步 Auth、撤销该账号会话并审计。审核必填资质仅营业执照与统一社会信用代码;法人身份证选填,可随营业执照提交,也可在已有营业执照后单独补传或替换。前端角色树到服务端权限的映射统一维护在 `contracts.ts` 的 `FRONTEND_TO_MERCHANT_PERMISSION_CODES`,海外入仓、采购、本土卡车和散板权限必须显式进入该映射 | -| 工作台与系统入口 | `src/modules/dashboard/`、`src/modules/system/` | `src/navigation/clientNavigation.ts`、`src/router/index.ts` | -| 企业成员消息收件箱 | `src/modules/notifications/` | Backend `iam/src/main/java/com/baiyue/iam/notification/`;列表、详情、已读与未读数契约见 `iam/src/main/resources/openapi/notification-v1.yaml` | -| 头程下单、中文批量导入、入仓码关系、箱唛、履约进度与入库费用确认 | `src/modules/first-leg/`;列表与批量入口 `views/FirstLegOrdersView.vue`,模板/导入 API `api/firstLeg.ts`,入仓详情 `components/InboundCodeAssociationDialog.vue`、`api/inboundCodeAssociations.ts`,独立计费重弹框 `components/InboundBillingPanel.vue`、`api/inboundBilling.ts` | 中文 XLSX 模板按当前客户生成地址、全部有效仓库和渠道下拉,最多 50 行,只创建草稿并支持部分/原子导入;导入结果只显示 Excel 行号,不展示内部 `clientRowId`。所有订单行均可进入计费重弹框,尚未推送时展示明确空态;只有具备编辑权限且服务端返回当前 `SENT` 费用时才可二次确认并提交 `expectedFeeVersion` 与幂等键,成功后版本锁定、平台不得再修改。Backend `order/`、`firstleg/`、`warehouse/`,不得用内部 ID 补齐缺失业务编号 | -| 装柜完成后的退税报关资料 | `src/modules/first-leg/views/FirstLegOrdersView.vue` 的“报关资料”、`components/CustomsDocumentsDialog.vue`、`api/customs.ts` | 只显示登录客户在同柜的退税入仓码;报关开始前可上传、替换、调整同一资料覆盖的一个或多个入仓码及删除误传,全部写入携带幂等键和版本;开始后只读冻结资料,整柜完成后才能下载平台回传的一对一完成文件。Backend `firstleg/customs/container/`、`customs-v1.yaml`,二进制归 `file/` | -| 统一订单展示 | `src/modules/order-management/` | `src/modules/first-leg/` 与对应后端订单契约 | -| 客户财务账单、入仓码联查、收款、未收余额与客户确认 | `src/modules/finance/`;确认与明细入口 `views/FinanceBillsView.vue`、`api/finance.ts`,入仓码通过 `src/modules/first-leg/public.ts` 复用 `InboundCodeAssociationDialog` | Backend `finance/`;账单行只显示服务端 `inboundCode` 并按登录客户查询业务关系,缺失显示“—”,不得回退内部 ID。收款方式由服务端枚举映射中文。出账或已出账调整后为 `PENDING_CONFIRMATION`,客户确认提交登录成员、`expectedVersion` 和幂等键 | -| 客户资金账户、授信与充值申请 | `src/modules/finance/views/FinanceAccountView.vue`、`api/account.ts` | 显示当前企业 CNY 实际余额、冻结金额、授信额度、授信有效期、已用授信、冻结授信、总可用余额和不可变流水;数据加载前显示“—”,不得先伪装为零。授信到期日当天仍有效,次日已用授信并入实际余额,负数实际余额会阻止提交新订单。充值只展示服务端启用的平台统一静态收款码,客户上传 JPG/PNG 付款截图后进入待审核。客户范围只取登录态,无收款码时禁止提交,不模拟支付成功 | -| 海外采购 | `src/modules/cross-border/routes.ts`、`views/ProcurementView.vue`、`components/ProcurementDetailPanel.vue`、`api/procurement.ts` | 独立列表、新建及详情路由;客户范围只取 Token,创建的 `customerName` 只取认证会话企业名称。详情消费独立客户投影,展示履约、已确认箱明细、实际运单和费用结果,不开放内部文件或平台操作。Backend `lastleg-service/procurement/` | -| 美国本土卡与美本土散板 | `src/modules/cross-border/routes.ts`、`components/BusinessOrdersView.vue`、`api/businessOrders.ts` | 两个运输业务共用列表/表单组件,客户范围只取 Token;不消费采购字段或状态。Backend `lastleg-service/` 的 `trucking/`、`looseboard/` 客户接口 | -| 海外仓入库订单 | `src/modules/order-management/routes.ts`、`views/InboundOrdersView.vue`、`api/inboundPlans.ts` | `orders/inbound` 为只读列表/详情,权限 `merchant:warehouse:inbound:list`;不展示或提交内部 ID | -| 海外仓贴标、仓储转运、货物废弃 | `src/modules/overseas-warehouse/routes.ts`、`views/WarehouseServiceView.vue`、`api/warehouseServices.ts` | 三个路由均为 `active`;退货预报另见本节独立入口;贴标申请先选国外中转仓和有库存历史入仓单,换标类型为只换 SKU、只换箱唛、SKU+箱唛。涉及箱唛换标时,整张贴标申请只选择一个 FBA 目的仓,全部箱级明细统一提交该目的仓,模板和明细不再逐行选择目的仓。箱唛换标明细一行固定代表一箱,箱数不可编辑,提交也固定为 1。货物明细外层始终提供“SKU 关联”“箱号关联”“SKU 混箱换标”三套模板的下载、上传,不随换标类型切换;上传只进入待导入关系区,客户可勾选部分或全选后导入本次明细,只有进入明细的库存才随申请提交和冻结。明细也支持手动新增编辑,组合换标以仓库唛头连接旧/新 SKU 和新 FBA 箱唛关系。涉及箱唛换标时必须选择卡车、自提或快递;快递可选择打印面单并上传 PDF/图片。PDA 逐箱换标、上传照片并由仓库复核后,客户详情展示完工照片和“换标完成确认”;客户确认前不得继续出库,确认后卡车进入预排车,自提/快递进入换标出库待创建。历史库存位置不能解释成箱唛与具体托盘快照,客户页面不暴露托盘、库位和内部库存 ID | +| 业务 | 首个入口 | +|---|---| +| 登录、注册与会话 | `src/modules/auth/`、`src/api/session.ts`、`src/api/http.ts` | +| 企业资料、成员与角色 | `src/modules/tenant-management/`、`src/modules/account/` | +| 工作台与系统入口 | `src/modules/dashboard/`、`src/modules/system/` | +| 企业成员消息收件箱 | `src/modules/notifications/` | +| 头程下单、中文批量导入、入仓码关系、箱唛、履约进度与入库费用确认 | `src/modules/first-leg/`、`src/modules/first-leg/views/FirstLegOrdersView.vue`、`src/modules/first-leg/api/firstLeg.ts` | +| 装柜完成后的退税报关资料 | `src/modules/first-leg/views/FirstLegOrdersView.vue`、`src/modules/first-leg/components/CustomsDocumentsDialog.vue`、`src/modules/first-leg/api/customs.ts` | +| 统一订单展示 | `src/modules/order-management/` | +| 客户财务账单、入仓码联查、收款、未收余额与客户确认 | `src/modules/finance/`、`src/modules/finance/views/FinanceBillsView.vue`、`src/modules/finance/api/finance.ts` | +| 客户资金账户、授信与充值申请 | `src/modules/finance/views/FinanceAccountView.vue`、`src/modules/finance/api/account.ts` | +| 海外采购 | `src/modules/cross-border/routes.ts`、`src/modules/cross-border/views/ProcurementView.vue`、`src/modules/cross-border/components/ProcurementDetailPanel.vue` | +| 美国本土卡与美本土散板 | `src/modules/cross-border/routes.ts`、`src/modules/cross-border/components/BusinessOrdersView.vue`、`src/modules/cross-border/api/businessOrders.ts` | +| 海外仓入库订单 | `src/modules/order-management/routes.ts`、`src/modules/order-management/views/InboundOrdersView.vue`、`src/modules/order-management/api/inboundPlans.ts` | +| 海外仓贴标、仓储转运、货物废弃 | `src/modules/overseas-warehouse/routes.ts`、`src/modules/overseas-warehouse/views/WarehouseServiceView.vue`、`src/modules/overseas-warehouse/api/warehouseServices.ts` | ## 租户、认证与数据安全 @@ -97,7 +97,7 @@ pnpm typecheck pnpm build ``` -- 日常变更至少运行 `pnpm typecheck`;交付前运行 `pnpm build`。 +- 源码、依赖或构建配置变更至少运行 `pnpm typecheck` 和受影响测试,交付前运行 `pnpm build`;不影响代码、契约或配置的纯文档/索引变更只检查路径、链接及索引合同。 - 当前测试使用 Node.js 内置测试运行器,执行 `pnpm test`;项目仍未声明 lint 脚本,不得声称 lint 已通过。 - 不手工修改 `dist/` 或 `node_modules/`;依赖变更必须同步当前项目依赖清单和锁文件。 @@ -110,11 +110,4 @@ pnpm build ## Client 全量验收快速索引 -当前按客户侧全量业务闭环验收。业务域文档用于核对功能来源和验收证据,不限制本次验收的模块范围;按“模块 `routes.ts` → 页面/组件 → `api`、store、contracts”定位。本表只描述 Client 页面与客户可见契约,服务端实现详见后端模块指引。 - -| 验收域 | Client 范围与代码入口 | 验收首读 | -|---|---|---| -| 客户基础与订单 | 登录/注册、企业资料与资质、成员/角色权限、工作台和订单 | `src/modules/auth/`、`tenant-management/`、`account/`、`system/`、`dashboard/`、`first-leg/`、`order-management/`;按客户可见契约核对权限、状态、幂等和错误处理 | -| 履约、海外仓与财务 | 入库计费、柜次进度、船期、海外仓贴标/转运/废弃/退货换标、消息和客户账单 | `src/modules/first-leg/`、`overseas-warehouse/`、`finance/`、`notifications/`;按服务端权限核对客户可见字段、客户隔离和只读/写入边界 | - -客户侧边界:仍以服务端权限和客户可见字段为准;PDA、平台后台操作和内部证据不因全量验收自动暴露到 Client。财务按 `../../docs/财务管理/` 与 `src/modules/finance/routes.ts` 定位。 +全量验收的两类客户可见范围按需查 `docs/business-index.md` 同名节;平台/PDA 操作与内部证据不得因此暴露给客户。普通修改保持最小模块范围,不自动加载验收资料。 diff --git a/docs/business-index.md b/docs/business-index.md new file mode 100644 index 0000000..08529dd --- /dev/null +++ b/docs/business-index.md @@ -0,0 +1,40 @@ +# Client业务详细索引 + +本文件保存当前页面/API 入口及业务规则,是[应用指引](../AGENTS.md)的按需补充。涉及字段、状态、权限、金额、资源或作业恢复时必须读取受影响条款,不作为普通定位任务的全文首读。 + +正文代码路径以工作区 `apps/client-web/` 应用根起算,源码包缩写与裸文件名沿对应条目的目录解释;明确标注“工作区”的路径从工作区根起算。Markdown 相对链接以本文件所在的 `docs/` 为起点。已知文件直接定位;行为变更同时核对最近模块指引及服务端合同。 + +原条款在本文件维护,主 AGENTS 仅保留短入口和通用边界;不要将新业务细节复制回主指引。 + +## 模块快速查询 + +退货预报与签收结果:`src/modules/overseas-warehouse/views/ReturnOrdersView.vue`、`api/returnOrders.ts`;菜单仍名为“退货换标”。列表路由 `return-relabel`、独立新建路由 `return-relabel/create` 与详情路由均复用该页面,并分别由 `createPage/detailPage` 明确模式;新建页直接填写海外仓、联系人、客户参考号及 SKU 预报明细,取消或提交后返回列表,不再由 `WarehouseServiceView.vue` 或列表内弹框承载。客户退货库存/箱号共用 `views/ReturnInventoryView.vue`、`api/returnInventory.ts`,`return-cartons` 传 `cartonOnly=true`。Backend 为 Lastleg `returnreceipt/`,退货单签收完成不等于换标或退货出库完成;后续换标、出库须按各自业务状态核对。 + +先从 `src/modules/registry.ts` 确认模块,再按目标模块的 `routes.ts → views/ → api/、store/、contracts.ts 或 public.ts` 查询。页面是否正式开放以 `routes.ts` 的 `release` 为准,不从任务或进度文档推断。 + +| 业务 | 模块入口 | 联查模块或服务端 | +|---|---|---| +| 登录、注册与会话 | `src/modules/auth/`、`src/api/session.ts`、`src/api/http.ts` | Backend `auth/`、`iam/`;正常轮换的统一 Access/Refresh Token 会话持久化,“记住账号”只记登录标识;访问令牌失效但刷新令牌仍在时先恢复会话,公开认证接口不得携带旧 Token | +| 企业资料、成员与角色 | `src/modules/tenant-management/`、`src/modules/account/` | Backend `iam/`、`auth/`、`file/`;公司联系电话/邮箱是企业资料,不作为登录标识;成员登录手机号可选,修改时同步 Auth、撤销该账号会话并审计。审核必填资质仅营业执照与统一社会信用代码;法人身份证选填,可随营业执照提交,也可在已有营业执照后单独补传或替换。前端角色树到服务端权限的映射统一维护在 `contracts.ts` 的 `FRONTEND_TO_MERCHANT_PERMISSION_CODES`,海外入仓、采购、本土卡车和散板权限必须显式进入该映射 | +| 工作台与系统入口 | `src/modules/dashboard/`、`src/modules/system/` | `src/navigation/clientNavigation.ts`、`src/router/index.ts` | +| 企业成员消息收件箱 | `src/modules/notifications/` | Backend `iam/src/main/java/com/baiyue/iam/notification/`;列表、详情、已读与未读数契约见 `iam/src/main/resources/openapi/notification-v1.yaml` | +| 头程下单、中文批量导入、入仓码关系、箱唛、履约进度与入库费用确认 | `src/modules/first-leg/`;列表与批量入口 `views/FirstLegOrdersView.vue`,模板/导入 API `api/firstLeg.ts`,入仓详情 `components/InboundCodeAssociationDialog.vue`、`api/inboundCodeAssociations.ts`,独立计费重弹框 `components/InboundBillingPanel.vue`、`api/inboundBilling.ts` | 中文 XLSX 模板按当前客户生成地址、全部有效仓库和渠道下拉,最多 50 行,只创建草稿并支持部分/原子导入;导入结果只显示 Excel 行号,不展示内部 `clientRowId`。所有订单行均可进入计费重弹框,尚未推送时展示明确空态;只有具备编辑权限且服务端返回当前 `SENT` 费用时才可二次确认并提交 `expectedFeeVersion` 与幂等键,成功后版本锁定、平台不得再修改。Backend `order/`、`firstleg/`、`warehouse/`,不得用内部 ID 补齐缺失业务编号 | +| 装柜完成后的退税报关资料 | `src/modules/first-leg/views/FirstLegOrdersView.vue` 的“报关资料”、`components/CustomsDocumentsDialog.vue`、`api/customs.ts` | 只显示登录客户在同柜的退税入仓码;报关开始前可上传、替换、调整同一资料覆盖的一个或多个入仓码及删除误传,全部写入携带幂等键和版本;开始后只读冻结资料,整柜完成后才能下载平台回传的一对一完成文件。Backend `firstleg/customs/container/`、`customs-v1.yaml`,二进制归 `file/` | +| 统一订单展示 | `src/modules/order-management/` | `src/modules/first-leg/` 与对应后端订单契约 | +| 客户财务账单、入仓码联查、收款、未收余额与客户确认 | `src/modules/finance/`;确认与明细入口 `views/FinanceBillsView.vue`、`api/finance.ts`,入仓码通过 `src/modules/first-leg/public.ts` 复用 `InboundCodeAssociationDialog` | Backend `finance/`;账单行只显示服务端 `inboundCode` 并按登录客户查询业务关系,缺失显示“—”,不得回退内部 ID。收款方式由服务端枚举映射中文。出账或已出账调整后为 `PENDING_CONFIRMATION`,客户确认提交登录成员、`expectedVersion` 和幂等键 | +| 客户资金账户、授信与充值申请 | `src/modules/finance/views/FinanceAccountView.vue`、`api/account.ts` | 显示当前企业 CNY 实际余额、冻结金额、授信额度、授信有效期、已用授信、冻结授信、总可用余额和不可变流水;数据加载前显示“—”,不得先伪装为零。授信到期日当天仍有效,次日已用授信并入实际余额,负数实际余额会阻止提交新订单。充值只展示服务端启用的平台统一静态收款码,客户上传 JPG/PNG 付款截图后进入待审核。客户范围只取登录态,无收款码时禁止提交,不模拟支付成功 | +| 海外采购 | `src/modules/cross-border/routes.ts`、`views/ProcurementView.vue`、`components/ProcurementDetailPanel.vue`、`api/procurement.ts` | 独立列表、新建及详情路由;客户范围只取 Token,创建的 `customerName` 只取认证会话企业名称。详情消费独立客户投影,展示履约、已确认箱明细、实际运单和费用结果,不开放内部文件或平台操作。Backend `lastleg-service/procurement/` | +| 美国本土卡与美本土散板 | `src/modules/cross-border/routes.ts`、`components/BusinessOrdersView.vue`、`api/businessOrders.ts` | 两个运输业务共用列表/表单组件,客户范围只取 Token;不消费采购字段或状态。Backend `lastleg-service/` 的 `trucking/`、`looseboard/` 客户接口 | +| 海外仓入库订单 | `src/modules/order-management/routes.ts`、`views/InboundOrdersView.vue`、`api/inboundPlans.ts` | `orders/inbound` 为只读列表/详情,权限 `merchant:warehouse:inbound:list`;不展示或提交内部 ID | +| 海外仓贴标、仓储转运、货物废弃 | `src/modules/overseas-warehouse/routes.ts`、`views/WarehouseServiceView.vue`、`api/warehouseServices.ts` | 三个路由均为 `active`;退货预报另见本节独立入口;贴标申请先选国外中转仓和有库存历史入仓单,换标类型为只换 SKU、只换箱唛、SKU+箱唛。涉及箱唛换标时,整张贴标申请只选择一个 FBA 目的仓,全部箱级明细统一提交该目的仓,模板和明细不再逐行选择目的仓。箱唛换标明细一行固定代表一箱,箱数不可编辑,提交也固定为 1。货物明细外层始终提供“SKU 关联”“箱号关联”“SKU 混箱换标”三套模板的下载、上传,不随换标类型切换;上传只进入待导入关系区,客户可勾选部分或全选后导入本次明细,只有进入明细的库存才随申请提交和冻结。明细也支持手动新增编辑,组合换标以仓库唛头连接旧/新 SKU 和新 FBA 箱唛关系。涉及箱唛换标时必须选择卡车、自提或快递;快递可选择打印面单并上传 PDF/图片。PDA 逐箱换标、上传照片并由仓库复核后,客户详情展示完工照片和“换标完成确认”;客户确认前不得继续出库,确认后卡车进入预排车,自提/快递进入换标出库待创建。历史库存位置不能解释成箱唛与具体托盘快照,客户页面不暴露托盘、库位和内部库存 ID | + +## Client 全量验收快速索引 + +当前按客户侧全量业务闭环验收。业务域文档用于核对功能来源和验收证据,不限制本次验收的模块范围;按“模块 `routes.ts` → 页面/组件 → `api`、store、contracts”定位。本表只描述 Client 页面与客户可见契约,服务端实现详见后端模块指引。 + +| 验收域 | Client 范围与代码入口 | 验收首读 | +|---|---|---| +| 客户基础与订单 | 登录/注册、企业资料与资质、成员/角色权限、工作台和订单 | `src/modules/auth/`、`tenant-management/`、`account/`、`system/`、`dashboard/`、`first-leg/`、`order-management/`;按客户可见契约核对权限、状态、幂等和错误处理 | +| 履约、海外仓与财务 | 入库计费、柜次进度、船期、海外仓贴标/转运/废弃/退货换标、消息和客户账单 | `src/modules/first-leg/`、`overseas-warehouse/`、`finance/`、`notifications/`;按服务端权限核对客户可见字段、客户隔离和只读/写入边界 | + +客户侧边界:仍以服务端权限和客户可见字段为准;PDA、平台后台操作和内部证据不因全量验收自动暴露到 Client。财务按 `../../docs/财务管理/` 与 `src/modules/finance/routes.ts` 定位。 diff --git a/src/modules/order-management/api/deliveryOrders.ts b/src/modules/order-management/api/deliveryOrders.ts index a4859b6..f64aab9 100644 --- a/src/modules/order-management/api/deliveryOrders.ts +++ b/src/modules/order-management/api/deliveryOrders.ts @@ -104,17 +104,6 @@ interface DeliveryOrderDetailResponse { }> } -export interface DeliveryTransferResult { - dispatchNo: string - outboundNo: string - outboundStatus: string - deliveryNo: string - deliveryStatus: string - palletCount: number - packageCount: number - replayed: boolean -} - export interface DeliveryQuery { page: number size: number @@ -140,26 +129,6 @@ export function normalizeDeliveryPage(data: PageResult): PageResult } } -export interface DeliveryTransferPayload { - dispatchNo: string - idempotencyKey: string - plannedDeliveryAt: string - deliveryMethod: string - pickupMethod: string - loadingType: string - waybillNo?: string - carrierName?: string - vehicleType?: string - vehicleSize?: string - driverName?: string - driverPhone?: string - vehiclePlate?: string - appointmentAccount?: string - sellerName?: string - remark?: string - fees: DeliveryOrderFee[] -} - export function listDeliveryOrders(params: DeliveryQuery) { return apiRequest>({ method: 'GET', @@ -179,13 +148,3 @@ export function getDeliveryOrder(deliveryNo: string) { items: skus, })) } - -export function createDeliveryOrder(payload: DeliveryTransferPayload) { - const { dispatchNo, ...request } = payload - return apiRequest({ - method: 'POST', - url: '/merchant/v1/last-leg/delivery-orders', - params: { dispatchNo }, - data: request, - }) -} diff --git a/src/modules/order-management/deliveryOrders.test.mjs b/src/modules/order-management/deliveryOrders.test.mjs index e321608..31fc5d5 100644 --- a/src/modules/order-management/deliveryOrders.test.mjs +++ b/src/modules/order-management/deliveryOrders.test.mjs @@ -1,5 +1,5 @@ import assert from 'node:assert/strict' -import { readFileSync } from 'node:fs' +import { existsSync, readFileSync } from 'node:fs' import test from 'node:test' import ts from 'typescript' @@ -8,7 +8,6 @@ const routesSource = readFileSync(new URL('./routes.ts', import.meta.url), 'utf8 const listSource = readFileSync(new URL('./views/DeliveryOrdersView.vue', import.meta.url), 'utf8') const detailSource = readFileSync(new URL('./views/DeliveryOrderDetailView.vue', import.meta.url), 'utf8') const deliveryStyles = readFileSync(new URL('./views/deliveryOrders.css', import.meta.url), 'utf8') -const createSource = readFileSync(new URL('./views/DeliveryOrderCreateView.vue', import.meta.url), 'utf8') const permissionSource = readFileSync(new URL('../tenant-management/contracts.ts', import.meta.url), 'utf8') async function importTypeScriptModule(source) { @@ -33,22 +32,18 @@ for (const signedPackages of [null, 0, 3]) test(`preserves actual signed quantit test('uses only authenticated merchant delivery endpoints without customer scope or mock fallback', () => { assert.match(apiSource, /method: 'GET'[\s\S]*url: '\/merchant\/v1\/last-leg\/delivery-orders'/) assert.match(apiSource, /delivery-orders\/\$\{encodeURIComponent\(deliveryNo\)\}/) - assert.match(apiSource, /method: 'POST'[\s\S]*url: '\/merchant\/v1\/last-leg\/delivery-orders'/) - assert.match(apiSource, /const \{ dispatchNo, \.\.\.request \} = payload/) - assert.match(apiSource, /params: \{ dispatchNo \}/) + assert.doesNotMatch(apiSource, /method: '(?:POST|PUT|PATCH|DELETE)'|createDeliveryOrder|DeliveryTransferPayload|DeliveryTransferResult/) assert.match(apiSource, /apiRequest/) assert.match(apiSource, /\.then\(\(\{ order, skus, fees \}\) => \(\{[\s\S]*\.\.\.order,[\s\S]*fees,[\s\S]*items: skus/) - assert.match(apiSource, /apiRequest/) assert.doesNotMatch(apiSource, /customerId|tenantId|Record|mock|fallback/i) }) -test('keeps list, read-only detail and create permissions independent in active routes', () => { +test('keeps customer delivery list and read-only detail active without a creation route', () => { const listRoute = routesSource.match(/path: 'orders\/delivery',[\s\S]*?\n\s*},/)?.[0] ?? '' assert.match(listRoute, /permission: 'merchant:warehouse:delivery:list'/) assert.match(listRoute, /navigation: \{ key: 'orders-delivery', icon: Truck \}/) - assert.match(routesSource, /path: 'orders\/delivery\/create'[\s\S]*DeliveryOrderCreateView\.vue[\s\S]*permission: 'merchant:warehouse:delivery:create'/) + assert.doesNotMatch(routesSource, /orders\/delivery\/create|orders-delivery-create|DeliveryOrderCreateView/) assert.match(routesSource, /path: 'orders\/delivery\/:deliveryNo'[\s\S]*DeliveryOrderDetailView\.vue[\s\S]*props: true[\s\S]*permission: 'merchant:warehouse:delivery:list'/) - assert.match(routesSource, /orders-delivery-create[\s\S]*keepAlive: false/) assert.match(routesSource, /orders-delivery-detail[\s\S]*keepAlive: false/) }) @@ -70,10 +65,9 @@ test('renders the operation column first and covers loading, empty, error and pa assert.doesNotMatch(listSource, /row\.id[^a-zA-Z]|deliveryNo \|\| row\.id|customerId|tenantId/) }) -test('shows the create button only with the independent create permission', () => { - assert.match(listSource, /auth\.hasPermission\('merchant:warehouse:delivery:create'\)/) - assert.match(listSource, / { + assert.doesNotMatch(listSource, /delivery:create|canCreate|openCreate|orders-delivery-create|创建派送单/) + assert.equal(existsSync(new URL('./views/DeliveryOrderCreateView.vue', import.meta.url)), false) }) test('loads a typed detail route by delivery number and never exposes an internal id fallback', () => { @@ -126,33 +120,12 @@ test('renders a compact customer-readable delivery detail surface', () => { assert.match(deliveryStyles, /delivery-detail-surface \.delivery-detail-grid div\{display:grid;min-height:42px;/) }) -test('submits the complete create contract with stable retry idempotency and recoverable failures', () => { - for (const model of ['dispatchNo', 'plannedDeliveryAt', 'deliveryMethod', 'pickupMethod', 'loadingType', 'waybillNo', 'carrierName', 'vehicleType', 'vehicleSize', 'driverName', 'driverPhone', 'vehiclePlate', 'appointmentAccount', 'sellerName', 'remark']) { - assert.match(createSource, new RegExp(`v-model="form\\.${model}"`)) - } - assert.match(createSource, /if \(!pendingIdempotencyKey\) pendingIdempotencyKey = createIdempotencyKey\(\)/) - assert.match(createSource, /createDeliveryOrder\(requestPayload\(\)\)/) - assert.match(createSource, /fees: form\.fees\.map/) - assert.match(createSource, /if \(form\.fees\.length === 0\)/) - assert.match(createSource, /请至少添加一条费用明细/) - for (const value of ['FBA_PICKUP', 'CUSTOMER_PICKUP', 'TRUCK_PALLET', 'TRUCK_FLOOR', 'EXPRESS', 'OUTSOURCED', 'DEDICATED_TRUCK', 'LTL']) { - assert.match(createSource, new RegExp(`value="${value}"`)) - } - for (const value of ['ONE_PICKUP_ONE_DROP', 'ONE_PICKUP_MULTI_DROP', 'MULTI_PICKUP_ONE_DROP', 'MULTI_PICKUP_MULTI_DROP', 'WAREHOUSE_PICKUP', 'PALLET', 'FLOOR', 'MIXED']) { - assert.match(createSource, new RegExp(`value="${value}"`)) - } - assert.doesNotMatch(createSource, /value="(?:TRUCK|COURIER|SELF_PICKUP|DOOR_PICKUP|LOOSE_CARTON)"/) - assert.match(createSource, /submitError\.value = error instanceof Error/) - assert.match(createSource, /useUnsavedWorkRegistration/) - assert.doesNotMatch(createSource, /customerId|tenantId|mock|fallback/i) -}) - -test('maps both merchant delivery permissions independently', async () => { +test('keeps merchant delivery reads and removes creation from the advertised permission map', async () => { assert.match(permissionSource, /'merchant:warehouse:delivery:list': \['merchant:warehouse:delivery:list'\]/) - assert.match(permissionSource, /'merchant:warehouse:delivery:create': \['merchant:warehouse:delivery:create'\]/) + assert.doesNotMatch(permissionSource, /merchant:warehouse:delivery:create/) const contracts = await importTypeScriptModule(permissionSource) assert.equal(contracts.hasMappedMerchantPermission('merchant:warehouse:delivery:list', new Set(['merchant:warehouse:delivery:list'])), true) assert.equal(contracts.hasMappedMerchantPermission('merchant:warehouse:delivery:list', new Set(['merchant:warehouse:delivery:create'])), false) - assert.equal(contracts.hasMappedMerchantPermission('merchant:warehouse:delivery:create', new Set(['merchant:warehouse:delivery:create'])), true) + assert.equal(Object.hasOwn(contracts.FRONTEND_TO_MERCHANT_PERMISSION_CODES, 'merchant:warehouse:delivery:create'), false) assert.equal(contracts.hasMappedMerchantPermission('merchant:warehouse:delivery:create', new Set(['merchant:warehouse:delivery:list'])), false) }) diff --git a/src/modules/order-management/routes.ts b/src/modules/order-management/routes.ts index 9deae96..ca12722 100644 --- a/src/modules/order-management/routes.ts +++ b/src/modules/order-management/routes.ts @@ -37,13 +37,6 @@ export const orderManagementModule = { meta: { title: '派送订单列表', description: '查询海外仓派送订单', permission: 'merchant:warehouse:delivery:list' }, navigation: { key: 'orders-delivery', icon: Truck }, }, - { - path: 'orders/delivery/create', - name: 'orders-delivery-create', - component: () => import('./views/DeliveryOrderCreateView.vue'), - release: 'active', - meta: { title: '创建派送单', workspaceTitle: '派送订单 · 新建', description: '从排车单创建海外仓派送单', permission: 'merchant:warehouse:delivery:create', keepAlive: false }, - }, { path: 'orders/delivery/:deliveryNo', name: 'orders-delivery-detail', diff --git a/src/modules/order-management/views/DeliveryOrderCreateView.vue b/src/modules/order-management/views/DeliveryOrderCreateView.vue deleted file mode 100644 index 0468eb7..0000000 --- a/src/modules/order-management/views/DeliveryOrderCreateView.vue +++ /dev/null @@ -1,201 +0,0 @@ - - - - - diff --git a/src/modules/order-management/views/DeliveryOrdersView.vue b/src/modules/order-management/views/DeliveryOrdersView.vue index 37c3ac7..4137b89 100644 --- a/src/modules/order-management/views/DeliveryOrdersView.vue +++ b/src/modules/order-management/views/DeliveryOrdersView.vue @@ -1,16 +1,13 @@