Commit 2356540b7e02620174bb90d3739bf664aeff718c

Authored by “wangming”
1 parent 79503e87

对成本进行了全逻辑的修改和添加

Antis.Erp.Plat/SERVICES_CONFIG.md
@@ -88,7 +88,7 @@ python3 -m http.server 8888 @@ -88,7 +88,7 @@ python3 -m http.server 8888
88 "http://localhost:3021", 88 "http://localhost:3021",
89 "http://localhost:3014", 89 "http://localhost:3014",
90 "http://localhost:3015", 90 "http://localhost:3015",
91 - "http://localhost:2015", 91 + "http://localhost:2011",
92 "http://localhost:3009", 92 "http://localhost:3009",
93 "http://localhost:2016", 93 "http://localhost:2016",
94 "http://localhost:3000", 94 "http://localhost:3000",
Antis.Erp.Plat/antis-ncc-admin/.env.development
1 # 开发 1 # 开发
2 2
3 VUE_CLI_BABEL_TRANSPILE_MODULES = true 3 VUE_CLI_BABEL_TRANSPILE_MODULES = true
4 -VUE_APP_BASE_API = 'http://localhost:2015' 4 +VUE_APP_BASE_API = 'http://localhost:2011'
5 VUE_APP_BASE_WSS = 'ws://localhost:2011/websocket' 5 VUE_APP_BASE_WSS = 'ws://localhost:2011/websocket'
Antis.Erp.Plat/antis-ncc-admin/src/views/wtCgthd/Form.vue
@@ -99,6 +99,11 @@ @@ -99,6 +99,11 @@
99 <el-input v-model="scope.row.dj" placeholder="选择入库编号自动填入" readonly :style="{ background: '#f5f7fa' }"></el-input> 99 <el-input v-model="scope.row.dj" placeholder="选择入库编号自动填入" readonly :style="{ background: '#f5f7fa' }"></el-input>
100 </template> 100 </template>
101 </el-table-column> 101 </el-table-column>
  102 + <el-table-column prop="thdj" label="退货单价">
  103 + <template slot-scope="scope">
  104 + <el-input v-model="scope.row.thdj" placeholder="请输入退货单价" clearable @input="handleRefundPriceChange(scope.row)"></el-input>
  105 + </template>
  106 + </el-table-column>
102 <el-table-column prop="je" label="金额"> 107 <el-table-column prop="je" label="金额">
103 <template slot-scope="scope"> 108 <template slot-scope="scope">
104 <el-input v-model="scope.row.je" placeholder="请输入" clearable @input="handleAmountFieldChange(scope.row)"></el-input> 109 <el-input v-model="scope.row.je" placeholder="请输入" clearable @input="handleAmountFieldChange(scope.row)"></el-input>
@@ -364,9 +369,13 @@ @@ -364,9 +369,13 @@
364 if (!item.hasOwnProperty('dyddbh')) { 369 if (!item.hasOwnProperty('dyddbh')) {
365 this.$set(item, 'dyddbh', item.dyddbh || undefined); 370 this.$set(item, 'dyddbh', item.dyddbh || undefined);
366 } 371 }
  372 + if (!item.hasOwnProperty('thdj')) {
  373 + this.$set(item, 'thdj', item.thdj || item.dj || undefined);
  374 + }
367 }); 375 });
368 this.syncDetailWarehouses(); 376 this.syncDetailWarehouses();
369 this.updateTotalAmount(); 377 this.updateTotalAmount();
  378 + this.reloadInboundOrdersForExistingRows();
