Commit 60226f1594559ffdd5fd714f4109f0bd497098cc
1 parent
03c75022
修改调价调拨单
Showing
13 changed files
with
953 additions
and
71 deletions
Antis.Erp.Plat/antis-ncc-admin/src/views/wtBjdbd/Form.vue
| 1 | -<template> | |
| 1 | +<template> | |
| 2 | 2 | <el-dialog :title="!dataForm.id ? '新建' : isDetail ? '详情':'编辑'" :close-on-click-modal="false" :visible.sync="visible" class="NCC-dialog NCC-dialog_center" lock-scroll width="80%"> |
| 3 | 3 | <el-row :gutter="15" class="" > |
| 4 | 4 | <el-form ref="elForm" :model="dataForm" size="small" label-width="100px" label-position="right" :disabled="!!isDetail" :rules="rules"> |
| ... | ... | @@ -41,11 +41,16 @@ |
| 41 | 41 | </el-col> |
| 42 | 42 | <el-col :span="12"> |
| 43 | 43 | <el-form-item label="变价系数%" prop="bjsx"> |
| 44 | - <el-input v-model="dataForm.bjsx" placeholder="请输入变价系数" clearable :style='{"width":"100%"}' @input="handlePriceAdjustmentChange"> | |
| 44 | + <el-input v-model="dataForm.bjsx" placeholder="如 95 表示 95%" clearable :style='{"width":"100%"}' @input="handlePriceAdjustmentChange" @change="handlePriceAdjustmentChange"> | |
| 45 | 45 | <template slot="append">%</template> |
| 46 | 46 | </el-input> |
| 47 | 47 | </el-form-item> |
| 48 | 48 | </el-col> |
| 49 | + <el-col :span="12"> | |
| 50 | + <el-form-item label="变价成本合计"> | |
| 51 | + <el-input :value="cbjeDisplay" placeholder="保存后由系统按变价后金额汇总" readonly :style='{"width":"100%"}' /> | |
| 52 | + </el-form-item> | |
| 53 | + </el-col> | |
| 49 | 54 | <el-col :span="12" v-if="false"> |
| 50 | 55 | <el-form-item label="供应商" prop="gys"> |
| 51 | 56 | <el-select v-model="dataForm.gys" placeholder="请选择" clearable :style='{"width":"100%"}' filterable > |
| ... | ... | @@ -134,17 +139,18 @@ |
| 134 | 139 | ></el-input> |
| 135 | 140 | </template> |
| 136 | 141 | </el-table-column> |
| 137 | - <el-table-column prop="dj" label="成本单价"> | |
| 142 | + <el-table-column prop="dj" label="成本单价" width="110"> | |
| 138 | 143 | <template slot-scope="scope"> |
| 139 | 144 | <el-input |
| 140 | 145 | v-model="scope.row.dj" |
| 141 | - placeholder="请输入" | |
| 146 | + placeholder="参考成本,过账以服务端为准" | |
| 142 | 147 | clearable |
| 148 | + :readonly="!!isDetail" | |
| 143 | 149 | @input="handleAmountChange(scope.row)" |
| 144 | 150 | ></el-input> |
| 145 | 151 | </template> |
| 146 | 152 | </el-table-column> |
| 147 | - <el-table-column prop="bjhcb" label="变价后成本" width="120"> | |
| 153 | + <el-table-column prop="bjhcb" label="变价后单价" width="120"> | |
| 148 | 154 | <template slot-scope="scope"> |
| 149 | 155 | <el-input |
| 150 | 156 | v-model="scope.row.bjhcb" |
| ... | ... | @@ -155,6 +161,11 @@ |
| 155 | 161 | ></el-input> |
| 156 | 162 | </template> |
| 157 | 163 | </el-table-column> |
| 164 | + <el-table-column prop="cbje" label="变价后金额" width="100" show-overflow-tooltip> | |
| 165 | + <template slot-scope="scope"> | |
| 166 | + <span>{{ formatMxCbje(scope.row) }}</span> | |
| 167 | + </template> | |
| 168 | + </el-table-column> | |
| 158 | 169 | <el-table-column prop="je" label="销售总额" v-if="false"> |
| 159 | 170 | <template slot-scope="scope"> |
| 160 | 171 | <el-input v-model="scope.row.je" placeholder="请输入" clearable readonly></el-input> |
| ... | ... | @@ -300,6 +311,7 @@ |
| 300 | 311 | kh:undefined, |
| 301 | 312 | gys:undefined, |
| 302 | 313 | bjsx:undefined, // 变价系数% |
| 314 | + cbje: undefined, // 主表变价成本合计(服务端汇总) | |
| 303 | 315 | wtXsckdMxList:[], |
| 304 | 316 | ysje:undefined, |
| 305 | 317 | skzh:undefined, |
| ... | ... | @@ -336,6 +348,12 @@ |
| 336 | 348 | totalJe() { |
| 337 | 349 | return (this.dataForm.wtXsckdMxList || []).reduce((sum, row) => sum + (parseFloat(row.je) || 0), 0) |
| 338 | 350 | }, |
| 351 | + cbjeDisplay() { | |
| 352 | + const v = this.dataForm.cbje | |
| 353 | + if (v == null || v === '') return '无' | |
| 354 | + const n = Number(v) | |
| 355 | + return isNaN(n) ? '无' : n.toFixed(2) | |
| 356 | + }, | |
| 339 | 357 | |
| 340 | 358 | }, |
| 341 | 359 | watch: { |
| ... | ... | @@ -371,6 +389,11 @@ |
| 371 | 389 | }, |
| 372 | 390 | deep: true, |
| 373 | 391 | immediate: false |
| 392 | + }, | |
| 393 | + 'dataForm.bjsx'() { | |
| 394 | + this.$nextTick(() => { | |
| 395 | + this.calculateAllAdjustedCosts(); | |
| 396 | + }); | |
| 374 | 397 | } |
| 375 | 398 | }, |
| 376 | 399 | created() { |
| ... | ... | @@ -641,6 +664,88 @@ |
| 641 | 664 | goBack() { |
| 642 | 665 | this.$emit('refresh') |
| 643 | 666 | }, |
| 667 | + /** 明细变价后金额:已过账用服务端 cbje;未过账用数量×变价后单价预览 */ | |
| 668 | + formatMxCbje(row) { | |
| 669 | + if (!row) return '无' | |
| 670 | + if (row.cbje != null && row.cbje !== '') { | |
| 671 | + const n = Number(row.cbje) | |
| 672 | + return isNaN(n) ? '无' : n.toFixed(2) | |
| 673 | + } | |
| 674 | + const sl = parseInt(row.sl, 10) | |
| 675 | + const bjh = parseFloat(row.bjhcb) | |
| 676 | + if (sl > 0 && !isNaN(bjh) && bjh > 0) { | |
| 677 | + return (sl * bjh).toFixed(2) | |
| 678 | + } | |
| 679 | + return '无' | |
| 680 | + }, | |
| 681 | + /** | |
| 682 | + * 从 wt_sp_cost 拉取调出方成本单价并回填 dj,再算变价后单价 | |
| 683 | + * @param {boolean} silent 无成本记录时不弹警告(避免刷屏) | |
| 684 | + */ | |
| 685 | + async applyOutboundCostPrice(row, silent = false) { | |
| 686 | + if (!row || !row.spbh || !row.ckck) return | |
| 687 | + try { | |
| 688 | + const spbh = encodeURIComponent(String(row.spbh)) | |
| 689 | + const ck = encodeURIComponent(String(row.ckck)) | |
| 690 | + const res = await request({ | |
| 691 | + url: `/api/Extend/WtXsckd/GetOutboundCostPrice?spbh=${spbh}&ckOrMdId=${ck}`, | |
| 692 | + method: 'get' | |
| 693 | + }) | |
| 694 | + const payload = res.data | |
| 695 | + if (payload && payload.success && payload.data != null) { | |
| 696 | + const p = Number(payload.data) | |
| 697 | + if (!isNaN(p) && p > 0) { | |
| 698 | + this.$set(row, 'dj', p.toFixed(4)) | |
| 699 | + this.calculateRowAdjustedCost(row) | |
| 700 | + this.$forceUpdate() | |
| 701 | + } | |
| 702 | + } else if (!silent && payload && payload.msg) { | |
| 703 | + this.$message.warning(payload.msg) | |
| 704 | + } | |
| 705 | + } catch (e) { | |
| 706 | + if (!silent) console.error(e) | |
| 707 | + } | |
| 708 | + }, | |
| 709 | + /** 组装提交体:变价系数转数值、显式 isDraft、去掉前端临时字段 */ | |
| 710 | + buildPayload(isDraft) { | |
| 711 | + const bjsxNum = parseFloat(this.dataForm.bjsx) | |
| 712 | + const mx = (this.dataForm.wtXsckdMxList || []).map(r => { | |
| 713 | + const { productQuery, spxlhLoaded, loadingStock, ...rest } = r | |
| 714 | + return rest | |
| 715 | + }) | |
| 716 | + return { | |
| 717 | + ...this.dataForm, | |
| 718 | + bjsx: isNaN(bjsxNum) ? null : bjsxNum, | |
| 719 | + isDraft: !!isDraft, | |
| 720 | + djlx: '变价调拨单', | |
| 721 | + // 正式保存一律待审核(含从草稿转正);草稿仅保存草稿 | |
| 722 | + djzt: isDraft ? '草稿' : '待审核', | |
| 723 | + wtXsckdMxList: mx | |
| 724 | + } | |
| 725 | + }, | |
| 726 | + /** 正式保存前业务校验(草稿不走此函数) */ | |
| 727 | + validateBjdFormalBasics() { | |
| 728 | + if (!this.dataForm.cjck || !this.dataForm.rkck) | |
| 729 | + return '请选择出库仓库与入库仓库' | |
| 730 | + if (this.dataForm.cjck === this.dataForm.rkck) | |
| 731 | + return '出库仓库与入库仓库不能相同' | |
| 732 | + const coef = parseFloat(this.dataForm.bjsx) | |
| 733 | + if (this.dataForm.bjsx === '' || this.dataForm.bjsx === undefined || isNaN(coef) || coef <= 0) | |
| 734 | + return '请输入大于 0 的变价系数(%)' | |
| 735 | + if (!this.dataForm.wtXsckdMxList || !this.dataForm.wtXsckdMxList.length) | |
| 736 | + return '请添加至少一条明细' | |
| 737 | + let hasSp = false | |
| 738 | + for (let i = 0; i < this.dataForm.wtXsckdMxList.length; i++) { | |
| 739 | + const row = this.dataForm.wtXsckdMxList[i] | |
| 740 | + if (!row.spbh) continue | |
| 741 | + hasSp = true | |
| 742 | + const q = parseInt(row.sl, 10) | |
| 743 | + if (!q || q <= 0) | |
| 744 | + return `第${i + 1}行请填写有效数量或选择序列号` | |
| 745 | + } | |
| 746 | + if (!hasSp) return '请至少选择一条商品明细' | |
| 747 | + return '' | |
| 748 | + }, | |
| 644 | 749 | init(id, isDetail) { |
| 645 | 750 | var _this = this; |
| 646 | 751 | console.log('id',id,'detail',isDetail) |
| ... | ... | @@ -666,6 +771,8 @@ |
| 666 | 771 | _this.dataForm = res.data; |
| 667 | 772 | console.log('编辑时加载的数据:', _this.dataForm); |
| 668 | 773 | console.log('明细数据:', _this.dataForm.wtXsckdMxList); |
| 774 | + if (_this.dataForm.bjsx != null && _this.dataForm.bjsx !== '') | |
| 775 | + _this.$set(_this.dataForm, 'bjsx', String(_this.dataForm.bjsx)) | |
| 669 | 776 | |
| 670 | 777 | // 为每个明细项添加productQuery字段 |
| 671 | 778 | if (_this.dataForm.wtXsckdMxList) { |
| ... | ... | @@ -673,6 +780,13 @@ |
| 673 | 780 | if (!item.hasOwnProperty('productQuery')) { |
| 674 | 781 | _this.$set(item, 'productQuery', ''); |
| 675 | 782 | } |
| 783 | + // 服务端过账快照成本 → 与「成本单价」展示一致 | |
| 784 | + if (item.cbdj != null && item.cbdj !== '') { | |
| 785 | + _this.$set(item, 'dj', Number(item.cbdj)) | |
| 786 | + } | |
| 787 | + if (item.bjhcb != null && item.bjhcb !== '') { | |
| 788 | + _this.$set(item, 'bjhcb', Number(item.bjhcb).toFixed(4)) | |
| 789 | + } | |
| 676 | 790 | }); |
| 677 | 791 | } |
| 678 | 792 | |
| ... | ... | @@ -700,9 +814,7 @@ |
| 700 | 814 | this.$refs['elForm'].validate(async (valid) => { |
| 701 | 815 | if (valid) { |
| 702 | 816 | try { |
| 703 | - // 标记为草稿 | |
| 704 | - this.dataForm.djzt = '草稿'; | |
| 705 | - const draftData = { ...this.dataForm, isDraft: true, djzt: '草稿' }; | |
| 817 | + const draftData = this.buildPayload(true); | |
| 706 | 818 | let res; |
| 707 | 819 | if (!this.dataForm.id) { |
| 708 | 820 | res = await request({ |
| ... | ... | @@ -734,6 +846,11 @@ |
| 734 | 846 | }); |
| 735 | 847 | }, |
| 736 | 848 | async dataFormSubmit() { |
| 849 | + const basicErr = this.validateBjdFormalBasics() | |
| 850 | + if (basicErr) { | |
| 851 | + this.$message.error(basicErr) | |
| 852 | + return | |
| 853 | + } | |
| 737 | 854 | // 1. 明细校验:序列号数量与销售数量一致性 |
| 738 | 855 | let validationErrors = []; |
| 739 | 856 | for (let i = 0; i < this.dataForm.wtXsckdMxList.length; i++) { |
| ... | ... | @@ -878,7 +995,7 @@ |
| 878 | 995 | const res = await request({ |
| 879 | 996 | url: `/api/Extend/WtXsckd`, |
| 880 | 997 | method: 'post', |
| 881 | - data: this.dataForm, | |
| 998 | + data: this.buildPayload(false), | |
| 882 | 999 | }) |
| 883 | 1000 | |
| 884 | 1001 | // 更新序列号状态 |
| ... | ... | @@ -891,7 +1008,7 @@ |
| 891 | 1008 | const res = await request({ |
| 892 | 1009 | url: '/api/Extend/WtXsckd/' + this.dataForm.id, |
| 893 | 1010 | method: 'PUT', |
| 894 | - data: this.dataForm | |
| 1011 | + data: this.buildPayload(false) | |
| 895 | 1012 | }) |
| 896 | 1013 | |
| 897 | 1014 | // 更新序列号状态 |
| ... | ... | @@ -947,7 +1064,7 @@ |
| 947 | 1064 | const res = await request({ |
| 948 | 1065 | url: `/api/Extend/WtXsckd`, |
| 949 | 1066 | method: 'post', |
| 950 | - data: this.dataForm, | |
| 1067 | + data: this.buildPayload(false), | |
| 951 | 1068 | }) |
| 952 | 1069 | |
| 953 | 1070 | // 更新序列号状态 |
| ... | ... | @@ -960,7 +1077,7 @@ |
| 960 | 1077 | const res = await request({ |
| 961 | 1078 | url: '/api/Extend/WtXsckd/' + this.dataForm.id, |
| 962 | 1079 | method: 'PUT', |
| 963 | - data: this.dataForm | |
| 1080 | + data: this.buildPayload(false) | |
| 964 | 1081 | }) |
| 965 | 1082 | |
| 966 | 1083 | // 更新序列号状态 |
| ... | ... | @@ -1109,7 +1226,7 @@ |
| 1109 | 1226 | handleMainWarehouseChange(value) { |
| 1110 | 1227 | // 同步更新所有明细行的出库仓库 |
| 1111 | 1228 | this.syncDetailWarehouses(); |
| 1112 | - // 更新所有明细行的库存 | |
| 1229 | + // 更新所有明细行的库存与成本参考价 | |
| 1113 | 1230 | this.dataForm.wtXsckdMxList.forEach(row => { |
| 1114 | 1231 | if (row.spbh) { |
| 1115 | 1232 | this.getStockQuantity(row); |
| ... | ... | @@ -1232,6 +1349,10 @@ |
| 1232 | 1349 | row.loadingStock = false; |
| 1233 | 1350 | console.log('=== 库存查询完成 ==='); |
| 1234 | 1351 | } |
| 1352 | + // 无论库存接口是否成功,都尝试拉取成本单价(与过账取价一致) | |
| 1353 | + if (row.spbh && row.ckck) { | |
| 1354 | + await this.applyOutboundCostPrice(row, true); | |
| 1355 | + } | |
| 1235 | 1356 | }, |
| 1236 | 1357 | |
| 1237 | 1358 | // 计算总金额 |
| ... | ... | @@ -1493,11 +1614,13 @@ |
| 1493 | 1614 | sums[index] = '合计'; |
| 1494 | 1615 | return; |
| 1495 | 1616 | } |
| 1496 | - if (['kucun', 'sl', 'je'].includes(column.property)) { | |
| 1617 | + if (['kucun', 'sl', 'je', 'cbje', 'dj', 'bjhcb'].includes(column.property)) { | |
| 1497 | 1618 | sums[index] = data.reduce((total, row) => { |
| 1498 | 1619 | const value = parseFloat(row[column.property]); |
| 1499 | 1620 | return total + (isNaN(value) ? 0 : value); |
| 1500 | 1621 | }, 0); |
| 1622 | + if (['cbje', 'dj', 'bjhcb'].includes(column.property) && sums[index] !== '') | |
| 1623 | + sums[index] = Number(sums[index]).toFixed(2) | |
| 1501 | 1624 | } else { |
| 1502 | 1625 | sums[index] = ''; |
| 1503 | 1626 | } |
| ... | ... | @@ -1509,7 +1632,7 @@ |
| 1509 | 1632 | const label = (option.label || '').toLowerCase(); |
| 1510 | 1633 | return label.includes(query.toLowerCase()); |
| 1511 | 1634 | }, |
| 1512 | - handleProductChange(row) { | |
| 1635 | + async handleProductChange(row) { | |
| 1513 | 1636 | // 选中商品后可自动回填商品名称等信息 |
| 1514 | 1637 | const product = this.spbhOptions.find(item => item.F_Id === row.spbh); |
| 1515 | 1638 | if (product) { |
| ... | ... | @@ -1540,14 +1663,9 @@ |
| 1540 | 1663 | return; |
| 1541 | 1664 | } |
| 1542 | 1665 | |
| 1543 | - // 如果已选择出库仓库,自动获取库存 | |
| 1666 | + // 如果已选择出库仓库,自动获取库存(内部会再拉成本单价) | |
| 1544 | 1667 | console.log('开始获取库存...'); |
| 1545 | - this.getStockQuantity(row); | |
| 1546 | - | |
| 1547 | - // 如果有变价系数,计算该行的变价后成本 | |
| 1548 | - if (this.dataForm.bjsx && this.dataForm.bjsx !== '') { | |
| 1549 | - this.calculateRowAdjustedCost(row); | |
| 1550 | - } | |
| 1668 | + await this.getStockQuantity(row); | |
| 1551 | 1669 | } |
| 1552 | 1670 | }, |
| 1553 | 1671 | handleProductQuery(val, scope) { |
| ... | ... | @@ -1567,7 +1685,10 @@ |
| 1567 | 1685 | }, |
| 1568 | 1686 | // 处理变价系数变化,重新计算所有明细的变价后成本 |
| 1569 | 1687 | handlePriceAdjustmentChange() { |
| 1570 | - this.calculateAllAdjustedCosts(); | |
| 1688 | + this.$nextTick(() => { | |
| 1689 | + this.calculateAllAdjustedCosts(); | |
| 1690 | + this.$forceUpdate(); | |
| 1691 | + }); | |
| 1571 | 1692 | }, |
| 1572 | 1693 | |
| 1573 | 1694 | // 计算所有明细的变价后成本 |
| ... | ... | @@ -1575,7 +1696,7 @@ |
| 1575 | 1696 | if (!this.dataForm.bjsx || this.dataForm.bjsx === '') { |
| 1576 | 1697 | // 如果没有输入变价系数,清空所有变价后成本 |
| 1577 | 1698 | this.dataForm.wtXsckdMxList.forEach(row => { |
| 1578 | - row.bjhcb = ''; | |
| 1699 | + this.$set(row, 'bjhcb', ''); | |
| 1579 | 1700 | }); |
| 1580 | 1701 | return; |
| 1581 | 1702 | } |
| ... | ... | @@ -1593,24 +1714,25 @@ |
| 1593 | 1714 | // 例如:成本100,系数95,则变价后成本 = 100 / (95/100) = 100 / 0.95 = 105.26 |
| 1594 | 1715 | |
| 1595 | 1716 | this.dataForm.wtXsckdMxList.forEach(row => { |
| 1596 | - if (row.dj && row.dj !== '') { | |
| 1717 | + if (row.dj !== undefined && row.dj !== null && row.dj !== '') { | |
| 1597 | 1718 | const cost = parseFloat(row.dj); |
| 1598 | 1719 | if (!isNaN(cost) && cost > 0) { |
| 1599 | 1720 | const adjustedCost = cost / (coefficient / 100); |
| 1600 | - row.bjhcb = adjustedCost.toFixed(2); | |
| 1721 | + this.$set(row, 'bjhcb', adjustedCost.toFixed(4)); | |
| 1601 | 1722 | } else { |
| 1602 | - row.bjhcb = ''; | |
| 1723 | + this.$set(row, 'bjhcb', ''); | |
| 1603 | 1724 | } |
| 1604 | 1725 | } else { |
| 1605 | - row.bjhcb = ''; | |
| 1726 | + this.$set(row, 'bjhcb', ''); | |
| 1606 | 1727 | } |
| 1607 | 1728 | }); |
| 1729 | + this.$forceUpdate(); | |
| 1608 | 1730 | }, |
| 1609 | 1731 | |
| 1610 | 1732 | // 计算单行的变价后成本 |
| 1611 | 1733 | calculateRowAdjustedCost(row) { |
| 1612 | 1734 | if (!this.dataForm.bjsx || this.dataForm.bjsx === '' || !row.dj || row.dj === '') { |
| 1613 | - row.bjhcb = ''; | |
| 1735 | + this.$set(row, 'bjhcb', ''); | |
| 1614 | 1736 | return; |
| 1615 | 1737 | } |
| 1616 | 1738 | |
| ... | ... | @@ -1618,17 +1740,15 @@ |
| 1618 | 1740 | const cost = parseFloat(row.dj); |
| 1619 | 1741 | |
| 1620 | 1742 | if (isNaN(coefficient) || isNaN(cost) || cost <= 0) { |
| 1621 | - row.bjhcb = ''; | |
| 1743 | + this.$set(row, 'bjhcb', ''); | |
| 1622 | 1744 | return; |
| 1623 | 1745 | } |
| 1624 | 1746 | |
| 1625 | 1747 | // 按照公式计算:变价后成本 = 成本 / (系数/100) |
| 1626 | 1748 | // 例如:成本100,系数95,则变价后成本 = 100 / (95/100) = 100 / 0.95 = 105.26 |
| 1627 | 1749 | const adjustedCost = cost / (coefficient / 100); |
| 1628 | - row.bjhcb = adjustedCost.toFixed(2); | |
| 1629 | - }, | |
| 1630 | - | |
| 1631 | - // 处理变价系数变化,重新计算所有明细的变价后成本 | |
| 1750 | + this.$set(row, 'bjhcb', adjustedCost.toFixed(4)); | |
| 1751 | + } | |
| 1632 | 1752 | } |
| 1633 | 1753 | } |
| 1634 | 1754 | </script> | ... | ... |
Antis.Erp.Plat/antis-ncc-admin/src/views/wtBjdbd/index.vue
| 1 | -<template> | |
| 1 | +<template> | |
| 2 | 2 | <div class="NCC-common-layout"> |
| 3 | 3 | <div class="NCC-common-layout-center"> |
| 4 | 4 | <el-row class="NCC-common-search-box" :gutter="16"> |
| ... | ... | @@ -106,10 +106,30 @@ |
| 106 | 106 | </div> |
| 107 | 107 | </div> |
| 108 | 108 | <NCC-table v-loading="listLoading" :data="list" has-c @selection-change="handleSelectionChange"> |
| 109 | - <el-table-column prop="id" label="单据编号" align="left" /> | |
| 109 | + <el-table-column prop="id" label="单据编号" align="left" width="160"> | |
| 110 | + <template slot-scope="scope"> | |
| 111 | + <i class="el-icon-document" style="color:#409EFF;margin-right:4px" /> | |
| 112 | + {{ scope.row.id || '无' }} | |
| 113 | + </template> | |
| 114 | + </el-table-column> | |
| 110 | 115 | <el-table-column prop="djrq" label="单据日期" align="left" :formatter="ncc.tableDateFormat" /> |
| 111 | - <el-table-column label="出库仓库" prop="cjck" align="left"> | |
| 112 | - <template slot-scope="scope">{{ scope.row.cjck | dynamicText(cjckOptions) }}</template> | |
| 116 | + <el-table-column label="出库仓库" prop="cjck" align="left" min-width="100" show-overflow-tooltip> | |
| 117 | + <template slot-scope="scope"> | |
| 118 | + <i class="el-icon-office-building" style="color:#909399;margin-right:4px" /> | |
| 119 | + {{ scope.row.cjck | dynamicText(cjckOptions) }} | |
| 120 | + </template> | |
| 121 | + </el-table-column> | |
| 122 | + <el-table-column prop="bjsx" label="变价系数%" align="right" width="100"> | |
| 123 | + <template slot-scope="scope"> | |
| 124 | + <i class="el-icon-sort" style="color:#E6A23C;margin-right:4px" /> | |
| 125 | + {{ scope.row.bjsx != null && scope.row.bjsx !== '' ? scope.row.bjsx + '%' : '无' }} | |
| 126 | + </template> | |
| 127 | + </el-table-column> | |
| 128 | + <el-table-column prop="cbje" label="变价成本合计" align="right" width="120"> | |
| 129 | + <template slot-scope="scope"> | |
| 130 | + <i class="el-icon-coin" style="color:#67C23A;margin-right:4px" /> | |
| 131 | + {{ scope.row.cbje != null && scope.row.cbje !== '' ? scope.row.cbje : '无' }} | |
| 132 | + </template> | |
| 113 | 133 | </el-table-column> |
| 114 | 134 | <el-table-column prop="jsr" label="经手人" align="left" /> |
| 115 | 135 | <el-table-column prop="ysje" label="优惠金额" align="left" /> |
| ... | ... | @@ -122,7 +142,14 @@ |
| 122 | 142 | <el-table-column prop="gzr" label="过账人" align="left" /> |
| 123 | 143 | <el-table-column prop="bz" label="备注" align="left" /> |
| 124 | 144 | <el-table-column prop="djlx" label="单据类型" align="left" /> |
| 125 | - <el-table-column prop="djzt" label="单据状态" align="left" /> | |
| 145 | + <el-table-column prop="djzt" label="单据状态" align="left" width="100"> | |
| 146 | + <template slot-scope="scope"> | |
| 147 | + <el-tag v-if="scope.row.djzt === '草稿'" type="info" size="mini">草稿</el-tag> | |
| 148 | + <el-tag v-else-if="scope.row.djzt === '待审核'" type="warning" size="mini">待审核</el-tag> | |
| 149 | + <el-tag v-else-if="scope.row.djzt === '已审核'" type="success" size="mini">已审核</el-tag> | |
| 150 | + <el-tag v-else type="warning" size="mini">{{ scope.row.djzt || '无' }}</el-tag> | |
| 151 | + </template> | |
| 152 | + </el-table-column> | |
| 126 | 153 | <el-table-column label="操作" fixed="right" width="100"> |
| 127 | 154 | <template slot-scope="scope"> |
| 128 | 155 | <el-button type="text" @click="addOrUpdateHandle(scope.row.id)" >编辑</el-button> |
| ... | ... | @@ -174,10 +201,12 @@ |
| 174 | 201 | }, |
| 175 | 202 | formVisible: false, |
| 176 | 203 | exportBoxVisible: false, |
| 177 | - columnList: [ | |
| 204 | + columnList: [ | |
| 178 | 205 | { prop: 'id', label: '单据编号' }, |
| 179 | 206 | { prop: 'djrq', label: '单据日期' }, |
| 180 | 207 | { prop: 'cjck', label: '出库仓库' }, |
| 208 | + { prop: 'bjsx', label: '变价系数%' }, | |
| 209 | + { prop: 'cbje', label: '变价成本合计' }, | |
| 181 | 210 | { prop: 'jsr', label: '经手人' }, |
| 182 | 211 | { prop: 'ysje', label: '优惠金额' }, |
| 183 | 212 | { prop: 'skzh', label: '收款账户' }, |
| ... | ... | @@ -187,6 +216,7 @@ |
| 187 | 216 | { prop: 'gzr', label: '过账人' }, |
| 188 | 217 | { prop: 'bz', label: '备注' }, |
| 189 | 218 | { prop: 'djlx', label: '单据类型' }, |
| 219 | + { prop: 'djzt', label: '单据状态' }, | |
| 190 | 220 | ], |
| 191 | 221 | cjckOptions : [], |
| 192 | 222 | rkckOptions : [], |
| ... | ... | @@ -335,6 +365,7 @@ |
| 335 | 365 | for (let key in this.query) { |
| 336 | 366 | this.query[key] = undefined |
| 337 | 367 | } |
| 368 | + this.query.djlx = '变价调拨单' | |
| 338 | 369 | this.listQuery = { |
| 339 | 370 | currentPage: 1, |
| 340 | 371 | pageSize: 20, | ... | ... |
Antis.Erp.Plat/antis-ncc-admin/src/views/wtXsckd/Form.vue
| ... | ... | @@ -83,7 +83,7 @@ |
| 83 | 83 | <el-option |
| 84 | 84 | v-for="item in getFilteredSpbhOptions(scope.row.productQuery)" |
| 85 | 85 | :key="item.F_Id" |
| 86 | - :label="item.spbm + ' ' + (item.F_Spmc || '')" | |
| 86 | + :label="getSpOptionLabel(item)" | |
| 87 | 87 | :value="item.F_Id" |
| 88 | 88 | /> |
| 89 | 89 | </el-select> |
| ... | ... | @@ -147,6 +147,16 @@ |
| 147 | 147 | <el-input v-model="scope.row.je" placeholder="请输入" clearable readonly></el-input> |
| 148 | 148 | </template> |
| 149 | 149 | </el-table-column> |
| 150 | + <el-table-column prop="cbdj" label="成本单价" width="100" align="right"> | |
| 151 | + <template slot-scope="scope"> | |
| 152 | + <span class="cost-cell">{{ formatCostCell(scope.row.cbdj) }}</span> | |
| 153 | + </template> | |
| 154 | + </el-table-column> | |
| 155 | + <el-table-column prop="cbje" label="成本金额" width="100" align="right"> | |
| 156 | + <template slot-scope="scope"> | |
| 157 | + <span class="cost-cell">{{ formatCostCell(scope.row.cbje) }}</span> | |
| 158 | + </template> | |
| 159 | + </el-table-column> | |
| 150 | 160 | <el-table-column prop="description" label="备注"> |
| 151 | 161 | <template slot-scope="scope"> |
| 152 | 162 | <el-input v-model="scope.row.description" placeholder="请输入备注" clearable></el-input> |
| ... | ... | @@ -218,6 +228,11 @@ |
| 218 | 228 | </el-form-item> |
| 219 | 229 | </el-col> |
| 220 | 230 | <el-col :span="8"> |
| 231 | + <el-form-item label="出库成本合计"> | |
| 232 | + <el-input :value="displayOutboundCostTotalText" placeholder="按明细成本汇总" readonly :style='{"width":"100%"}' /> | |
| 233 | + </el-form-item> | |
| 234 | + </el-col> | |
| 235 | + <el-col :span="8"> | |
| 221 | 236 | <el-form-item label="收款金额" prop="skje"> |
| 222 | 237 | <el-input v-model="dataForm.skje" placeholder="自动计算" clearable :style='{"width":"100%"}' readonly> |
| 223 | 238 | </el-input> |
| ... | ... | @@ -295,6 +310,7 @@ |
| 295 | 310 | id:'', |
| 296 | 311 | djrq:undefined, |
| 297 | 312 | cjck:undefined, |
| 313 | + cjckId: undefined, | |
| 298 | 314 | rkck:undefined, |
| 299 | 315 | jsr:undefined, |
| 300 | 316 | kh:undefined, |
| ... | ... | @@ -312,9 +328,12 @@ |
| 312 | 328 | bz:undefined, |
| 313 | 329 | djlx:undefined, |
| 314 | 330 | djzt: '', // 单据状态 |
| 331 | + cbje: undefined, // 主表出库成本合计(详情/已保存单据由后端返回) | |
| 315 | 332 | }, |
| 316 | 333 | rules: { |
| 317 | 334 | }, |
| 335 | + // 当前出库门店下各商品成本预览(下拉展示用,key=商品F_Id) | |
| 336 | + productCostPreviewMap: {}, | |
| 318 | 337 | cjckOptions : [], |
| 319 | 338 | rkckOptions : [], |
| 320 | 339 | khOptions : [], |
| ... | ... | @@ -338,6 +357,18 @@ |
| 338 | 357 | totalJe() { |
| 339 | 358 | return (this.dataForm.wtXsckdMxList || []).reduce((sum, row) => sum + (parseFloat(row.je) || 0), 0) |
| 340 | 359 | }, |
| 360 | + // 详情页优先用主表 cbje;新建/编辑按明细成本金额汇总 | |
| 361 | + displayOutboundCostTotal() { | |
| 362 | + if (this.isDetail && this.dataForm.cbje != null && this.dataForm.cbje !== '') { | |
| 363 | + const m = parseFloat(this.dataForm.cbje) | |
| 364 | + if (!isNaN(m)) return m | |
| 365 | + } | |
| 366 | + return (this.dataForm.wtXsckdMxList || []).reduce((s, r) => s + (parseFloat(r.cbje) || 0), 0) | |
| 367 | + }, | |
| 368 | + displayOutboundCostTotalText() { | |
| 369 | + const v = this.displayOutboundCostTotal | |
| 370 | + return (typeof v === 'number' && !isNaN(v)) ? v.toFixed(2) : '0.00' | |
| 371 | + }, | |
| 341 | 372 | |
| 342 | 373 | }, |
| 343 | 374 | watch: {}, |
| ... | ... | @@ -633,6 +664,13 @@ |
| 633 | 664 | method: 'get' |
| 634 | 665 | }).then(res =>{ |
| 635 | 666 | _this.dataForm = res.data; |
| 667 | + // 详情接口 cjck 可能为门店名称,下拉需绑定原始ID | |
| 668 | + if (res.data.cjckId) { | |
| 669 | + _this.dataForm.cjck = res.data.cjckId; | |
| 670 | + _this.dataForm.cjckId = res.data.cjckId; | |
| 671 | + } else if (res.data.cjck) { | |
| 672 | + _this.dataForm.cjckId = res.data.cjck; | |
| 673 | + } | |
| 636 | 674 | console.log('编辑时加载的数据:', _this.dataForm); |
| 637 | 675 | console.log('明细数据:', _this.dataForm.wtXsckdMxList); |
| 638 | 676 | |
| ... | ... | @@ -672,6 +710,7 @@ |
| 672 | 710 | _this.restoreSerialNumbers(); |
| 673 | 711 | // 编辑时自动获取所有明细行的账面库存 |
| 674 | 712 | _this.$nextTick(() => { |
| 713 | + _this.loadProductCostPreviewMap(); | |
| 675 | 714 | _this.dataForm.wtXsckdMxList.forEach(row => { |
| 676 | 715 | if (row.spbh && row.ckck) { |
| 677 | 716 | _this.getStockQuantity(row); |
| ... | ... | @@ -684,10 +723,14 @@ |
| 684 | 723 | // 新建时加载默认选项设置 |
| 685 | 724 | request({ url: '/api/Extend/WtMrsz', method: 'get' }).then(res => { |
| 686 | 725 | if (res.data) { |
| 687 | - if (res.data.mrck) _this.dataForm.cjck = res.data.mrck | |
| 726 | + if (res.data.mrck) { | |
| 727 | + _this.dataForm.cjck = res.data.mrck | |
| 728 | + _this.dataForm.cjckId = res.data.mrck | |
| 729 | + } | |
| 688 | 730 | if (res.data.mrwldw) _this.dataForm.kh = res.data.mrwldw |
| 689 | 731 | if (res.data.mrskzh) _this.dataForm.skzh = res.data.mrskzh |
| 690 | 732 | } |
| 733 | + _this.loadProductCostPreviewMap(); | |
| 691 | 734 | }).catch(() => {}) |
| 692 | 735 | } |
| 693 | 736 | }) |
| ... | ... | @@ -1030,6 +1073,8 @@ |
| 1030 | 1073 | sl:undefined, |
| 1031 | 1074 | dj:undefined, |
| 1032 | 1075 | je:undefined, |
| 1076 | + cbdj: undefined, | |
| 1077 | + cbje: undefined, | |
| 1033 | 1078 | selectedSerialNumbers: [], // 添加序列号数组 |
| 1034 | 1079 | loadingStock: false, // 库存加载状态 |
| 1035 | 1080 | productQuery: '', // 为每一行添加独立的查询条件 |
| ... | ... | @@ -1050,7 +1095,10 @@ |
| 1050 | 1095 | this.currentBarcodeRow.sptm = barcodeData.barcode |
| 1051 | 1096 | this.currentBarcodeRow.spmc = barcodeData.productName |
| 1052 | 1097 | this.currentBarcodeRow.spbh = barcodeData.productCode |
| 1053 | - // 可根据需要回填其他字段 | |
| 1098 | + if (!this.currentBarcodeRow.ckck && this.dataForm.cjck) { | |
| 1099 | + this.currentBarcodeRow.ckck = this.dataForm.cjck | |
| 1100 | + } | |
| 1101 | + this.fetchRowCost(this.currentBarcodeRow) | |
| 1054 | 1102 | } |
| 1055 | 1103 | }, |
| 1056 | 1104 | |
| ... | ... | @@ -1087,6 +1135,7 @@ |
| 1087 | 1135 | if (currentRow.dj) { |
| 1088 | 1136 | currentRow.je = (parseFloat(currentRow.sl) * parseFloat(currentRow.dj)).toFixed(2) |
| 1089 | 1137 | } |
| 1138 | + this.updateRowCostAmount(currentRow) | |
| 1090 | 1139 | // 更新总收款金额 |
| 1091 | 1140 | this.calculateTotalAmount(); |
| 1092 | 1141 | |
| ... | ... | @@ -1097,6 +1146,7 @@ |
| 1097 | 1146 | const sl = parseFloat(row.sl) || 0; |
| 1098 | 1147 | const dj = parseFloat(row.dj) || 0; |
| 1099 | 1148 | row.je = (sl * dj).toFixed(2); |
| 1149 | + this.updateRowCostAmount(row) | |
| 1100 | 1150 | // 自动计算总收款金额 |
| 1101 | 1151 | this.calculateTotalAmount(); |
| 1102 | 1152 | // 检查数量与序列号数量是否一致 |
| ... | ... | @@ -1113,12 +1163,17 @@ |
| 1113 | 1163 | |
| 1114 | 1164 | // 处理主表出库仓库变化 |
| 1115 | 1165 | handleMainWarehouseChange(value) { |
| 1166 | + if (value != null && value !== '') { | |
| 1167 | + this.dataForm.cjckId = value | |
| 1168 | + } | |
| 1116 | 1169 | // 同步更新所有明细行的出库仓库 |
| 1117 | 1170 | this.syncDetailWarehouses(); |
| 1118 | - // 更新所有明细行的库存 | |
| 1171 | + this.loadProductCostPreviewMap() | |
| 1172 | + // 更新所有明细行的库存与成本预览 | |
| 1119 | 1173 | this.dataForm.wtXsckdMxList.forEach(row => { |
| 1120 | 1174 | if (row.spbh) { |
| 1121 | 1175 | this.getStockQuantity(row); |
| 1176 | + this.fetchRowCost(row); | |
| 1122 | 1177 | } |
| 1123 | 1178 | }); |
| 1124 | 1179 | }, |
| ... | ... | @@ -1147,7 +1202,7 @@ |
| 1147 | 1202 | try { |
| 1148 | 1203 | // 确保参数不为空且转换为字符串 |
| 1149 | 1204 | const productId = String(row.spbh || ''); |
| 1150 | - const warehouseId = String(row.ckck || ''); | |
| 1205 | + const warehouseId = String(row.ckck || this.dataForm.cjckId || this.dataForm.cjck || ''); | |
| 1151 | 1206 | |
| 1152 | 1207 | // 构建URL参数 |
| 1153 | 1208 | const url = `/api/Extend/WtXsckd/GetStockQuantity?productId=${encodeURIComponent(productId)}&warehouseId=${encodeURIComponent(warehouseId)}`; |
| ... | ... | @@ -1487,11 +1542,12 @@ |
| 1487 | 1542 | sums[index] = '合计'; |
| 1488 | 1543 | return; |
| 1489 | 1544 | } |
| 1490 | - if (['kucun', 'sl', 'je'].includes(column.property)) { | |
| 1491 | - sums[index] = data.reduce((total, row) => { | |
| 1545 | + if (['kucun', 'sl', 'je', 'cbje'].includes(column.property)) { | |
| 1546 | + const t = data.reduce((total, row) => { | |
| 1492 | 1547 | const value = parseFloat(row[column.property]); |
| 1493 | 1548 | return total + (isNaN(value) ? 0 : value); |
| 1494 | 1549 | }, 0); |
| 1550 | + sums[index] = (column.property === 'cbje' || column.property === 'je') ? t.toFixed(2) : t; | |
| 1495 | 1551 | } else { |
| 1496 | 1552 | sums[index] = ''; |
| 1497 | 1553 | } |
| ... | ... | @@ -1503,7 +1559,93 @@ |
| 1503 | 1559 | const label = (option.label || '').toLowerCase(); |
| 1504 | 1560 | return label.includes(query.toLowerCase()); |
| 1505 | 1561 | }, |
| 1562 | + // 成本/库存接口使用的门店或仓库ID | |
| 1563 | + costContextCk() { | |
| 1564 | + if (this.dataForm.cjckId != null && this.dataForm.cjckId !== '') { | |
| 1565 | + return this.dataForm.cjckId | |
| 1566 | + } | |
| 1567 | + return this.dataForm.cjck | |
| 1568 | + }, | |
| 1569 | + formatCostCell(val) { | |
| 1570 | + if (val == null || val === '') return '无' | |
| 1571 | + const n = parseFloat(val) | |
| 1572 | + if (isNaN(n)) return '无' | |
| 1573 | + return n.toFixed(2) | |
| 1574 | + }, | |
| 1575 | + getSpOptionLabel(item) { | |
| 1576 | + const base = ((item.spbm || '') + ' ' + (item.F_Spmc || '')).trim() | |
| 1577 | + const c = this.productCostPreviewMap[item.F_Id] | |
| 1578 | + if (c != null && c !== '' && !isNaN(parseFloat(c))) { | |
| 1579 | + return base + ' (成本¥' + parseFloat(c).toFixed(2) + ')' | |
| 1580 | + } | |
| 1581 | + if (!this.costContextCk()) { | |
| 1582 | + return base + ' (选仓库后显示成本)' | |
| 1583 | + } | |
| 1584 | + return base | |
| 1585 | + }, | |
| 1586 | + async loadProductCostPreviewMap() { | |
| 1587 | + const ck = this.costContextCk() | |
| 1588 | + if (!ck) { | |
| 1589 | + this.productCostPreviewMap = {} | |
| 1590 | + return | |
| 1591 | + } | |
| 1592 | + try { | |
| 1593 | + const res = await request({ | |
| 1594 | + url: '/api/Extend/WtTjd/Actions/GetCostPreviewForStore', | |
| 1595 | + method: 'get', | |
| 1596 | + data: { ck } | |
| 1597 | + }) | |
| 1598 | + let items = res.data | |
| 1599 | + if (items && items.items) items = items.items | |
| 1600 | + else if (res.items) items = res.items | |
| 1601 | + const map = {} | |
| 1602 | + ;(Array.isArray(items) ? items : []).forEach(x => { | |
| 1603 | + if (x && x.spbh != null && map[x.spbh] == null) { | |
| 1604 | + map[x.spbh] = parseFloat(x.cbj) || 0 | |
| 1605 | + } | |
| 1606 | + }) | |
| 1607 | + this.productCostPreviewMap = map | |
| 1608 | + } catch (e) { | |
| 1609 | + console.warn('loadProductCostPreviewMap failed', e) | |
| 1610 | + this.productCostPreviewMap = {} | |
| 1611 | + } | |
| 1612 | + }, | |
| 1613 | + async fetchRowCost(row) { | |
| 1614 | + const ck = row.ckck || this.costContextCk() | |
| 1615 | + if (!row.spbh || !ck) { | |
| 1616 | + this.$set(row, 'cbdj', undefined) | |
| 1617 | + this.$set(row, 'cbje', undefined) | |
| 1618 | + return | |
| 1619 | + } | |
| 1620 | + try { | |
| 1621 | + const res = await request({ | |
| 1622 | + url: '/api/Extend/WtTjd/Actions/GetCost', | |
| 1623 | + method: 'get', | |
| 1624 | + data: { spbh: row.spbh, ck } | |
| 1625 | + }) | |
| 1626 | + let payload = res.data | |
| 1627 | + if (payload && typeof payload.cbj === 'undefined' && res.data && res.data.data) { | |
| 1628 | + payload = res.data.data | |
| 1629 | + } | |
| 1630 | + const cbj = parseFloat(payload && payload.cbj != null ? payload.cbj : 0) || 0 | |
| 1631 | + this.$set(row, 'cbdj', cbj) | |
| 1632 | + this.updateRowCostAmount(row) | |
| 1633 | + } catch (e) { | |
| 1634 | + this.$set(row, 'cbdj', 0) | |
| 1635 | + this.updateRowCostAmount(row) | |
| 1636 | + } | |
| 1637 | + }, | |
| 1638 | + updateRowCostAmount(row) { | |
| 1639 | + const sl = parseFloat(row.sl) || 0 | |
| 1640 | + const cbdj = parseFloat(row.cbdj) || 0 | |
| 1641 | + this.$set(row, 'cbje', (cbdj * sl).toFixed(2)) | |
| 1642 | + }, | |
| 1506 | 1643 | handleProductChange(row) { |
| 1644 | + if (!row.spbh) { | |
| 1645 | + this.$set(row, 'cbdj', undefined) | |
| 1646 | + this.$set(row, 'cbje', undefined) | |
| 1647 | + return | |
| 1648 | + } | |
| 1507 | 1649 | // 选中商品后可自动回填商品名称等信息 |
| 1508 | 1650 | const product = this.spbhOptions.find(item => item.F_Id === row.spbh); |
| 1509 | 1651 | if (product) { |
| ... | ... | @@ -1536,9 +1678,11 @@ |
| 1536 | 1678 | return; |
| 1537 | 1679 | } |
| 1538 | 1680 | |
| 1539 | - // 如果已选择出库仓库,自动获取库存 | |
| 1681 | + // 如果已选择出库仓库,自动获取库存与成本 | |
| 1540 | 1682 | console.log('开始获取库存...'); |
| 1541 | - this.getStockQuantity(row); | |
| 1683 | + this.getStockQuantity(row).finally(() => { | |
| 1684 | + this.fetchRowCost(row) | |
| 1685 | + }); | |
| 1542 | 1686 | } |
| 1543 | 1687 | }, |
| 1544 | 1688 | handleProductQuery(val, scope) { | ... | ... |
Antis.Erp.Plat/antis-ncc-admin/src/views/wtXsckd/index.vue
| ... | ... | @@ -127,6 +127,9 @@ |
| 127 | 127 | <el-table-column label="优惠金额" align="left" min-width="90"> |
| 128 | 128 | <template slot-scope="scope">{{ getDisplayDiscountAmount(scope.row) }}</template> |
| 129 | 129 | </el-table-column> |
| 130 | + <el-table-column label="出库成本" align="left" min-width="90"> | |
| 131 | + <template slot-scope="scope">{{ getDisplayOutboundCost(scope.row) }}</template> | |
| 132 | + </el-table-column> | |
| 130 | 133 | <el-table-column label="收款账户" prop="skzh" align="left"> |
| 131 | 134 | <template slot-scope="scope">{{ scope.row.skzh | dynamicText(skzhOptions) }}</template> |
| 132 | 135 | </el-table-column> |
| ... | ... | @@ -259,6 +262,11 @@ |
| 259 | 262 | } |
| 260 | 263 | return (row.skje != null && row.skje !== '') ? parseFloat(row.skje).toFixed(2) : '0.00'; |
| 261 | 264 | }, |
| 265 | + // ✅ 出库成本:使用主表字段 cbje,空值按金额风格显示为 0.00 | |
| 266 | + getDisplayOutboundCost(row) { | |
| 267 | + const val = parseFloat(row.cbje); | |
| 268 | + return isNaN(val) ? '0.00' : val.toFixed(2); | |
| 269 | + }, | |
| 262 | 270 | getcjckOptions(){ |
| 263 | 271 | previewDataInterface('681758216954053893').then(res => { |
| 264 | 272 | this.cjckOptions = res.data | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtXsckdCrInput.cs
| ... | ... | @@ -39,6 +39,21 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd |
| 39 | 39 | public decimal? ydje { get; set; } |
| 40 | 40 | |
| 41 | 41 | /// <summary> |
| 42 | + /// 成本金额(主表汇总) | |
| 43 | + /// </summary> | |
| 44 | + public decimal? cbje { get; set; } | |
| 45 | + | |
| 46 | + /// <summary> | |
| 47 | + /// 变价系数%(变价调拨单) | |
| 48 | + /// </summary> | |
| 49 | + public decimal? bjsx { get; set; } | |
| 50 | + | |
| 51 | + /// <summary> | |
| 52 | + /// 是否草稿保存(草稿不触发变价调拨成本过账) | |
| 53 | + /// </summary> | |
| 54 | + public bool isDraft { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 42 | 57 | /// 优惠金额 |
| 43 | 58 | /// </summary> |
| 44 | 59 | public decimal ysje { get; set; } | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtXsckdInfoOutput.cs
| ... | ... | @@ -24,6 +24,11 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd |
| 24 | 24 | public string cjck { get; set; } |
| 25 | 25 | |
| 26 | 26 | /// <summary> |
| 27 | + /// 出库仓库/门店原始ID(cjck 可能为展示名称,成本与库存接口请用此字段) | |
| 28 | + /// </summary> | |
| 29 | + public string cjckId { get; set; } | |
| 30 | + | |
| 31 | + /// <summary> | |
| 27 | 32 | /// 入库仓库 |
| 28 | 33 | /// </summary> |
| 29 | 34 | public string rkck { get; set; } |
| ... | ... | @@ -39,6 +44,16 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd |
| 39 | 44 | public decimal? ydje { get; set; } |
| 40 | 45 | |
| 41 | 46 | /// <summary> |
| 47 | + /// 出库成本合计(主表汇总) | |
| 48 | + /// </summary> | |
| 49 | + public decimal? cbje { get; set; } | |
| 50 | + | |
| 51 | + /// <summary> | |
| 52 | + /// 变价系数%(变价调拨单) | |
| 53 | + /// </summary> | |
| 54 | + public decimal? bjsx { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 42 | 57 | /// 优惠金额 |
| 43 | 58 | /// </summary> |
| 44 | 59 | public decimal ysje { get; set; } | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtXsckdListOutput.cs
| ... | ... | @@ -33,6 +33,16 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd |
| 33 | 33 | public decimal? ydje { get; set; } |
| 34 | 34 | |
| 35 | 35 | /// <summary> |
| 36 | + /// 成本金额(主表汇总) | |
| 37 | + /// </summary> | |
| 38 | + public decimal? cbje { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 变价系数%(变价调拨单) | |
| 42 | + /// </summary> | |
| 43 | + public decimal? bjsx { get; set; } | |
| 44 | + | |
| 45 | + /// <summary> | |
| 36 | 46 | /// 优惠金额 |
| 37 | 47 | /// </summary> |
| 38 | 48 | public decimal ysje { get; set; } | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtXsckdMxCrInput.cs
| ... | ... | @@ -64,6 +64,11 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd |
| 64 | 64 | public decimal je { get; set; } |
| 65 | 65 | |
| 66 | 66 | /// <summary> |
| 67 | + /// 变价后成本单价(变价调拨单,服务端会按系数重算覆盖) | |
| 68 | + /// </summary> | |
| 69 | + public decimal? bjhcb { get; set; } | |
| 70 | + | |
| 71 | + /// <summary> | |
| 67 | 72 | /// 单据类型 |
| 68 | 73 | /// </summary> |
| 69 | 74 | public string djlx { get; set; } | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtXsckdMxInfoOutput.cs
| ... | ... | @@ -67,6 +67,22 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd |
| 67 | 67 | /// 金额 |
| 68 | 68 | /// </summary> |
| 69 | 69 | public decimal je { get; set; } |
| 70 | + | |
| 71 | + /// <summary> | |
| 72 | + /// 成本单价(出库快照) | |
| 73 | + /// </summary> | |
| 74 | + public decimal? cbdj { get; set; } | |
| 75 | + | |
| 76 | + /// <summary> | |
| 77 | + /// 成本金额(出库快照) | |
| 78 | + /// </summary> | |
| 79 | + public decimal? cbje { get; set; } | |
| 80 | + | |
| 81 | + /// <summary> | |
| 82 | + /// 变价后成本单价(变价调拨单) | |
| 83 | + /// </summary> | |
| 84 | + public decimal? bjhcb { get; set; } | |
| 85 | + | |
| 70 | 86 | /// <summary> |
| 71 | 87 | /// 单据类型 |
| 72 | 88 | /// </summary> | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtXsckdEntity.cs
| ... | ... | @@ -48,6 +48,18 @@ namespace NCC.Extend.Entitys |
| 48 | 48 | public decimal? Ydje { get; set; } |
| 49 | 49 | |
| 50 | 50 | /// <summary> |
| 51 | + /// 成本金额(主表汇总) | |
| 52 | + /// </summary> | |
| 53 | + [SugarColumn(ColumnName = "cbje", IsNullable = true)] | |
| 54 | + public decimal? Cbje { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 变价系数%(变价调拨单:统一应用到所有明细,变价后单价=原成本/(系数/100)) | |
| 58 | + /// </summary> | |
| 59 | + [SugarColumn(ColumnName = "bjsx", IsNullable = true)] | |
| 60 | + public decimal? Bjsx { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 51 | 63 | /// 优惠金额 |
| 52 | 64 | /// </summary> |
| 53 | 65 | [SugarColumn(ColumnName = "ysje")] | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtXsckdMxEntity.cs
| ... | ... | @@ -107,5 +107,11 @@ namespace NCC.Extend.Entitys |
| 107 | 107 | [SugarColumn(ColumnName = "cbje")] |
| 108 | 108 | public decimal? Cbje { get; set; } |
| 109 | 109 | |
| 110 | + /// <summary> | |
| 111 | + /// 变价后成本单价(变价调拨单入库计价) | |
| 112 | + /// </summary> | |
| 113 | + [SugarColumn(ColumnName = "bjhcb", IsNullable = true)] | |
| 114 | + public decimal? Bjhcb { get; set; } | |
| 115 | + | |
| 110 | 116 | } |
| 111 | 117 | } | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtTjdService.cs
| ... | ... | @@ -426,18 +426,79 @@ namespace NCC.Extend.WtTjd |
| 426 | 426 | /// 查询指定商品在指定仓库的成本价 |
| 427 | 427 | /// </summary> |
| 428 | 428 | /// <param name="spbh">商品编号</param> |
| 429 | - /// <param name="ck">仓库ID</param> | |
| 430 | - /// <returns>成本价</returns> | |
| 429 | + /// <param name="ck">门店ID或仓库ID(与销售出库单主表 cjck / 明细 ckck 一致)</param> | |
| 430 | + /// <returns>成本价与在库数量</returns> | |
| 431 | 431 | [HttpGet("Actions/GetCost")] |
| 432 | 432 | public async Task<dynamic> GetCost([FromQuery] string spbh, [FromQuery] string ck) |
| 433 | 433 | { |
| 434 | 434 | if (string.IsNullOrEmpty(spbh) || string.IsNullOrEmpty(ck)) |
| 435 | - return new { cbj = 0m }; | |
| 435 | + return new { cbj = 0m, sl = 0 }; | |
| 436 | + | |
| 437 | + var warehouseIds = await _db.Queryable<WtCkEntity>() | |
| 438 | + .Where(c => c.Ssmd == ck || c.Id == ck) | |
| 439 | + .Select(c => c.Id) | |
| 440 | + .ToListAsync(); | |
| 441 | + if (warehouseIds == null || warehouseIds.Count == 0) | |
| 442 | + warehouseIds = new List<string> { ck }; | |
| 443 | + | |
| 444 | + var inClause = string.Join(",", warehouseIds.Select((_, i) => $"@ck{i}")); | |
| 445 | + var parms = warehouseIds | |
| 446 | + .Select((id, i) => new SugarParameter($"@ck{i}", id)) | |
| 447 | + .Concat(new[] { new SugarParameter("@spbh", spbh) }) | |
| 448 | + .ToArray(); | |
| 449 | + | |
| 450 | + var rows = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 451 | + $"SELECT cbj, sl FROM wt_sp_cost WHERE spbh = @spbh AND ck IN ({inClause}) AND cbj > 0 LIMIT 1", | |
| 452 | + parms); | |
| 453 | + | |
| 454 | + if (rows != null && rows.Count > 0) | |
| 455 | + { | |
| 456 | + return new | |
| 457 | + { | |
| 458 | + cbj = Convert.ToDecimal(rows[0].cbj), | |
| 459 | + sl = Convert.ToInt32(rows[0].sl) | |
| 460 | + }; | |
| 461 | + } | |
| 462 | + | |
| 463 | + return new { cbj = 0m, sl = 0 }; | |
| 464 | + } | |
| 465 | + | |
| 466 | + /// <summary> | |
| 467 | + /// 按出库门店/仓库批量查询成本价(用于销售出库单商品下拉展示,口径与 GetCost 一致) | |
| 468 | + /// </summary> | |
| 469 | + /// <param name="ck">门店ID或仓库ID</param> | |
| 470 | + [HttpGet("Actions/GetCostPreviewForStore")] | |
| 471 | + public async Task<dynamic> GetCostPreviewForStore([FromQuery] string ck) | |
| 472 | + { | |
| 473 | + if (string.IsNullOrEmpty(ck)) | |
| 474 | + return new { items = Array.Empty<object>() }; | |
| 475 | + | |
| 476 | + var warehouseIds = await _db.Queryable<WtCkEntity>() | |
| 477 | + .Where(c => c.Ssmd == ck || c.Id == ck) | |
| 478 | + .Select(c => c.Id) | |
| 479 | + .ToListAsync(); | |
| 480 | + if (warehouseIds == null || warehouseIds.Count == 0) | |
| 481 | + warehouseIds = new List<string> { ck }; | |
| 436 | 482 | |
| 437 | - var cost = await _db.Queryable<WtSpCostEntity>() | |
| 438 | - .FirstAsync(c => c.Spbh == spbh && c.Ck == ck); | |
| 483 | + var inClause = string.Join(",", warehouseIds.Select((_, i) => $"@ck{i}")); | |
| 484 | + var parms = warehouseIds.Select((id, i) => new SugarParameter($"@ck{i}", id)).ToArray(); | |
| 485 | + | |
| 486 | + var rows = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 487 | + $"SELECT spbh, cbj FROM wt_sp_cost WHERE ck IN ({inClause}) AND cbj > 0", | |
| 488 | + parms); | |
| 489 | + | |
| 490 | + var list = new List<object>(); | |
| 491 | + var seen = new HashSet<string>(); | |
| 492 | + foreach (var r in rows ?? new List<dynamic>()) | |
| 493 | + { | |
| 494 | + var spbh = r.spbh?.ToString(); | |
| 495 | + if (string.IsNullOrEmpty(spbh) || seen.Contains(spbh)) | |
| 496 | + continue; | |
| 497 | + seen.Add(spbh); | |
| 498 | + list.Add(new { spbh, cbj = Convert.ToDecimal(r.cbj) }); | |
| 499 | + } | |
| 439 | 500 | |
| 440 | - return new { cbj = cost?.Cbj ?? 0m, sl = cost?.Sl ?? 0 }; | |
| 501 | + return new { items = list }; | |
| 441 | 502 | } |
| 442 | 503 | |
| 443 | 504 | /// <summary> | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtXsckdService.cs
| ... | ... | @@ -47,6 +47,9 @@ namespace NCC.Extend.WtXsckd |
| 47 | 47 | // 序列号生成信号量,确保线程安全 |
| 48 | 48 | private static readonly SemaphoreSlim _serialNumberSemaphore = new SemaphoreSlim(1, 1); |
| 49 | 49 | private static bool _ydjeColumnChecked; |
| 50 | + private static bool _cbjeColumnChecked; | |
| 51 | + private static bool _bjsxColumnChecked; | |
| 52 | + private static bool _bjhcbColumnChecked; | |
| 50 | 53 | |
| 51 | 54 | /// <summary> |
| 52 | 55 | /// 确保原价金额列存在(迁移) |
| ... | ... | @@ -74,6 +77,81 @@ namespace NCC.Extend.WtXsckd |
| 74 | 77 | } |
| 75 | 78 | |
| 76 | 79 | /// <summary> |
| 80 | + /// 确保成本金额列存在(迁移) | |
| 81 | + /// </summary> | |
| 82 | + private void EnsureCbjeColumn() | |
| 83 | + { | |
| 84 | + if (_cbjeColumnChecked) return; | |
| 85 | + lock (typeof(WtXsckdService)) | |
| 86 | + { | |
| 87 | + if (_cbjeColumnChecked) return; | |
| 88 | + try | |
| 89 | + { | |
| 90 | + if (!_db.DbMaintenance.IsAnyTable("wt_xsckd")) { _cbjeColumnChecked = true; return; } | |
| 91 | + var columns = _db.DbMaintenance.GetColumnInfosByTableName("wt_xsckd"); | |
| 92 | + var columnNames = columns.Select(c => c.DbColumnName.ToLower()).ToHashSet(); | |
| 93 | + if (!columnNames.Contains("cbje")) | |
| 94 | + { | |
| 95 | + _db.Ado.ExecuteCommand("ALTER TABLE `wt_xsckd` ADD COLUMN `cbje` decimal(18,2) NULL COMMENT '成本金额'"); | |
| 96 | + Console.WriteLine("✅ 已添加字段: cbje (成本金额)"); | |
| 97 | + } | |
| 98 | + } | |
| 99 | + catch (Exception ex) { Console.WriteLine($"EnsureCbjeColumn: {ex.Message}"); } | |
| 100 | + _cbjeColumnChecked = true; | |
| 101 | + } | |
| 102 | + } | |
| 103 | + | |
| 104 | + /// <summary> | |
| 105 | + /// 确保变价系数列存在(变价调拨单主表) | |
| 106 | + /// </summary> | |
| 107 | + private void EnsureBjsxColumn() | |
| 108 | + { | |
| 109 | + if (_bjsxColumnChecked) return; | |
| 110 | + lock (typeof(WtXsckdService)) | |
| 111 | + { | |
| 112 | + if (_bjsxColumnChecked) return; | |
| 113 | + try | |
| 114 | + { | |
| 115 | + if (!_db.DbMaintenance.IsAnyTable("wt_xsckd")) { _bjsxColumnChecked = true; return; } | |
| 116 | + var columns = _db.DbMaintenance.GetColumnInfosByTableName("wt_xsckd"); | |
| 117 | + var columnNames = columns.Select(c => c.DbColumnName.ToLower()).ToHashSet(); | |
| 118 | + if (!columnNames.Contains("bjsx")) | |
| 119 | + { | |
| 120 | + _db.Ado.ExecuteCommand("ALTER TABLE `wt_xsckd` ADD COLUMN `bjsx` decimal(18,4) NULL COMMENT '变价系数%'"); | |
| 121 | + Console.WriteLine("✅ 已添加字段: bjsx (变价系数%)"); | |
| 122 | + } | |
| 123 | + } | |
| 124 | + catch (Exception ex) { Console.WriteLine($"EnsureBjsxColumn: {ex.Message}"); } | |
| 125 | + _bjsxColumnChecked = true; | |
| 126 | + } | |
| 127 | + } | |
| 128 | + | |
| 129 | + /// <summary> | |
| 130 | + /// 确保变价后成本单价列存在(变价调拨单明细) | |
| 131 | + /// </summary> | |
| 132 | + private void EnsureBjhcbColumn() | |
| 133 | + { | |
| 134 | + if (_bjhcbColumnChecked) return; | |
| 135 | + lock (typeof(WtXsckdService)) | |
| 136 | + { | |
| 137 | + if (_bjhcbColumnChecked) return; | |
| 138 | + try | |
| 139 | + { | |
| 140 | + if (!_db.DbMaintenance.IsAnyTable("wt_xsckd_mx")) { _bjhcbColumnChecked = true; return; } | |
| 141 | + var columns = _db.DbMaintenance.GetColumnInfosByTableName("wt_xsckd_mx"); | |
| 142 | + var columnNames = columns.Select(c => c.DbColumnName.ToLower()).ToHashSet(); | |
| 143 | + if (!columnNames.Contains("bjhcb")) | |
| 144 | + { | |
| 145 | + _db.Ado.ExecuteCommand("ALTER TABLE `wt_xsckd_mx` ADD COLUMN `bjhcb` decimal(18,4) NULL COMMENT '变价后成本单价'"); | |
| 146 | + Console.WriteLine("✅ 已添加字段: bjhcb (变价后成本单价)"); | |
| 147 | + } | |
| 148 | + } | |
| 149 | + catch (Exception ex) { Console.WriteLine($"EnsureBjhcbColumn: {ex.Message}"); } | |
| 150 | + _bjhcbColumnChecked = true; | |
| 151 | + } | |
| 152 | + } | |
| 153 | + | |
| 154 | + /// <summary> | |
| 77 | 155 | /// 初始化一个<see cref="WtXsckdService"/>类型的新实例 |
| 78 | 156 | /// </summary> |
| 79 | 157 | public WtXsckdService( |
| ... | ... | @@ -95,8 +173,11 @@ namespace NCC.Extend.WtXsckd |
| 95 | 173 | [HttpGet("{id}")] |
| 96 | 174 | public async Task<dynamic> GetInfo(string id) |
| 97 | 175 | { |
| 176 | + EnsureBjsxColumn(); | |
| 177 | + EnsureBjhcbColumn(); | |
| 98 | 178 | var entity = await _db.Queryable<WtXsckdEntity>().FirstAsync(p => p.Id == id); |
| 99 | 179 | var output = entity.Adapt<WtXsckdInfoOutput>(); |
| 180 | + output.cjckId = entity.Cjck; | |
| 100 | 181 | |
| 101 | 182 | // ✅ 将出库仓库ID转换为门店名称 |
| 102 | 183 | if (!string.IsNullOrEmpty(entity.Cjck)) |
| ... | ... | @@ -193,6 +274,8 @@ namespace NCC.Extend.WtXsckd |
| 193 | 274 | public async Task<dynamic> GetList([FromQuery] WtXsckdListQueryInput input) |
| 194 | 275 | { |
| 195 | 276 | EnsureYdjeColumn(); |
| 277 | + EnsureCbjeColumn(); | |
| 278 | + EnsureBjsxColumn(); | |
| 196 | 279 | var sidx = input.sidx == null ? "id" : input.sidx; |
| 197 | 280 | List<string> queryDjrq = input.djrq != null ? input.djrq.Split(',').ToObeject<List<string>>() : null; |
| 198 | 281 | DateTime? startDjrq = queryDjrq != null ? Ext.GetDateTime(queryDjrq.First()) : null; |
| ... | ... | @@ -230,6 +313,8 @@ namespace NCC.Extend.WtXsckd |
| 230 | 313 | // jsr=xsckd.Jsr, |
| 231 | 314 | jsr = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == xsckd.Jsr).Select(u => u.RealName), |
| 232 | 315 | ydje = xsckd.Ydje, |
| 316 | + cbje = xsckd.Cbje, | |
| 317 | + bjsx = xsckd.Bjsx, | |
| 233 | 318 | ysje = xsckd.Ysje, |
| 234 | 319 | skzh = xsckd.Skzh, |
| 235 | 320 | skje = xsckd.Skje, |
| ... | ... | @@ -262,10 +347,15 @@ namespace NCC.Extend.WtXsckd |
| 262 | 347 | [AllowAnonymous] // 允许抖音物流系统匿名调用 |
| 263 | 348 | public async Task<dynamic> Create([FromBody] WtXsckdCrInput input) |
| 264 | 349 | { |
| 350 | + EnsureCbjeColumn(); | |
| 351 | + EnsureBjsxColumn(); | |
| 352 | + EnsureBjhcbColumn(); | |
| 265 | 353 | // 匿名访问时,userInfo可能为null,需要安全处理 |
| 266 | 354 | var userInfo = await _userManager.GetUserInfo(); |
| 267 | 355 | var entity = input.Adapt<WtXsckdEntity>(); |
| 268 | 356 | entity.Ydje = input.ydje; |
| 357 | + entity.Cbje = input.cbje; | |
| 358 | + entity.Bjsx = input.bjsx; | |
| 269 | 359 | |
| 270 | 360 | // 获取用户ID(匿名访问时为null或空字符串) |
| 271 | 361 | string? userId = null; |
| ... | ... | @@ -675,7 +765,9 @@ namespace NCC.Extend.WtXsckd |
| 675 | 765 | } |
| 676 | 766 | |
| 677 | 767 | // ========== P4: 自动维护 wt_sp_cost 成本表 ========== |
| 678 | - await UpdateSpCostOnCreate(input.djlx, entity, wtXsckdMxEntityList); | |
| 768 | + await UpdateSpCostOnCreate(input.djlx, entity, wtXsckdMxEntityList, input.isDraft); | |
| 769 | + await RecalculateMainCbje(newEntity.Id, input.djlx, | |
| 770 | + input.isDraft || string.Equals(entity.Djzt, "草稿", StringComparison.Ordinal)); | |
| 679 | 771 | |
| 680 | 772 | //关闭事务 |
| 681 | 773 | _db.CommitTran(); |
| ... | ... | @@ -898,7 +990,7 @@ namespace NCC.Extend.WtXsckd |
| 898 | 990 | { |
| 899 | 991 | exportData = await this.GetNoPagingList(input); |
| 900 | 992 | } |
| 901 | - List<ParamsModel> paramList = "[{\"value\":\"单据编号\",\"field\":\"id\"},{\"value\":\"单据日期\",\"field\":\"djrq\"},{\"value\":\"出库仓库\",\"field\":\"cjck\"},{\"value\":\"经手人\",\"field\":\"jsr\"},{\"value\":\"优惠金额\",\"field\":\"ysje\"},{\"value\":\"收款账户\",\"field\":\"skzh\"},{\"value\":\"收款金额\",\"field\":\"skje\"},{\"value\":\"制单人\",\"field\":\"zdr\"},{\"value\":\"审核人\",\"field\":\"shr\"},{\"value\":\"过账人\",\"field\":\"gzr\"},{\"value\":\"备注\",\"field\":\"bz\"},{\"value\":\"单据类型\",\"field\":\"djlx\"},]".ToList<ParamsModel>(); | |
| 993 | + List<ParamsModel> paramList = "[{\"value\":\"单据编号\",\"field\":\"id\"},{\"value\":\"单据日期\",\"field\":\"djrq\"},{\"value\":\"出库仓库\",\"field\":\"cjck\"},{\"value\":\"变价系数%\",\"field\":\"bjsx\"},{\"value\":\"成本金额\",\"field\":\"cbje\"},{\"value\":\"经手人\",\"field\":\"jsr\"},{\"value\":\"优惠金额\",\"field\":\"ysje\"},{\"value\":\"收款账户\",\"field\":\"skzh\"},{\"value\":\"收款金额\",\"field\":\"skje\"},{\"value\":\"制单人\",\"field\":\"zdr\"},{\"value\":\"审核人\",\"field\":\"shr\"},{\"value\":\"过账人\",\"field\":\"gzr\"},{\"value\":\"备注\",\"field\":\"bz\"},{\"value\":\"单据类型\",\"field\":\"djlx\"},{\"value\":\"单据状态\",\"field\":\"djzt\"},]".ToList<ParamsModel>(); | |
| 902 | 994 | ExcelConfig excelconfig = new ExcelConfig(); |
| 903 | 995 | excelconfig.FileName = "销售出库单.xls"; |
| 904 | 996 | excelconfig.HeadFont = "微软雅黑"; |
| ... | ... | @@ -968,7 +1060,13 @@ namespace NCC.Extend.WtXsckd |
| 968 | 1060 | [HttpPut("{id}")] |
| 969 | 1061 | public async Task Update(string id, [FromBody] WtXsckdUpInput input) |
| 970 | 1062 | { |
| 1063 | + EnsureCbjeColumn(); | |
| 1064 | + EnsureBjsxColumn(); | |
| 1065 | + EnsureBjhcbColumn(); | |
| 1066 | + var oldHeader = await _db.Queryable<WtXsckdEntity>().FirstAsync(p => p.Id == id); | |
| 1067 | + var oldMxList = await _db.Queryable<WtXsckdMxEntity>().Where(u => u.Djbh == id).ToListAsync(); | |
| 971 | 1068 | var entity = input.Adapt<WtXsckdEntity>(); |
| 1069 | + entity.Bjsx = input.bjsx; | |
| 972 | 1070 | |
| 973 | 1071 | // ✅ 确保会员手机号码字段被正确映射和保存 |
| 974 | 1072 | entity.Hysjh = input.hysjh ?? ""; |
| ... | ... | @@ -978,6 +1076,14 @@ namespace NCC.Extend.WtXsckd |
| 978 | 1076 | { |
| 979 | 1077 | //开启事务 |
| 980 | 1078 | _db.BeginTran(); |
| 1079 | + | |
| 1080 | + // 变价调拨单:编辑前先按库中旧单回滚成本,避免重复过账 | |
| 1081 | + if (oldHeader.Djlx == "变价调拨单" | |
| 1082 | + && !string.Equals(oldHeader.Djzt, "草稿", StringComparison.Ordinal) | |
| 1083 | + && oldMxList.Count > 0) | |
| 1084 | + { | |
| 1085 | + await RollbackVariablePriceTransferCost(oldHeader, oldMxList); | |
| 1086 | + } | |
| 981 | 1087 | |
| 982 | 1088 | //更新销售出库单记录 |
| 983 | 1089 | await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); |
| ... | ... | @@ -997,6 +1103,10 @@ namespace NCC.Extend.WtXsckd |
| 997 | 1103 | await _db.Insertable(wtXsckdMxEntityList).ExecuteCommandAsync(); |
| 998 | 1104 | } |
| 999 | 1105 | |
| 1106 | + await UpdateSpCostOnCreate(entity.Djlx, entity, wtXsckdMxEntityList, input.isDraft); | |
| 1107 | + await RecalculateMainCbje(id, entity.Djlx, | |
| 1108 | + input.isDraft || string.Equals(entity.Djzt, "草稿", StringComparison.Ordinal)); | |
| 1109 | + | |
| 1000 | 1110 | // 自动生成导购提成记录 |
| 1001 | 1111 | if (entity.Djlx == "销售出库单") |
| 1002 | 1112 | { |
| ... | ... | @@ -1286,6 +1396,53 @@ ORDER BY t.`商品编号`"; |
| 1286 | 1396 | } |
| 1287 | 1397 | } |
| 1288 | 1398 | |
| 1399 | + /// <summary> | |
| 1400 | + /// 按商品与出库门店/仓库查询加权成本单价(与销售出库、变价调拨过账取价逻辑一致,供前端录入参考) | |
| 1401 | + /// </summary> | |
| 1402 | + /// <param name="spbh">商品主键 F_Id</param> | |
| 1403 | + /// <param name="ckOrMdId">明细 ckck 或主表 cjck(可为门店ID或仓库ID)</param> | |
| 1404 | + [HttpGet("GetOutboundCostPrice")] | |
| 1405 | + public async Task<dynamic> GetOutboundCostPrice(string spbh, string ckOrMdId) | |
| 1406 | + { | |
| 1407 | + if (string.IsNullOrWhiteSpace(spbh) || string.IsNullOrWhiteSpace(ckOrMdId)) | |
| 1408 | + { | |
| 1409 | + return new { success = false, msg = "商品编号与仓库不能为空", data = (decimal?)null }; | |
| 1410 | + } | |
| 1411 | + | |
| 1412 | + try | |
| 1413 | + { | |
| 1414 | + var warehouseIds = await _db.Queryable<WtCkEntity>() | |
| 1415 | + .Where(c => c.Ssmd == ckOrMdId || c.Id == ckOrMdId) | |
| 1416 | + .Select(c => c.Id) | |
| 1417 | + .ToListAsync(); | |
| 1418 | + if (warehouseIds == null || warehouseIds.Count == 0) | |
| 1419 | + { | |
| 1420 | + warehouseIds = new List<string> { ckOrMdId.Trim() }; | |
| 1421 | + } | |
| 1422 | + | |
| 1423 | + var ps = new List<SugarParameter> { new SugarParameter("@spbh", spbh.Trim()) }; | |
| 1424 | + for (var i = 0; i < warehouseIds.Count; i++) | |
| 1425 | + ps.Add(new SugarParameter($"@ck{i}", warehouseIds[i])); | |
| 1426 | + var inSql = string.Join(",", warehouseIds.Select((_, i) => $"@ck{i}")); | |
| 1427 | + | |
| 1428 | + var rows = await _db.Ado.SqlQueryAsync<decimal?>( | |
| 1429 | + $"SELECT cbj FROM wt_sp_cost WHERE spbh = @spbh AND ck IN ({inSql}) AND cbj > 0 ORDER BY sl DESC LIMIT 1", | |
| 1430 | + ps.ToArray()); | |
| 1431 | + | |
| 1432 | + var price = rows?.FirstOrDefault(); | |
| 1433 | + if (price == null || price <= 0) | |
| 1434 | + { | |
| 1435 | + return new { success = false, msg = "未查询到成本单价,请先维护采购/入库成本", data = (decimal?)null }; | |
| 1436 | + } | |
| 1437 | + | |
| 1438 | + return new { success = true, data = Math.Round(price.Value, 4), msg = "操作成功" }; | |
| 1439 | + } | |
| 1440 | + catch (Exception ex) | |
| 1441 | + { | |
| 1442 | + return new { success = false, msg = "查询成本失败: " + ex.Message, data = (decimal?)null }; | |
| 1443 | + } | |
| 1444 | + } | |
| 1445 | + | |
| 1289 | 1446 | [HttpGet("GetSaleStatistics")] |
| 1290 | 1447 | public async Task<dynamic> GetSaleStatistics() |
| 1291 | 1448 | { |
| ... | ... | @@ -2820,6 +2977,30 @@ ORDER BY t.`商品编号`;"; |
| 2820 | 2977 | } |
| 2821 | 2978 | } |
| 2822 | 2979 | |
| 2980 | + /// <summary> | |
| 2981 | + /// 销售/预售/变价调拨单重算并回写主表成本金额(变价调拨为明细变价后成本金额合计) | |
| 2982 | + /// </summary> | |
| 2983 | + /// <param name="skipForBjdDraft">变价调拨草稿时不回写主表 cbje(明细未过账)</param> | |
| 2984 | + private async Task RecalculateMainCbje(string djbh, string djlx, bool skipForBjdDraft = false) | |
| 2985 | + { | |
| 2986 | + if (string.IsNullOrEmpty(djbh)) return; | |
| 2987 | + if (djlx == "变价调拨单" && skipForBjdDraft) return; | |
| 2988 | + if (djlx != "销售出库单" && djlx != "预售出库单" && djlx != "变价调拨单") return; | |
| 2989 | + | |
| 2990 | + decimal cbje = 0; | |
| 2991 | + var result = await _db.Ado.SqlQueryAsync<decimal?>( | |
| 2992 | + "SELECT IFNULL(SUM(IFNULL(cbje, 0)), 0) FROM wt_xsckd_mx WHERE djbh = @djbh", | |
| 2993 | + new { djbh }); | |
| 2994 | + if (result != null && result.Count > 0) | |
| 2995 | + { | |
| 2996 | + cbje = result[0] ?? 0; | |
| 2997 | + } | |
| 2998 | + | |
| 2999 | + await _db.Ado.ExecuteCommandAsync( | |
| 3000 | + "UPDATE wt_xsckd SET cbje = @cbje WHERE F_Id = @id", | |
| 3001 | + new { cbje, id = djbh }); | |
| 3002 | + } | |
| 3003 | + | |
| 2823 | 3004 | #region P4: wt_sp_cost 成本表自动维护 |
| 2824 | 3005 | |
| 2825 | 3006 | /// <summary> |
| ... | ... | @@ -2880,10 +3061,29 @@ ORDER BY t.`商品编号`;"; |
| 2880 | 3061 | /// 采购入库 → 审核通过后才更新(见 UpdateSpCostOnPurchaseApproval) |
| 2881 | 3062 | /// 销售/预售出库 → 快照成本到明细 + 减少数量 |
| 2882 | 3063 | /// 销售退货 → 恢复数量(成本不变) |
| 3064 | + /// 变价调拨单 → 调出仓按原成本加权减少,调入仓按变价后单价加权增加(草稿/ isDraft 不过账) | |
| 2883 | 3065 | /// </summary> |
| 2884 | - private async Task UpdateSpCostOnCreate(string djlx, WtXsckdEntity entity, List<WtXsckdMxEntity> mxList) | |
| 3066 | + private async Task UpdateSpCostOnCreate(string djlx, WtXsckdEntity entity, List<WtXsckdMxEntity> mxList, bool isDraft = false) | |
| 2885 | 3067 | { |
| 2886 | 3068 | if (mxList == null || mxList.Count == 0) return; |
| 3069 | + | |
| 3070 | + if (djlx == "变价调拨单") | |
| 3071 | + { | |
| 3072 | + EnsureBjsxColumn(); | |
| 3073 | + EnsureBjhcbColumn(); | |
| 3074 | + if (isDraft || string.Equals(entity.Djzt, "草稿", StringComparison.Ordinal)) | |
| 3075 | + return; | |
| 3076 | + try | |
| 3077 | + { | |
| 3078 | + await ApplyVariablePriceTransferCost(entity, mxList); | |
| 3079 | + } | |
| 3080 | + catch (Exception ex) | |
| 3081 | + { | |
| 3082 | + Console.WriteLine($"变价调拨成本过账失败: {ex.Message}"); | |
| 3083 | + throw; | |
| 3084 | + } | |
| 3085 | + return; | |
| 3086 | + } | |
| 2887 | 3087 | |
| 2888 | 3088 | try |
| 2889 | 3089 | { |
| ... | ... | @@ -2891,17 +3091,22 @@ ORDER BY t.`商品编号`;"; |
| 2891 | 3091 | { |
| 2892 | 3092 | var outStoreId = entity.Cjck; |
| 2893 | 3093 | if (string.IsNullOrEmpty(outStoreId)) return; |
| 2894 | - | |
| 2895 | - var warehouseIds = await _db.Queryable<WtCkEntity>() | |
| 2896 | - .Where(c => c.Ssmd == outStoreId) | |
| 2897 | - .Select(c => c.Id) | |
| 2898 | - .ToListAsync(); | |
| 2899 | - if (warehouseIds == null || warehouseIds.Count == 0) return; | |
| 2900 | - | |
| 3094 | + | |
| 2901 | 3095 | foreach (var detail in mxList) |
| 2902 | 3096 | { |
| 2903 | 3097 | if (string.IsNullOrEmpty(detail.Spbh)) continue; |
| 2904 | 3098 | if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; |
| 3099 | + | |
| 3100 | + // 兼容前端传门店ID或仓库ID:按明细仓库优先,其次主表出库仓库 | |
| 3101 | + var detailStoreOrWarehouseId = !string.IsNullOrEmpty(detail.Ckck) ? detail.Ckck : outStoreId; | |
| 3102 | + var warehouseIds = await _db.Queryable<WtCkEntity>() | |
| 3103 | + .Where(c => c.Ssmd == detailStoreOrWarehouseId || c.Id == detailStoreOrWarehouseId) | |
| 3104 | + .Select(c => c.Id) | |
| 3105 | + .ToListAsync(); | |
| 3106 | + if (warehouseIds == null || warehouseIds.Count == 0) | |
| 3107 | + { | |
| 3108 | + warehouseIds = new List<string> { detailStoreOrWarehouseId }; | |
| 3109 | + } | |
| 2905 | 3110 | |
| 2906 | 3111 | var costResult = await _db.Ado.SqlQueryAsync<decimal?>( |
| 2907 | 3112 | $"SELECT cbj FROM wt_sp_cost WHERE spbh = @spbh AND ck IN ({string.Join(",", warehouseIds.Select((_, i) => $"@ck{i}"))}) AND cbj > 0 LIMIT 1", |
| ... | ... | @@ -2910,12 +3115,9 @@ ORDER BY t.`商品编号`;"; |
| 2910 | 3115 | |
| 2911 | 3116 | decimal costPrice = costResult?.FirstOrDefault() ?? 0; |
| 2912 | 3117 | |
| 2913 | - if (costPrice > 0) | |
| 2914 | - { | |
| 2915 | - await _db.Ado.ExecuteCommandAsync( | |
| 2916 | - "UPDATE wt_xsckd_mx SET cbdj = @cbdj, cbje = @cbje WHERE F_Id = @id", | |
| 2917 | - new { cbdj = costPrice, cbje = Math.Round(costPrice * qty, 2), id = detail.Id }); | |
| 2918 | - } | |
| 3118 | + await _db.Ado.ExecuteCommandAsync( | |
| 3119 | + "UPDATE wt_xsckd_mx SET cbdj = @cbdj, cbje = @cbje WHERE F_Id = @id", | |
| 3120 | + new { cbdj = costPrice, cbje = Math.Round(costPrice * qty, 2), id = detail.Id }); | |
| 2919 | 3121 | |
| 2920 | 3122 | foreach (var whId in warehouseIds) |
| 2921 | 3123 | { |
| ... | ... | @@ -3209,6 +3411,243 @@ ORDER BY t.`商品编号`;"; |
| 3209 | 3411 | } |
| 3210 | 3412 | } |
| 3211 | 3413 | } |
| 3414 | + else if (djlx == "变价调拨单") | |
| 3415 | + { | |
| 3416 | + if (string.Equals(entity.Djzt, "草稿", StringComparison.Ordinal)) return; | |
| 3417 | + EnsureBjsxColumn(); | |
| 3418 | + EnsureBjhcbColumn(); | |
| 3419 | + var mxList = await _db.Queryable<WtXsckdMxEntity>().Where(d => d.Djbh == id).ToListAsync(); | |
| 3420 | + if (mxList.Count > 0) | |
| 3421 | + await RollbackVariablePriceTransferCost(entity, mxList); | |
| 3422 | + } | |
| 3423 | + } | |
| 3424 | + | |
| 3425 | + /// <summary> | |
| 3426 | + /// 将门店ID或仓库ID解析为仓库ID列表(与同价/销售成本逻辑一致) | |
| 3427 | + /// </summary> | |
| 3428 | + private async Task<List<string>> ResolveWarehouseIdListAsync(string storeOrWarehouseId) | |
| 3429 | + { | |
| 3430 | + if (string.IsNullOrEmpty(storeOrWarehouseId)) return new List<string>(); | |
| 3431 | + var warehouseIds = await _db.Queryable<WtCkEntity>() | |
| 3432 | + .Where(c => c.Ssmd == storeOrWarehouseId || c.Id == storeOrWarehouseId) | |
| 3433 | + .Select(c => c.Id) | |
| 3434 | + .ToListAsync(); | |
| 3435 | + if (warehouseIds == null || warehouseIds.Count == 0) | |
| 3436 | + warehouseIds = new List<string> { storeOrWarehouseId }; | |
| 3437 | + return warehouseIds; | |
| 3438 | + } | |
| 3439 | + | |
| 3440 | + /// <summary> | |
| 3441 | + /// 变价后单价 = 原成本单价 / (系数% / 100),如 100 与 95% → 105.26 | |
| 3442 | + /// </summary> | |
| 3443 | + private static decimal ComputeVariableAdjustedUnitCost(decimal originalUnit, decimal bjsxPercent) | |
| 3444 | + { | |
| 3445 | + if (bjsxPercent <= 0) throw new Exception("变价系数必须大于0"); | |
| 3446 | + return Math.Round(originalUnit / (bjsxPercent / 100m), 4); | |
| 3447 | + } | |
| 3448 | + | |
| 3449 | + /// <summary> | |
| 3450 | + /// 调出仓选取 wt_sp_cost 行:优先库存不少于数量且成本>0,否则取有库存且成本>0的第一条 | |
| 3451 | + /// </summary> | |
| 3452 | + private async Task<(string ck, decimal unitCost, int sl)> PickOutboundCostRowForTransferAsync(string spbh, List<string> outWarehouseIds, int qty) | |
| 3453 | + { | |
| 3454 | + if (outWarehouseIds == null || outWarehouseIds.Count == 0) | |
| 3455 | + throw new Exception($"商品 {spbh} 未解析到调出仓库"); | |
| 3456 | + | |
| 3457 | + var ps = new List<SugarParameter> { new SugarParameter("@spbh", spbh) }; | |
| 3458 | + for (var i = 0; i < outWarehouseIds.Count; i++) | |
| 3459 | + ps.Add(new SugarParameter($"@ck{i}", outWarehouseIds[i])); | |
| 3460 | + var inSql = string.Join(",", outWarehouseIds.Select((_, i) => $"@ck{i}")); | |
| 3461 | + | |
| 3462 | + var rows = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 3463 | + $"SELECT ck, cbj, sl FROM wt_sp_cost WHERE spbh = @spbh AND ck IN ({inSql}) ORDER BY sl DESC", | |
| 3464 | + ps.ToArray()); | |
| 3465 | + | |
| 3466 | + if (rows == null || rows.Count == 0) | |
| 3467 | + throw new Exception($"商品 {spbh} 在调出方无成本数据,请先维护采购/入库成本"); | |
| 3468 | + | |
| 3469 | + foreach (var r in rows) | |
| 3470 | + { | |
| 3471 | + int sl = Convert.ToInt32(r.sl); | |
| 3472 | + decimal cbj = Convert.ToDecimal(r.cbj); | |
| 3473 | + if (sl >= qty && cbj > 0) | |
| 3474 | + return (r.ck.ToString(), cbj, sl); | |
| 3475 | + } | |
| 3476 | + foreach (var r in rows) | |
| 3477 | + { | |
| 3478 | + int sl = Convert.ToInt32(r.sl); | |
| 3479 | + decimal cbj = Convert.ToDecimal(r.cbj); | |
| 3480 | + if (sl > 0 && cbj > 0) | |
| 3481 | + return (r.ck.ToString(), cbj, sl); | |
| 3482 | + } | |
| 3483 | + throw new Exception($"商品 {spbh} 在调出仓可用数量不足或成本为0,无法变价调拨"); | |
| 3484 | + } | |
| 3485 | + | |
| 3486 | + /// <summary> | |
| 3487 | + /// 回滚时按原成本加回调出仓:优先匹配与过账时快照成本单价一致的仓库行 | |
| 3488 | + /// </summary> | |
| 3489 | + private async Task<string> PickOutboundCkForRollbackAddAsync(string spbh, List<string> outWarehouseIds, decimal snapshotOriginalCbj, int qty) | |
| 3490 | + { | |
| 3491 | + if (outWarehouseIds == null || outWarehouseIds.Count == 0) | |
| 3492 | + throw new Exception($"商品 {spbh} 未解析到调出仓库"); | |
| 3493 | + var ps = new List<SugarParameter> { new SugarParameter("@spbh", spbh) }; | |
| 3494 | + for (var i = 0; i < outWarehouseIds.Count; i++) | |
| 3495 | + ps.Add(new SugarParameter($"@ck{i}", outWarehouseIds[i])); | |
| 3496 | + var inSql = string.Join(",", outWarehouseIds.Select((_, i) => $"@ck{i}")); | |
| 3497 | + var rows = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 3498 | + $"SELECT ck, cbj, sl FROM wt_sp_cost WHERE spbh = @spbh AND ck IN ({inSql}) ORDER BY sl ASC, cbj ASC", | |
| 3499 | + ps.ToArray()); | |
| 3500 | + if (rows != null && rows.Count > 0) | |
| 3501 | + { | |
| 3502 | + foreach (var r in rows) | |
| 3503 | + { | |
| 3504 | + decimal cbj = Convert.ToDecimal(r.cbj); | |
| 3505 | + if (Math.Abs(cbj - snapshotOriginalCbj) < 0.02m) | |
| 3506 | + return r.ck.ToString(); | |
| 3507 | + } | |
| 3508 | + } | |
| 3509 | + try | |
| 3510 | + { | |
| 3511 | + var picked = await PickOutboundCostRowForTransferAsync(spbh, outWarehouseIds, Math.Max(1, qty)); | |
| 3512 | + return picked.ck; | |
| 3513 | + } | |
| 3514 | + catch | |
| 3515 | + { | |
| 3516 | + return outWarehouseIds[0]; | |
| 3517 | + } | |
| 3518 | + } | |
| 3519 | + | |
| 3520 | + /// <summary> | |
| 3521 | + /// 加权减少库存与成本(与采购退货扣减一致) | |
| 3522 | + /// </summary> | |
| 3523 | + private async Task SpCostWeightedRemoveAsync(string spbh, string ck, int qty, decimal unitPrice) | |
| 3524 | + { | |
| 3525 | + var existing = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 3526 | + "SELECT cbj, sl FROM wt_sp_cost WHERE spbh = @spbh AND ck = @ck LIMIT 1", | |
| 3527 | + new { spbh, ck }); | |
| 3528 | + if (existing == null || existing.Count == 0) | |
| 3529 | + throw new Exception($"成本记录不存在: {spbh} 仓库 {ck}"); | |
| 3530 | + decimal currentCbj = Convert.ToDecimal(existing[0].cbj); | |
| 3531 | + int currentSl = Convert.ToInt32(existing[0].sl); | |
| 3532 | + if (currentSl < qty) | |
| 3533 | + throw new Exception($"库存不足: {spbh} 仓库 {ck} 当前{currentSl} 需要{qty}"); | |
| 3534 | + int newSl = currentSl - qty; | |
| 3535 | + decimal newCbj = 0; | |
| 3536 | + if (newSl > 0) | |
| 3537 | + newCbj = Math.Round((currentCbj * currentSl - unitPrice * qty) / newSl, 2); | |
| 3538 | + await _db.Ado.ExecuteCommandAsync( | |
| 3539 | + "UPDATE wt_sp_cost SET cbj = @cbj, sl = @sl, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 3540 | + new { cbj = newCbj, sl = newSl, spbh, ck }); | |
| 3541 | + } | |
| 3542 | + | |
| 3543 | + /// <summary> | |
| 3544 | + /// 加权增加库存与成本(与采购入库增加一致) | |
| 3545 | + /// </summary> | |
| 3546 | + private async Task SpCostWeightedAddAsync(string spbh, string ck, int qty, decimal unitPrice) | |
| 3547 | + { | |
| 3548 | + var existing = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 3549 | + "SELECT cbj, sl FROM wt_sp_cost WHERE spbh = @spbh AND ck = @ck LIMIT 1", | |
| 3550 | + new { spbh, ck }); | |
| 3551 | + if (existing != null && existing.Count > 0) | |
| 3552 | + { | |
| 3553 | + decimal oldCbj = Convert.ToDecimal(existing[0].cbj); | |
| 3554 | + int oldSl = Convert.ToInt32(existing[0].sl); | |
| 3555 | + int newSl = oldSl + qty; | |
| 3556 | + decimal newCbj = newSl > 0 | |
| 3557 | + ? Math.Round((oldCbj * oldSl + unitPrice * qty) / newSl, 2) | |
| 3558 | + : unitPrice; | |
| 3559 | + await _db.Ado.ExecuteCommandAsync( | |
| 3560 | + "UPDATE wt_sp_cost SET cbj = @cbj, sl = @sl, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 3561 | + new { cbj = newCbj, sl = newSl, spbh, ck }); | |
| 3562 | + } | |
| 3563 | + else | |
| 3564 | + { | |
| 3565 | + var rowId = $"{spbh}_{ck}"; | |
| 3566 | + await _db.Ado.ExecuteCommandAsync( | |
| 3567 | + "INSERT INTO wt_sp_cost (F_Id, spbh, ck, cbj, sl, update_time) VALUES (@id, @spbh, @ck, @cbj, @sl, NOW())", | |
| 3568 | + new { id = rowId, spbh, ck, cbj = unitPrice, sl = qty }); | |
| 3569 | + } | |
| 3570 | + } | |
| 3571 | + | |
| 3572 | + /// <summary> | |
| 3573 | + /// 变价调拨单过账:调出按快照原成本扣减,调入按变价后单价增加,并回写明细 cbdj/bjhcb/cbje | |
| 3574 | + /// </summary> | |
| 3575 | + private async Task ApplyVariablePriceTransferCost(WtXsckdEntity entity, List<WtXsckdMxEntity> mxList) | |
| 3576 | + { | |
| 3577 | + if (string.Equals((entity.Cjck ?? "").Trim(), (entity.Rkck ?? "").Trim(), StringComparison.Ordinal)) | |
| 3578 | + throw new Exception("变价调拨单出库仓与入库仓不能相同"); | |
| 3579 | + var coef = entity.Bjsx ?? 0; | |
| 3580 | + if (coef <= 0) | |
| 3581 | + throw new Exception("变价系数必须大于0"); | |
| 3582 | + | |
| 3583 | + foreach (var detail in mxList) | |
| 3584 | + { | |
| 3585 | + if (string.IsNullOrEmpty(detail.Spbh)) continue; | |
| 3586 | + if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; | |
| 3587 | + | |
| 3588 | + var outRef = !string.IsNullOrEmpty(detail.Ckck) ? detail.Ckck : entity.Cjck; | |
| 3589 | + var inRef = !string.IsNullOrEmpty(detail.Rkck) ? detail.Rkck : entity.Rkck; | |
| 3590 | + if (string.IsNullOrEmpty(outRef) || string.IsNullOrEmpty(inRef)) | |
| 3591 | + throw new Exception($"商品 {detail.Spmc ?? detail.Spbh} 未指定完整的出库/入库仓库"); | |
| 3592 | + | |
| 3593 | + var outIds = await ResolveWarehouseIdListAsync(outRef); | |
| 3594 | + var (outCk, originalUnit, _) = await PickOutboundCostRowForTransferAsync(detail.Spbh, outIds, qty); | |
| 3595 | + var adjustedUnit = ComputeVariableAdjustedUnitCost(originalUnit, coef); | |
| 3596 | + var inIds = await ResolveWarehouseIdListAsync(inRef); | |
| 3597 | + if (inIds == null || inIds.Count == 0) | |
| 3598 | + throw new Exception($"商品 {detail.Spbh} 未解析到入库仓库"); | |
| 3599 | + var inCk = inIds[0]; | |
| 3600 | + | |
| 3601 | + await SpCostWeightedRemoveAsync(detail.Spbh, outCk, qty, originalUnit); | |
| 3602 | + await SpCostWeightedAddAsync(detail.Spbh, inCk, qty, adjustedUnit); | |
| 3603 | + | |
| 3604 | + await _db.Ado.ExecuteCommandAsync( | |
| 3605 | + "UPDATE wt_xsckd_mx SET cbdj = @cbdj, bjhcb = @bjhcb, cbje = @cbje WHERE F_Id = @id", | |
| 3606 | + new | |
| 3607 | + { | |
| 3608 | + cbdj = originalUnit, | |
| 3609 | + bjhcb = adjustedUnit, | |
| 3610 | + cbje = Math.Round(adjustedUnit * qty, 2), | |
| 3611 | + id = detail.Id | |
| 3612 | + }); | |
| 3613 | + } | |
| 3614 | + } | |
| 3615 | + | |
| 3616 | + /// <summary> | |
| 3617 | + /// 变价调拨单成本回滚:调入按变价后单价扣回,调出按原成本加回(删除/编辑前调用) | |
| 3618 | + /// </summary> | |
| 3619 | + private async Task RollbackVariablePriceTransferCost(WtXsckdEntity entity, List<WtXsckdMxEntity> mxList) | |
| 3620 | + { | |
| 3621 | + foreach (var detail in mxList) | |
| 3622 | + { | |
| 3623 | + if (string.IsNullOrEmpty(detail.Spbh)) continue; | |
| 3624 | + if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; | |
| 3625 | + | |
| 3626 | + var outRef = !string.IsNullOrEmpty(detail.Ckck) ? detail.Ckck : entity.Cjck; | |
| 3627 | + var inRef = !string.IsNullOrEmpty(detail.Rkck) ? detail.Rkck : entity.Rkck; | |
| 3628 | + if (string.IsNullOrEmpty(outRef) || string.IsNullOrEmpty(inRef)) continue; | |
| 3629 | + | |
| 3630 | + decimal originalUnit = detail.Cbdj ?? detail.Dj; | |
| 3631 | + if (originalUnit <= 0) continue; | |
| 3632 | + | |
| 3633 | + decimal adjustedUnit = 0; | |
| 3634 | + if (detail.Bjhcb.HasValue && detail.Bjhcb.Value > 0) | |
| 3635 | + adjustedUnit = detail.Bjhcb.Value; | |
| 3636 | + else if (entity.Bjsx.HasValue && entity.Bjsx.Value > 0) | |
| 3637 | + adjustedUnit = ComputeVariableAdjustedUnitCost(originalUnit, entity.Bjsx.Value); | |
| 3638 | + if (adjustedUnit <= 0) continue; | |
| 3639 | + | |
| 3640 | + var inIds = await ResolveWarehouseIdListAsync(inRef); | |
| 3641 | + var outIds = await ResolveWarehouseIdListAsync(outRef); | |
| 3642 | + if (inIds == null || inIds.Count == 0 || outIds == null || outIds.Count == 0) continue; | |
| 3643 | + var inCk = inIds[0]; | |
| 3644 | + | |
| 3645 | + // 调入仓按变价后单价扣减(与过账时增加互逆) | |
| 3646 | + await SpCostWeightedRemoveAsync(detail.Spbh, inCk, qty, adjustedUnit); | |
| 3647 | + | |
| 3648 | + var outCk = await PickOutboundCkForRollbackAddAsync(detail.Spbh, outIds, originalUnit, qty); | |
| 3649 | + await SpCostWeightedAddAsync(detail.Spbh, outCk, qty, originalUnit); | |
| 3650 | + } | |
| 3212 | 3651 | } |
| 3213 | 3652 | |
| 3214 | 3653 | #endregion | ... | ... |