Commit cc46620492b701b3c0819587cc97f1784f6214b5
1 parent
c11a79f9
fix(xsckd): 显式落库 ly/djzt/skmx,按来源调整免审与审核拦截
- 销售出库单保存时显式写入 ly、djzt、skmx,避免 Mapster 未映射导致不落库 - 免审逻辑改为:ly 非空且非「后台」则免审,保留备注「抖音订单:」兼容 - 通用审核路径增加回滚与非后台来源无需审核提示;更新相关文案 - 同步前端与 Dashboard、PACKAGE_README 调整 Made-with: Cursor
Showing
5 changed files
with
52 additions
and
31 deletions
Antis.Erp.Plat/antis-ncc-admin/src/views/wtXsckd/detail-view.vue
| @@ -320,6 +320,7 @@ export default { | @@ -320,6 +320,7 @@ export default { | ||
| 320 | if (ly == null || ly === '') return '无' | 320 | if (ly == null || ly === '') return '无' |
| 321 | if (ly === '门店') return '门店(收银台)' | 321 | if (ly === '门店') return '门店(收银台)' |
| 322 | if (ly === '抖音订单') return '抖音订单' | 322 | if (ly === '抖音订单') return '抖音订单' |
| 323 | + if (ly === '后台') return '后台' | ||
| 323 | return ly | 324 | return ly |
| 324 | }, | 325 | }, |
| 325 | formatDjrq(ts) { | 326 | formatDjrq(ts) { |
Antis.Erp.Plat/antis-ncc-admin/src/views/wtXsckd/index.vue
| @@ -240,14 +240,14 @@ | @@ -240,14 +240,14 @@ | ||
| 240 | this.getskzhOptions(); | 240 | this.getskzhOptions(); |
| 241 | }, | 241 | }, |
| 242 | methods: { | 242 | methods: { |
| 243 | - /** 收银台/抖音等:不走 ERP 二级审核、仅查看 */ | 243 | + /** 非「后台」来源:不走 ERP 审核(与后端 IsSalesOutboundSkipErpAudit 一致;ly 空视为后台需审) */ |
| 244 | isSkipErpAuditSource(row) { | 244 | isSkipErpAuditSource(row) { |
| 245 | if (!row) return false | 245 | if (!row) return false |
| 246 | - const ly = row.ly | ||
| 247 | - if (ly === '门店' || ly === '抖音订单' || ly === '抖音抓单') return true | ||
| 248 | const bz = row.bz != null && row.bz !== '' ? String(row.bz).trim() : '' | 246 | const bz = row.bz != null && row.bz !== '' ? String(row.bz).trim() : '' |
| 249 | if (bz.startsWith('抖音订单:')) return true | 247 | if (bz.startsWith('抖音订单:')) return true |
| 250 | - return false | 248 | + const ly = row.ly != null && row.ly !== '' ? String(row.ly).trim() : '' |
| 249 | + if (ly === '') return false | ||
| 250 | + return ly !== '后台' | ||
| 251 | }, | 251 | }, |
| 252 | /** 后台来源且草稿可编辑 */ | 252 | /** 后台来源且草稿可编辑 */ |
| 253 | canEditDraft(row) { | 253 | canEditDraft(row) { |
| @@ -272,6 +272,7 @@ | @@ -272,6 +272,7 @@ | ||
| 272 | if (ly == null || ly === '') return '无' | 272 | if (ly == null || ly === '') return '无' |
| 273 | if (ly === '门店') return '门店(收银台)' | 273 | if (ly === '门店') return '门店(收银台)' |
| 274 | if (ly === '抖音订单') return '抖音订单' | 274 | if (ly === '抖音订单') return '抖音订单' |
| 275 | + if (ly === '后台') return '后台' | ||
| 275 | return ly | 276 | return ly |
| 276 | }, | 277 | }, |
| 277 | // ✅ 原价:优先使用后端存储的 ydje(与收银台订单原价一致),无则用 收款+优惠 兼容旧数据 | 278 | // ✅ 原价:优先使用后端存储的 ydje(与收银台订单原价一致),无则用 收款+优惠 兼容旧数据 |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtXsckdService.cs
| @@ -832,6 +832,11 @@ namespace NCC.Extend.WtXsckd | @@ -832,6 +832,11 @@ namespace NCC.Extend.WtXsckd | ||
| 832 | entity.Ydje = input.ydje; | 832 | entity.Ydje = input.ydje; |
| 833 | entity.Cbje = input.cbje; | 833 | entity.Cbje = input.cbje; |
| 834 | entity.Bjsx = input.bjsx; | 834 | entity.Bjsx = input.bjsx; |
| 835 | + // 收银台/抖音等 JSON 为小写 ly、djzt、skmx;Mapster 在部分环境下未映射到实体 Pascal 属性, | ||
| 836 | + // 配合 Insertable(ignoreNullColumn: true) 会导致整列不落库 → 免审与待办过滤失效。显式写入。 | ||
| 837 | + entity.Ly = input.ly; | ||
| 838 | + entity.Djzt = input.djzt; | ||
| 839 | + entity.Skmx = input.skmx; | ||
| 835 | 840 | ||
| 836 | // 获取用户ID(匿名访问时为null或空字符串) | 841 | // 获取用户ID(匿名访问时为null或空字符串) |
| 837 | string? userId = null; | 842 | string? userId = null; |
| @@ -1587,7 +1592,7 @@ namespace NCC.Extend.WtXsckd | @@ -1587,7 +1592,7 @@ namespace NCC.Extend.WtXsckd | ||
| 1587 | var oldHeader = await _db.Queryable<WtXsckdEntity>().FirstAsync(p => p.Id == id); | 1592 | var oldHeader = await _db.Queryable<WtXsckdEntity>().FirstAsync(p => p.Id == id); |
| 1588 | var oldMxList = await _db.Queryable<WtXsckdMxEntity>().Where(u => u.Djbh == id).ToListAsync(); | 1593 | var oldMxList = await _db.Queryable<WtXsckdMxEntity>().Where(u => u.Djbh == id).ToListAsync(); |
| 1589 | if (IsSalesOutboundSkipErpAudit(oldHeader)) | 1594 | if (IsSalesOutboundSkipErpAudit(oldHeader)) |
| 1590 | - throw NCCException.Bah("收银台或抖音等外部来源的销售出库单不允许在此修改"); | 1595 | + throw NCCException.Bah("非后台来源的销售出库单不允许在此修改"); |
| 1591 | var entity = input.Adapt<WtXsckdEntity>(); | 1596 | var entity = input.Adapt<WtXsckdEntity>(); |
| 1592 | entity.Bjsx = input.bjsx; | 1597 | entity.Bjsx = input.bjsx; |
| 1593 | 1598 | ||
| @@ -2861,23 +2866,20 @@ LIMIT {offset}, {pageSize}"; | @@ -2861,23 +2866,20 @@ LIMIT {offset}, {pageSize}"; | ||
| 2861 | } | 2866 | } |
| 2862 | 2867 | ||
| 2863 | /// <summary> | 2868 | /// <summary> |
| 2864 | - /// 销售出库单是否跳过 ERP 二级审核:收银台「门店」、抖音「抖音订单/抖音抓单」、或备注为抖音发货脚本格式(抖音订单:…)。 | 2869 | + /// 销售出库单是否跳过 ERP 审核:仅「单据来源 ly 为后台或空(未标记,视同后台录入需审)」走审核; |
| 2870 | + /// ly 有值且不为「后台」则免审;备注「抖音订单:」开头免审(兼容早期仅写备注的抖音单)。 | ||
| 2865 | /// </summary> | 2871 | /// </summary> |
| 2866 | private static bool IsSalesOutboundSkipErpAudit(WtXsckdEntity entity) | 2872 | private static bool IsSalesOutboundSkipErpAudit(WtXsckdEntity entity) |
| 2867 | { | 2873 | { |
| 2868 | if (entity == null) return false; | 2874 | if (entity == null) return false; |
| 2869 | if (!string.Equals(entity.Djlx, "销售出库单", StringComparison.Ordinal)) return false; | 2875 | if (!string.Equals(entity.Djlx, "销售出库单", StringComparison.Ordinal)) return false; |
| 2870 | - var ly = entity.Ly?.Trim(); | ||
| 2871 | - if (!string.IsNullOrEmpty(ly)) | ||
| 2872 | - { | ||
| 2873 | - if (string.Equals(ly, "门店", StringComparison.Ordinal)) return true; | ||
| 2874 | - if (string.Equals(ly, "抖音订单", StringComparison.Ordinal)) return true; | ||
| 2875 | - if (string.Equals(ly, "抖音抓单", StringComparison.Ordinal)) return true; | ||
| 2876 | - } | ||
| 2877 | var bz = entity.Bz?.Trim(); | 2876 | var bz = entity.Bz?.Trim(); |
| 2878 | if (!string.IsNullOrEmpty(bz) && bz.StartsWith("抖音订单:", StringComparison.Ordinal)) | 2877 | if (!string.IsNullOrEmpty(bz) && bz.StartsWith("抖音订单:", StringComparison.Ordinal)) |
| 2879 | return true; | 2878 | return true; |
| 2880 | - return false; | 2879 | + var ly = entity.Ly?.Trim(); |
| 2880 | + if (string.IsNullOrEmpty(ly)) | ||
| 2881 | + return false; | ||
| 2882 | + return !string.Equals(ly, "后台", StringComparison.Ordinal); | ||
| 2881 | } | 2883 | } |
| 2882 | 2884 | ||
| 2883 | /// <summary> | 2885 | /// <summary> |
| @@ -2905,10 +2907,22 @@ LIMIT {offset}, {pageSize}"; | @@ -2905,10 +2907,22 @@ LIMIT {offset}, {pageSize}"; | ||
| 2905 | 2907 | ||
| 2906 | var entity = await _db.Queryable<WtXsckdEntity>().Where(x => x.Id == id).FirstAsync(); | 2908 | var entity = await _db.Queryable<WtXsckdEntity>().Where(x => x.Id == id).FirstAsync(); |
| 2907 | if (entity == null) | 2909 | if (entity == null) |
| 2910 | + { | ||
| 2911 | + _db.RollbackTran(); | ||
| 2908 | return new { success = false, message = "单据不存在" }; | 2912 | return new { success = false, message = "单据不存在" }; |
| 2913 | + } | ||
| 2909 | 2914 | ||
| 2910 | if (!string.IsNullOrEmpty(expectedDjlx) && entity.Djlx != expectedDjlx) | 2915 | if (!string.IsNullOrEmpty(expectedDjlx) && entity.Djlx != expectedDjlx) |
| 2916 | + { | ||
| 2917 | + _db.RollbackTran(); | ||
| 2911 | return new { success = false, message = $"该单据不是{expectedDjlx},无法审核" }; | 2918 | return new { success = false, message = $"该单据不是{expectedDjlx},无法审核" }; |
| 2919 | + } | ||
| 2920 | + | ||
| 2921 | + if (entity.Djlx == "销售出库单" && IsSalesOutboundSkipErpAudit(entity)) | ||
| 2922 | + { | ||
| 2923 | + _db.RollbackTran(); | ||
| 2924 | + return new { success = false, message = "非后台来源的销售出库单无需在此审核" }; | ||
| 2925 | + } | ||
| 2912 | 2926 | ||
| 2913 | var actualDjlx = entity.Djlx; | 2927 | var actualDjlx = entity.Djlx; |
| 2914 | 2928 | ||
| @@ -3108,7 +3122,7 @@ LIMIT {offset}, {pageSize}"; | @@ -3108,7 +3122,7 @@ LIMIT {offset}, {pageSize}"; | ||
| 3108 | } | 3122 | } |
| 3109 | 3123 | ||
| 3110 | /// <summary> | 3124 | /// <summary> |
| 3111 | - /// 审核销售出库单(支持两级审核)。收银台「门店」、抖音「抖音订单」等外部来源无需在此审核。 | 3125 | + /// 审核销售出库单(支持两级审核)。仅单据来源为「后台」或未标记来源的需审;其它来源无需在此审核。 |
| 3112 | /// </summary> | 3126 | /// </summary> |
| 3113 | /// <param name="id">销售出库单主键</param> | 3127 | /// <param name="id">销售出库单主键</param> |
| 3114 | [HttpPost("ApproveSalesOutbound/{id}")] | 3128 | [HttpPost("ApproveSalesOutbound/{id}")] |
| @@ -3116,7 +3130,7 @@ LIMIT {offset}, {pageSize}"; | @@ -3116,7 +3130,7 @@ LIMIT {offset}, {pageSize}"; | ||
| 3116 | { | 3130 | { |
| 3117 | var header = await _db.Queryable<WtXsckdEntity>().FirstAsync(p => p.Id == id); | 3131 | var header = await _db.Queryable<WtXsckdEntity>().FirstAsync(p => p.Id == id); |
| 3118 | if (header != null && IsSalesOutboundSkipErpAudit(header)) | 3132 | if (header != null && IsSalesOutboundSkipErpAudit(header)) |
| 3119 | - return new { success = false, message = "收银台或抖音等外部来源的销售出库单无需在此审核" }; | 3133 | + return new { success = false, message = "非后台来源的销售出库单无需在此审核" }; |
| 3120 | return await ApproveDocument(id, "销售出库单", input?.remark); | 3134 | return await ApproveDocument(id, "销售出库单", input?.remark); |
| 3121 | } | 3135 | } |
| 3122 | 3136 | ||
| @@ -3156,7 +3170,7 @@ LIMIT {offset}, {pageSize}"; | @@ -3156,7 +3170,7 @@ LIMIT {offset}, {pageSize}"; | ||
| 3156 | return new { success = false, message = $"该单据不是{expectedDjlx},无法操作" }; | 3170 | return new { success = false, message = $"该单据不是{expectedDjlx},无法操作" }; |
| 3157 | 3171 | ||
| 3158 | if (entity.Djlx == "销售出库单" && IsSalesOutboundSkipErpAudit(entity)) | 3172 | if (entity.Djlx == "销售出库单" && IsSalesOutboundSkipErpAudit(entity)) |
| 3159 | - return new { success = false, message = "收银台或抖音等外部来源的销售出库单无需在此审核" }; | 3173 | + return new { success = false, message = "非后台来源的销售出库单无需在此审核" }; |
| 3160 | 3174 | ||
| 3161 | if (entity.Djzt == "已审核") | 3175 | if (entity.Djzt == "已审核") |
| 3162 | return new { success = false, message = "该单据已审核通过,如需修改请使用反审" }; | 3176 | return new { success = false, message = "该单据已审核通过,如需修改请使用反审" }; |
Antis.Erp.Plat/netcore/src/Modularity/VisualDev/NCC.VisualDev/DashboardService.cs
| @@ -346,12 +346,10 @@ namespace NCC.VisualDev | @@ -346,12 +346,10 @@ namespace NCC.VisualDev | ||
| 346 | { | 346 | { |
| 347 | var list = new List<FlowTodoOutput>(); | 347 | var list = new List<FlowTodoOutput>(); |
| 348 | list.AddRange(await QueryWtXsckdAuditTodosAsync("同价调拨单", userId, userAccount, null)); | 348 | list.AddRange(await QueryWtXsckdAuditTodosAsync("同价调拨单", userId, userAccount, null)); |
| 349 | - // 与 ApproveSalesOutbound 一致:收银台门店、抖音来源、备注「抖音订单:」开头的不进待办 | 349 | + // 仅单据来源为「后台」或未标记(ly 空)的销售出库单进待办;备注「抖音订单:」同后端免审规则 |
| 350 | list.AddRange(await QueryWtXsckdAuditTodosAsync("销售出库单", userId, userAccount, | 350 | list.AddRange(await QueryWtXsckdAuditTodosAsync("销售出库单", userId, userAccount, |
| 351 | - @"NOT ( | ||
| 352 | - TRIM(IFNULL(d.ly, '')) IN ('门店', '抖音订单', '抖音抓单') | ||
| 353 | - OR (TRIM(IFNULL(d.bz, '')) <> '' AND TRIM(d.bz) LIKE '抖音订单:%') | ||
| 354 | -)")); | 351 | + @"( TRIM(IFNULL(d.ly, '')) = '' OR TRIM(IFNULL(d.ly, '')) = '后台' ) |
| 352 | + AND NOT ( TRIM(IFNULL(d.bz, '')) <> '' AND TRIM(d.bz) LIKE '抖音订单:%' )")); | ||
| 355 | list.AddRange(await QueryWtXsckdAuditTodosAsync("销售退货单", userId, userAccount, null)); | 353 | list.AddRange(await QueryWtXsckdAuditTodosAsync("销售退货单", userId, userAccount, null)); |
| 356 | list.AddRange(await QueryWtXsckdAuditTodosAsync("采购入库单", userId, userAccount, null)); | 354 | list.AddRange(await QueryWtXsckdAuditTodosAsync("采购入库单", userId, userAccount, null)); |
| 357 | return list.OrderByDescending(x => x.creatorTime).ToList(); | 355 | return list.OrderByDescending(x => x.creatorTime).ToList(); |
Antis.Erp.Plat/sy/PACKAGE_README.md
| @@ -23,6 +23,7 @@ package.bat | @@ -23,6 +23,7 @@ package.bat | ||
| 23 | ``` | 23 | ``` |
| 24 | 24 | ||
| 25 | **注意**:Windows系统需要安装以下工具之一: | 25 | **注意**:Windows系统需要安装以下工具之一: |
| 26 | + | ||
| 26 | - 7-Zip(推荐) | 27 | - 7-Zip(推荐) |
| 27 | - WinRAR | 28 | - WinRAR |
| 28 | 29 | ||
| @@ -31,6 +32,7 @@ package.bat | @@ -31,6 +32,7 @@ package.bat | ||
| 31 | ## 📋 打包包含的文件 | 32 | ## 📋 打包包含的文件 |
| 32 | 33 | ||
| 33 | ### HTML文件 | 34 | ### HTML文件 |
| 35 | + | ||
| 34 | - `login.html` - 登录页 | 36 | - `login.html` - 登录页 |
| 35 | - `home.html` - 商品列表页 | 37 | - `home.html` - 商品列表页 |
| 36 | - `settlement.html` - 收银台(核心功能) | 38 | - `settlement.html` - 收银台(核心功能) |
| @@ -39,11 +41,13 @@ package.bat | @@ -39,11 +41,13 @@ package.bat | ||
| 39 | - `from.html` - 其他功能页 | 41 | - `from.html` - 其他功能页 |
| 40 | 42 | ||
| 41 | ### 资源目录 | 43 | ### 资源目录 |
| 44 | + | ||
| 42 | - `css/` - 样式文件 | 45 | - `css/` - 样式文件 |
| 43 | - `js/` - JavaScript库(Vue.js, jQuery等) | 46 | - `js/` - JavaScript库(Vue.js, jQuery等) |
| 44 | - `images/` - 图片资源 | 47 | - `images/` - 图片资源 |
| 45 | 48 | ||
| 46 | ### 文档文件 | 49 | ### 文档文件 |
| 50 | + | ||
| 47 | - `README.md` - 项目说明 | 51 | - `README.md` - 项目说明 |
| 48 | - `QUICK_START.md` - 快速开始指南 | 52 | - `QUICK_START.md` - 快速开始指南 |
| 49 | - `API_CONFIG.md` - API配置说明 | 53 | - `API_CONFIG.md` - API配置说明 |
| @@ -51,6 +55,7 @@ package.bat | @@ -51,6 +55,7 @@ package.bat | ||
| 51 | - `LAUNCH_QUICK_REFERENCE.txt` - 快速参考 | 55 | - `LAUNCH_QUICK_REFERENCE.txt` - 快速参考 |
| 52 | 56 | ||
| 53 | ### 启动脚本 | 57 | ### 启动脚本 |
| 58 | + | ||
| 54 | - `start.sh` - Linux/macOS启动脚本 | 59 | - `start.sh` - Linux/macOS启动脚本 |
| 55 | - `start.bat` - Windows启动脚本 | 60 | - `start.bat` - Windows启动脚本 |
| 56 | - `start_3001.sh` - 指定端口启动脚本 | 61 | - `start_3001.sh` - 指定端口启动脚本 |
| @@ -59,6 +64,7 @@ package.bat | @@ -59,6 +64,7 @@ package.bat | ||
| 59 | ## 🚫 排除的文件 | 64 | ## 🚫 排除的文件 |
| 60 | 65 | ||
| 61 | 以下文件**不会**被打包: | 66 | 以下文件**不会**被打包: |
| 67 | + | ||
| 62 | - `*.bak` - 备份文件 | 68 | - `*.bak` - 备份文件 |
| 63 | - `home copy.html` - 副本文件 | 69 | - `home copy.html` - 副本文件 |
| 64 | - `axios-1.x/` - 开发依赖(已包含在js目录中) | 70 | - `axios-1.x/` - 开发依赖(已包含在js目录中) |
| @@ -105,6 +111,7 @@ npx http-server . -p 8888 -c-1 | @@ -105,6 +111,7 @@ npx http-server . -p 8888 -c-1 | ||
| 105 | ### Linux/macOS (`package.sh`) | 111 | ### Linux/macOS (`package.sh`) |
| 106 | 112 | ||
| 107 | 修改以下部分: | 113 | 修改以下部分: |
| 114 | + | ||
| 108 | ```bash | 115 | ```bash |
| 109 | # 添加要排除的文件 | 116 | # 添加要排除的文件 |
| 110 | EXCLUDE_LIST=( | 117 | EXCLUDE_LIST=( |
| @@ -117,6 +124,7 @@ EXCLUDE_LIST=( | @@ -117,6 +124,7 @@ EXCLUDE_LIST=( | ||
| 117 | ### Windows (`package.bat`) | 124 | ### Windows (`package.bat`) |
| 118 | 125 | ||
| 119 | 修改文件复制部分: | 126 | 修改文件复制部分: |
| 127 | + | ||
| 120 | ```batch | 128 | ```batch |
| 121 | REM 添加要复制的文件或目录 | 129 | REM 添加要复制的文件或目录 |
| 122 | copy "your_file.txt" "%TEMP_DIR%\" >nul | 130 | copy "your_file.txt" "%TEMP_DIR%\" >nul |
| @@ -133,22 +141,21 @@ copy "your_file.txt" "%TEMP_DIR%\" >nul | @@ -133,22 +141,21 @@ copy "your_file.txt" "%TEMP_DIR%\" >nul | ||
| 133 | 141 | ||
| 134 | 解压后检查以下文件是否存在: | 142 | 解压后检查以下文件是否存在: |
| 135 | 143 | ||
| 136 | -- [ ] 所有HTML文件 | ||
| 137 | -- [ ] `css/style.css` | ||
| 138 | -- [ ] `js/vue.min.js` | ||
| 139 | -- [ ] `js/jquery.min.js` | ||
| 140 | -- [ ] `images/` 目录下的所有图标 | ||
| 141 | -- [ ] 启动脚本文件 | 144 | +- 所有HTML文件 |
| 145 | +- `css/style.css` | ||
| 146 | +- `js/vue.min.js` | ||
| 147 | +- `js/jquery.min.js` | ||
| 148 | +- `images/` 目录下的所有图标 | ||
| 149 | +- 启动脚本文件 | ||
| 142 | 150 | ||
| 143 | ## 📞 技术支持 | 151 | ## 📞 技术支持 |
| 144 | 152 | ||
| 145 | 如有问题,请检查: | 153 | 如有问题,请检查: |
| 154 | + | ||
| 146 | 1. 打包脚本执行权限(Linux/macOS) | 155 | 1. 打包脚本执行权限(Linux/macOS) |
| 147 | 2. 压缩工具是否正确安装(Windows) | 156 | 2. 压缩工具是否正确安装(Windows) |
| 148 | 3. 文件路径是否正确 | 157 | 3. 文件路径是否正确 |
| 149 | 158 | ||
| 150 | --- | 159 | --- |
| 151 | 160 | ||
| 152 | -**最后更新**:2025-12-22 | ||
| 153 | - | ||
| 154 | - | 161 | +**最后更新**:2025-12-22 |
| 155 | \ No newline at end of file | 162 | \ No newline at end of file |