370 }) 379 })
371 } else { 380 } else {
372 this.dataForm.wtXsckdMxList = []; 381 this.dataForm.wtXsckdMxList = [];
@@ -510,6 +519,7 @@ @@ -510,6 +519,7 @@
510 dw:undefined, 519 dw:undefined,
511 sl:undefined, 520 sl:undefined,
512 dj:undefined, 521 dj:undefined,
  522 + thdj: undefined,
513 je:undefined, 523 je:undefined,
514 dyddbh: undefined, 524 dyddbh: undefined,
515 description: undefined, 525 description: undefined,
@@ -527,6 +537,8 @@ @@ -527,6 +537,8 @@
527 }, 537 },
528 handleDelWtXsckdMxEntityList(index) { 538 handleDelWtXsckdMxEntityList(index) {
529 this.dataForm.wtXsckdMxList.splice(index, 1); 539 this.dataForm.wtXsckdMxList.splice(index, 1);
  540 + // 删除行后,重建下拉缓存索引,避免错位
  541 + this.rebuildInboundOrdersMap();
530 // 删除后更新总金额 542 // 删除后更新总金额
531 this.updateTotalAmount(); 543 this.updateTotalAmount();
532 }, 544 },
@@ -537,6 +549,7 @@ @@ -537,6 +549,7 @@
537 549
538 this.$set(row, 'dyddbh', undefined); 550 this.$set(row, 'dyddbh', undefined);
539 this.$set(row, 'dj', ''); 551 this.$set(row, 'dj', '');
  552 + this.$set(row, 'thdj', '');
540 this.$set(row, 'je', ''); 553 this.$set(row, 'je', '');
541 554
542 await this.loadInboundOrders(row); 555 await this.loadInboundOrders(row);
@@ -558,10 +571,12 @@ @@ -558,10 +571,12 @@
558 if (!row.spbh || !warehouse) return; 571 if (!row.spbh || !warehouse) return;
559 572
560 try { 573 try {
  574 + const requestData = { spbh: row.spbh, ck: warehouse };
  575 + console.log('[loadInboundOrders] GET请求参数:', requestData);
561 const res = await request({ 576 const res = await request({
562 url: '/api/Extend/WtXsckd/GetPurchaseInboundOrders', 577 url: '/api/Extend/WtXsckd/GetPurchaseInboundOrders',
563 method: 'get', 578 method: 'get',
564 - params: { spbh: row.spbh, ck: warehouse } 579 + data: requestData
565 }); 580 });
566 let rawList = []; 581 let rawList = [];
567 if (Array.isArray(res)) { 582 if (Array.isArray(res)) {
@@ -582,6 +597,29 @@ @@ -582,6 +597,29 @@
582 console.error('加载入库单列表失败', e); 597 console.error('加载入库单列表失败', e);
583 } 598 }
584 }, 599 },
  600 + rebuildInboundOrdersMap() {
  601 + const oldMap = this.inboundOrdersMap || {};
  602 + const newMap = {};
  603 + (this.dataForm.wtXsckdMxList || []).forEach((row, idx) => {
  604 + const oldIdx = Object.keys(oldMap).find(key => {
  605 + const list = oldMap[key] || [];
  606 + return list.some(item => item.orderid === row.dyddbh);
  607 + });
  608 + if (oldIdx !== undefined) {
  609 + this.$set(newMap, idx, oldMap[oldIdx] || []);
  610 + }
  611 + });
  612 + this.inboundOrdersMap = newMap;
  613 + },
  614 + async reloadInboundOrdersForExistingRows() {
  615 + if (!this.dataForm || !Array.isArray(this.dataForm.wtXsckdMxList)) return;
  616 + const tasks = this.dataForm.wtXsckdMxList
  617 + .filter(row => row && row.spbh)
  618 + .map(row => this.loadInboundOrders(row));
  619 + if (tasks.length > 0) {
  620 + await Promise.all(tasks);
  621 + }
  622 + },
