Commit cc46620492b701b3c0819587cc97f1784f6214b5

Authored by “wangming”
1 parent c11a79f9

fix(xsckd): 显式落库 ly/djzt/skmx,按来源调整免审与审核拦截

- 销售出库单保存时显式写入 ly、djzt、skmx,避免 Mapster 未映射导致不落库
- 免审逻辑改为:ly 非空且非「后台」则免审,保留备注「抖音订单:」兼容
- 通用审核路径增加回滚与非后台来源无需审核提示;更新相关文案
- 同步前端与 Dashboard、PACKAGE_README 调整

Made-with: Cursor
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}&quot;; @@ -2861,23 +2866,20 @@ LIMIT {offset}, {pageSize}&quot;;
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}&quot;; @@ -2905,10 +2907,22 @@ LIMIT {offset}, {pageSize}&quot;;
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}&quot;; @@ -3108,7 +3122,7 @@ LIMIT {offset}, {pageSize}&quot;;
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}&quot;; @@ -3116,7 +3130,7 @@ LIMIT {offset}, {pageSize}&quot;;
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}&quot;; @@ -3156,7 +3170,7 @@ LIMIT {offset}, {pageSize}&quot;;
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 &quot;your_file.txt&quot; &quot;%TEMP_DIR%\&quot; &gt;nul @@ -133,22 +141,21 @@ copy &quot;your_file.txt&quot; &quot;%TEMP_DIR%\&quot; &gt;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