585 normalizeInboundOrder(item) { 623 normalizeInboundOrder(item) {
586 const getVal = (obj, keys) => { 624 const getVal = (obj, keys) => {
587 for (const key of keys) { 625 for (const key of keys) {
@@ -604,6 +642,7 @@ @@ -604,6 +642,7 @@
604 const selected = orders.find(o => o.orderid === row.dyddbh); 642 const selected = orders.find(o => o.orderid === row.dyddbh);
605 if (selected) { 643 if (selected) {
606 this.$set(row, 'dj', selected.dj); 644 this.$set(row, 'dj', selected.dj);
  645 + this.$set(row, 'thdj', selected.dj);
607 const sl = parseFloat(row.sl) || 0; 646 const sl = parseFloat(row.sl) || 0;
608 if (sl > 0) { 647 if (sl > 0) {
609 this.$set(row, 'je', (sl * parseFloat(selected.dj)).toFixed(2)); 648 this.$set(row, 'je', (sl * parseFloat(selected.dj)).toFixed(2));
@@ -629,14 +668,17 @@ @@ -629,14 +668,17 @@
629 }, 668 },
630 669
631 // 处理主表出库仓库变化,同步更新所有明细行的出库仓库 670 // 处理主表出库仓库变化,同步更新所有明细行的出库仓库
632 - handleMainWarehouseChange(value) { 671 + async handleMainWarehouseChange(value) {
633 this.syncDetailWarehouses(); 672 this.syncDetailWarehouses();
634 this.inboundOrdersMap = {}; 673 this.inboundOrdersMap = {};
635 this.dataForm.wtXsckdMxList.forEach(row => { 674 this.dataForm.wtXsckdMxList.forEach(row => {
636 this.$set(row, 'dyddbh', undefined); 675 this.$set(row, 'dyddbh', undefined);
637 this.$set(row, 'dj', ''); 676 this.$set(row, 'dj', '');
  677 + this.$set(row, 'thdj', '');
638 this.$set(row, 'je', ''); 678 this.$set(row, 'je', '');
639 }); 679 });
  680 + // 关键修复:仓库变化后,自动为已选商品重新加载入库编号
  681 + await this.reloadInboundOrdersForExistingRows();
640 this.updateTotalAmount(); 682 this.updateTotalAmount();
641 }, 683 },
642 684
@@ -688,7 +730,7 @@ @@ -688,7 +730,7 @@
688 // 处理数量变化 730 // 处理数量变化
689 handleAmountChange(row) { 731 handleAmountChange(row) {
690 const sl = parseFloat(row.sl) || 0; 732 const sl = parseFloat(row.sl) || 0;
691 - const dj = parseFloat(row.dj) || 0; 733 + const dj = parseFloat(row.thdj) || 0;
692 row.je = (sl * dj).toFixed(2); 734 row.je = (sl * dj).toFixed(2);
693 735
694 // 自动更新主表退款金额 736 // 自动更新主表退款金额
@@ -722,10 +764,10 @@ @@ -722,10 +764,10 @@
722 } 764 }
723 }, 765 },
724 766
725 - // 处理单价变化  
726 - handlePriceChange(row) { 767 + // 处理退货单价变化(金额按退货单价计算)
  768 + handleRefundPriceChange(row) {
727 const sl = parseFloat(row.sl) || 0; 769 const sl = parseFloat(row.sl) || 0;
728 - const dj = parseFloat(row.dj) || 0; 770 + const dj = parseFloat(row.thdj) || 0;
729 row.je = (sl * dj).toFixed(2); 771 row.je = (sl * dj).toFixed(2);
730 772
731 // 自动更新主表退款金额 773 // 自动更新主表退款金额
@@ -764,8 +806,9 @@ @@ -764,8 +806,9 @@
764 // 自动设置数量为选择的序列号数量 806 // 自动设置数量为选择的序列号数量
765 currentRow.sl = selectedSerialNumbers.length.toString() 807 currentRow.sl = selectedSerialNumbers.length.toString()
766 // 自动计算金额 808 // 自动计算金额
767 - if (currentRow.dj) {  
768 - currentRow.je = (parseFloat(currentRow.sl) * parseFloat(currentRow.dj)).toFixed(2) 809 + if (currentRow.thdj || currentRow.dj) {
  810 + const usePrice = parseFloat(currentRow.thdj || currentRow.dj || 0)
  811 + currentRow.je = (parseFloat(currentRow.sl) * usePrice).toFixed(2)
769 } 812 }
770 813
771 // 更新总金额 814 // 更新总金额
Antis.Erp.Plat/netcore/src/Application/NCC.API/Properties/launchSettings.json
@@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
19 "commandName": "Project", 19 "commandName": "Project",
20 "dotnetRunMessages": "true", 20 "dotnetRunMessages": "true",
21 "launchBrowser": false, 21 "launchBrowser": false,
22 - "applicationUrl": "http://localhost:2015", 22 + "applicationUrl": "http://localhost:2011",
23 "environmentVariables": { 23 "environmentVariables": {
24 "ASPNETCORE_ENVIRONMENT": "Development" 24 "ASPNETCORE_ENVIRONMENT": "Development"
25 } 25 }
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtXsckdMxCrInput.cs
1 -using System; 1 +using System;
2 using System.Collections.Generic; 2 using System.Collections.Generic;
3 3
4 namespace NCC.Extend.Entitys.Dto.WtXsckd 4 namespace NCC.Extend.Entitys.Dto.WtXsckd
@@ -54,6 +54,11 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd @@ -54,6 +54,11 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd
54 public decimal dj { get; set; } 54 public decimal dj { get; set; }
55 55
56 /// <summary> 56 /// <summary>
  57 + /// 退货单价(采购退货业务使用)
  58 + /// </summary>
  59 + public decimal? thdj { get; set; }
  60 +
  61 + /// <summary>
57 /// 金额 62 /// 金额
58 /// </summary> 63 /// </summary>
59 public decimal je { get; set; } 64 public decimal je { get; set; }
@@ -68,5 +73,10 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd @@ -68,5 +73,10 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd
68 /// </summary> 73 /// </summary>
69 public string mdxx { get; set; } 74 public string mdxx { get; set; }
70 75
  76 + /// <summary>
  77 + /// 已选择的序列号列表
  78 + /// </summary>
  79 + public List<string> selectedSerialNumbers { get; set; }
  80 +
71 } 81 }
72 } 82 }
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtXsckdMxInfoOutput.cs
1 -using System; 1 +using System;
2 using System.Collections.Generic; 2 using System.Collections.Generic;
3 3
4 namespace NCC.Extend.Entitys.Dto.WtXsckd 4 namespace NCC.Extend.Entitys.Dto.WtXsckd
@@ -59,6 +59,11 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd @@ -59,6 +59,11 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd
59 public decimal dj { get; set; } 59 public decimal dj { get; set; }
60 60
61 /// <summary> 61 /// <summary>
  62 + /// 退货单价(采购退货业务使用)
  63 + /// </summary>
  64 + public decimal? thdj { get; set; }
  65 +
  66 + /// <summary>
62 /// 金额 67 /// 金额
63 /// </summary> 68 /// </summary>
64 public decimal je { get; set; } 69 public decimal je { get; set; }
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtXsckdMxEntity.cs
@@ -72,6 +72,12 @@ namespace NCC.Extend.Entitys @@ -72,6 +72,12 @@ namespace NCC.Extend.Entitys
72 public decimal Dj { get; set; } 72 public decimal Dj { get; set; }
73 73
74 /// <summary> 74 /// <summary>
  75 + /// 退货单价(采购退货业务使用)
  76 + /// </summary>
  77 + [SugarColumn(ColumnName = "thdj")]
  78 + public decimal? Thdj { get; set; }
  79 +
  80 + /// <summary>
75 /// 金额 81 /// 金额
76 /// </summary> 82 /// </summary>
77 [SugarColumn(ColumnName = "je")] 83 [SugarColumn(ColumnName = "je")]
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtXsckdService.cs
@@ -574,6 +574,94 @@ namespace NCC.Extend.WtXsckd @@ -574,6 +574,94 @@ namespace NCC.Extend.WtXsckd
574 } 574 }
575 } 575 }
576 } 576 }
  577 + // ✅ 处理采购退货单:扣减在库序列号(Status 0 -> 1)
  578 + else if (input.djlx == "采购退货单")
  579 + {
  580 + Console.WriteLine($"Create方法 - 准备处理采购退货单序列号出库");
  581 + Console.WriteLine($" - 单据ID: {newEntity.Id}");
  582 + Console.WriteLine($" - 单据类型: {input.djlx}");
  583 + Console.WriteLine($" - 出库仓库: {entity.Cjck}");
  584 +
  585 + if (wtXsckdMxEntityList != null && wtXsckdMxEntityList.Any())
  586 + {
  587 + for (int i = 0; i < wtXsckdMxEntityList.Count; i++)
  588 + {
  589 + var detail = wtXsckdMxEntityList[i];
  590 + var inputDetail = (input.wtXsckdMxList != null && input.wtXsckdMxList.Count > i)
  591 + ? input.wtXsckdMxList[i]
  592 + : null;
  593 +
  594 + if (!string.IsNullOrEmpty(detail.Spbh) && int.TryParse(detail.Sl, out int slValue) && slValue > 0)
  595 + {
  596 + var outStoreId = !string.IsNullOrEmpty(detail.Ckck) ? detail.Ckck : entity.Cjck;
  597 + if (string.IsNullOrEmpty(outStoreId))
  598 + {
  599 + throw new Exception($"商品 {detail.Spmc} 未指定出库仓库,无法处理采购退货序列号");
  600 + }
  601 +
  602 + var warehouseIds = await _db.Queryable<WtCkEntity>()
  603 + .Where(c => c.Ssmd == outStoreId || c.Id == outStoreId)
  604 + .Select(c => c.Id)
  605 + .ToListAsync();
  606 + if (warehouseIds == null || warehouseIds.Count == 0)
  607 + {
  608 + warehouseIds = new List<string> { outStoreId };
  609 + }
  610 +
  611 + var selectedSerials = inputDetail?.selectedSerialNumbers?
  612 + .Where(s => !string.IsNullOrWhiteSpace(s))
  613 + .Select(s => s.Trim())
  614 + .Distinct()
  615 + .ToList() ?? new List<string>();
  616 +
  617 + List<string> serialsToOut;
  618 + if (selectedSerials.Count > 0)
  619 + {
  620 + serialsToOut = await _db.Queryable<WtSerialNumberEntity>()
  621 + .Where(s => s.Spbh == detail.Spbh
  622 + && s.Status == 0
  623 + && warehouseIds.Contains(s.InWarehouse)
  624 + && selectedSerials.Contains(s.SerialNumber))
  625 + .Select(s => s.SerialNumber)
  626 + .ToListAsync();
  627 +
  628 + if (serialsToOut.Count != slValue)
  629 + {
  630 + throw new Exception($"商品 {detail.Spmc} 选择的序列号数量与明细数量不一致,或包含非在库序列号");
  631 + }
  632 + }
  633 + else
  634 + {
  635 + serialsToOut = await _db.Queryable<WtSerialNumberEntity>()
  636 + .Where(s => s.Spbh == detail.Spbh
  637 + && s.Status == 0
  638 + && warehouseIds.Contains(s.InWarehouse))
  639 + .OrderBy(s => s.InTime)
  640 + .Take(slValue)
  641 + .Select(s => s.SerialNumber)
  642 + .ToListAsync();
  643 + if (serialsToOut.Count < slValue)
  644 + {
  645 + throw new Exception($"商品 {detail.Spmc} 在仓库可用序列号不足,需{slValue}个,实得{serialsToOut.Count}个");
  646 + }
  647 + }
  648 +
  649 + await _db.Updateable<WtSerialNumberEntity>()
  650 + .SetColumns(it => new WtSerialNumberEntity
  651 + {
  652 + Status = 1,
  653 + OutDjbh = newEntity.Id,
  654 + OutDjlx = input.djlx,
  655 + OutWarehouse = outStoreId,
  656 + OutTime = DateTime.Now,
  657 + OutOperator = userId ?? ""
  658 + })
  659 + .Where(it => serialsToOut.Contains(it.SerialNumber))
  660 + .ExecuteCommandAsync();
  661 + }
  662 + }
  663 + }
  664 + }
577 665
578 // 自动生成导购提成记录 666 // 自动生成导购提成记录
579 if (input.djlx == "销售出库单") 667 if (input.djlx == "销售出库单")
@@ -2882,7 +2970,7 @@ ORDER BY t.`商品编号`;&quot;; @@ -2882,7 +2970,7 @@ ORDER BY t.`商品编号`;&quot;;
2882 if (string.IsNullOrEmpty(detail.Spbh)) continue; 2970 if (string.IsNullOrEmpty(detail.Spbh)) continue;
2883 if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; 2971 if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue;
2884 2972
2885 - decimal returnPrice = detail.Dj; 2973 + decimal returnPrice = detail.Thdj.HasValue && detail.Thdj.Value > 0 ? detail.Thdj.Value : detail.Dj;
2886 2974
2887 foreach (var whId in warehouseIds) 2975 foreach (var whId in warehouseIds)
2888 { 2976 {
@@ -2910,6 +2998,14 @@ ORDER BY t.`商品编号`;&quot;; @@ -2910,6 +2998,14 @@ ORDER BY t.`商品编号`;&quot;;
2910 "UPDATE wt_sp_cost SET cbj = @cbj, sl = @sl, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", 2998 "UPDATE wt_sp_cost SET cbj = @cbj, sl = @sl, update_time = NOW() WHERE spbh = @spbh AND ck = @ck",
2911 new { cbj = newCbj, sl = newSl, spbh = detail.Spbh, ck = whId }); 2999 new { cbj = newCbj, sl = newSl, spbh = detail.Spbh, ck = whId });
2912 3000
  3001 + // 库存口径统一:以序列号在库数量为准
  3002 + var actualCount = await _db.Ado.GetIntAsync(
  3003 + "SELECT COUNT(*) FROM wt_serial_number WHERE spbh = @spbh AND in_warehouse = @ck AND status = 0",
  3004 + new SugarParameter[] { new SugarParameter("@spbh", detail.Spbh), new SugarParameter("@ck", whId) });
  3005 + await _db.Ado.ExecuteCommandAsync(
  3006 + "UPDATE wt_sp_cost SET sl = @sl, update_time = NOW() WHERE spbh = @spbh AND ck = @ck",
  3007 + new { sl = actualCount, spbh = detail.Spbh, ck = whId });
  3008 +
2913 await _db.Ado.ExecuteCommandAsync( 3009 await _db.Ado.ExecuteCommandAsync(
2914 "UPDATE wt_xsckd_mx SET cbdj = @cbdj, cbje = @cbje WHERE F_Id = @id", 3010 "UPDATE wt_xsckd_mx SET cbdj = @cbdj, cbje = @cbje WHERE F_Id = @id",
2915 new { cbdj = returnPrice, cbje = Math.Round(returnPrice * qty, 2), id = detail.Id }); 3011 new { cbdj = returnPrice, cbje = Math.Round(returnPrice * qty, 2), id = detail.Id });
@@ -3067,7 +3163,28 @@ ORDER BY t.`商品编号`;&quot;; @@ -3067,7 +3163,28 @@ ORDER BY t.`商品编号`;&quot;;
3067 if (string.IsNullOrEmpty(detail.Spbh)) continue; 3163 if (string.IsNullOrEmpty(detail.Spbh)) continue;
3068 if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; 3164 if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue;
3069 3165
3070 - decimal returnPrice = detail.Dj; 3166 + decimal returnPrice = detail.Thdj.HasValue && detail.Thdj.Value > 0 ? detail.Thdj.Value : detail.Dj;
  3167 +
  3168 + // 先回退序列号状态(采购退货出库的序列号恢复为在库)
  3169 + var serials = await _db.Queryable<WtSerialNumberEntity>()
  3170 + .Where(s => s.OutDjbh == id && s.Spbh == detail.Spbh)
  3171 + .Select(s => s.SerialNumber)
  3172 + .ToListAsync();
  3173 + if (serials.Any())
  3174 + {
  3175 + await _db.Updateable<WtSerialNumberEntity>()
  3176 + .SetColumns(it => new WtSerialNumberEntity
  3177 + {
  3178 + Status = 0,
  3179 + OutDjbh = "",
  3180 + OutDjlx = "",
  3181 + OutWarehouse = "",
  3182 + OutTime = null,
  3183 + OutOperator = ""
  3184 + })
  3185 + .Where(it => serials.Contains(it.SerialNumber))
  3186 + .ExecuteCommandAsync();
  3187 + }
3071 3188
3072 foreach (var whId in warehouseIds) 3189 foreach (var whId in warehouseIds)
3073 { 3190 {