Commit dfbda420886155d80732a83291d4268813e4b1c8
1 parent
b14267c9
更改了一堆东西
Showing
20 changed files
with
3333 additions
and
967 deletions
Antis.Erp.Plat/antis-ncc-admin/src/views/wtCgthd/Form.vue
| 1 | -<template> | |
| 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%"> | |
| 1 | +<template> | |
| 2 | + <el-dialog :title="isNew ? '新建' : 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"> |
| 5 | - <el-col :span="12"> | |
| 6 | - <el-form-item label="单据编号" prop="id"> | |
| 7 | - <el-input v-model="dataForm.id" placeholder="请输入" clearable readonly :style='{"width":"100%"}' > | |
| 8 | - </el-input> | |
| 9 | - </el-form-item> | |
| 10 | - </el-col> | |
| 5 | + <el-col :span="12"> | |
| 6 | + <el-form-item label="单据编号" prop="id"> | |
| 7 | + <el-input v-model="dataForm.id" placeholder="保存后自动生成" readonly :style='{"width":"100%"}' /> | |
| 8 | + </el-form-item> | |
| 9 | + </el-col> | |
| 11 | 10 | <el-col :span="12"> |
| 12 | 11 | <el-form-item label="单据日期" prop="djrq"> |
| 13 | 12 | <el-date-picker v-model="dataForm.djrq" placeholder="请选择" clearable :style='{"width":"100%"}' type='date' format="yyyy-MM-dd" value-format="timestamp" readonly > |
| ... | ... | @@ -61,12 +60,19 @@ |
| 61 | 60 | </el-select> |
| 62 | 61 | </template> |
| 63 | 62 | </el-table-column> |
| 64 | - <el-table-column prop="spmc" label="商品名称"> | |
| 65 | - <template slot-scope="scope"> | |
| 66 | - <el-input v-model="scope.row.spmc" placeholder="请输入" clearable ></el-input> | |
| 67 | - </template> | |
| 68 | - </el-table-column> | |
| 69 | - <!-- 明细表商品条码字段 --> | |
| 63 | + <el-table-column prop="spmc" label="商品名称"> | |
| 64 | + <template slot-scope="scope"> | |
| 65 | + <el-input v-model="scope.row.spmc" placeholder="请输入" clearable ></el-input> | |
| 66 | + </template> | |
| 67 | + </el-table-column> | |
| 68 | + <el-table-column prop="dyddbh" label="入库编号" width="200"> | |
| 69 | + <template slot-scope="scope"> | |
| 70 | + <el-select v-model="scope.row.dyddbh" placeholder="请选择入库单" clearable filterable @change="handleInboundOrderChange(scope.row, scope.$index)" :disabled="!scope.row.spbh"> | |
| 71 | + <el-option v-for="item in (inboundOrdersMap[scope.$index] || [])" :key="item.orderid" :label="item.orderid + ' (¥' + item.dj + ' × ' + item.sl + ')'" :value="item.orderid"></el-option> | |
| 72 | + </el-select> | |
| 73 | + </template> | |
| 74 | + </el-table-column> | |
| 75 | + <!-- 明细表商品条码字段 --> | |
| 70 | 76 | <!-- <el-table-column prop="sptm" label="商品条码" v-if="false"> |
| 71 | 77 | <template slot-scope="scope"> |
| 72 | 78 | <el-input v-model="scope.row.sptm" placeholder="请输入" clearable ></el-input> |
| ... | ... | @@ -88,11 +94,11 @@ |
| 88 | 94 | ></el-input> |
| 89 | 95 | </template> |
| 90 | 96 | </el-table-column> |
| 91 | - <el-table-column prop="dj" label="单价"> | |
| 92 | - <template slot-scope="scope"> | |
| 93 | - <el-input v-model="scope.row.dj" placeholder="请输入" clearable @input="handlePriceChange(scope.row)"></el-input> | |
| 94 | - </template> | |
| 95 | - </el-table-column> | |
| 97 | + <el-table-column prop="dj" label="单价"> | |
| 98 | + <template slot-scope="scope"> | |
| 99 | + <el-input v-model="scope.row.dj" placeholder="选择入库编号自动填入" readonly :style="{ background: '#f5f7fa' }"></el-input> | |
| 100 | + </template> | |
| 101 | + </el-table-column> | |
| 96 | 102 | <el-table-column prop="je" label="金额"> |
| 97 | 103 | <template slot-scope="scope"> |
| 98 | 104 | <el-input v-model="scope.row.je" placeholder="请输入" clearable @input="handleAmountFieldChange(scope.row)"></el-input> |
| ... | ... | @@ -234,6 +240,7 @@ |
| 234 | 240 | loading: false, |
| 235 | 241 | visible: false, |
| 236 | 242 | isDetail: false, |
| 243 | + isNew: false, | |
| 237 | 244 | dataForm: { |
| 238 | 245 | id:'', |
| 239 | 246 | id:undefined, |
| ... | ... | @@ -259,7 +266,8 @@ |
| 259 | 266 | ckckOptions : [], |
| 260 | 267 | spbhOptions : [], |
| 261 | 268 | skzhOptions : [], |
| 262 | - productCache: new Map(), // 商品信息缓存 | |
| 269 | + productCache: new Map(), | |
| 270 | + inboundOrdersMap: {}, | |
| 263 | 271 | } |
| 264 | 272 | }, |
| 265 | 273 | computed: { |
| ... | ... | @@ -326,45 +334,53 @@ |
| 326 | 334 | }, |
| 327 | 335 | init(id, isDetail) { |
| 328 | 336 | this.dataForm.id = id || 0; |
| 337 | + this.isNew = !id; | |
| 329 | 338 | this.visible = true; |
| 330 | 339 | this.isDetail = isDetail || false; |
| 331 | 340 | this.$nextTick(() => { |
| 332 | 341 | this.$refs['elForm'].resetFields(); |
| 333 | - if (this.dataForm.id) { | |
| 334 | - request({ | |
| 335 | - url: '/api/Extend/WtXsckd/' + this.dataForm.id, | |
| 336 | - method: 'get' | |
| 337 | - }).then(res =>{ | |
| 338 | - this.dataForm = res.data; | |
| 339 | - // 确保wtXsckdMxList存在 | |
| 340 | - if (!this.dataForm.wtXsckdMxList) { | |
| 341 | - this.dataForm.wtXsckdMxList = []; | |
| 342 | + if (this.dataForm.id) { | |
| 343 | + request({ | |
| 344 | + url: '/api/Extend/WtXsckd/' + this.dataForm.id, | |
| 345 | + method: 'get' | |
| 346 | + }).then(res =>{ | |
| 347 | + this.dataForm = res.data; | |
| 348 | + if (!this.dataForm.wtXsckdMxList) { | |
| 349 | + this.dataForm.wtXsckdMxList = []; | |
| 350 | + } | |
| 351 | + this.dataForm.wtXsckdMxList.forEach(item => { | |
| 352 | + if (!item.selectedSerialNumbers) { | |
| 353 | + this.$set(item, 'selectedSerialNumbers', []); | |
| 342 | 354 | } |
| 343 | - // 为每个明细项添加序列号相关字段 | |
| 344 | - this.dataForm.wtXsckdMxList.forEach(item => { | |
| 345 | - if (!item.selectedSerialNumbers) { | |
| 346 | - this.$set(item, 'selectedSerialNumbers', []); | |
| 347 | - } | |
| 348 | - if (!item.hasOwnProperty('spxlhLoaded')) { | |
| 349 | - this.$set(item, 'spxlhLoaded', false); | |
| 350 | - } | |
| 351 | - if (!item.hasOwnProperty('description')) { | |
| 352 | - this.$set(item, 'description', undefined); | |
| 353 | - } | |
| 354 | - if (!item.hasOwnProperty('productQuery')) { | |
| 355 | - this.$set(item, 'productQuery', ''); | |
| 356 | - } | |
| 357 | - }); | |
| 358 | - | |
| 359 | - // 同步明细表的出库仓库与主表保持一致 | |
| 360 | - this.syncDetailWarehouses(); | |
| 361 | - // 计算总金额 | |
| 362 | - this.updateTotalAmount(); | |
| 363 | - }) | |
| 364 | - } else { | |
| 365 | - // 新建时确保wtXsckdMxList为空数组 | |
| 366 | - this.dataForm.wtXsckdMxList = []; | |
| 367 | - } | |
| 355 | + if (!item.hasOwnProperty('spxlhLoaded')) { | |
| 356 | + this.$set(item, 'spxlhLoaded', false); | |
| 357 | + } | |
| 358 | + if (!item.hasOwnProperty('description')) { | |
| 359 | + this.$set(item, 'description', undefined); | |
| 360 | + } | |
| 361 | + if (!item.hasOwnProperty('productQuery')) { | |
| 362 | + this.$set(item, 'productQuery', ''); | |
| 363 | + } | |
| 364 | + if (!item.hasOwnProperty('dyddbh')) { | |
| 365 | + this.$set(item, 'dyddbh', item.dyddbh || undefined); | |
| 366 | + } | |
| 367 | + }); | |
| 368 | + this.syncDetailWarehouses(); | |
| 369 | + this.updateTotalAmount(); | |
| 370 | + }) | |
| 371 | + } else { | |
| 372 | + this.dataForm.wtXsckdMxList = []; | |
| 373 | + // 预生成单据编号 | |
| 374 | + request({ | |
| 375 | + url: '/api/Extend/WtXsckd/Actions/GenerateBillNo', | |
| 376 | + method: 'GET', | |
| 377 | + data: { djlx: this.dataForm.djlx } | |
| 378 | + }).then(res => { | |
| 379 | + if (res.data && res.data.billNo) { | |
| 380 | + this.dataForm.id = res.data.billNo; | |
| 381 | + } | |
| 382 | + }).catch(() => {}); | |
| 383 | + } | |
| 368 | 384 | }) |
| 369 | 385 | }, |
| 370 | 386 | dataFormSubmit() { |
| ... | ... | @@ -445,7 +461,7 @@ |
| 445 | 461 | |
| 446 | 462 | this.$refs['elForm'].validate((valid) => { |
| 447 | 463 | if (valid) { |
| 448 | - if (!this.dataForm.id) { | |
| 464 | + if (this.isNew) { | |
| 449 | 465 | request({ |
| 450 | 466 | url: `/api/Extend/WtXsckd`, |
| 451 | 467 | method: 'post', |
| ... | ... | @@ -487,7 +503,7 @@ |
| 487 | 503 | |
| 488 | 504 | let item = { |
| 489 | 505 | rkck:undefined, |
| 490 | - ckck:this.dataForm.cjck, // 自动使用主表的出库仓库 | |
| 506 | + ckck:this.dataForm.cjck, | |
| 491 | 507 | spbh:undefined, |
| 492 | 508 | spmc:undefined, |
| 493 | 509 | sptm:undefined, |
| ... | ... | @@ -495,10 +511,11 @@ |
| 495 | 511 | sl:undefined, |
| 496 | 512 | dj:undefined, |
| 497 | 513 | je:undefined, |
| 514 | + dyddbh: undefined, | |
| 498 | 515 | description: undefined, |
| 499 | 516 | selectedSerialNumbers: [], |
| 500 | 517 | spxlhLoaded: false, |
| 501 | - productQuery: '', // 为每一行添加独立的查询条件 | |
| 518 | + productQuery: '', | |
| 502 | 519 | } |
| 503 | 520 | |
| 504 | 521 | if (!this.dataForm.wtXsckdMxList) { |
| ... | ... | @@ -515,42 +532,86 @@ |
| 515 | 532 | }, |
| 516 | 533 | async handleProductChange(row) { |
| 517 | 534 | const product = this.spbhOptions.find(item => item.F_Id === row.spbh); |
| 518 | - console.log('选中商品', row.spbh, product); | |
| 519 | 535 | if (product) { |
| 520 | 536 | row.spmc = product.F_Spmc || ''; |
| 521 | - // 请求后端接口获取该商品最近一次采购入库单价 | |
| 522 | - try { | |
| 523 | - const res = await request({ url: '/api/Extend/WtXsckd/GetAllPurchaseInboundDetails', method: 'get' }); | |
| 524 | - console.log('采购入库明细接口返回', res); | |
| 525 | - const arr = res && res.data && res.data.data ? res.data.data : []; | |
| 526 | - const details = arr.filter(x => x.Spbh === row.spbh); | |
| 527 | - console.log('该商品所有入库明细', details); | |
| 528 | - let latest = null; | |
| 529 | - if (details.length > 0) { | |
| 530 | - latest = details.sort((a, b) => (b.Djbh || b.Id || 0) < (a.Djbh || a.Id || 0) ? 1 : -1)[0]; | |
| 531 | - } | |
| 532 | - console.log('最新一条明细', latest); | |
| 533 | - row.dj = latest ? latest.Dj : ''; | |
| 534 | - } catch (e) { | |
| 535 | - row.dj = ''; | |
| 536 | - } | |
| 537 | 537 | |
| 538 | - // 获取商品序列号类型信息 | |
| 538 | + this.$set(row, 'dyddbh', undefined); | |
| 539 | + this.$set(row, 'dj', ''); | |
| 540 | + this.$set(row, 'je', ''); | |
| 541 | + | |
| 542 | + await this.loadInboundOrders(row); | |
| 543 | + | |
| 539 | 544 | this.getProductInfo(row.spbh).then(productInfo => { |
| 540 | 545 | this.$set(row, 'spxlhLoaded', true); |
| 541 | 546 | this.$forceUpdate(); |
| 542 | - if (!(productInfo && productInfo.spxlhType)) { | |
| 543 | - this.$message.warning('无法获取序列号类型信息'); | |
| 544 | - } else { | |
| 545 | - // 如果商品需要序列号,给出提示 | |
| 547 | + if (productInfo && productInfo.spxlhType) { | |
| 546 | 548 | const spxlhType = productInfo.spxlhType; |
| 547 | 549 | if (spxlhType === '1' || spxlhType === '2') { |
| 548 | - this.$message.info(`商品"${row.spmc}"需要选择序列号,请在明细中选择序列号`); | |
| 550 | + this.$message.info(`商品"${row.spmc}"需要选择序列号`); | |
| 549 | 551 | } |
| 550 | 552 | } |
| 551 | 553 | }); |
| 552 | 554 | } |
| 553 | 555 | }, |
| 556 | + async loadInboundOrders(row) { | |
| 557 | + const warehouse = row.ckck || this.dataForm.cjck; | |
| 558 | + if (!row.spbh || !warehouse) return; | |
| 559 | + | |
| 560 | + try { | |
| 561 | + const res = await request({ | |
| 562 | + url: '/api/Extend/WtXsckd/GetPurchaseInboundOrders', | |
| 563 | + method: 'get', | |
| 564 | + params: { spbh: row.spbh, ck: warehouse } | |
| 565 | + }); | |
| 566 | + let rawList = []; | |
| 567 | + if (Array.isArray(res)) { | |
| 568 | + rawList = res; | |
| 569 | + } else if (Array.isArray(res.data)) { | |
| 570 | + rawList = res.data; | |
| 571 | + } else if (res.data && Array.isArray(res.data.data)) { | |
| 572 | + rawList = res.data.data; | |
| 573 | + } else if (res.data && res.data.data && Array.isArray(res.data.data.data)) { | |
| 574 | + rawList = res.data.data.data; | |
| 575 | + } | |
| 576 | + const list = (rawList || []).map(item => this.normalizeInboundOrder(item)); | |
| 577 | + const idx = this.dataForm.wtXsckdMxList.indexOf(row); | |
| 578 | + if (idx >= 0) { | |
| 579 | + this.$set(this.inboundOrdersMap, idx, list); | |
| 580 | + } | |
| 581 | + } catch (e) { | |
| 582 | + console.error('加载入库单列表失败', e); | |
| 583 | + } | |
| 584 | + }, | |
| 585 | + normalizeInboundOrder(item) { | |
| 586 | + const getVal = (obj, keys) => { | |
| 587 | + for (const key of keys) { | |
| 588 | + if (obj && obj[key] !== undefined && obj[key] !== null && obj[key] !== '') { | |
| 589 | + return obj[key]; | |
| 590 | + } | |
| 591 | + } | |
| 592 | + return ''; | |
| 593 | + }; | |
| 594 | + return { | |
| 595 | + orderid: getVal(item, ['orderid', 'orderId', 'OrderId', 'ORDERID', 'f_id', 'F_Id']), | |
| 596 | + dj: getVal(item, ['dj', 'Dj', 'DJ']), | |
| 597 | + sl: getVal(item, ['sl', 'Sl', 'SL']), | |
| 598 | + spmc: getVal(item, ['spmc', 'Spmc', 'Spmc', 'SPMC']), | |
| 599 | + djrq: getVal(item, ['djrq', 'Djrq', 'DJRQ']) | |
| 600 | + }; | |
| 601 | + }, | |
| 602 | + handleInboundOrderChange(row, index) { | |
| 603 | + const orders = this.inboundOrdersMap[index] || []; | |
| 604 | + const selected = orders.find(o => o.orderid === row.dyddbh); | |
| 605 | + if (selected) { | |
| 606 | + this.$set(row, 'dj', selected.dj); | |
| 607 | + const sl = parseFloat(row.sl) || 0; | |
| 608 | + if (sl > 0) { | |
| 609 | + this.$set(row, 'je', (sl * parseFloat(selected.dj)).toFixed(2)); | |
| 610 | + } | |
| 611 | + this.updateTotalAmount(); | |
| 612 | + this.$message.success(`已自动填入采购单价: ¥${selected.dj}`); | |
| 613 | + } | |
| 614 | + }, | |
| 554 | 615 | handleProductQuery(val, scope) { |
| 555 | 616 | // 为每一行设置独立的查询条件 |
| 556 | 617 | this.$set(scope.row, 'productQuery', val); |
| ... | ... | @@ -569,9 +630,14 @@ |
| 569 | 630 | |
| 570 | 631 | // 处理主表出库仓库变化,同步更新所有明细行的出库仓库 |
| 571 | 632 | handleMainWarehouseChange(value) { |
| 572 | - console.log('主表出库仓库变化:', value); | |
| 573 | - // 同步更新所有明细行的出库仓库 | |
| 574 | 633 | this.syncDetailWarehouses(); |
| 634 | + this.inboundOrdersMap = {}; | |
| 635 | + this.dataForm.wtXsckdMxList.forEach(row => { | |
| 636 | + this.$set(row, 'dyddbh', undefined); | |
| 637 | + this.$set(row, 'dj', ''); | |
| 638 | + this.$set(row, 'je', ''); | |
| 639 | + }); | |
| 640 | + this.updateTotalAmount(); | |
| 575 | 641 | }, |
| 576 | 642 | |
| 577 | 643 | // 同步明细表的出库仓库与主表保持一致 | ... | ... |
Antis.Erp.Plat/antis-ncc-admin/src/views/wtCzd/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="90%"> |
| 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"> |
| ... | ... | @@ -42,16 +42,6 @@ |
| 42 | 42 | </el-col> |
| 43 | 43 | <el-col :span="24"> |
| 44 | 44 | <el-form-item label-width="0"> |
| 45 | - <el-button type="info" size="mini" @click="debugFrontendData" style="margin-bottom: 10px;"> | |
| 46 | - 调试数据状态 | |
| 47 | - </el-button> | |
| 48 | - <el-button type="warning" size="mini" @click="quickDiagnosis" style="margin-bottom: 10px;"> | |
| 49 | - 快速诊断 | |
| 50 | - </el-button> | |
| 51 | - </el-form-item> | |
| 52 | - </el-col> | |
| 53 | - <el-col :span="24"> | |
| 54 | - <el-form-item label-width="0"> | |
| 55 | 45 | <el-table :data="dataForm.wtCzdmxList" size='mini'> |
| 56 | 46 | <el-table-column type="index" width="50" label="序号" align="center" /> |
| 57 | 47 | <el-table-column prop="chck" label="出货仓库"> |
| ... | ... | @@ -290,22 +280,16 @@ |
| 290 | 280 | newVal.forEach((row, index) => { |
| 291 | 281 | // 确保行数据有效且仓库和商品编号都有值 |
| 292 | 282 | if (row && typeof row === 'object' && |
| 293 | - row.chck && row.chck !== '' && | |
| 294 | - row.spbh && row.spbh !== '' && | |
| 295 | - !row.zmkc && !row.stockLoading && !row.hasQueriedStock) { | |
| 296 | - // 标记已查询,避免重复查询 | |
| 297 | - this.$set(row, 'hasQueriedStock', true); | |
| 298 | - try { | |
| 299 | - console.log('自动触发库存查询:', { | |
| 300 | - row: row, | |
| 301 | - chck: row.chck, | |
| 302 | - spbh: row.spbh | |
| 303 | - }); | |
| 304 | - this.queryStock(row, 'wtCzdmxList'); | |
| 305 | - } catch (err) { | |
| 306 | - console.warn('自动查询库存失败:', err, row); | |
| 307 | - } | |
| 283 | + row.chck && row.chck !== '' && | |
| 284 | + row.spbh && row.spbh !== '' && | |
| 285 | + !row.zmkc && !row.stockLoading && !row.hasQueriedStock) { | |
| 286 | + this.$set(row, 'hasQueriedStock', true); | |
| 287 | + try { | |
| 288 | + this.queryStock(row, 'wtCzdmxList'); | |
| 289 | + } catch (err) { | |
| 290 | + console.warn('自动查询库存失败:', err); | |
| 308 | 291 | } |
| 292 | + } | |
| 309 | 293 | }); |
| 310 | 294 | } |
| 311 | 295 | } |
| ... | ... | @@ -321,22 +305,16 @@ |
| 321 | 305 | newVal.forEach((row, index) => { |
| 322 | 306 | // 确保行数据有效且仓库和商品编号都有值 |
| 323 | 307 | if (row && typeof row === 'object' && |
| 324 | - row.chck && row.chck !== '' && | |
| 325 | - row.spbh && row.spbh !== '' && | |
| 326 | - !row.zmkc && !row.stockLoading && !row.hasQueriedStock) { | |
| 327 | - // 标记已查询,避免重复查询 | |
| 328 | - this.$set(row, 'hasQueriedStock', true); | |
| 329 | - try { | |
| 330 | - console.log('自动触发库存查询:', { | |
| 331 | - row: row, | |
| 332 | - chck: row.chck, | |
| 333 | - spbh: row.spbh | |
| 334 | - }); | |
| 335 | - this.queryStock(row, 'wtCzdmx2List'); | |
| 336 | - } catch (err) { | |
| 337 | - console.warn('自动查询库存失败:', err, row); | |
| 338 | - } | |
| 308 | + row.chck && row.chck !== '' && | |
| 309 | + row.spbh && row.spbh !== '' && | |
| 310 | + !row.zmkc && !row.stockLoading && !row.hasQueriedStock) { | |
| 311 | + this.$set(row, 'hasQueriedStock', true); | |
| 312 | + try { | |
| 313 | + this.queryStock(row, 'wtCzdmx2List'); | |
| 314 | + } catch (err) { | |
| 315 | + console.warn('自动查询库存失败:', err); | |
| 339 | 316 | } |
| 317 | + } | |
| 340 | 318 | }); |
| 341 | 319 | } |
| 342 | 320 | } |
| ... | ... | @@ -353,207 +331,131 @@ |
| 353 | 331 | mounted() { |
| 354 | 332 | }, |
| 355 | 333 | methods: { |
| 356 | - getchckOptions(){ | |
| 357 | - previewDataInterface('681758216954053893').then(res => { | |
| 358 | - this.chckOptions = res.data | |
| 359 | - console.log('出库仓库选项数据:', this.chckOptions); | |
| 360 | - | |
| 361 | - // 检查仓库数据是否正确获取 | |
| 362 | - if (this.chckOptions && this.chckOptions.length > 0) { | |
| 363 | - console.log('成功获取出库仓库数据,数量:', this.chckOptions.length); | |
| 364 | - console.log('第一个仓库示例:', this.chckOptions[0]); | |
| 365 | - } else { | |
| 366 | - console.warn('出库仓库数据为空,请检查接口配置'); | |
| 367 | - // 尝试备用方法 | |
| 368 | - this.getWarehouseOptionsFallback(); | |
| 369 | - } | |
| 370 | - }).catch(err => { | |
| 371 | - console.error('获取出库仓库数据失败:', err); | |
| 372 | - this.$message.error('获取出库仓库数据失败,请检查网络连接'); | |
| 373 | - // 尝试备用方法 | |
| 334 | + getchckOptions(){ | |
| 335 | + previewDataInterface('681758216954053893').then(res => { | |
| 336 | + this.chckOptions = res.data | |
| 337 | + if (!this.chckOptions || this.chckOptions.length === 0) { | |
| 374 | 338 | this.getWarehouseOptionsFallback(); |
| 375 | - }); | |
| 376 | - }, | |
| 339 | + } | |
| 340 | + }).catch(err => { | |
| 341 | + console.error('获取出库仓库数据失败:', err); | |
| 342 | + this.$message.error('获取出库仓库数据失败,请检查网络连接'); | |
| 343 | + this.getWarehouseOptionsFallback(); | |
| 344 | + }); | |
| 345 | + }, | |
| 377 | 346 | getdwOptions(){ |
| 378 | 347 | getDictionaryDataSelector('681805755032012037').then(res => { |
| 379 | 348 | this.dwOptions = res.data.list |
| 380 | 349 | }); |
| 381 | 350 | }, |
| 382 | - getspbhOptions(){ | |
| 383 | - previewDataInterface('675937572047815941').then(res => { | |
| 384 | - this.spbhOptions = res.data | |
| 385 | - console.log('商品选项数据:', this.spbhOptions); | |
| 386 | - | |
| 387 | - // 检查商品数据是否正确获取 | |
| 388 | - if (this.spbhOptions && this.spbhOptions.length > 0) { | |
| 389 | - console.log('成功获取商品数据,数量:', this.spbhOptions.length); | |
| 390 | - console.log('第一个商品示例:', this.spbhOptions[0]); | |
| 391 | - } else { | |
| 392 | - console.warn('商品数据为空,请检查接口配置'); | |
| 393 | - } | |
| 394 | - }).catch(err => { | |
| 395 | - console.error('获取商品数据失败:', err); | |
| 396 | - this.$message.error('获取商品数据失败,请检查网络连接'); | |
| 397 | - }); | |
| 398 | - }, | |
| 399 | - getrhckOptions() { | |
| 400 | - previewDataInterface('681758216954053893').then(res => { | |
| 401 | - this.rhckOptions = res.data | |
| 402 | - console.log('入货仓库选项数据:', this.rhckOptions); | |
| 403 | - | |
| 404 | - // 检查仓库数据是否正确获取 | |
| 405 | - if (this.rhckOptions && this.rhckOptions.length > 0) { | |
| 406 | - console.log('成功获取入货仓库数据,数量:', this.rhckOptions.length); | |
| 407 | - console.log('第一个仓库示例:', this.rhckOptions[0]); | |
| 408 | - } else { | |
| 409 | - console.warn('入货仓库数据为空,请检查接口配置'); | |
| 410 | - // 尝试备用方法 | |
| 411 | - this.getWarehouseOptionsFallback(); | |
| 412 | - } | |
| 413 | - }).catch(err => { | |
| 414 | - console.error('获取入货仓库数据失败:', err); | |
| 415 | - this.$message.error('获取入货仓库数据失败,请检查网络连接'); | |
| 416 | - // 尝试备用方法 | |
| 351 | + getspbhOptions(){ | |
| 352 | + previewDataInterface('675937572047815941').then(res => { | |
| 353 | + this.spbhOptions = res.data | |
| 354 | + }).catch(err => { | |
| 355 | + console.error('获取商品数据失败:', err); | |
| 356 | + this.$message.error('获取商品数据失败,请检查网络连接'); | |
| 357 | + }); | |
| 358 | + }, | |
| 359 | + getrhckOptions() { | |
| 360 | + previewDataInterface('681758216954053893').then(res => { | |
| 361 | + this.rhckOptions = res.data | |
| 362 | + if (!this.rhckOptions || this.rhckOptions.length === 0) { | |
| 417 | 363 | this.getWarehouseOptionsFallback(); |
| 418 | - }); | |
| 419 | - }, | |
| 420 | - // 备用仓库获取方法 | |
| 421 | - getWarehouseOptionsFallback() { | |
| 422 | - console.log('尝试使用备用方法获取仓库数据...'); | |
| 423 | - // 尝试使用字典数据接口 | |
| 424 | - getDictionaryDataSelector('681758216954053893').then(res => { | |
| 425 | - if (res.data && res.data.list && res.data.list.length > 0) { | |
| 426 | - console.log('备用方法成功获取仓库数据:', res.data.list); | |
| 427 | - this.chckOptions = res.data.list; | |
| 428 | - this.rhckOptions = res.data.list; | |
| 429 | - } else { | |
| 430 | - console.warn('备用方法也无法获取仓库数据'); | |
| 431 | - // 设置一些默认的仓库选项用于测试 | |
| 432 | - this.setDefaultWarehouseOptions(); | |
| 433 | - } | |
| 434 | - }).catch(err => { | |
| 435 | - console.error('备用方法获取仓库数据失败:', err); | |
| 436 | - // 设置一些默认的仓库选项用于测试 | |
| 364 | + } | |
| 365 | + }).catch(err => { | |
| 366 | + console.error('获取入货仓库数据失败:', err); | |
| 367 | + this.$message.error('获取入货仓库数据失败,请检查网络连接'); | |
| 368 | + this.getWarehouseOptionsFallback(); | |
| 369 | + }); | |
| 370 | + }, | |
| 371 | + getWarehouseOptionsFallback() { | |
| 372 | + getDictionaryDataSelector('681758216954053893').then(res => { | |
| 373 | + if (res.data && res.data.list && res.data.list.length > 0) { | |
| 374 | + this.chckOptions = res.data.list; | |
| 375 | + this.rhckOptions = res.data.list; | |
| 376 | + } else { | |
| 437 | 377 | this.setDefaultWarehouseOptions(); |
| 438 | - }); | |
| 439 | - }, | |
| 440 | - // 设置默认仓库选项(用于测试) | |
| 441 | - setDefaultWarehouseOptions() { | |
| 442 | - console.log('设置默认仓库选项用于测试...'); | |
| 443 | - const defaultWarehouses = [ | |
| 444 | - { F_Id: '1', F_mdmc: '主仓库' }, | |
| 445 | - { F_Id: '2', F_mdmc: '分仓库A' }, | |
| 446 | - { F_Id: '3', F_mdmc: '分仓库B' } | |
| 447 | - ]; | |
| 448 | - this.chckOptions = defaultWarehouses; | |
| 449 | - this.rhckOptions = defaultWarehouses; | |
| 450 | - console.log('已设置默认仓库选项:', defaultWarehouses); | |
| 451 | - }, | |
| 452 | - handleProductChange(row) { | |
| 453 | - // 增强参数安全检查 | |
| 454 | - if (!row || typeof row !== 'object') { | |
| 455 | - console.warn('handleProductChange: row参数无效', row); | |
| 456 | - return; | |
| 457 | 378 | } |
| 379 | + }).catch(() => { | |
| 380 | + this.setDefaultWarehouseOptions(); | |
| 381 | + }); | |
| 382 | + }, | |
| 383 | + setDefaultWarehouseOptions() { | |
| 384 | + const defaultWarehouses = [ | |
| 385 | + { F_Id: '1', F_mdmc: '主仓库' }, | |
| 386 | + { F_Id: '2', F_mdmc: '分仓库A' }, | |
| 387 | + { F_Id: '3', F_mdmc: '分仓库B' } | |
| 388 | + ]; | |
| 389 | + this.chckOptions = defaultWarehouses; | |
| 390 | + this.rhckOptions = defaultWarehouses; | |
| 391 | + }, | |
| 392 | + handleProductChange(row) { | |
| 393 | + if (!row || typeof row !== 'object') return; | |
| 394 | + | |
| 395 | + const product = this.spbhOptions.find(item => item.F_Id === row.spbh); | |
| 396 | + if (product) { | |
| 397 | + this.$set(row, 'spmc', product.F_Spmc || ''); | |
| 458 | 398 | |
| 459 | - // 选中商品后可自动回填商品名称等信息 | |
| 460 | - const product = this.spbhOptions.find(item => item.F_Id === row.spbh); | |
| 461 | - if (product) { | |
| 462 | - row.spmc = product.F_Spmc || ''; | |
| 463 | - | |
| 464 | - // 如果已选择仓库,自动获取成本价格和库存 | |
| 465 | - if (row.chck) { | |
| 466 | - // 自动获取成本价格 | |
| 467 | - this.getProductCost(row); | |
| 468 | - | |
| 469 | - // 判断当前行属于哪个明细表 | |
| 470 | - try { | |
| 471 | - const isInWtCzdmxList = this.dataForm.wtCzdmxList && Array.isArray(this.dataForm.wtCzdmxList) && this.dataForm.wtCzdmxList.includes(row); | |
| 472 | - const isInWtCzdmx2List = this.dataForm.wtCzdmx2List && Array.isArray(this.dataForm.wtCzdmx2List) && this.dataForm.wtCzdmx2List.includes(row); | |
| 473 | - | |
| 474 | - if (isInWtCzdmxList) { | |
| 475 | - this.queryStock(row, 'wtCzdmxList'); | |
| 476 | - } else if (isInWtCzdmx2List) { | |
| 477 | - this.queryStock(row, 'wtCzdmx2List'); | |
| 478 | - } | |
| 479 | - } catch (err) { | |
| 480 | - console.warn('判断明细表归属失败:', err, row); | |
| 481 | - } | |
| 482 | - } | |
| 399 | + const isInList1 = this.dataForm.wtCzdmxList && this.dataForm.wtCzdmxList.includes(row); | |
| 400 | + const listName = isInList1 ? 'wtCzdmxList' : 'wtCzdmx2List'; | |
| 401 | + | |
| 402 | + // 获取成本价格(getProductCost 内部会自动从主表取仓库) | |
| 403 | + this.getProductCost(row); | |
| 404 | + | |
| 405 | + // 自动查询库存(需要确保仓库有值) | |
| 406 | + const warehouseId = row.chck || (isInList1 ? this.dataForm.chck : this.dataForm.rhck); | |
| 407 | + if (warehouseId) { | |
| 408 | + if (!row.chck) this.$set(row, 'chck', warehouseId); | |
| 409 | + this.queryStock(row, listName); | |
| 483 | 410 | } |
| 484 | - }, | |
| 411 | + } | |
| 412 | + }, | |
| 485 | 413 | |
| 486 | - // 获取商品成本价格 | |
| 487 | - async getProductCost(row) { | |
| 488 | - if (!row.spbh || !row.chck) { | |
| 489 | - console.warn('getProductCost: 商品编号或仓库不能为空', { | |
| 490 | - spbh: row.spbh, | |
| 491 | - chck: row.chck, | |
| 492 | - row: row | |
| 493 | - }); | |
| 494 | - return; | |
| 495 | - } | |
| 496 | - | |
| 497 | - console.log('开始获取商品成本价格:', { | |
| 498 | - productId: row.spbh, | |
| 499 | - warehouseId: row.chck, | |
| 500 | - productName: row.spmc | |
| 414 | + async getProductCost(row) { | |
| 415 | + if (!row.spbh) return; | |
| 416 | + | |
| 417 | + const isInList1 = this.dataForm.wtCzdmxList && this.dataForm.wtCzdmxList.includes(row); | |
| 418 | + const warehouseId = row.chck || (isInList1 ? this.dataForm.chck : this.dataForm.rhck) || ''; | |
| 419 | + | |
| 420 | + try { | |
| 421 | + const res = await request({ | |
| 422 | + url: '/api/Extend/WtCzd/GetProductCost', | |
| 423 | + method: 'post', | |
| 424 | + data: { | |
| 425 | + ProductId: row.spbh, | |
| 426 | + WarehouseId: warehouseId | |
| 427 | + } | |
| 501 | 428 | }); |
| 502 | 429 | |
| 503 | - try { | |
| 504 | - const res = await request({ | |
| 505 | - url: '/api/Extend/WtCzd/GetProductCost', | |
| 506 | - method: 'post', | |
| 507 | - data: { | |
| 508 | - ProductId: row.spbh, | |
| 509 | - WarehouseId: row.chck | |
| 510 | - } | |
| 511 | - }); | |
| 512 | - | |
| 513 | - console.log('获取商品成本价格接口响应:', res); | |
| 514 | - | |
| 515 | - if (res.data && res.data.success) { | |
| 516 | - const costPrice = res.data.data; | |
| 517 | - console.log('成本价格数据:', { | |
| 518 | - costPrice: costPrice, | |
| 519 | - source: res.data.source, | |
| 520 | - type: typeof costPrice | |
| 521 | - }); | |
| 522 | - | |
| 523 | - if (costPrice > 0) { | |
| 524 | - row.cbdj = costPrice; | |
| 525 | - console.log(`商品 ${row.spmc} 成本价格获取成功:`, costPrice, '来源:', res.data.source); | |
| 526 | - | |
| 527 | - // 如果数量已填写,自动计算成本金额 | |
| 528 | - if (row.sl && row.sl > 0) { | |
| 529 | - row.cbje = (parseFloat(row.sl) * costPrice).toFixed(2); | |
| 530 | - } | |
| 531 | - | |
| 532 | - // 强制更新视图 | |
| 533 | - this.$forceUpdate(); | |
| 534 | - } else { | |
| 535 | - console.log(`商品 ${row.spmc} 未找到成本价格信息`); | |
| 536 | - row.cbdj = ''; | |
| 537 | - } | |
| 538 | - } else { | |
| 539 | - console.warn('获取商品成本价格失败:', (res.data && res.data.msg) || '未知错误'); | |
| 540 | - console.warn('完整响应数据:', res); | |
| 430 | + let costPrice = 0; | |
| 431 | + if (res.data !== undefined && res.data !== null) { | |
| 432 | + if (typeof res.data === 'object' && res.data.success) { | |
| 433 | + costPrice = parseFloat(res.data.data) || 0; | |
| 434 | + } else if (typeof res.data === 'number') { | |
| 435 | + costPrice = res.data; | |
| 436 | + } else if (typeof res.data === 'object') { | |
| 437 | + costPrice = parseFloat(res.data.cbdj || res.data.data || res.data.price || 0); | |
| 541 | 438 | } |
| 542 | - } catch (error) { | |
| 543 | - console.error('获取商品成本价格异常:', error); | |
| 544 | 439 | } |
| 545 | - }, | |
| 440 | + | |
| 441 | + this.$set(row, 'cbdj', costPrice || ''); | |
| 442 | + if (costPrice > 0 && row.sl && parseFloat(row.sl) > 0) { | |
| 443 | + this.$set(row, 'cbje', (parseFloat(row.sl) * costPrice).toFixed(2)); | |
| 444 | + } | |
| 445 | + } catch (error) { | |
| 446 | + console.error('获取商品成本价格异常:', error); | |
| 447 | + } | |
| 448 | + }, | |
| 546 | 449 | |
| 547 | - // 处理数量变化,自动计算成本金额 | |
| 548 | - handleQuantityChange(row) { | |
| 549 | - if (row.sl && row.cbdj) { | |
| 550 | - const quantity = parseFloat(row.sl); | |
| 551 | - const costPrice = parseFloat(row.cbdj); | |
| 552 | - if (!isNaN(quantity) && !isNaN(costPrice)) { | |
| 553 | - row.cbje = (quantity * costPrice).toFixed(2); | |
| 554 | - } | |
| 450 | + handleQuantityChange(row) { | |
| 451 | + if (row.sl && row.cbdj) { | |
| 452 | + const quantity = parseFloat(row.sl); | |
| 453 | + const costPrice = parseFloat(row.cbdj); | |
| 454 | + if (!isNaN(quantity) && !isNaN(costPrice)) { | |
| 455 | + this.$set(row, 'cbje', (quantity * costPrice).toFixed(2)); | |
| 555 | 456 | } |
| 556 | - }, | |
| 457 | + } | |
| 458 | + }, | |
| 557 | 459 | handleProductQuery(val, scope) { |
| 558 | 460 | // 增强参数安全检查 |
| 559 | 461 | if (!scope || !scope.row || typeof scope.row !== 'object') { |
| ... | ... | @@ -661,63 +563,10 @@ |
| 661 | 563 | } |
| 662 | 564 | }); |
| 663 | 565 | }, |
| 664 | - dataFormSubmit() { | |
| 665 | - this.$refs['elForm'].validate((valid) => { | |
| 666 | - if (valid) { | |
| 667 | - // 数据提交前的调试信息 | |
| 668 | - console.log('=== 保存拆装单前的数据检查 ==='); | |
| 669 | - console.log('出货明细表:', this.dataForm.wtCzdmxList); | |
| 670 | - console.log('入货明细表:', this.dataForm.wtCzdmx2List); | |
| 671 | - | |
| 672 | - // 检查出货明细的序列号信息 | |
| 673 | - if (this.dataForm.wtCzdmxList && this.dataForm.wtCzdmxList.length > 0) { | |
| 674 | - this.dataForm.wtCzdmxList.forEach((item, index) => { | |
| 675 | - console.log(`出货明细${index + 1}:`, { | |
| 676 | - 商品: item.spmc, | |
| 677 | - 序列号字段: item.xlh, | |
| 678 | - 选择序列号: item.selectedSerialNumbers, | |
| 679 | - 选择序列号数量: item.selectedSerialNumbers ? item.selectedSerialNumbers.length : 0 | |
| 680 | - }); | |
| 681 | - }); | |
| 682 | - } | |
| 683 | - | |
| 684 | - // 检查入货明细的序列号信息 | |
| 685 | - if (this.dataForm.wtCzdmx2List && this.dataForm.wtCzdmx2List.length > 0) { | |
| 686 | - this.dataForm.wtCzdmx2List.forEach((item, index) => { | |
| 687 | - console.log(`入货明细${index + 1}:`, { | |
| 688 | - 商品: item.spmc, | |
| 689 | - 序列号字段: item.xlh, | |
| 690 | - 选择序列号: item.selectedSerialNumbers, | |
| 691 | - 选择序列号数量: item.selectedSerialNumbers ? item.selectedSerialNumbers.length : 0 | |
| 692 | - }); | |
| 693 | - }); | |
| 694 | - } | |
| 695 | - | |
| 696 | - // 检查序列化后的数据 | |
| 697 | - console.log('=== 序列化后的数据检查 ==='); | |
| 698 | - console.log('原始数据:', JSON.stringify(this.dataForm, null, 2)); | |
| 699 | - | |
| 700 | - // 检查是否有循环引用或其他问题 | |
| 701 | - try { | |
| 702 | - const serializedData = JSON.parse(JSON.stringify(this.dataForm)); | |
| 703 | - console.log('序列化成功,数据:', serializedData); | |
| 704 | - | |
| 705 | - // 检查序列化后的序列号数据 | |
| 706 | - if (serializedData.wtCzdmxList && serializedData.wtCzdmxList.length > 0) { | |
| 707 | - serializedData.wtCzdmxList.forEach((item, index) => { | |
| 708 | - console.log(`序列化后出货明细${index + 1}:`, { | |
| 709 | - 商品: item.spmc, | |
| 710 | - 序列号字段: item.xlh, | |
| 711 | - 选择序列号: item.selectedSerialNumbers, | |
| 712 | - 选择序列号数量: item.selectedSerialNumbers ? item.selectedSerialNumbers.length : 0 | |
| 713 | - }); | |
| 714 | - }); | |
| 715 | - } | |
| 716 | - } catch (error) { | |
| 717 | - console.error('数据序列化失败:', error); | |
| 718 | - } | |
| 719 | - | |
| 720 | - this.loading = true; | |
| 566 | + dataFormSubmit() { | |
| 567 | + this.$refs['elForm'].validate((valid) => { | |
| 568 | + if (valid) { | |
| 569 | + this.loading = true; | |
| 721 | 570 | let method = 'post'; |
| 722 | 571 | let url = '/api/Extend/WtCzd'; |
| 723 | 572 | |
| ... | ... | @@ -774,17 +623,11 @@ |
| 774 | 623 | }); |
| 775 | 624 | } |
| 776 | 625 | |
| 777 | - console.log('=== 提交的干净数据 ==='); | |
| 778 | - console.log('cleanData:', cleanData); | |
| 779 | - console.log('出货明细selectedSerialNumbers:', cleanData.wtCzdmxList.map(item => item.selectedSerialNumbers)); | |
| 780 | - | |
| 781 | - request({ | |
| 626 | + request({ | |
| 782 | 627 | url: url, |
| 783 | 628 | method: method, |
| 784 | 629 | data: cleanData |
| 785 | - }).then((res) => { | |
| 786 | - console.log('后端响应:', res); | |
| 787 | - console.log('响应数据:', res.data); | |
| 630 | + }).then((res) => { | |
| 788 | 631 | |
| 789 | 632 | // 检查响应格式 |
| 790 | 633 | if (res.data && res.data.success) { |
| ... | ... | @@ -807,17 +650,8 @@ |
| 807 | 650 | this.visible = false; |
| 808 | 651 | this.$emit('refresh', true); |
| 809 | 652 | } |
| 810 | - }).catch(err => { | |
| 811 | - console.error('保存拆装单失败:', err); | |
| 812 | - console.error('错误详情:', { | |
| 813 | - message: err.message, | |
| 814 | - response: err.response, | |
| 815 | - status: (err.response && err.response.status), | |
| 816 | - data: (err.response && err.response.data), | |
| 817 | - stack: err.stack | |
| 818 | - }); | |
| 819 | - | |
| 820 | - // 显示具体的错误信息 | |
| 653 | + }).catch(err => { | |
| 654 | + console.error('保存拆装单失败:', err); | |
| 821 | 655 | let errorMessage = '保存拆装单失败'; |
| 822 | 656 | if (err.response && err.response.data) { |
| 823 | 657 | if (err.response.data.msg) { |
| ... | ... | @@ -843,14 +677,14 @@ |
| 843 | 677 | spmc: '', |
| 844 | 678 | xlh: '', |
| 845 | 679 | dw: '', |
| 846 | - zmkc: '', | |
| 680 | + zmkc: 0, | |
| 847 | 681 | stockLoading: false, |
| 848 | 682 | productQuery: '', |
| 849 | - sl: '', | |
| 683 | + sl: 1, | |
| 850 | 684 | cbdj: '', |
| 851 | 685 | cbje: '', |
| 852 | 686 | hasQueriedStock: false, |
| 853 | - selectedSerialNumbers: [], // 初始化序列号数组 | |
| 687 | + selectedSerialNumbers: [], | |
| 854 | 688 | }; |
| 855 | 689 | this.dataForm.wtCzdmxList.push(item); |
| 856 | 690 | }, |
| ... | ... | @@ -866,14 +700,14 @@ |
| 866 | 700 | spmc: '', |
| 867 | 701 | xlh: '', |
| 868 | 702 | dw: '', |
| 869 | - zmkc: '', | |
| 703 | + zmkc: 0, | |
| 870 | 704 | stockLoading: false, |
| 871 | 705 | productQuery: '', |
| 872 | - sl: '', | |
| 706 | + sl: 1, | |
| 873 | 707 | cbdj: '', |
| 874 | 708 | cbje: '', |
| 875 | 709 | hasQueriedStock: false, |
| 876 | - selectedSerialNumbers: [], // 初始化序列号数组 | |
| 710 | + selectedSerialNumbers: [], | |
| 877 | 711 | }; |
| 878 | 712 | this.dataForm.wtCzdmx2List.push(item); |
| 879 | 713 | }, |
| ... | ... | @@ -882,261 +716,48 @@ |
| 882 | 716 | this.dataForm.wtCzdmx2List.splice(index, 1); |
| 883 | 717 | } |
| 884 | 718 | }, |
| 885 | - queryStock(row, listName) { | |
| 886 | - // 增强参数安全检查 | |
| 887 | - if (!row || typeof row !== 'object') { | |
| 888 | - console.warn('queryStock: row参数无效', row); | |
| 889 | - return; | |
| 890 | - } | |
| 891 | - | |
| 892 | - // 检查仓库和商品编号是否有效(不能为空字符串或undefined) | |
| 893 | - if (!row.chck || row.chck === '' || !row.spbh || row.spbh === '') { | |
| 894 | - console.warn('queryStock: 仓库或商品编号无效', { | |
| 895 | - chck: row.chck, | |
| 896 | - spbh: row.spbh, | |
| 897 | - row: row | |
| 898 | - }); | |
| 899 | - this.$message.warning('请先选择仓库和商品编号'); | |
| 900 | - return; | |
| 901 | - } | |
| 902 | - | |
| 903 | - // 防抖处理,避免频繁查询 | |
| 904 | - if (row.stockLoading) return; | |
| 905 | - | |
| 906 | - row.stockLoading = true; | |
| 907 | - | |
| 908 | - // 添加调试信息 | |
| 909 | - console.log('查询库存参数:', { | |
| 910 | - warehouseId: row.chck, | |
| 911 | - productId: row.spbh, | |
| 912 | - serialNumber: row.xlh || '', | |
| 913 | - listName: listName | |
| 914 | - }); | |
| 915 | - | |
| 916 | - // 先尝试调试接口 | |
| 917 | - console.log('开始请求库存接口...'); | |
| 918 | - console.log('请求参数详情:', { | |
| 919 | - warehouseId: row.chck, | |
| 920 | - productId: row.spbh, | |
| 921 | - serialNumber: row.xlh || '', | |
| 922 | - warehouseIdType: typeof row.chck, | |
| 923 | - productIdType: typeof row.spbh, | |
| 924 | - warehouseIdLength: row.chck ? row.chck.length : 0, | |
| 925 | - productIdLength: row.spbh ? row.spbh.length : 0 | |
| 926 | - }); | |
| 927 | - | |
| 928 | - // 构建完整的URL用于调试 | |
| 929 | - const fullUrl = `/api/Extend/WtCzd/QueryStock?warehouseId=${encodeURIComponent(row.chck)}&productId=${encodeURIComponent(row.spbh)}&serialNumber=${encodeURIComponent(row.xlh || '')}`; | |
| 930 | - console.log('完整请求URL:', fullUrl); | |
| 931 | - | |
| 932 | - request({ | |
| 933 | - url: `/api/Extend/WtCzd/QueryStock`, | |
| 934 | - method: 'post', | |
| 935 | - data: { | |
| 936 | - warehouseId: row.chck, | |
| 937 | - productId: row.spbh, | |
| 938 | - serialNumber: row.xlh || '' | |
| 939 | - } | |
| 940 | - }).then(res => { | |
| 941 | - console.log('库存查询响应:', res); | |
| 942 | - console.log('响应数据结构:', { | |
| 943 | - status: res.status, | |
| 944 | - statusText: res.statusText, | |
| 945 | - data: res.data, | |
| 946 | - dataType: typeof res.data, | |
| 947 | - hasData: !!res.data, | |
| 948 | - hasStock: res.data && res.data.success && res.data.data !== undefined | |
| 949 | - }); | |
| 950 | - | |
| 951 | - // 检查响应数据的原始结构 | |
| 952 | - console.log('原始响应对象:', { | |
| 953 | - keys: Object.keys(res), | |
| 954 | - values: Object.values(res), | |
| 955 | - entries: Object.entries(res) | |
| 956 | - }); | |
| 957 | - | |
| 958 | - // 处理新的响应格式:{ success: true, data: 数量 } | |
| 959 | - let stockData = null; | |
| 960 | - | |
| 961 | - // 优先处理新的响应格式 | |
| 962 | - if (res.data && res.data.success && res.data.data !== undefined) { | |
| 963 | - stockData = res.data.data; | |
| 964 | - console.log('从新格式获取库存:', stockData); | |
| 965 | - } else if (res.data && res.data.stock !== undefined) { | |
| 966 | - stockData = res.data.stock; | |
| 967 | - console.log('从旧格式获取库存:', stockData); | |
| 968 | - } else if (res.success && res.data !== undefined) { | |
| 969 | - stockData = res.data; | |
| 970 | - console.log('从根级新格式获取库存:', stockData); | |
| 971 | - } else if (res.stock !== undefined) { | |
| 972 | - stockData = res.stock; | |
| 973 | - console.log('从根级旧格式获取库存:', stockData); | |
| 974 | - } | |
| 975 | - | |
| 976 | - // 调试信息:显示实际的数据路径 | |
| 977 | - console.log('数据访问路径检查:', { | |
| 978 | - 'res.data.success': res.data && res.data.success, | |
| 979 | - 'res.data.data': res.data && res.data.data, | |
| 980 | - 'res.data.stock': res.data && res.data.stock, | |
| 981 | - 'res.success': res.success, | |
| 982 | - 'res.data': res.data, | |
| 983 | - 'res.stock': res.stock | |
| 984 | - }); | |
| 985 | - | |
| 986 | - if (stockData !== null) { | |
| 987 | - row.zmkc = stockData; | |
| 988 | - console.log('库存查询成功,设置库存值:', stockData); | |
| 989 | - this.$message.success(`库存查询成功: ${stockData}`); | |
| 990 | - | |
| 991 | - // 如果库存为0,自动调用调试接口诊断问题 | |
| 992 | - if (stockData === 0) { | |
| 993 | - console.log('库存为0,自动调用调试接口诊断问题...'); | |
| 994 | - this.debugStockQuery(row, listName); | |
| 995 | - } | |
| 996 | - } else { | |
| 997 | - row.zmkc = 0; | |
| 998 | - console.log('库存查询响应无效,设置默认值0'); | |
| 999 | - if (res.data && res.data.msg) { | |
| 1000 | - console.log('响应消息:', res.data.msg); | |
| 1001 | - this.$message.info(res.data.msg); | |
| 1002 | - } else if (res.msg) { | |
| 1003 | - console.log('响应消息:', res.msg); | |
| 1004 | - this.$message.info(res.msg); | |
| 1005 | - } else { | |
| 1006 | - this.$message.info('该商品在当前仓库无库存'); | |
| 1007 | - } | |
| 1008 | - } | |
| 1009 | - }).catch(err => { | |
| 1010 | - console.error('查询库存失败:', err); | |
| 1011 | - console.error('错误详情:', { | |
| 1012 | - message: err.message, | |
| 1013 | - response: err.response, | |
| 1014 | - status: err.response && err.response.status, | |
| 1015 | - data: err.response && err.response.data | |
| 1016 | - }); | |
| 1017 | - | |
| 1018 | - // 如果库存查询失败,尝试调试接口 | |
| 1019 | - this.debugStockQuery(row, listName); | |
| 1020 | - | |
| 1021 | - row.zmkc = 0; | |
| 1022 | - | |
| 1023 | - // 根据错误类型显示不同的提示 | |
| 1024 | - if (err.response && err.response.status === 404) { | |
| 1025 | - this.$message.error('库存查询接口不存在,请联系管理员'); | |
| 1026 | - } else if (err.response && err.response.status === 500) { | |
| 1027 | - this.$message.error('服务器内部错误,请联系管理员'); | |
| 1028 | - } else if (err.code === 'NETWORK_ERROR') { | |
| 1029 | - this.$message.error('网络连接失败,请检查网络设置'); | |
| 1030 | - } else { | |
| 1031 | - this.$message.error(`查询库存失败: ${err.message || '未知错误'}`); | |
| 1032 | - } | |
| 1033 | - }).finally(() => { | |
| 1034 | - row.stockLoading = false; | |
| 1035 | - }); | |
| 1036 | - }, | |
| 719 | + queryStock(row, listName) { | |
| 720 | + if (!row || typeof row !== 'object') return; | |
| 721 | + if (!row.chck || !row.spbh) { | |
| 722 | + this.$message.warning('请先选择仓库和商品编号'); | |
| 723 | + return; | |
| 724 | + } | |
| 725 | + if (row.stockLoading) return; | |
| 1037 | 726 | |
| 1038 | - // 调试库存查询问题 | |
| 1039 | - debugStockQuery(row, listName) { | |
| 1040 | - console.log('=== 开始调试库存查询问题 ==='); | |
| 1041 | - console.log('调试参数:', { | |
| 1042 | - warehouse: row.chck, | |
| 1043 | - productCode: row.spbh, | |
| 1044 | - listName: listName | |
| 1045 | - }); | |
| 1046 | - | |
| 1047 | - request({ | |
| 1048 | - url: `/api/Extend/WtCzd/DebugStock`, | |
| 1049 | - method: 'get', | |
| 1050 | - params: { | |
| 1051 | - warehouseId: row.chck, | |
| 1052 | - productId: row.spbh | |
| 1053 | - } | |
| 1054 | - }).then(res => { | |
| 1055 | - console.log('调试库存查询响应:', res); | |
| 1056 | - console.log('调试响应数据结构:', { | |
| 1057 | - 'res.data': res.data, | |
| 1058 | - 'res.data.data': res.data && res.data.data, | |
| 1059 | - 'res.data.data.checks': res.data && res.data.data && res.data.data.checks | |
| 1060 | - }); | |
| 1061 | - | |
| 1062 | - if (res.data && res.data.data) { | |
| 1063 | - console.log('=== 调试信息详情 ==='); | |
| 1064 | - console.log('仓库信息:', res.data.data.warehouse); | |
| 1065 | - console.log('商品信息:', res.data.data.productCode); | |
| 1066 | - console.log('时间戳:', res.data.data.timestamp); | |
| 1067 | - | |
| 1068 | - if (res.data.data.checks && Array.isArray(res.data.data.checks)) { | |
| 1069 | - console.log('=== 数据表检查结果 ==='); | |
| 1070 | - res.data.data.checks.forEach((check, index) => { | |
| 1071 | - console.log(`检查项 ${index + 1}:`, check); | |
| 1072 | - }); | |
| 1073 | - } | |
| 1074 | - | |
| 1075 | - // 分析库存为0的原因 | |
| 1076 | - this.analyzeStockZeroReason(res.data.data); | |
| 1077 | - } else if (res.data) { | |
| 1078 | - console.log('调试信息:', res.data); | |
| 1079 | - } | |
| 1080 | - | |
| 1081 | - console.log('=== 调试库存查询问题结束 ==='); | |
| 1082 | - }).catch(err => { | |
| 1083 | - console.error('调试库存查询失败:', err); | |
| 1084 | - }); | |
| 1085 | - }, | |
| 727 | + row.stockLoading = true; | |
| 1086 | 728 | |
| 1087 | - // 分析库存为0的原因 | |
| 1088 | - analyzeStockZeroReason(debugData) { | |
| 1089 | - console.log('=== 分析库存为0的原因 ==='); | |
| 1090 | - | |
| 1091 | - if (debugData.checks && Array.isArray(debugData.checks)) { | |
| 1092 | - debugData.checks.forEach((check, index) => { | |
| 1093 | - if (check.table === 'WtSerialNumberEntity') { | |
| 1094 | - console.log('序列号表分析:'); | |
| 1095 | - console.log(`- 总记录数: ${check.totalCount}`); | |
| 1096 | - console.log(`- 按商品查询: ${check.byProduct}`); | |
| 1097 | - console.log(`- 按仓库查询: ${check.byWarehouse}`); | |
| 1098 | - console.log(`- 按商品+仓库查询: ${check.byBoth}`); | |
| 1099 | - console.log(`- 在库状态: ${check.inStock}`); | |
| 1100 | - | |
| 1101 | - if (check.totalCount === 0) { | |
| 1102 | - console.log('❌ 问题: 序列号表为空,没有商品库存记录'); | |
| 1103 | - } else if (check.byProduct === 0) { | |
| 1104 | - console.log('❌ 问题: 该商品在序列号表中不存在'); | |
| 1105 | - } else if (check.byWarehouse === 0) { | |
| 1106 | - console.log('❌ 问题: 该仓库在序列号表中不存在'); | |
| 1107 | - } else if (check.byBoth === 0) { | |
| 1108 | - console.log('❌ 问题: 该商品在该仓库中不存在'); | |
| 1109 | - } else if (check.inStock === 0) { | |
| 1110 | - console.log('❌ 问题: 该商品在该仓库中无在库状态的记录'); | |
| 1111 | - } | |
| 1112 | - } | |
| 1113 | - | |
| 1114 | - if (check.table === 'WtSpEntity') { | |
| 1115 | - console.log('商品档案表分析:'); | |
| 1116 | - console.log(`- 总商品数: ${check.totalCount}`); | |
| 1117 | - if (check.product) { | |
| 1118 | - console.log(`- 商品信息:`, check.product); | |
| 1119 | - if (check.product.Kc === 0 || check.product.Kc === null) { | |
| 1120 | - console.log('❌ 问题: 商品档案表中的库存字段为0或空'); | |
| 1121 | - } | |
| 1122 | - } else { | |
| 1123 | - console.log('❌ 问题: 商品档案表中找不到该商品'); | |
| 1124 | - } | |
| 1125 | - } | |
| 1126 | - | |
| 1127 | - if (check.table === 'WtCkEntity') { | |
| 1128 | - console.log('仓库表分析:'); | |
| 1129 | - if (check.warehouse) { | |
| 1130 | - console.log(`- 仓库信息:`, check.warehouse); | |
| 1131 | - } else { | |
| 1132 | - console.log('❌ 问题: 仓库表中找不到该仓库'); | |
| 1133 | - } | |
| 1134 | - } | |
| 1135 | - }); | |
| 729 | + request({ | |
| 730 | + url: `/api/Extend/WtCzd/QueryStock`, | |
| 731 | + method: 'post', | |
| 732 | + data: { | |
| 733 | + warehouseId: row.chck, | |
| 734 | + productId: row.spbh, | |
| 735 | + serialNumber: row.xlh || '' | |
| 736 | + } | |
| 737 | + }).then(res => { | |
| 738 | + let stockData = null; | |
| 739 | + if (res.data && res.data.success && res.data.data !== undefined) { | |
| 740 | + stockData = res.data.data; | |
| 741 | + } else if (res.data && res.data.stock !== undefined) { | |
| 742 | + stockData = res.data.stock; | |
| 743 | + } else if (res.success && res.data !== undefined) { | |
| 744 | + stockData = res.data; | |
| 745 | + } else if (res.stock !== undefined) { | |
| 746 | + stockData = res.stock; | |
| 1136 | 747 | } |
| 1137 | 748 | |
| 1138 | - console.log('=== 库存为0原因分析结束 ==='); | |
| 1139 | - }, | |
| 749 | + if (stockData !== null) { | |
| 750 | + this.$set(row, 'zmkc', stockData); | |
| 751 | + } else { | |
| 752 | + this.$set(row, 'zmkc', 0); | |
| 753 | + } | |
| 754 | + }).catch(err => { | |
| 755 | + console.error('查询库存失败:', err); | |
| 756 | + this.$set(row, 'zmkc', 0); | |
| 757 | + }).finally(() => { | |
| 758 | + row.stockLoading = false; | |
| 759 | + }); | |
| 760 | + }, | |
| 1140 | 761 | |
| 1141 | 762 | // 调试前端数据状态 |
| 1142 | 763 | debugFrontendData() { |
| ... | ... | @@ -1256,74 +877,43 @@ |
| 1256 | 877 | console.log('商品选项:', this.spbhOptions); |
| 1257 | 878 | console.log('=== 快速诊断结束 ==='); |
| 1258 | 879 | }, |
| 1259 | - handleCostPriceChange(row) { | |
| 1260 | - // 当成本单价变化时,自动计算成本金额 | |
| 1261 | - if (row.sl && row.cbdj) { | |
| 1262 | - const quantity = parseFloat(row.sl); | |
| 1263 | - const costPrice = parseFloat(row.cbdj); | |
| 1264 | - if (!isNaN(quantity) && !isNaN(costPrice)) { | |
| 1265 | - row.cbje = (quantity * costPrice).toFixed(2); | |
| 1266 | - } | |
| 880 | + handleCostPriceChange(row) { | |
| 881 | + if (row.sl && row.cbdj) { | |
| 882 | + const quantity = parseFloat(row.sl); | |
| 883 | + const costPrice = parseFloat(row.cbdj); | |
| 884 | + if (!isNaN(quantity) && !isNaN(costPrice)) { | |
| 885 | + this.$set(row, 'cbje', (quantity * costPrice).toFixed(2)); | |
| 1267 | 886 | } |
| 1268 | - }, | |
| 887 | + } | |
| 888 | + }, | |
| 1269 | 889 | |
| 1270 | - // 打开序列号选择弹窗 | |
| 1271 | - openSerialNumberSelect(row, listName) { | |
| 1272 | - console.log('=== 序列号选择按钮被点击 ==='); | |
| 1273 | - console.log('行数据:', row); | |
| 1274 | - console.log('明细表类型:', listName); | |
| 1275 | - console.log('商品编号:', row.spbh); | |
| 1276 | - console.log('仓库:', row.chck); | |
| 1277 | - console.log('当前行对象:', JSON.stringify(row, null, 2)); | |
| 1278 | - | |
| 1279 | - // 检查基本参数 | |
| 1280 | - if (!row) { | |
| 1281 | - console.error('行数据为空'); | |
| 1282 | - this.$message.error('行数据为空'); | |
| 1283 | - return; | |
| 1284 | - } | |
| 1285 | - | |
| 1286 | - if (!row.spbh || !row.chck) { | |
| 1287 | - console.warn('缺少商品或仓库信息'); | |
| 1288 | - this.$message.warning('请先选择商品和仓库'); | |
| 1289 | - return; | |
| 1290 | - } | |
| 1291 | - | |
| 1292 | - // 检查组件引用 | |
| 1293 | - console.log('检查组件引用...'); | |
| 1294 | - console.log('this.$refs:', this.$refs); | |
| 1295 | - console.log('this.$refs.serialNumberSelect:', this.$refs.serialNumberSelect); | |
| 1296 | - | |
| 1297 | - if (!this.$refs.serialNumberSelect) { | |
| 1298 | - console.error('序列号选择组件引用不存在'); | |
| 1299 | - this.$message.error('序列号选择组件加载失败'); | |
| 1300 | - return; | |
| 1301 | - } | |
| 1302 | - | |
| 1303 | - // 根据明细表类型选择对应的仓库选项 | |
| 1304 | - const warehouseOptions = listName === 'wtCzdmxList' ? this.chckOptions : this.rhckOptions; | |
| 1305 | - console.log('仓库选项:', warehouseOptions); | |
| 1306 | - | |
| 1307 | - console.log('准备打开序列号选择弹窗...'); | |
| 1308 | - | |
| 1309 | - // 打开序列号选择弹窗 | |
| 1310 | - try { | |
| 1311 | - // 确保当前行有selectedSerialNumbers数组 | |
| 1312 | - if (!row.selectedSerialNumbers) { | |
| 1313 | - console.log('初始化selectedSerialNumbers数组'); | |
| 1314 | - this.$set(row, 'selectedSerialNumbers', []); | |
| 1315 | - } | |
| 1316 | - | |
| 1317 | - console.log('调用组件open方法...'); | |
| 1318 | - this.$refs.serialNumberSelect.open(row, listName, warehouseOptions); | |
| 1319 | - console.log('序列号选择弹窗打开成功'); | |
| 1320 | - } catch (error) { | |
| 1321 | - console.error('打开序列号选择弹窗失败:', error); | |
| 1322 | - console.error('错误堆栈:', error.stack); | |
| 1323 | - this.$message.error('打开序列号选择弹窗失败: ' + error.message); | |
| 890 | + openSerialNumberSelect(row, listName) { | |
| 891 | + if (!row) { | |
| 892 | + this.$message.error('行数据为空'); | |
| 893 | + return; | |
| 894 | + } | |
| 895 | + if (!row.spbh || !row.chck) { | |
| 896 | + this.$message.warning('请先选择商品和仓库'); | |
| 897 | + return; | |
| 898 | + } | |
| 899 | + if (!this.$refs.serialNumberSelect) { | |
| 900 | + this.$message.error('序列号选择组件加载失败'); | |
| 901 | + return; | |
| 902 | + } | |
| 903 | + | |
| 904 | + const warehouseOptions = listName === 'wtCzdmxList' ? this.chckOptions : this.rhckOptions; | |
| 905 | + | |
| 906 | + try { | |
| 907 | + if (!row.selectedSerialNumbers) { | |
| 908 | + this.$set(row, 'selectedSerialNumbers', []); | |
| 1324 | 909 | } |
| 910 | + this.$refs.serialNumberSelect.open(row, listName, warehouseOptions); | |
| 911 | + } catch (error) { | |
| 912 | + console.error('打开序列号选择弹窗失败:', error); | |
| 913 | + this.$message.error('打开序列号选择弹窗失败: ' + error.message); | |
| 1325 | 914 | } |
| 1326 | 915 | } |
| 916 | + } | |
| 1327 | 917 | } |
| 1328 | 918 | </script> |
| 1329 | 919 | <style scoped> | ... | ... |
Antis.Erp.Plat/antis-ncc-admin/src/views/wtPriceAdjust/Form.vue
| 1 | 1 | <template> |
| 2 | - <transition name="el-zoom-in-center"> | |
| 3 | - <div class="NCC-preview-main"> | |
| 4 | - <div class="NCC-common-page-header"> | |
| 5 | - <el-page-header @back="goBack" :content="!dataForm.id ? '新建调价单' : isDetail ? '调价单详情' : '编辑调价单'" /> | |
| 6 | - <div class="options"> | |
| 7 | - <el-button type="primary" @click="dataFormSubmit()" v-if="!isDetail">确 定</el-button> | |
| 8 | - <el-button @click="goBack">取 消</el-button> | |
| 9 | - </div> | |
| 10 | - </div> | |
| 11 | - <el-row :gutter="15" class="main" style="margin: 0 auto; width: 100%"> | |
| 12 | - <el-form ref="elForm" :model="dataForm" size="medium" label-width="110px" label-position="right" :disabled="!!isDetail" :rules="rules"> | |
| 13 | - <el-col :span="24"> | |
| 14 | - <el-form-item label="商品名称" prop="productName"> | |
| 15 | - <el-input v-model="dataForm.productName" placeholder="商品名称" clearable required style="width:100%" /> | |
| 16 | - </el-form-item> | |
| 17 | - </el-col> | |
| 18 | - <el-col :span="12"> | |
| 19 | - <el-form-item label="原价" prop="oldPrice"> | |
| 20 | - <el-input v-model="dataForm.oldPrice" placeholder="原价" clearable required style="width:100%" /> | |
| 21 | - </el-form-item> | |
| 22 | - </el-col> | |
| 23 | - <el-col :span="12"> | |
| 24 | - <el-form-item label="新价" prop="newPrice"> | |
| 25 | - <el-input v-model="dataForm.newPrice" placeholder="新价" clearable required style="width:100%" /> | |
| 26 | - </el-form-item> | |
| 27 | - </el-col> | |
| 28 | - <el-col :span="12"> | |
| 29 | - <el-form-item label="调价日期" prop="adjustDate"> | |
| 30 | - <el-date-picker v-model="dataForm.adjustDate" type="date" value-format="yyyy-MM-dd" placeholder="选择日期" style="width:100%" /> | |
| 31 | - </el-form-item> | |
| 32 | - </el-col> | |
| 33 | - <el-col :span="12"> | |
| 34 | - <el-form-item label="状态" prop="status"> | |
| 35 | - <el-select v-model="dataForm.status" placeholder="请选择状态" clearable style="width:100%"> | |
| 36 | - <el-option label="草稿" value="draft" /> | |
| 37 | - <el-option label="已生效" value="active" /> | |
| 38 | - <el-option label="已作废" value="void" /> | |
| 39 | - </el-select> | |
| 40 | - </el-form-item> | |
| 41 | - </el-col> | |
| 42 | - </el-form> | |
| 43 | - </el-row> | |
| 44 | - </div> | |
| 45 | - </transition> | |
| 46 | -</template> | |
| 2 | + <el-dialog :title="dialogTitle" :close-on-click-modal="false" :visible.sync="visible" class="NCC-dialog NCC-dialog_center tjd-dialog" lock-scroll width="1200px" top="5vh"> | |
| 3 | + <el-form ref="elForm" :model="dataForm" size="small" label-width="80px" label-position="right" :disabled="!!isDetail" :rules="rules"> | |
| 4 | + <div class="tjd-section"> | |
| 5 | + <div class="tjd-section__header"> | |
| 6 | + <span class="tjd-section__title">基本信息</span> | |
| 7 | + <el-tag v-if="dataForm.djzt === '已审核'" type="success" size="small" effect="dark">已审核</el-tag> | |
| 8 | + <el-tag v-else-if="dataForm.id" type="info" size="small">草稿</el-tag> | |
| 9 | + </div> | |
| 10 | + <el-row :gutter="16"> | |
| 11 | + <el-col :span="8"> | |
| 12 | + <el-form-item label="单据编号"> | |
| 13 | + <el-input v-model="dataForm.id" placeholder="自动生成" readonly /> | |
| 14 | + </el-form-item> | |
| 15 | + </el-col> | |
| 16 | + <el-col :span="8"> | |
| 17 | + <el-form-item label="单据日期" prop="djrq"> | |
| 18 | + <el-date-picker v-model="dataForm.djrq" placeholder="请选择" style="width:100%" type="date" format="yyyy-MM-dd" value-format="timestamp" /> | |
| 19 | + </el-form-item> | |
| 20 | + </el-col> | |
| 21 | + <el-col :span="8"> | |
| 22 | + <el-form-item label="仓库" prop="ck"> | |
| 23 | + <el-select v-model="dataForm.ck" placeholder="请选择仓库" clearable filterable style="width:100%" @change="handleWarehouseChange"> | |
| 24 | + <el-option v-for="item in ckOptions" :key="item.F_Id" :label="item.F_mdmc" :value="item.F_Id" /> | |
| 25 | + </el-select> | |
| 26 | + </el-form-item> | |
| 27 | + </el-col> | |
| 28 | + <el-col :span="8"> | |
| 29 | + <el-form-item label="经手人"> | |
| 30 | + <user-select v-model="dataForm.jsr" placeholder="自动设置" disabled /> | |
| 31 | + </el-form-item> | |
| 32 | + </el-col> | |
| 33 | + <el-col :span="16"> | |
| 34 | + <el-form-item label="备注"> | |
| 35 | + <el-input v-model="dataForm.bz" placeholder="请输入备注" /> | |
| 36 | + </el-form-item> | |
| 37 | + </el-col> | |
| 38 | + </el-row> | |
| 39 | + </div> | |
| 40 | + | |
| 41 | + <div class="tjd-section"> | |
| 42 | + <div class="tjd-section__header"> | |
| 43 | + <span class="tjd-section__title">商品明细</span> | |
| 44 | + <span class="tjd-section__count" v-if="dataForm.wtTjdMxList.length">{{ dataForm.wtTjdMxList.length }} 条</span> | |
| 45 | + </div> | |
| 46 | + | |
| 47 | + <div class="tjd-toolbar" v-if="!isDetail"> | |
| 48 | + <div class="tjd-toolbar__left"> | |
| 49 | + <el-button type="primary" size="small" icon="el-icon-plus" :disabled="!dataForm.ck" @click="addMxRow">添加商品</el-button> | |
| 50 | + <el-divider direction="vertical" /> | |
| 51 | + <el-checkbox v-model="loadAllFlag" :disabled="!dataForm.ck" @change="handleLoadAllChange"> | |
| 52 | + 加载全部商品 | |
| 53 | + </el-checkbox> | |
| 54 | + <span class="tjd-toolbar__hint" v-if="!dataForm.ck">请先选择仓库</span> | |
| 55 | + </div> | |
| 56 | + <div class="tjd-toolbar__right"> | |
| 57 | + <el-button size="small" icon="el-icon-delete" :disabled="!dataForm.wtTjdMxList.length" @click="clearAllMx">清空</el-button> | |
| 58 | + </div> | |
| 59 | + </div> | |
| 60 | + | |
| 61 | + <div class="tjd-empty" v-if="!dataForm.wtTjdMxList.length && !productsLoading"> | |
| 62 | + <i class="el-icon-box" style="font-size:36px;color:#C0C4CC" /> | |
| 63 | + <p>暂无商品明细</p> | |
| 64 | + <p class="tjd-empty__hint" v-if="!isDetail">选择仓库后勾选「加载全部商品」或点击「添加商品」</p> | |
| 65 | + </div> | |
| 66 | + | |
| 67 | + <el-table | |
| 68 | + v-show="dataForm.wtTjdMxList.length || productsLoading" | |
| 69 | + :data="dataForm.wtTjdMxList" | |
| 70 | + size="mini" | |
| 71 | + show-summary | |
| 72 | + :summary-method="getSummaries" | |
| 73 | + border | |
| 74 | + stripe | |
| 75 | + v-loading="productsLoading" | |
| 76 | + element-loading-text="正在加载..." | |
| 77 | + max-height="380" | |
| 78 | + class="tjd-table" | |
| 79 | + > | |
| 80 | + <el-table-column type="index" width="40" label="#" align="center" /> | |
| 81 | + <el-table-column prop="spbh" label="商品" min-width="200"> | |
| 82 | + <template slot-scope="scope"> | |
| 83 | + <el-select | |
| 84 | + v-if="scope.row._manual && !isDetail" | |
| 85 | + v-model="scope.row.spbh" | |
| 86 | + placeholder="搜索商品" | |
| 87 | + filterable | |
| 88 | + clearable | |
| 89 | + size="mini" | |
| 90 | + style="width:100%" | |
| 91 | + @change="handleProductSelect(scope.row)" | |
| 92 | + > | |
| 93 | + <el-option | |
| 94 | + v-for="item in warehouseProducts" | |
| 95 | + :key="item.spbh" | |
| 96 | + :label="(item.spbm || '') + ' ' + (item.spmc || '')" | |
| 97 | + :value="item.spbh" | |
| 98 | + /> | |
| 99 | + </el-select> | |
| 100 | + <span v-else>{{ scope.row.spmc || scope.row.spbh || '-' }}</span> | |
| 101 | + </template> | |
| 102 | + </el-table-column> | |
| 103 | + <el-table-column prop="spbm" label="编码" width="120"> | |
| 104 | + <template slot-scope="scope"> | |
| 105 | + <span class="tjd-code">{{ scope.row.spbm || '-' }}</span> | |
| 106 | + </template> | |
| 107 | + </el-table-column> | |
| 108 | + <el-table-column prop="sl" label="库存" width="70" align="right"> | |
| 109 | + <template slot-scope="scope"> | |
| 110 | + <span :class="{ 'text-warning': scope.row.sl === 0 }">{{ scope.row.sl }}</span> | |
| 111 | + </template> | |
| 112 | + </el-table-column> | |
| 113 | + <el-table-column prop="tqdj" label="调前单价" width="90" align="right"> | |
| 114 | + <template slot-scope="scope"> | |
| 115 | + <span :class="{ 'text-warning': !(parseFloat(scope.row.tqdj) > 0) }">{{ formatPrice(scope.row.tqdj) }}</span> | |
| 116 | + </template> | |
| 117 | + </el-table-column> | |
| 118 | + <el-table-column prop="tjbl" label="比率%" width="100" class-name="tjd-editable-col"> | |
| 119 | + <template slot-scope="scope"> | |
| 120 | + <el-input v-model="scope.row.tjbl" size="mini" placeholder="如10" :disabled="!!isDetail" @input="handleRatioChange(scope.row)"> | |
| 121 | + <template slot="append">%</template> | |
| 122 | + </el-input> | |
| 123 | + </template> | |
| 124 | + </el-table-column> | |
| 125 | + <el-table-column prop="thdj" label="调后单价" width="100" class-name="tjd-editable-col"> | |
| 126 | + <template slot-scope="scope"> | |
| 127 | + <el-input v-model="scope.row.thdj" size="mini" placeholder="价格" :disabled="!!isDetail" @input="handleNewPriceChange(scope.row)" /> | |
| 128 | + </template> | |
| 129 | + </el-table-column> | |
| 130 | + <el-table-column prop="tqje" label="调前金额" width="90" align="right"> | |
| 131 | + <template slot-scope="scope">{{ formatPrice(scope.row.tqje) }}</template> | |
| 132 | + </el-table-column> | |
| 133 | + <el-table-column prop="thje" label="调后金额" width="90" align="right"> | |
| 134 | + <template slot-scope="scope"> | |
| 135 | + <span class="text-primary" v-if="scope.row.thje">{{ formatPrice(scope.row.thje) }}</span> | |
| 136 | + <span v-else>-</span> | |
| 137 | + </template> | |
| 138 | + </el-table-column> | |
| 139 | + <el-table-column prop="bz" label="备注" min-width="80"> | |
| 140 | + <template slot-scope="scope"> | |
| 141 | + <el-input v-model="scope.row.bz" size="mini" placeholder="备注" :disabled="!!isDetail" /> | |
| 142 | + </template> | |
| 143 | + </el-table-column> | |
| 144 | + <el-table-column label="" width="45" align="center" v-if="!isDetail"> | |
| 145 | + <template slot-scope="scope"> | |
| 146 | + <el-button size="mini" type="text" icon="el-icon-delete" class="NCC-table-delBtn" @click="handleDelMx(scope.$index)" /> | |
| 147 | + </template> | |
| 148 | + </el-table-column> | |
| 149 | + </el-table> | |
| 150 | + </div> | |
| 151 | + </el-form> | |
| 47 | 152 | |
| 153 | + <span slot="footer" class="dialog-footer"> | |
| 154 | + <el-button @click="visible = false">取 消</el-button> | |
| 155 | + <el-button type="warning" icon="el-icon-check" @click="handleAudit()" v-if="dataForm.id && (!dataForm.djzt || dataForm.djzt === '草稿') && !isDetail">审 核</el-button> | |
| 156 | + <el-button type="primary" icon="el-icon-document-checked" @click="dataFormSubmit()" :loading="submitLoading" v-if="!isDetail">保 存</el-button> | |
| 157 | + </span> | |
| 158 | + </el-dialog> | |
| 159 | +</template> | |
| 48 | 160 | <script> |
| 49 | -export default { | |
| 50 | - name: 'PriceAdjustForm', | |
| 51 | - props: { | |
| 52 | - id: String | |
| 53 | - }, | |
| 54 | - data() { | |
| 55 | - return { | |
| 56 | - isDetail: false, | |
| 57 | - dataForm: { | |
| 58 | - id: '', | |
| 59 | - productName: '', | |
| 60 | - oldPrice: '', | |
| 61 | - newPrice: '', | |
| 62 | - adjustDate: '', | |
| 63 | - status: 'draft' | |
| 64 | - }, | |
| 65 | - rules: { | |
| 66 | - productName: [{ required: true, message: '请输入商品名称', trigger: 'blur' }], | |
| 67 | - oldPrice: [{ required: true, message: '请输入原价', trigger: 'blur' }], | |
| 68 | - newPrice: [{ required: true, message: '请输入新价', trigger: 'blur' }], | |
| 69 | - adjustDate: [{ required: true, message: '请选择调价日期', trigger: 'change' }], | |
| 70 | - status: [{ required: true, message: '请选择状态', trigger: 'change' }] | |
| 71 | - } | |
| 72 | - } | |
| 73 | - }, | |
| 74 | - watch: { | |
| 75 | - id: { | |
| 76 | - immediate: true, | |
| 77 | - handler(val) { | |
| 78 | - if (val) { | |
| 79 | - // TODO: 获取调价单详情,填充 dataForm | |
| 80 | - this.dataForm.id = val | |
| 81 | - } else { | |
| 82 | - this.dataForm = { | |
| 83 | - id: '', | |
| 84 | - productName: '', | |
| 85 | - oldPrice: '', | |
| 86 | - newPrice: '', | |
| 87 | - adjustDate: '', | |
| 88 | - status: 'draft' | |
| 89 | - } | |
| 90 | - } | |
| 91 | - } | |
| 92 | - } | |
| 93 | - }, | |
| 94 | - methods: { | |
| 95 | - dataFormSubmit() { | |
| 96 | - this.$refs.elForm.validate(valid => { | |
| 97 | - if (!valid) return | |
| 98 | - // TODO: 新增或编辑调价单 | |
| 99 | - this.$emit('refresh') | |
| 100 | - }) | |
| 101 | - }, | |
| 102 | - goBack() { | |
| 103 | - this.$emit('close') | |
| 104 | - } | |
| 105 | - } | |
| 106 | -} | |
| 161 | + import request from '@/utils/request' | |
| 162 | + import { previewDataInterface } from '@/api/systemData/dataInterface' | |
| 163 | + export default { | |
| 164 | + props: [], | |
| 165 | + data() { | |
| 166 | + return { | |
| 167 | + visible: false, | |
| 168 | + isDetail: false, | |
| 169 | + submitLoading: false, | |
| 170 | + productsLoading: false, | |
| 171 | + loadAllFlag: false, | |
| 172 | + dataForm: { | |
| 173 | + id: '', | |
| 174 | + djrq: undefined, | |
| 175 | + ck: undefined, | |
| 176 | + jsr: undefined, | |
| 177 | + zdr: undefined, | |
| 178 | + djzt: '草稿', | |
| 179 | + bz: undefined, | |
| 180 | + wtTjdMxList: [], | |
| 181 | + }, | |
| 182 | + rules: { | |
| 183 | + ck: [{ required: true, message: '请选择仓库', trigger: 'change' }], | |
| 184 | + }, | |
| 185 | + ckOptions: [], | |
| 186 | + warehouseProducts: [], | |
| 187 | + } | |
| 188 | + }, | |
| 189 | + computed: { | |
| 190 | + dialogTitle() { | |
| 191 | + if (!this.dataForm.id) return '新建调价单' | |
| 192 | + if (this.isDetail) return '调价单详情' | |
| 193 | + return '编辑调价单' | |
| 194 | + } | |
| 195 | + }, | |
| 196 | + created() { | |
| 197 | + this.getCkOptions() | |
| 198 | + }, | |
| 199 | + methods: { | |
| 200 | + getCkOptions() { | |
| 201 | + previewDataInterface('681758216954053893').then(res => { | |
| 202 | + this.ckOptions = res.data | |
| 203 | + }) | |
| 204 | + }, | |
| 205 | + formatPrice(val) { | |
| 206 | + const n = parseFloat(val) | |
| 207 | + if (isNaN(n)) return '-' | |
| 208 | + return n.toFixed(2) | |
| 209 | + }, | |
| 210 | + init(id, isDetail) { | |
| 211 | + this.visible = true | |
| 212 | + this.isDetail = isDetail || false | |
| 213 | + this.loadAllFlag = false | |
| 214 | + this.warehouseProducts = [] | |
| 215 | + this.$nextTick(() => { | |
| 216 | + if (this.$refs['elForm']) this.$refs['elForm'].resetFields() | |
| 217 | + if (id) { | |
| 218 | + request({ url: '/api/Extend/WtTjd/' + id, method: 'get' }).then(res => { | |
| 219 | + this.dataForm = { ...res.data, wtTjdMxList: (res.data.wtTjdMxList || []).map(item => ({ ...item, _manual: false })) } | |
| 220 | + if (this.dataForm.ck) this.loadWarehouseProducts(this.dataForm.ck) | |
| 221 | + }) | |
| 222 | + } else { | |
| 223 | + this.dataForm = { | |
| 224 | + id: '', | |
| 225 | + djrq: Date.now(), | |
| 226 | + ck: undefined, | |
| 227 | + jsr: undefined, | |
| 228 | + zdr: undefined, | |
| 229 | + djzt: '草稿', | |
| 230 | + bz: undefined, | |
| 231 | + wtTjdMxList: [], | |
| 232 | + } | |
| 233 | + var user = (this.$store && this.$store.getters && this.$store.getters.userInfo) ? this.$store.getters.userInfo : {} | |
| 234 | + this.dataForm.jsr = user.userId || user.id || '' | |
| 235 | + } | |
| 236 | + }) | |
| 237 | + }, | |
| 238 | + loadWarehouseProducts(ck) { | |
| 239 | + if (!ck) { this.warehouseProducts = []; return } | |
| 240 | + request({ | |
| 241 | + url: '/api/Extend/WtTjd/Actions/LoadAllProducts', | |
| 242 | + method: 'GET', | |
| 243 | + data: { ck } | |
| 244 | + }).then(res => { | |
| 245 | + this.warehouseProducts = (res.data && Array.isArray(res.data)) ? res.data : [] | |
| 246 | + }).catch(() => { this.warehouseProducts = [] }) | |
| 247 | + }, | |
| 248 | + handleWarehouseChange(val) { | |
| 249 | + if (!val) { | |
| 250 | + this.dataForm.wtTjdMxList = [] | |
| 251 | + this.loadAllFlag = false | |
| 252 | + this.warehouseProducts = [] | |
| 253 | + return | |
| 254 | + } | |
| 255 | + this.loadWarehouseProducts(val) | |
| 256 | + this.dataForm.wtTjdMxList.forEach(row => { | |
| 257 | + this.$set(row, 'ck', val) | |
| 258 | + }) | |
| 259 | + if (this.loadAllFlag) { | |
| 260 | + this.loadProducts() | |
| 261 | + } | |
| 262 | + }, | |
| 263 | + handleLoadAllChange(checked) { | |
| 264 | + if (checked && this.dataForm.ck) { | |
| 265 | + this.loadProducts() | |
| 266 | + } else if (!checked) { | |
| 267 | + this.dataForm.wtTjdMxList = [] | |
| 268 | + } | |
| 269 | + }, | |
| 270 | + loadProducts() { | |
| 271 | + const ck = this.dataForm.ck | |
| 272 | + if (!ck) { this.$message.warning('请先选择仓库'); return } | |
| 273 | + this.productsLoading = true | |
| 274 | + request({ | |
| 275 | + url: '/api/Extend/WtTjd/Actions/LoadAllProducts', | |
| 276 | + method: 'GET', | |
| 277 | + data: { ck } | |
| 278 | + }).then(res => { | |
| 279 | + if (res.data && Array.isArray(res.data)) { | |
| 280 | + this.dataForm.wtTjdMxList = res.data.map(item => { | |
| 281 | + const sl = parseFloat(item.sl) || 0 | |
| 282 | + const tqdj = parseFloat(item.tqdj) || 0 | |
| 283 | + return { | |
| 284 | + ck: this.dataForm.ck, spbh: item.spbh, spbm: item.spbm || '', | |
| 285 | + spmc: item.spmc, sl, tqdj, tjbl: '', thdj: '', | |
| 286 | + tqje: (sl * tqdj).toFixed(2), thje: '', bz: '', _manual: false, | |
| 287 | + } | |
| 288 | + }) | |
| 289 | + if (!this.dataForm.wtTjdMxList.length) this.$message.info('该仓库暂无商品') | |
| 290 | + } else { | |
| 291 | + this.dataForm.wtTjdMxList = [] | |
| 292 | + this.$message.info('该仓库暂无商品') | |
| 293 | + } | |
| 294 | + }).catch(() => { | |
| 295 | + this.$message.error('加载商品失败') | |
| 296 | + }).finally(() => { this.productsLoading = false }) | |
| 297 | + }, | |
| 298 | + handleProductSelect(row) { | |
| 299 | + const product = this.warehouseProducts.find(item => item.spbh === row.spbh) | |
| 300 | + if (product) { | |
| 301 | + this.$set(row, 'spmc', product.spmc || '') | |
| 302 | + this.$set(row, 'spbm', product.spbm || '') | |
| 303 | + this.$set(row, 'sl', parseFloat(product.sl) || 0) | |
| 304 | + this.$set(row, 'tqdj', parseFloat(product.tqdj) || 0) | |
| 305 | + const sl = parseFloat(product.sl) || 0 | |
| 306 | + const tqdj = parseFloat(product.tqdj) || 0 | |
| 307 | + this.$set(row, 'tqje', (sl * tqdj).toFixed(2)) | |
| 308 | + } | |
| 309 | + this.$set(row, 'ck', this.dataForm.ck || '') | |
| 310 | + }, | |
| 311 | + handleRatioChange(row) { | |
| 312 | + const tqdj = parseFloat(row.tqdj) || 0 | |
| 313 | + const tjbl = parseFloat(row.tjbl) | |
| 314 | + if (!isNaN(tjbl) && tqdj > 0) { | |
| 315 | + const thdj = tqdj * (1 + tjbl / 100) | |
| 316 | + this.$set(row, 'thdj', thdj.toFixed(2)) | |
| 317 | + const sl = parseFloat(row.sl) || 0 | |
| 318 | + this.$set(row, 'thje', (sl * thdj).toFixed(2)) | |
| 319 | + } | |
| 320 | + }, | |
| 321 | + handleNewPriceChange(row) { | |
| 322 | + const tqdj = parseFloat(row.tqdj) || 0 | |
| 323 | + const thdj = parseFloat(row.thdj) | |
| 324 | + if (!isNaN(thdj)) { | |
| 325 | + if (tqdj > 0) { | |
| 326 | + this.$set(row, 'tjbl', (((thdj - tqdj) / tqdj) * 100).toFixed(2)) | |
| 327 | + } | |
| 328 | + const sl = parseFloat(row.sl) || 0 | |
| 329 | + this.$set(row, 'thje', (sl * thdj).toFixed(2)) | |
| 330 | + } | |
| 331 | + }, | |
| 332 | + addMxRow() { | |
| 333 | + if (!this.dataForm.ck) { this.$message.warning('请先选择仓库'); return } | |
| 334 | + this.dataForm.wtTjdMxList.push({ | |
| 335 | + ck: this.dataForm.ck, spbh: '', spbm: '', spmc: '', | |
| 336 | + sl: 0, tqdj: 0, tjbl: '', thdj: '', | |
| 337 | + tqje: '0.00', thje: '', bz: '', _manual: true, | |
| 338 | + }) | |
| 339 | + }, | |
| 340 | + handleDelMx(index) { | |
| 341 | + this.dataForm.wtTjdMxList.splice(index, 1) | |
| 342 | + }, | |
| 343 | + clearAllMx() { | |
| 344 | + this.$confirm('确认清空所有商品明细?', '提示', { type: 'warning' }).then(() => { | |
| 345 | + this.dataForm.wtTjdMxList = [] | |
| 346 | + this.loadAllFlag = false | |
| 347 | + }).catch(() => {}) | |
| 348 | + }, | |
| 349 | + getSummaries(param) { | |
| 350 | + const { columns, data } = param | |
| 351 | + const sums = [] | |
| 352 | + columns.forEach((col, i) => { | |
| 353 | + if (i === 0) { sums[i] = '合计'; return } | |
| 354 | + if (col.property === 'sl') sums[i] = data.reduce((t, r) => t + (parseFloat(r.sl) || 0), 0) | |
| 355 | + else if (col.property === 'tqje') sums[i] = data.reduce((t, r) => t + (parseFloat(r.tqje) || 0), 0).toFixed(2) | |
| 356 | + else if (col.property === 'thje') sums[i] = data.reduce((t, r) => t + (parseFloat(r.thje) || 0), 0).toFixed(2) | |
| 357 | + else sums[i] = '' | |
| 358 | + }) | |
| 359 | + return sums | |
| 360 | + }, | |
| 361 | + dataFormSubmit() { | |
| 362 | + this.$refs['elForm'].validate((valid) => { | |
| 363 | + if (!valid) return | |
| 364 | + if (!this.dataForm.wtTjdMxList || this.dataForm.wtTjdMxList.length === 0) { | |
| 365 | + this.$message.warning('请至少添加一条商品明细'); return | |
| 366 | + } | |
| 367 | + this.submitLoading = true | |
| 368 | + const isNew = !this.dataForm.id | |
| 369 | + const method = isNew ? 'post' : 'put' | |
| 370 | + const url = isNew ? '/api/Extend/WtTjd' : `/api/Extend/WtTjd/${this.dataForm.id}` | |
| 371 | + request({ url, method, data: this.dataForm }).then((res) => { | |
| 372 | + this.$message({ type: 'success', message: res.msg || '保存成功', duration: 1000, onClose: () => { this.visible = false; this.$emit('refresh', true) } }) | |
| 373 | + }).finally(() => { this.submitLoading = false }) | |
| 374 | + }) | |
| 375 | + }, | |
| 376 | + handleAudit() { | |
| 377 | + if (!this.dataForm.id) return | |
| 378 | + this.$confirm('确认审核?审核后不可修改,调后价格将写入成本价表。', '审核确认', { type: 'warning' }).then(() => { | |
| 379 | + request({ url: `/api/Extend/WtTjd/Actions/Audit/${this.dataForm.id}`, method: 'POST' }).then(res => { | |
| 380 | + this.$message({ type: 'success', message: res.msg || '审核成功', duration: 1000, onClose: () => { this.visible = false; this.$emit('refresh', true) } }) | |
| 381 | + }) | |
| 382 | + }).catch(() => {}) | |
| 383 | + } | |
| 384 | + } | |
| 385 | + } | |
| 107 | 386 | </script> |
| 108 | - | |
| 109 | 387 | <style scoped> |
| 110 | -</style> | |
| 111 | 388 | \ No newline at end of file |
| 389 | +.tjd-section { margin-bottom: 14px; } | |
| 390 | +.tjd-section__header { | |
| 391 | + display: flex; align-items: center; gap: 10px; | |
| 392 | + margin-bottom: 10px; padding-bottom: 6px; border-bottom: 1px solid #EBEEF5; | |
| 393 | +} | |
| 394 | +.tjd-section__title { font-size: 14px; font-weight: 600; color: #303133; } | |
| 395 | +.tjd-section__count { font-size: 12px; color: #909399; margin-left: auto; } | |
| 396 | +.tjd-toolbar { | |
| 397 | + display: flex; align-items: center; justify-content: space-between; | |
| 398 | + padding: 6px 12px; background: #F5F7FA; border-radius: 4px; margin-bottom: 8px; | |
| 399 | +} | |
| 400 | +.tjd-toolbar__left { display: flex; align-items: center; gap: 8px; } | |
| 401 | +.tjd-toolbar__hint { font-size: 12px; color: #C0C4CC; } | |
| 402 | +.tjd-empty { text-align: center; padding: 30px 0; color: #909399; } | |
| 403 | +.tjd-empty p { margin: 6px 0 0; font-size: 13px; } | |
| 404 | +.tjd-empty__hint { font-size: 12px !important; color: #C0C4CC; } | |
| 405 | +.tjd-code { font-family: 'SFMono-Regular', Consolas, monospace; font-size: 12px; color: #606266; } | |
| 406 | +.text-warning { color: #E6A23C; } | |
| 407 | +.text-primary { color: #409EFF; font-weight: 500; } | |
| 408 | +</style> | |
| 409 | +<style> | |
| 410 | +.tjd-dialog .el-dialog__body { padding: 14px 20px; max-height: calc(100vh - 200px); overflow-y: auto; } | |
| 411 | +.tjd-table .tjd-editable-col { background-color: #FFF9EF !important; } | |
| 412 | +.tjd-table .el-input-group__append { padding: 0 5px; } | |
| 413 | +</style> | ... | ... |
Antis.Erp.Plat/antis-ncc-admin/src/views/wtPriceAdjust/index.vue
| 1 | 1 | <template> |
| 2 | - <div class="NCC-common-layout"> | |
| 3 | - <div class="NCC-common-layout-center"> | |
| 4 | - <el-row class="NCC-common-search-box" :gutter="16"> | |
| 5 | - <el-form @submit.native.prevent> | |
| 6 | - <el-col :span="6"> | |
| 7 | - <el-form-item label="调价单编号"> | |
| 8 | - <el-input v-model="query.code" placeholder="调价单编号" clearable /> | |
| 9 | - </el-form-item> | |
| 10 | - </el-col> | |
| 11 | - <el-col :span="6"> | |
| 12 | - <el-form-item label="商品名称"> | |
| 13 | - <el-input v-model="query.productName" placeholder="商品名称" clearable /> | |
| 14 | - </el-form-item> | |
| 15 | - </el-col> | |
| 16 | - <el-col :span="6"> | |
| 17 | - <el-form-item label="调价日期"> | |
| 18 | - <el-date-picker v-model="query.adjustDate" type="daterange" value-format="yyyy-MM-dd" start-placeholder="开始日期" end-placeholder="结束日期" /> | |
| 19 | - </el-form-item> | |
| 20 | - </el-col> | |
| 21 | - <el-col :span="6"> | |
| 22 | - <el-form-item> | |
| 23 | - <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> | |
| 24 | - <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> | |
| 25 | - </el-form-item> | |
| 26 | - </el-col> | |
| 27 | - </el-form> | |
| 28 | - </el-row> | |
| 29 | - <div class="NCC-common-layout-main NCC-flex-main"> | |
| 30 | - <div class="NCC-common-head"> | |
| 31 | - <div> | |
| 32 | - <el-button type="primary" icon="el-icon-plus" @click="addOrUpdateHandle()">新增</el-button> | |
| 33 | - </div> | |
| 34 | - <div class="NCC-common-head-right"> | |
| 35 | - <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 36 | - <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" @click="reset()" /> | |
| 37 | - </el-tooltip> | |
| 38 | - <screenfull isContainer /> | |
| 39 | - </div> | |
| 40 | - </div> | |
| 41 | - <NCC-table v-loading="listLoading" :data="list"> | |
| 42 | - <el-table-column prop="code" label="调价单编号" align="left" /> | |
| 43 | - <el-table-column prop="productName" label="商品名称" align="left" /> | |
| 44 | - <el-table-column prop="oldPrice" label="原价" align="left" /> | |
| 45 | - <el-table-column prop="newPrice" label="新价" align="left" /> | |
| 46 | - <el-table-column prop="adjustDate" label="调价日期" align="left" /> | |
| 47 | - <el-table-column prop="status" label="状态" align="left" /> | |
| 48 | - <el-table-column label="操作" fixed="right" width="120"> | |
| 49 | - <template slot-scope="scope"> | |
| 50 | - <el-button type="text" @click="addOrUpdateHandle(scope.row.id)">编辑</el-button> | |
| 51 | - <el-button type="text" @click="handleDel(scope.row.id)" class="NCC-table-delBtn">删除</el-button> | |
| 52 | - </template> | |
| 53 | - </el-table-column> | |
| 54 | - </NCC-table> | |
| 55 | - <pagination :total="total" :page.sync="listQuery.currentPage" :limit.sync="listQuery.pageSize" @pagination="initData" /> | |
| 56 | - </div> | |
| 57 | - <PriceAdjustForm v-if="formVisible" :id="editId" @refresh="refresh" @close="formVisible=false" /> | |
| 58 | - </div> | |
| 59 | - </div> | |
| 2 | + <div class="NCC-common-layout"> | |
| 3 | + <div class="NCC-common-layout-center"> | |
| 4 | + <el-row class="NCC-common-search-box" :gutter="16"> | |
| 5 | + <el-form @submit.native.prevent> | |
| 6 | + <el-col :span="6"> | |
| 7 | + <el-form-item label="调价单编号"> | |
| 8 | + <el-input v-model="query.keyword" placeholder="编号或商品名" clearable /> | |
| 9 | + </el-form-item> | |
| 10 | + </el-col> | |
| 11 | + <el-col :span="6"> | |
| 12 | + <el-form-item label="仓库"> | |
| 13 | + <el-select v-model="query.ck" placeholder="全部仓库" clearable> | |
| 14 | + <el-option v-for="(item, index) in ckOptions" :key="index" :label="item.F_mdmc" :value="item.F_Id" /> | |
| 15 | + </el-select> | |
| 16 | + </el-form-item> | |
| 17 | + </el-col> | |
| 18 | + <el-col :span="6"> | |
| 19 | + <el-form-item label="状态"> | |
| 20 | + <el-select v-model="query.djzt" placeholder="全部" clearable> | |
| 21 | + <el-option label="草稿" value="草稿" /> | |
| 22 | + <el-option label="已审核" value="已审核" /> | |
| 23 | + </el-select> | |
| 24 | + </el-form-item> | |
| 25 | + </el-col> | |
| 26 | + <el-col :span="6"> | |
| 27 | + <el-form-item label="日期范围"> | |
| 28 | + <el-date-picker v-model="query.dateRange" type="daterange" value-format="timestamp" format="yyyy-MM-dd" start-placeholder="开始日期" end-placeholder="结束日期" /> | |
| 29 | + </el-form-item> | |
| 30 | + </el-col> | |
| 31 | + <el-col :span="6"> | |
| 32 | + <el-form-item> | |
| 33 | + <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button> | |
| 34 | + <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button> | |
| 35 | + </el-form-item> | |
| 36 | + </el-col> | |
| 37 | + </el-form> | |
| 38 | + </el-row> | |
| 39 | + <div class="NCC-common-layout-main NCC-flex-main"> | |
| 40 | + <div class="NCC-common-head"> | |
| 41 | + <div> | |
| 42 | + <el-button type="primary" icon="el-icon-plus" @click="addOrUpdateHandle()">新增</el-button> | |
| 43 | + </div> | |
| 44 | + <div class="NCC-common-head-right"> | |
| 45 | + <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 46 | + <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" @click="reset()" /> | |
| 47 | + </el-tooltip> | |
| 48 | + <screenfull isContainer /> | |
| 49 | + </div> | |
| 50 | + </div> | |
| 51 | + <NCC-table v-loading="listLoading" :data="list"> | |
| 52 | + <el-table-column prop="id" label="单据编号" align="left" /> | |
| 53 | + <el-table-column prop="djrq" label="单据日期" align="left" :formatter="ncc.tableDateFormat" /> | |
| 54 | + <el-table-column label="仓库" prop="ck" align="left"> | |
| 55 | + <template slot-scope="scope"> | |
| 56 | + {{ getWarehouseName(scope.row.ck) }} | |
| 57 | + </template> | |
| 58 | + </el-table-column> | |
| 59 | + <el-table-column prop="djzt" label="状态" align="left" width="100"> | |
| 60 | + <template slot-scope="scope"> | |
| 61 | + <el-tag v-if="scope.row.djzt === '已审核'" type="success" size="mini">已审核</el-tag> | |
| 62 | + <el-tag v-else type="info" size="mini">{{ scope.row.djzt || '草稿' }}</el-tag> | |
| 63 | + </template> | |
| 64 | + </el-table-column> | |
| 65 | + <el-table-column prop="zdr" label="制单人" align="left" /> | |
| 66 | + <el-table-column label="操作" fixed="right" width="220"> | |
| 67 | + <template slot-scope="scope"> | |
| 68 | + <el-button type="text" @click="addOrUpdateHandle(scope.row.id, true)">详情</el-button> | |
| 69 | + <el-button type="text" @click="addOrUpdateHandle(scope.row.id)" v-if="!scope.row.djzt || scope.row.djzt === '草稿'">编辑</el-button> | |
| 70 | + <el-button type="text" @click="handleAudit(scope.row.id)" v-if="!scope.row.djzt || scope.row.djzt === '草稿'" style="color:#E6A23C">审核</el-button> | |
| 71 | + <el-button type="text" @click="handleDel(scope.row.id)" v-if="!scope.row.djzt || scope.row.djzt === '草稿'" class="NCC-table-delBtn">删除</el-button> | |
| 72 | + </template> | |
| 73 | + </el-table-column> | |
| 74 | + </NCC-table> | |
| 75 | + <pagination :total="total" :page.sync="listQuery.currentPage" :limit.sync="listQuery.pageSize" @pagination="initData" /> | |
| 76 | + </div> | |
| 77 | + </div> | |
| 78 | + <NCC-Form v-if="formVisible" ref="NCCForm" @refresh="refresh" /> | |
| 79 | + </div> | |
| 60 | 80 | </template> |
| 61 | - | |
| 62 | 81 | <script> |
| 63 | -import NCCtable from '@/components/NCC-table' | |
| 64 | -import pagination from '@/components/Pagination' | |
| 65 | -import screenfull from '@/components/Screenfull' | |
| 66 | -import PriceAdjustForm from './Form.vue' | |
| 67 | - | |
| 68 | -export default { | |
| 69 | - name: 'PriceAdjustIndex', | |
| 70 | - components: { NCCtable, pagination, screenfull, PriceAdjustForm }, | |
| 71 | - data() { | |
| 72 | - return { | |
| 73 | - query: { | |
| 74 | - code: '', | |
| 75 | - productName: '', | |
| 76 | - adjustDate: [] | |
| 77 | - }, | |
| 78 | - list: [], | |
| 79 | - listLoading: false, | |
| 80 | - total: 0, | |
| 81 | - listQuery: { | |
| 82 | - currentPage: 1, | |
| 83 | - pageSize: 20 | |
| 84 | - }, | |
| 85 | - formVisible: false, | |
| 86 | - editId: null | |
| 87 | - } | |
| 88 | - }, | |
| 89 | - methods: { | |
| 90 | - search() { | |
| 91 | - this.listQuery.currentPage = 1 | |
| 92 | - this.initData() | |
| 93 | - }, | |
| 94 | - reset() { | |
| 95 | - this.query = { code: '', productName: '', adjustDate: [] } | |
| 96 | - this.search() | |
| 97 | - }, | |
| 98 | - initData() { | |
| 99 | - // TODO: 获取调价单列表数据 | |
| 100 | - }, | |
| 101 | - addOrUpdateHandle(id) { | |
| 102 | - this.editId = id || null | |
| 103 | - this.formVisible = true | |
| 104 | - }, | |
| 105 | - handleDel(id) { | |
| 106 | - // TODO: 删除调价单 | |
| 107 | - }, | |
| 108 | - refresh() { | |
| 109 | - this.initData() | |
| 110 | - this.formVisible = false | |
| 111 | - } | |
| 112 | - } | |
| 113 | -} | |
| 82 | + import request from '@/utils/request' | |
| 83 | + import NCCForm from './Form' | |
| 84 | + import { previewDataInterface } from '@/api/systemData/dataInterface' | |
| 85 | + export default { | |
| 86 | + components: { NCCForm }, | |
| 87 | + data() { | |
| 88 | + return { | |
| 89 | + query: { | |
| 90 | + keyword: undefined, | |
| 91 | + ck: undefined, | |
| 92 | + djzt: undefined, | |
| 93 | + dateRange: undefined, | |
| 94 | + }, | |
| 95 | + list: [], | |
| 96 | + listLoading: true, | |
| 97 | + total: 0, | |
| 98 | + listQuery: { | |
| 99 | + currentPage: 1, | |
| 100 | + pageSize: 20, | |
| 101 | + sort: "desc", | |
| 102 | + sidx: "", | |
| 103 | + }, | |
| 104 | + formVisible: false, | |
| 105 | + ckOptions: [], | |
| 106 | + } | |
| 107 | + }, | |
| 108 | + created() { | |
| 109 | + this.initData() | |
| 110 | + this.getCkOptions() | |
| 111 | + }, | |
| 112 | + methods: { | |
| 113 | + getCkOptions() { | |
| 114 | + previewDataInterface('681758216954053893').then(res => { | |
| 115 | + this.ckOptions = res.data | |
| 116 | + }) | |
| 117 | + }, | |
| 118 | + getWarehouseName(warehouseId) { | |
| 119 | + if (!warehouseId || !this.ckOptions || !Array.isArray(this.ckOptions)) return warehouseId || '无' | |
| 120 | + const warehouse = this.ckOptions.find(item => item.F_Id === warehouseId) | |
| 121 | + return warehouse ? warehouse.F_mdmc : warehouseId | |
| 122 | + }, | |
| 123 | + initData() { | |
| 124 | + this.listLoading = true | |
| 125 | + let query = { | |
| 126 | + ...this.listQuery, | |
| 127 | + keyword: this.query.keyword, | |
| 128 | + ck: this.query.ck, | |
| 129 | + djzt: this.query.djzt, | |
| 130 | + } | |
| 131 | + if (this.query.dateRange && this.query.dateRange.length === 2) { | |
| 132 | + query.startDate = this.query.dateRange[0] | |
| 133 | + query.endDate = this.query.dateRange[1] | |
| 134 | + } | |
| 135 | + let cleanQuery = {} | |
| 136 | + for (let key in query) { | |
| 137 | + if (query[key] !== undefined && query[key] !== null && query[key] !== '') { | |
| 138 | + cleanQuery[key] = query[key] | |
| 139 | + } | |
| 140 | + } | |
| 141 | + request({ | |
| 142 | + url: '/api/Extend/WtTjd', | |
| 143 | + method: 'GET', | |
| 144 | + data: cleanQuery | |
| 145 | + }).then(res => { | |
| 146 | + this.list = res.data.list | |
| 147 | + this.total = res.data.pagination.total | |
| 148 | + this.listLoading = false | |
| 149 | + }).catch(() => { | |
| 150 | + this.listLoading = false | |
| 151 | + }) | |
| 152 | + }, | |
| 153 | + handleDel(id) { | |
| 154 | + this.$confirm('此操作将永久删除该调价单, 是否继续?', '提示', { | |
| 155 | + type: 'warning' | |
| 156 | + }).then(() => { | |
| 157 | + request({ | |
| 158 | + url: `/api/Extend/WtTjd/${id}`, | |
| 159 | + method: 'DELETE' | |
| 160 | + }).then(res => { | |
| 161 | + this.$message({ | |
| 162 | + type: 'success', | |
| 163 | + message: res.msg, | |
| 164 | + onClose: () => { | |
| 165 | + this.initData() | |
| 166 | + } | |
| 167 | + }) | |
| 168 | + }) | |
| 169 | + }).catch(() => {}) | |
| 170 | + }, | |
| 171 | + handleAudit(id) { | |
| 172 | + this.$confirm('确认审核该调价单?审核后将不可修改。', '审核确认', { | |
| 173 | + type: 'warning' | |
| 174 | + }).then(() => { | |
| 175 | + request({ | |
| 176 | + url: `/api/Extend/WtTjd/Actions/Audit/${id}`, | |
| 177 | + method: 'POST' | |
| 178 | + }).then(res => { | |
| 179 | + this.$message({ | |
| 180 | + type: 'success', | |
| 181 | + message: res.msg || '审核成功', | |
| 182 | + onClose: () => { | |
| 183 | + this.initData() | |
| 184 | + } | |
| 185 | + }) | |
| 186 | + }) | |
| 187 | + }).catch(() => {}) | |
| 188 | + }, | |
| 189 | + addOrUpdateHandle(id, isDetail) { | |
| 190 | + this.formVisible = true | |
| 191 | + this.$nextTick(() => { | |
| 192 | + if (this.$refs.NCCForm) { | |
| 193 | + this.$refs.NCCForm.init(id, isDetail) | |
| 194 | + } else { | |
| 195 | + setTimeout(() => { | |
| 196 | + if (this.$refs.NCCForm) { | |
| 197 | + this.$refs.NCCForm.init(id, isDetail) | |
| 198 | + } | |
| 199 | + }, 100) | |
| 200 | + } | |
| 201 | + }) | |
| 202 | + }, | |
| 203 | + search() { | |
| 204 | + this.listQuery = { | |
| 205 | + currentPage: 1, | |
| 206 | + pageSize: 20, | |
| 207 | + sort: "desc", | |
| 208 | + sidx: "", | |
| 209 | + } | |
| 210 | + this.initData() | |
| 211 | + }, | |
| 212 | + refresh(isRefresh) { | |
| 213 | + this.formVisible = false | |
| 214 | + if (isRefresh) this.reset() | |
| 215 | + }, | |
| 216 | + reset() { | |
| 217 | + for (let key in this.query) { | |
| 218 | + this.query[key] = undefined | |
| 219 | + } | |
| 220 | + this.listQuery = { | |
| 221 | + currentPage: 1, | |
| 222 | + pageSize: 20, | |
| 223 | + sort: "desc", | |
| 224 | + sidx: "", | |
| 225 | + } | |
| 226 | + this.initData() | |
| 227 | + } | |
| 228 | + } | |
| 229 | + } | |
| 114 | 230 | </script> |
| 115 | - | |
| 116 | -<style scoped> | |
| 117 | -</style> | |
| 118 | 231 | \ No newline at end of file | ... | ... |
Antis.Erp.Plat/antis-ncc-admin/src/views/wtSpCost/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="NCC-common-layout"> | |
| 3 | + <div class="NCC-common-layout-center"> | |
| 4 | + <el-row class="NCC-common-search-box" :gutter="16"> | |
| 5 | + <el-form @submit.native.prevent> | |
| 6 | + <el-col :span="6"> | |
| 7 | + <el-form-item label="商品搜索"> | |
| 8 | + <el-input v-model="query.keyword" placeholder="商品名称或编码" clearable @keyup.enter.native="search" /> | |
| 9 | + </el-form-item> | |
| 10 | + </el-col> | |
| 11 | + <el-col :span="6"> | |
| 12 | + <el-form-item label="仓库"> | |
| 13 | + <el-select v-model="query.ck" placeholder="全部仓库" clearable filterable> | |
| 14 | + <el-option v-for="item in ckOptions" :key="item.F_Id" :label="item.F_mdmc" :value="item.F_Id" /> | |
| 15 | + </el-select> | |
| 16 | + </el-form-item> | |
| 17 | + </el-col> | |
| 18 | + <el-col :span="6"> | |
| 19 | + <el-form-item> | |
| 20 | + <el-button type="primary" icon="el-icon-search" @click="search">查询</el-button> | |
| 21 | + <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button> | |
| 22 | + </el-form-item> | |
| 23 | + </el-col> | |
| 24 | + </el-form> | |
| 25 | + </el-row> | |
| 26 | + <div class="NCC-common-layout-main NCC-flex-main"> | |
| 27 | + <div class="NCC-common-head"> | |
| 28 | + <div> | |
| 29 | + <span style="font-size:14px;font-weight:bold;color:#303133">商品成本价表</span> | |
| 30 | + <el-tag type="info" size="mini" style="margin-left:8px">共 {{ total }} 条</el-tag> | |
| 31 | + </div> | |
| 32 | + <div class="NCC-common-head-right"> | |
| 33 | + <el-tooltip effect="dark" content="刷新" placement="top"> | |
| 34 | + <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false" @click="reset" /> | |
| 35 | + </el-tooltip> | |
| 36 | + <screenfull isContainer /> | |
| 37 | + </div> | |
| 38 | + </div> | |
| 39 | + <NCC-table v-loading="listLoading" :data="list" show-summary :summary-method="getSummaries"> | |
| 40 | + <el-table-column type="index" width="50" label="#" align="center" /> | |
| 41 | + <el-table-column prop="ckmc" label="仓库" min-width="130"> | |
| 42 | + <template slot-scope="scope">{{ scope.row.ckmc || scope.row.ck || '-' }}</template> | |
| 43 | + </el-table-column> | |
| 44 | + <el-table-column prop="spbm" label="商品编码" width="150"> | |
| 45 | + <template slot-scope="scope">{{ scope.row.spbm || '-' }}</template> | |
| 46 | + </el-table-column> | |
| 47 | + <el-table-column prop="spmc" label="商品名称" min-width="180"> | |
| 48 | + <template slot-scope="scope">{{ scope.row.spmc || '-' }}</template> | |
| 49 | + </el-table-column> | |
| 50 | + <el-table-column prop="sl" label="在库数量" width="100" align="right"> | |
| 51 | + <template slot-scope="scope">{{ scope.row.sl }}</template> | |
| 52 | + </el-table-column> | |
| 53 | + <el-table-column prop="cbj" label="成本单价" width="120" align="right"> | |
| 54 | + <template slot-scope="scope"> | |
| 55 | + <span :style="{ color: parseFloat(scope.row.cbj) > 0 ? '#303133' : '#E6A23C' }">{{ formatPrice(scope.row.cbj) }}</span> | |
| 56 | + </template> | |
| 57 | + </el-table-column> | |
| 58 | + <el-table-column prop="cbje" label="成本金额" width="130" align="right"> | |
| 59 | + <template slot-scope="scope"> | |
| 60 | + <span style="font-weight:bold">{{ formatPrice(scope.row.cbje) }}</span> | |
| 61 | + </template> | |
| 62 | + </el-table-column> | |
| 63 | + <el-table-column prop="updateTime" label="最后更新" width="170" align="center"> | |
| 64 | + <template slot-scope="scope">{{ formatTime(scope.row.updateTime) }}</template> | |
| 65 | + </el-table-column> | |
| 66 | + </NCC-table> | |
| 67 | + <pagination :total="total" :page.sync="listQuery.currentPage" :limit.sync="listQuery.pageSize" @pagination="initData" /> | |
| 68 | + </div> | |
| 69 | + </div> | |
| 70 | + </div> | |
| 71 | +</template> | |
| 72 | +<script> | |
| 73 | + import request from '@/utils/request' | |
| 74 | + import { previewDataInterface } from '@/api/systemData/dataInterface' | |
| 75 | + export default { | |
| 76 | + data() { | |
| 77 | + return { | |
| 78 | + query: { | |
| 79 | + keyword: undefined, | |
| 80 | + ck: undefined, | |
| 81 | + }, | |
| 82 | + list: [], | |
| 83 | + listLoading: true, | |
| 84 | + total: 0, | |
| 85 | + listQuery: { | |
| 86 | + currentPage: 1, | |
| 87 | + pageSize: 20, | |
| 88 | + }, | |
| 89 | + ckOptions: [], | |
| 90 | + } | |
| 91 | + }, | |
| 92 | + created() { | |
| 93 | + this.initData() | |
| 94 | + this.getCkOptions() | |
| 95 | + }, | |
| 96 | + methods: { | |
| 97 | + getCkOptions() { | |
| 98 | + previewDataInterface('681758216954053893').then(res => { | |
| 99 | + this.ckOptions = res.data | |
| 100 | + }) | |
| 101 | + }, | |
| 102 | + formatPrice(val) { | |
| 103 | + const n = parseFloat(val) | |
| 104 | + if (isNaN(n)) return '-' | |
| 105 | + return n.toFixed(2) | |
| 106 | + }, | |
| 107 | + formatTime(val) { | |
| 108 | + if (!val) return '-' | |
| 109 | + const d = new Date(val) | |
| 110 | + if (isNaN(d.getTime())) return val | |
| 111 | + const pad = n => String(n).padStart(2, '0') | |
| 112 | + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}` | |
| 113 | + }, | |
| 114 | + initData() { | |
| 115 | + this.listLoading = true | |
| 116 | + const params = { | |
| 117 | + currentPage: this.listQuery.currentPage, | |
| 118 | + pageSize: this.listQuery.pageSize, | |
| 119 | + } | |
| 120 | + if (this.query.keyword) params.keyword = this.query.keyword | |
| 121 | + if (this.query.ck) params.ck = this.query.ck | |
| 122 | + request({ | |
| 123 | + url: '/api/Extend/WtTjd/Actions/GetCostList', | |
| 124 | + method: 'GET', | |
| 125 | + data: params | |
| 126 | + }).then(res => { | |
| 127 | + this.list = res.data.list || [] | |
| 128 | + this.total = (res.data.pagination && res.data.pagination.total) || 0 | |
| 129 | + this.listLoading = false | |
| 130 | + }).catch(() => { | |
| 131 | + this.listLoading = false | |
| 132 | + }) | |
| 133 | + }, | |
| 134 | + getSummaries(param) { | |
| 135 | + const { columns, data } = param | |
| 136 | + const sums = [] | |
| 137 | + columns.forEach((col, idx) => { | |
| 138 | + if (idx === 0) { sums[idx] = '合计'; return } | |
| 139 | + if (col.property === 'sl') { | |
| 140 | + sums[idx] = data.reduce((t, r) => t + (parseFloat(r.sl) || 0), 0) | |
| 141 | + } else if (col.property === 'cbje') { | |
| 142 | + sums[idx] = data.reduce((t, r) => t + (parseFloat(r.cbje) || 0), 0).toFixed(2) | |
| 143 | + } else { | |
| 144 | + sums[idx] = '' | |
| 145 | + } | |
| 146 | + }) | |
| 147 | + return sums | |
| 148 | + }, | |
| 149 | + search() { | |
| 150 | + this.listQuery.currentPage = 1 | |
| 151 | + this.initData() | |
| 152 | + }, | |
| 153 | + reset() { | |
| 154 | + this.query = { keyword: undefined, ck: undefined } | |
| 155 | + this.listQuery = { currentPage: 1, pageSize: 20 } | |
| 156 | + this.initData() | |
| 157 | + } | |
| 158 | + } | |
| 159 | + } | |
| 160 | +</script> | ... | ... |
Antis.Erp.Plat/docs/商品成本价与调价单开发计划.md
0 → 100644
| 1 | +# 商品成本价体系与调价单开发计划 | |
| 2 | + | |
| 3 | +> **现有基础**:前端 `wtPriceAdjust/index.vue` + `Form.vue`(骨架页面),后端和数据库均不存在 | |
| 4 | + | |
| 5 | +## 一、背景与目标 | |
| 6 | + | |
| 7 | +### 现状问题 | |
| 8 | +1. **没有统一的成本价存储**:当前成本价只能从最近一次采购入库单的单价推算,不准确 | |
| 9 | +2. **同一商品不同仓库成本不同**:如"卡带 开心镇"在莱迪仓平均成本 42.92,景枫仓 111.00 | |
| 10 | +3. **出库时不记录成本价**:`wt_xsckd_mx` 表只有售价(dj),无法计算毛利 | |
| 11 | +4. **拆装单成本价精度丢失**:`wt_czdmx.cbdj` 字段是 `decimal(50,0)`,没有小数位 | |
| 12 | +5. **缺少调价单功能**:无法对在库商品进行成本调整 | |
| 13 | + | |
| 14 | +### 目标 | |
| 15 | +建立完整的**加权平均成本价体系**,支持:采购入库自动算成本 → 调价单手动调整 → 出库时记录快照 → 拆装单/调拨单联动 | |
| 16 | + | |
| 17 | +--- | |
| 18 | + | |
| 19 | +## 二、数据库变更 | |
| 20 | + | |
| 21 | +### 2.1 新建表:`wt_sp_cost`(商品仓库成本表) | |
| 22 | + | |
| 23 | +> 核心表,按「商品+仓库」维度存储当前加权平均成本价 | |
| 24 | + | |
| 25 | +```sql | |
| 26 | +CREATE TABLE wt_sp_cost ( | |
| 27 | + F_Id VARCHAR(50) NOT NULL COMMENT '主键', | |
| 28 | + spbh VARCHAR(50) NOT NULL COMMENT '商品编号', | |
| 29 | + ck VARCHAR(50) NOT NULL COMMENT '仓库ID', | |
| 30 | + cbj DECIMAL(18,2) DEFAULT 0.00 COMMENT '当前加权平均成本价', | |
| 31 | + sl INT DEFAULT 0 COMMENT '当前在库数量', | |
| 32 | + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间', | |
| 33 | + PRIMARY KEY (F_Id), | |
| 34 | + UNIQUE KEY uk_sp_ck (spbh, ck) | |
| 35 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品仓库成本表'; | |
| 36 | +``` | |
| 37 | + | |
| 38 | +### 2.2 新建表:`wt_tjd`(调价单主表) | |
| 39 | + | |
| 40 | +```sql | |
| 41 | +CREATE TABLE wt_tjd ( | |
| 42 | + F_Id VARCHAR(50) NOT NULL COMMENT '单据编号(TJD+日期+序号)', | |
| 43 | + djrq DATETIME COMMENT '单据日期', | |
| 44 | + ck VARCHAR(50) COMMENT '仓库(选仓库加载全部商品)', | |
| 45 | + jsr VARCHAR(50) COMMENT '经手人', | |
| 46 | + zdr VARCHAR(50) COMMENT '制单人', | |
| 47 | + djzt VARCHAR(50) DEFAULT '草稿' COMMENT '单据状态(草稿/已审核)', | |
| 48 | + bz TEXT COMMENT '备注', | |
| 49 | + PRIMARY KEY (F_Id) | |
| 50 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品调价单'; | |
| 51 | +``` | |
| 52 | + | |
| 53 | +### 2.3 新建表:`wt_tjd_mx`(调价单明细) | |
| 54 | + | |
| 55 | +```sql | |
| 56 | +CREATE TABLE wt_tjd_mx ( | |
| 57 | + F_Id VARCHAR(50) NOT NULL COMMENT '主键', | |
| 58 | + djbh VARCHAR(50) NOT NULL COMMENT '单据编号(FK→wt_tjd)', | |
| 59 | + ck VARCHAR(50) COMMENT '仓库', | |
| 60 | + spbh VARCHAR(50) COMMENT '商品编号', | |
| 61 | + spmc VARCHAR(200) COMMENT '商品名称', | |
| 62 | + dw VARCHAR(50) COMMENT '单位', | |
| 63 | + sl INT DEFAULT 0 COMMENT '数量(在库数量,自动带出)', | |
| 64 | + tqdj DECIMAL(18,2) DEFAULT 0.00 COMMENT '调前单价(自动带出)', | |
| 65 | + tjbl DECIMAL(8,4) DEFAULT 0.00 COMMENT '调价比率%', | |
| 66 | + thdj DECIMAL(18,2) DEFAULT 0.00 COMMENT '调后单价', | |
| 67 | + tqje DECIMAL(18,2) DEFAULT 0.00 COMMENT '调前金额(sl×tqdj)', | |
| 68 | + thje DECIMAL(18,2) DEFAULT 0.00 COMMENT '调后金额(sl×thdj)', | |
| 69 | + bz VARCHAR(500) COMMENT '备注', | |
| 70 | + PRIMARY KEY (F_Id) | |
| 71 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品调价单明细'; | |
| 72 | +``` | |
| 73 | + | |
| 74 | +### 2.4 修改表:`wt_xsckd_mx`(出库单明细加成本价字段) | |
| 75 | + | |
| 76 | +> 出库时保存成本价快照,用于后续毛利计算 | |
| 77 | + | |
| 78 | +```sql | |
| 79 | +ALTER TABLE wt_xsckd_mx | |
| 80 | + ADD COLUMN cbdj DECIMAL(18,2) DEFAULT 0.00 COMMENT '成本单价(出库时快照)', | |
| 81 | + ADD COLUMN cbje DECIMAL(18,2) DEFAULT 0.00 COMMENT '成本金额(出库时快照)'; | |
| 82 | +``` | |
| 83 | + | |
| 84 | +### 2.5 修改表:`wt_czdmx` + `wt_czdmx_2`(修复精度) | |
| 85 | + | |
| 86 | +> 当前 cbdj/cbje 是 decimal(50,0) 没有小数位,需要修复 | |
| 87 | + | |
| 88 | +```sql | |
| 89 | +ALTER TABLE wt_czdmx MODIFY COLUMN cbdj DECIMAL(18,2) DEFAULT 0.00 COMMENT '成本单价'; | |
| 90 | +ALTER TABLE wt_czdmx MODIFY COLUMN cbje DECIMAL(18,2) DEFAULT 0.00 COMMENT '成本金额'; | |
| 91 | +ALTER TABLE wt_czdmx_2 MODIFY COLUMN cbdj DECIMAL(18,2) DEFAULT 0.00 COMMENT '成本单价'; | |
| 92 | +ALTER TABLE wt_czdmx_2 MODIFY COLUMN cbje DECIMAL(18,2) DEFAULT 0.00 COMMENT '成本金额'; | |
| 93 | +``` | |
| 94 | + | |
| 95 | +### 2.6 初始化 `wt_sp_cost` 数据 | |
| 96 | + | |
| 97 | +> 根据当前在库序列号的批次入库价计算加权平均成本 | |
| 98 | + | |
| 99 | +```sql | |
| 100 | +INSERT INTO wt_sp_cost (F_Id, spbh, ck, cbj, sl, update_time) | |
| 101 | +SELECT | |
| 102 | + CONCAT(sub.spbh, '_', sub.in_warehouse) AS F_Id, | |
| 103 | + sub.spbh, | |
| 104 | + sub.in_warehouse AS ck, | |
| 105 | + ROUND(SUM(IFNULL(mx.dj, 0) * sub.qty) / NULLIF(SUM(sub.qty), 0), 2) AS cbj, | |
| 106 | + SUM(sub.qty) AS sl, | |
| 107 | + NOW() AS update_time | |
| 108 | +FROM ( | |
| 109 | + SELECT sn.spbh, sn.in_warehouse, sn.in_djbh, COUNT(*) AS qty | |
| 110 | + FROM wt_serial_number sn | |
| 111 | + WHERE sn.status = 0 | |
| 112 | + GROUP BY sn.spbh, sn.in_warehouse, sn.in_djbh | |
| 113 | +) sub | |
| 114 | +LEFT JOIN wt_xsckd_mx mx ON sub.in_djbh = mx.djbh AND sub.spbh = mx.spbh | |
| 115 | +GROUP BY sub.spbh, sub.in_warehouse; | |
| 116 | +``` | |
| 117 | + | |
| 118 | +--- | |
| 119 | + | |
| 120 | +## 三、业务逻辑变更 | |
| 121 | + | |
| 122 | +### 3.1 成本价读取(统一入口) | |
| 123 | + | |
| 124 | +所有需要取成本价的地方,统一从 `wt_sp_cost` 读取: | |
| 125 | + | |
| 126 | +``` | |
| 127 | +GetProductCost(商品ID, 仓库ID) | |
| 128 | + → SELECT cbj FROM wt_sp_cost WHERE spbh = 商品ID AND ck = 仓库ID | |
| 129 | + → 如果没记录,兜底取最近采购入库单价 → 取零售价 → 返回 0 | |
| 130 | +``` | |
| 131 | + | |
| 132 | +**影响范围**:拆装单(WtCzdService)、出库单(WtXsckdService)、调价单(新) | |
| 133 | + | |
| 134 | +### 3.2 采购入库时更新成本(WtXsckdService.Create) | |
| 135 | + | |
| 136 | +> 当 djlx = "采购入库单" 时,保存后自动更新 wt_sp_cost | |
| 137 | + | |
| 138 | +``` | |
| 139 | +对每个明细行: | |
| 140 | + 旧记录 = SELECT cbj, sl FROM wt_sp_cost WHERE spbh AND ck | |
| 141 | + 如果存在: | |
| 142 | + 新成本 = (旧sl × 旧cbj + 入库数 × 入库单价) ÷ (旧sl + 入库数) | |
| 143 | + UPDATE wt_sp_cost SET cbj = 新成本, sl = 旧sl + 入库数 | |
| 144 | + 如果不存在: | |
| 145 | + INSERT wt_sp_cost (spbh, ck, cbj, sl) VALUES (商品, 仓库, 入库单价, 入库数) | |
| 146 | +``` | |
| 147 | + | |
| 148 | +### 3.3 销售出库时记录成本快照(WtXsckdService.Create) | |
| 149 | + | |
| 150 | +> 当 djlx = "销售出库单" 时 | |
| 151 | + | |
| 152 | +``` | |
| 153 | +对每个明细行: | |
| 154 | + cbdj = SELECT cbj FROM wt_sp_cost WHERE spbh AND ck | |
| 155 | + cbje = cbdj × 数量 | |
| 156 | + 保存到 wt_xsckd_mx.cbdj, wt_xsckd_mx.cbje | |
| 157 | + UPDATE wt_sp_cost SET sl = sl - 出库数 WHERE spbh AND ck | |
| 158 | +``` | |
| 159 | + | |
| 160 | +### 3.4 调价单审核时更新成本(WtTjdService,新建) | |
| 161 | + | |
| 162 | +``` | |
| 163 | +对每个明细行: | |
| 164 | + UPDATE wt_sp_cost SET cbj = 调后单价 WHERE spbh AND ck | |
| 165 | +``` | |
| 166 | + | |
| 167 | +### 3.5 拆装单保存时联动(WtCzdService.Create) | |
| 168 | + | |
| 169 | +``` | |
| 170 | +出货明细: | |
| 171 | + cbdj 从 wt_sp_cost 取出货仓库的成本价 | |
| 172 | + UPDATE wt_sp_cost SET sl = sl - 出库数 WHERE 出货仓库 | |
| 173 | + | |
| 174 | +入货明细: | |
| 175 | + cbdj = 出货总成本金额 ÷ 入货总数量(分摊成本) | |
| 176 | + UPSERT wt_sp_cost SET cbj = 加权后新成本, sl = sl + 入库数 WHERE 入货仓库 | |
| 177 | +``` | |
| 178 | + | |
| 179 | +### 3.6 调拨单联动 | |
| 180 | + | |
| 181 | +``` | |
| 182 | +出仓成本不变(从 wt_sp_cost 取) | |
| 183 | +入仓按出仓成本入库(加权平均计入) | |
| 184 | +``` | |
| 185 | + | |
| 186 | +### 3.7 采购退货 / 销售退货 | |
| 187 | + | |
| 188 | +``` | |
| 189 | +退货入库:按原出库成本价回入(不改变成本单价,只增加数量和总成本) | |
| 190 | +退货出库:减少数量,成本单价不变 | |
| 191 | +``` | |
| 192 | + | |
| 193 | +--- | |
| 194 | + | |
| 195 | +## 四、后端开发任务 | |
| 196 | + | |
| 197 | +### 4.1 数据库与 Entity | |
| 198 | + | |
| 199 | +| 任务 | 说明 | | |
| 200 | +|------|------| | |
| 201 | +| 执行 SQL 建表/改表 | 2.1~2.6 的 SQL 语句 | | |
| 202 | +| WtSpCostEntity | 映射 wt_sp_cost | | |
| 203 | +| WtTjdEntity | 映射 wt_tjd | | |
| 204 | +| WtTjdMxEntity | 映射 wt_tjd_mx | | |
| 205 | +| WtXsckdMxEntity 加字段 | 加 Cbdj、Cbje 属性 | | |
| 206 | +| WtCzdmxEntity 修复精度 | cbdj/cbje 改为 decimal | | |
| 207 | + | |
| 208 | +### 4.2 调价单 Service(WtTjdService,新建) | |
| 209 | + | |
| 210 | +| 接口 | 方法 | 说明 | | |
| 211 | +|------|------|------| | |
| 212 | +| GET /api/Extend/WtTjd/{id} | GetInfo | 获取调价单详情 | | |
| 213 | +| GET /api/Extend/WtTjd | GetList | 列表查询(分页) | | |
| 214 | +| POST /api/Extend/WtTjd | Create | 新建调价单 | | |
| 215 | +| PUT /api/Extend/WtTjd/{id} | Update | 修改调价单 | | |
| 216 | +| DELETE /api/Extend/WtTjd/{id} | Delete | 删除调价单 | | |
| 217 | +| POST /api/Extend/WtTjd/Actions/Audit/{id} | Audit | 审核(更新 wt_sp_cost) | | |
| 218 | +| GET /api/Extend/WtTjd/Actions/LoadProducts | LoadProducts | 按仓库加载商品+成本价+库存 | | |
| 219 | + | |
| 220 | +### 4.3 成本价 Service(WtSpCostService,新建或合并) | |
| 221 | + | |
| 222 | +| 接口 | 说明 | | |
| 223 | +|------|------| | |
| 224 | +| GET /api/Extend/WtSpCost/GetCost?spbh=&ck= | 查询单个商品在指定仓库的成本价 | | |
| 225 | +| POST /api/Extend/WtSpCost/RecalcAll | 重算所有成本(基于当前库存数据) | | |
| 226 | + | |
| 227 | +### 4.4 改造现有 Service | |
| 228 | + | |
| 229 | +| Service | 改造内容 | | |
| 230 | +|---------|---------| | |
| 231 | +| WtXsckdService.Create | 采购入库:更新 wt_sp_cost 加权平均<br>销售出库:快照 cbdj/cbje 到明细 | | |
| 232 | +| WtCzdService.Create | 出货:快照 cbdj 从 wt_sp_cost 取<br>入货:按出货总成本分摊 | | |
| 233 | +| WtCzdService.GetProductCost | 改为从 wt_sp_cost 读取 | | |
| 234 | + | |
| 235 | +--- | |
| 236 | + | |
| 237 | +## 五、前端开发任务 | |
| 238 | + | |
| 239 | +### 5.1 调价单页面(新建) | |
| 240 | + | |
| 241 | +**列表页 `views/wtTjd/index.vue`**: | |
| 242 | +- 搜索条件:单据编号、仓库、日期范围、状态 | |
| 243 | +- 列表字段:单据编号、单据日期、仓库、经手人、状态、操作(编辑/审核/删除) | |
| 244 | + | |
| 245 | +**表单页 `views/wtTjd/Form.vue`**: | |
| 246 | +- 主表:单据编号(自动生成 TJD+日期+序号)、单据日期、仓库(下拉选择)、经手人、备注 | |
| 247 | +- 明细表(el-table 内嵌编辑): | |
| 248 | + | |
| 249 | +| 列 | 说明 | 交互 | | |
| 250 | +|----|------|------| | |
| 251 | +| 仓库 | 自动带出(跟主表仓库) | 只读 | | |
| 252 | +| 商品编码 | 选择或仓库加载 | 可选/只读 | | |
| 253 | +| 商品名称 | 自动带出 | 只读 | | |
| 254 | +| 单位 | 自动带出 | 只读 | | |
| 255 | +| 数量 | 在库数量 | 只读 | | |
| 256 | +| 调前单价 | 自动从 wt_sp_cost 带出 | 只读 | | |
| 257 | +| 调价比率% | 用户输入 | **可编辑**,变化 → 自动算调后单价 | | |
| 258 | +| 调后单价 | 用户输入或自动算 | **可编辑**,变化 → 自动算比率 | | |
| 259 | +| 调前金额 | 数量×调前单价 | 自动算,只读 | | |
| 260 | +| 调后金额 | 数量×调后单价 | 自动算,只读 | | |
| 261 | +| 备注 | | 可编辑 | | |
| 262 | + | |
| 263 | +**交互逻辑**: | |
| 264 | +1. 选择仓库 → 调用 LoadProducts → 加载该仓库所有有库存商品到明细表 | |
| 265 | +2. 也可手动添加行选择商品 | |
| 266 | +3. 输入调价比率 → 调后单价 = 调前单价 × (1 + 比率/100) | |
| 267 | +4. 输入调后单价 → 调价比率 = (调后 - 调前) / 调前 × 100 | |
| 268 | +5. 审核按钮 → 确认后批量更新成本价 | |
| 269 | + | |
| 270 | +### 5.2 改造拆装单 | |
| 271 | + | |
| 272 | +- `GetProductCost` 接口改为从 `wt_sp_cost` 取成本价(后端改,前端无需变) | |
| 273 | + | |
| 274 | +### 5.3 改造出库单 | |
| 275 | + | |
| 276 | +- 保存时后端自动写入 cbdj/cbje(后端改,前端可选显示) | |
| 277 | + | |
| 278 | +--- | |
| 279 | + | |
| 280 | +## 六、开发顺序(建议) | |
| 281 | + | |
| 282 | +| 阶段 | 任务 | 依赖 | | |
| 283 | +|------|------|------| | |
| 284 | +| **P1** | 执行 SQL:建 wt_sp_cost 表 + 初始化数据 | 无 | | |
| 285 | +| **P1** | 执行 SQL:修复 wt_czdmx 精度 | 无 | | |
| 286 | +| **P1** | 执行 SQL:wt_xsckd_mx 加 cbdj/cbje | 无 | | |
| 287 | +| **P2** | 后端:WtSpCostEntity + GetCost 接口 | P1 | | |
| 288 | +| **P2** | 后端:改造 GetProductCost 从 wt_sp_cost 读 | P2 | | |
| 289 | +| **P3** | 执行 SQL:建 wt_tjd + wt_tjd_mx 表 | 无 | | |
| 290 | +| **P3** | 后端:WtTjdEntity/DTO/Service 全套 CRUD | P3 | | |
| 291 | +| **P3** | 后端:LoadProducts + Audit 接口 | P2+P3 | | |
| 292 | +| **P3** | 前端:调价单列表页 + 表单页 | P3 后端 | | |
| 293 | +| **P4** | 后端:改造 WtXsckdService 采购入库更新成本 | P2 | | |
| 294 | +| **P4** | 后端:改造 WtXsckdService 出库快照成本 | P2 | | |
| 295 | +| **P4** | 后端:改造 WtCzdService 拆装单成本联动 | P2 | | |
| 296 | +| **P5** | 测试:全流程验证 | 全部 | | |
| 297 | + | |
| 298 | +--- | |
| 299 | + | |
| 300 | +## 七、验收标准 | |
| 301 | + | |
| 302 | +1. ✅ `wt_sp_cost` 初始化后,每个商品+仓库组合有正确的加权平均成本价 | |
| 303 | +2. ✅ 拆装单选商品后自动带出成本单价(从 wt_sp_cost) | |
| 304 | +3. ✅ 调价单:选仓库加载所有商品,调价比率和调后单价双向联动 | |
| 305 | +4. ✅ 调价单审核后,wt_sp_cost.cbj 更新为调后单价 | |
| 306 | +5. ✅ 采购入库后,wt_sp_cost 自动重算加权平均成本 | |
| 307 | +6. ✅ 销售出库时,wt_xsckd_mx.cbdj/cbje 记录出库时刻的成本价 | |
| 308 | +7. ✅ 成本金额精度为 2 位小数 | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtCzd/GetProductCostInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.WtCzd | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 获取商品成本价输入参数 | |
| 7 | + /// </summary> | |
| 8 | + public class GetProductCostInput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 商品ID | |
| 12 | + /// </summary> | |
| 13 | + public string ProductId { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 仓库/门店ID | |
| 17 | + /// </summary> | |
| 18 | + public string WarehouseId { get; set; } | |
| 19 | + } | |
| 20 | +} | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtTjd/WtTjdCrInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | + | |
| 4 | +namespace NCC.Extend.Entitys.Dto.WtTjd | |
| 5 | +{ | |
| 6 | + /// <summary> | |
| 7 | + /// 调价单创建输入参数 | |
| 8 | + /// </summary> | |
| 9 | + public class WtTjdCrInput | |
| 10 | + { | |
| 11 | + /// <summary> | |
| 12 | + /// 单据日期 | |
| 13 | + /// </summary> | |
| 14 | + public DateTime? djrq { get; set; } | |
| 15 | + | |
| 16 | + /// <summary> | |
| 17 | + /// 仓库 | |
| 18 | + /// </summary> | |
| 19 | + public string ck { get; set; } | |
| 20 | + | |
| 21 | + /// <summary> | |
| 22 | + /// 经手人 | |
| 23 | + /// </summary> | |
| 24 | + public string jsr { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 备注 | |
| 28 | + /// </summary> | |
| 29 | + public string bz { get; set; } | |
| 30 | + | |
| 31 | + /// <summary> | |
| 32 | + /// 调价单明细列表 | |
| 33 | + /// </summary> | |
| 34 | + public List<WtTjdMxCrInput> wtTjdMxList { get; set; } | |
| 35 | + } | |
| 36 | + | |
| 37 | + /// <summary> | |
| 38 | + /// 调价单明细创建输入参数 | |
| 39 | + /// </summary> | |
| 40 | + public class WtTjdMxCrInput | |
| 41 | + { | |
| 42 | + /// <summary> | |
| 43 | + /// 仓库 | |
| 44 | + /// </summary> | |
| 45 | + public string ck { get; set; } | |
| 46 | + | |
| 47 | + /// <summary> | |
| 48 | + /// 商品编号 | |
| 49 | + /// </summary> | |
| 50 | + public string spbh { get; set; } | |
| 51 | + | |
| 52 | + /// <summary> | |
| 53 | + /// 商品名称 | |
| 54 | + /// </summary> | |
| 55 | + public string spmc { get; set; } | |
| 56 | + | |
| 57 | + /// <summary> | |
| 58 | + /// 单位 | |
| 59 | + /// </summary> | |
| 60 | + public string dw { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 数量 | |
| 64 | + /// </summary> | |
| 65 | + public int sl { get; set; } | |
| 66 | + | |
| 67 | + /// <summary> | |
| 68 | + /// 调前单价 | |
| 69 | + /// </summary> | |
| 70 | + public decimal tqdj { get; set; } | |
| 71 | + | |
| 72 | + /// <summary> | |
| 73 | + /// 调价比率% | |
| 74 | + /// </summary> | |
| 75 | + public decimal tjbl { get; set; } | |
| 76 | + | |
| 77 | + /// <summary> | |
| 78 | + /// 调后单价 | |
| 79 | + /// </summary> | |
| 80 | + public decimal thdj { get; set; } | |
| 81 | + | |
| 82 | + /// <summary> | |
| 83 | + /// 调前金额 | |
| 84 | + /// </summary> | |
| 85 | + public decimal tqje { get; set; } | |
| 86 | + | |
| 87 | + /// <summary> | |
| 88 | + /// 调后金额 | |
| 89 | + /// </summary> | |
| 90 | + public decimal thje { get; set; } | |
| 91 | + | |
| 92 | + /// <summary> | |
| 93 | + /// 备注 | |
| 94 | + /// </summary> | |
| 95 | + public string bz { get; set; } | |
| 96 | + } | |
| 97 | +} | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtTjd/WtTjdInfoOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | + | |
| 4 | +namespace NCC.Extend.Entitys.Dto.WtTjd | |
| 5 | +{ | |
| 6 | + /// <summary> | |
| 7 | + /// 调价单详情输出参数 | |
| 8 | + /// </summary> | |
| 9 | + public class WtTjdInfoOutput | |
| 10 | + { | |
| 11 | + /// <summary> | |
| 12 | + /// 单据编号 | |
| 13 | + /// </summary> | |
| 14 | + public string id { get; set; } | |
| 15 | + | |
| 16 | + /// <summary> | |
| 17 | + /// 单据日期 | |
| 18 | + /// </summary> | |
| 19 | + public DateTime? djrq { get; set; } | |
| 20 | + | |
| 21 | + /// <summary> | |
| 22 | + /// 仓库 | |
| 23 | + /// </summary> | |
| 24 | + public string ck { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 经手人 | |
| 28 | + /// </summary> | |
| 29 | + public string jsr { get; set; } | |
| 30 | + | |
| 31 | + /// <summary> | |
| 32 | + /// 制单人 | |
| 33 | + /// </summary> | |
| 34 | + public string zdr { get; set; } | |
| 35 | + | |
| 36 | + /// <summary> | |
| 37 | + /// 单据状态 | |
| 38 | + /// </summary> | |
| 39 | + public string djzt { get; set; } | |
| 40 | + | |
| 41 | + /// <summary> | |
| 42 | + /// 备注 | |
| 43 | + /// </summary> | |
| 44 | + public string bz { get; set; } | |
| 45 | + | |
| 46 | + /// <summary> | |
| 47 | + /// 调价单明细列表 | |
| 48 | + /// </summary> | |
| 49 | + public List<WtTjdMxInfoOutput> wtTjdMxList { get; set; } | |
| 50 | + } | |
| 51 | + | |
| 52 | + /// <summary> | |
| 53 | + /// 调价单明细详情输出参数 | |
| 54 | + /// </summary> | |
| 55 | + public class WtTjdMxInfoOutput | |
| 56 | + { | |
| 57 | + /// <summary> | |
| 58 | + /// 明细编号 | |
| 59 | + /// </summary> | |
| 60 | + public string id { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 单据编号 | |
| 64 | + /// </summary> | |
| 65 | + public string djbh { get; set; } | |
| 66 | + | |
| 67 | + /// <summary> | |
| 68 | + /// 仓库 | |
| 69 | + /// </summary> | |
| 70 | + public string ck { get; set; } | |
| 71 | + | |
| 72 | + /// <summary> | |
| 73 | + /// 商品编号 | |
| 74 | + /// </summary> | |
| 75 | + public string spbh { get; set; } | |
| 76 | + | |
| 77 | + /// <summary> | |
| 78 | + /// 商品名称 | |
| 79 | + /// </summary> | |
| 80 | + public string spmc { get; set; } | |
| 81 | + | |
| 82 | + /// <summary> | |
| 83 | + /// 单位 | |
| 84 | + /// </summary> | |
| 85 | + public string dw { get; set; } | |
| 86 | + | |
| 87 | + /// <summary> | |
| 88 | + /// 数量 | |
| 89 | + /// </summary> | |
| 90 | + public int sl { get; set; } | |
| 91 | + | |
| 92 | + /// <summary> | |
| 93 | + /// 调前单价 | |
| 94 | + /// </summary> | |
| 95 | + public decimal tqdj { get; set; } | |
| 96 | + | |
| 97 | + /// <summary> | |
| 98 | + /// 调价比率% | |
| 99 | + /// </summary> | |
| 100 | + public decimal tjbl { get; set; } | |
| 101 | + | |
| 102 | + /// <summary> | |
| 103 | + /// 调后单价 | |
| 104 | + /// </summary> | |
| 105 | + public decimal thdj { get; set; } | |
| 106 | + | |
| 107 | + /// <summary> | |
| 108 | + /// 调前金额 | |
| 109 | + /// </summary> | |
| 110 | + public decimal tqje { get; set; } | |
| 111 | + | |
| 112 | + /// <summary> | |
| 113 | + /// 调后金额 | |
| 114 | + /// </summary> | |
| 115 | + public decimal thje { get; set; } | |
| 116 | + | |
| 117 | + /// <summary> | |
| 118 | + /// 备注 | |
| 119 | + /// </summary> | |
| 120 | + public string bz { get; set; } | |
| 121 | + } | |
| 122 | +} | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtTjd/WtTjdListOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.WtTjd | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 调价单列表输出参数 | |
| 7 | + /// </summary> | |
| 8 | + public class WtTjdListOutput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 单据编号 | |
| 12 | + /// </summary> | |
| 13 | + public string id { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 单据日期 | |
| 17 | + /// </summary> | |
| 18 | + public DateTime? djrq { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 仓库名称 | |
| 22 | + /// </summary> | |
| 23 | + public string ck { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 经手人名称 | |
| 27 | + /// </summary> | |
| 28 | + public string jsr { get; set; } | |
| 29 | + | |
| 30 | + /// <summary> | |
| 31 | + /// 制单人名称 | |
| 32 | + /// </summary> | |
| 33 | + public string zdr { get; set; } | |
| 34 | + | |
| 35 | + /// <summary> | |
| 36 | + /// 单据状态 | |
| 37 | + /// </summary> | |
| 38 | + public string djzt { get; set; } | |
| 39 | + | |
| 40 | + /// <summary> | |
| 41 | + /// 备注 | |
| 42 | + /// </summary> | |
| 43 | + public string bz { get; set; } | |
| 44 | + } | |
| 45 | +} | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtTjd/WtTjdListQueryInput.cs
0 → 100644
| 1 | +using NCC.Common.Filter; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.WtTjd | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 调价单列表查询输入 | |
| 7 | + /// </summary> | |
| 8 | + public class WtTjdListQueryInput : PageInputBase | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 单据编号 | |
| 12 | + /// </summary> | |
| 13 | + public string code { get; set; } | |
| 14 | + | |
| 15 | + /// <summary> | |
| 16 | + /// 单据日期范围(逗号分隔) | |
| 17 | + /// </summary> | |
| 18 | + public string djrq { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 仓库 | |
| 22 | + /// </summary> | |
| 23 | + public string ck { get; set; } | |
| 24 | + | |
| 25 | + /// <summary> | |
| 26 | + /// 单据状态 | |
| 27 | + /// </summary> | |
| 28 | + public string djzt { get; set; } | |
| 29 | + } | |
| 30 | +} | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtTjd/WtTjdUpInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | + | |
| 4 | +namespace NCC.Extend.Entitys.Dto.WtTjd | |
| 5 | +{ | |
| 6 | + /// <summary> | |
| 7 | + /// 调价单更新输入参数 | |
| 8 | + /// </summary> | |
| 9 | + public class WtTjdUpInput : WtTjdCrInput | |
| 10 | + { | |
| 11 | + /// <summary> | |
| 12 | + /// 单据编号 | |
| 13 | + /// </summary> | |
| 14 | + public string id { get; set; } | |
| 15 | + } | |
| 16 | +} | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtSpCostEntity.cs
0 → 100644
| 1 | +using NCC.Common.Const; | |
| 2 | +using SqlSugar; | |
| 3 | +using System; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 商品仓库成本表 | |
| 9 | + /// </summary> | |
| 10 | + [SugarTable("wt_sp_cost")] | |
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 12 | + public class WtSpCostEntity | |
| 13 | + { | |
| 14 | + /// <summary> | |
| 15 | + /// 主键 | |
| 16 | + /// </summary> | |
| 17 | + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] | |
| 18 | + public string Id { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 商品编号 | |
| 22 | + /// </summary> | |
| 23 | + [SugarColumn(ColumnName = "spbh")] | |
| 24 | + public string Spbh { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 仓库ID | |
| 28 | + /// </summary> | |
| 29 | + [SugarColumn(ColumnName = "ck")] | |
| 30 | + public string Ck { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 当前加权平均成本价 | |
| 34 | + /// </summary> | |
| 35 | + [SugarColumn(ColumnName = "cbj")] | |
| 36 | + public decimal Cbj { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 当前在库数量 | |
| 40 | + /// </summary> | |
| 41 | + [SugarColumn(ColumnName = "sl")] | |
| 42 | + public int Sl { get; set; } | |
| 43 | + | |
| 44 | + /// <summary> | |
| 45 | + /// 最后更新时间 | |
| 46 | + /// </summary> | |
| 47 | + [SugarColumn(ColumnName = "update_time")] | |
| 48 | + public DateTime? UpdateTime { get; set; } | |
| 49 | + } | |
| 50 | +} | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtTjdEntity.cs
0 → 100644
| 1 | +using NCC.Common.Const; | |
| 2 | +using SqlSugar; | |
| 3 | +using System; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 调价单主表 | |
| 9 | + /// </summary> | |
| 10 | + [SugarTable("wt_tjd")] | |
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 12 | + public class WtTjdEntity | |
| 13 | + { | |
| 14 | + /// <summary> | |
| 15 | + /// 单据编号 | |
| 16 | + /// </summary> | |
| 17 | + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] | |
| 18 | + public string Id { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 单据日期 | |
| 22 | + /// </summary> | |
| 23 | + [SugarColumn(ColumnName = "djrq")] | |
| 24 | + public DateTime? Djrq { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 仓库 | |
| 28 | + /// </summary> | |
| 29 | + [SugarColumn(ColumnName = "ck")] | |
| 30 | + public string Ck { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 经手人 | |
| 34 | + /// </summary> | |
| 35 | + [SugarColumn(ColumnName = "jsr")] | |
| 36 | + public string Jsr { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 制单人 | |
| 40 | + /// </summary> | |
| 41 | + [SugarColumn(ColumnName = "zdr")] | |
| 42 | + public string Zdr { get; set; } | |
| 43 | + | |
| 44 | + /// <summary> | |
| 45 | + /// 单据状态(草稿/已审核) | |
| 46 | + /// </summary> | |
| 47 | + [SugarColumn(ColumnName = "djzt")] | |
| 48 | + public string Djzt { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 备注 | |
| 52 | + /// </summary> | |
| 53 | + [SugarColumn(ColumnName = "bz")] | |
| 54 | + public string Bz { get; set; } | |
| 55 | + } | |
| 56 | +} | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtTjdMxEntity.cs
0 → 100644
| 1 | +using NCC.Common.Const; | |
| 2 | +using SqlSugar; | |
| 3 | +using System; | |
| 4 | + | |
| 5 | +namespace NCC.Extend.Entitys | |
| 6 | +{ | |
| 7 | + /// <summary> | |
| 8 | + /// 调价单明细表 | |
| 9 | + /// </summary> | |
| 10 | + [SugarTable("wt_tjd_mx")] | |
| 11 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 12 | + public class WtTjdMxEntity | |
| 13 | + { | |
| 14 | + /// <summary> | |
| 15 | + /// 主键 | |
| 16 | + /// </summary> | |
| 17 | + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] | |
| 18 | + public string Id { get; set; } | |
| 19 | + | |
| 20 | + /// <summary> | |
| 21 | + /// 单据编号 | |
| 22 | + /// </summary> | |
| 23 | + [SugarColumn(ColumnName = "djbh")] | |
| 24 | + public string Djbh { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 仓库 | |
| 28 | + /// </summary> | |
| 29 | + [SugarColumn(ColumnName = "ck")] | |
| 30 | + public string Ck { get; set; } | |
| 31 | + | |
| 32 | + /// <summary> | |
| 33 | + /// 商品编号 | |
| 34 | + /// </summary> | |
| 35 | + [SugarColumn(ColumnName = "spbh")] | |
| 36 | + public string Spbh { get; set; } | |
| 37 | + | |
| 38 | + /// <summary> | |
| 39 | + /// 商品名称 | |
| 40 | + /// </summary> | |
| 41 | + [SugarColumn(ColumnName = "spmc")] | |
| 42 | + public string Spmc { get; set; } | |
| 43 | + | |
| 44 | + /// <summary> | |
| 45 | + /// 单位 | |
| 46 | + /// </summary> | |
| 47 | + [SugarColumn(ColumnName = "dw")] | |
| 48 | + public string Dw { get; set; } | |
| 49 | + | |
| 50 | + /// <summary> | |
| 51 | + /// 数量 | |
| 52 | + /// </summary> | |
| 53 | + [SugarColumn(ColumnName = "sl")] | |
| 54 | + public int Sl { get; set; } | |
| 55 | + | |
| 56 | + /// <summary> | |
| 57 | + /// 调前单价 | |
| 58 | + /// </summary> | |
| 59 | + [SugarColumn(ColumnName = "tqdj")] | |
| 60 | + public decimal Tqdj { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 调价比率% | |
| 64 | + /// </summary> | |
| 65 | + [SugarColumn(ColumnName = "tjbl")] | |
| 66 | + public decimal Tjbl { get; set; } | |
| 67 | + | |
| 68 | + /// <summary> | |
| 69 | + /// 调后单价 | |
| 70 | + /// </summary> | |
| 71 | + [SugarColumn(ColumnName = "thdj")] | |
| 72 | + public decimal Thdj { get; set; } | |
| 73 | + | |
| 74 | + /// <summary> | |
| 75 | + /// 调前金额 | |
| 76 | + /// </summary> | |
| 77 | + [SugarColumn(ColumnName = "tqje")] | |
| 78 | + public decimal Tqje { get; set; } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 调后金额 | |
| 82 | + /// </summary> | |
| 83 | + [SugarColumn(ColumnName = "thje")] | |
| 84 | + public decimal Thje { get; set; } | |
| 85 | + | |
| 86 | + /// <summary> | |
| 87 | + /// 备注 | |
| 88 | + /// </summary> | |
| 89 | + [SugarColumn(ColumnName = "bz")] | |
| 90 | + public string Bz { get; set; } | |
| 91 | + } | |
| 92 | +} | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtXsckdMxEntity.cs
| ... | ... | @@ -89,5 +89,17 @@ namespace NCC.Extend.Entitys |
| 89 | 89 | [SugarColumn(ColumnName = "mdxx")] |
| 90 | 90 | public string Mdxx { get; set; } |
| 91 | 91 | |
| 92 | + /// <summary> | |
| 93 | + /// 成本单价(出库时快照) | |
| 94 | + /// </summary> | |
| 95 | + [SugarColumn(ColumnName = "cbdj")] | |
| 96 | + public decimal? Cbdj { get; set; } | |
| 97 | + | |
| 98 | + /// <summary> | |
| 99 | + /// 成本金额(出库时快照) | |
| 100 | + /// </summary> | |
| 101 | + [SugarColumn(ColumnName = "cbje")] | |
| 102 | + public decimal? Cbje { get; set; } | |
| 103 | + | |
| 92 | 104 | } |
| 93 | 105 | } | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Interfaces/IWtTjdService.cs
0 → 100644
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtCzdService.cs
| 1 | -using NCC.Common.Core.Manager; | |
| 1 | +using NCC.Common.Core.Manager; | |
| 2 | 2 | using NCC.Common.Enum; |
| 3 | 3 | using NCC.Common.Extension; |
| 4 | 4 | using NCC.Common.Filter; |
| ... | ... | @@ -220,6 +220,9 @@ namespace NCC.Extend.WtCzd |
| 220 | 220 | // 处理序列号逻辑 |
| 221 | 221 | await ProcessDisassemblySerialNumbers(newEntity.Id, wtCzdmxEntityList, wtCzdmx2EntityList, entity.Chck, entity.Rhck); |
| 222 | 222 | |
| 223 | + // ========== P4: 拆装单成本联动 wt_sp_cost ========== | |
| 224 | + await UpdateSpCostOnCzdCreate(entity, wtCzdmxEntityList, wtCzdmx2EntityList); | |
| 225 | + | |
| 223 | 226 | //关闭事务 |
| 224 | 227 | _db.CommitTran(); |
| 225 | 228 | // 返回新创建的实体信息 |
| ... | ... | @@ -338,7 +341,7 @@ namespace NCC.Extend.WtCzd |
| 338 | 341 | } |
| 339 | 342 | |
| 340 | 343 | /// <summary> |
| 341 | - /// 批量删除拆装单 | |
| 344 | + /// 批量删除拆装单(含成本回退) | |
| 342 | 345 | /// </summary> |
| 343 | 346 | /// <param name="ids">主键数组</param> |
| 344 | 347 | /// <returns></returns> |
| ... | ... | @@ -350,21 +353,21 @@ namespace NCC.Extend.WtCzd |
| 350 | 353 | { |
| 351 | 354 | try |
| 352 | 355 | { |
| 353 | - //开启事务 | |
| 354 | 356 | _db.BeginTran(); |
| 355 | - //批量删除拆装单 | |
| 356 | - await _db.Deleteable<WtCzdEntity>().In(d => d.Id,ids).ExecuteCommandAsync(); | |
| 357 | 357 | |
| 358 | - //清空子表数据 | |
| 359 | - await _db.Deleteable<WtCzdmxEntity>().In(u => u.Djbh,ids).ExecuteCommandAsync(); | |
| 360 | - await _db.Deleteable<WtCzdmx2Entity>().In(u => u.Djbh,ids).ExecuteCommandAsync(); | |
| 358 | + foreach (var entity in entitys) | |
| 359 | + { | |
| 360 | + await RollbackSpCostOnCzdDelete(entity); | |
| 361 | + } | |
| 362 | + | |
| 363 | + await _db.Deleteable<WtCzdEntity>().In(d => d.Id, ids).ExecuteCommandAsync(); | |
| 364 | + await _db.Deleteable<WtCzdmxEntity>().In(u => u.Djbh, ids).ExecuteCommandAsync(); | |
| 365 | + await _db.Deleteable<WtCzdmx2Entity>().In(u => u.Djbh, ids).ExecuteCommandAsync(); | |
| 361 | 366 | |
| 362 | - //关闭事务 | |
| 363 | 367 | _db.CommitTran(); |
| 364 | 368 | } |
| 365 | 369 | catch (Exception) |
| 366 | 370 | { |
| 367 | - //回滚事务 | |
| 368 | 371 | _db.RollbackTran(); |
| 369 | 372 | throw NCCException.Oh(ErrorCode.COM1002); |
| 370 | 373 | } |
| ... | ... | @@ -434,7 +437,9 @@ namespace NCC.Extend.WtCzd |
| 434 | 437 | } |
| 435 | 438 | |
| 436 | 439 | /// <summary> |
| 437 | - /// 删除拆装单 | |
| 440 | + /// 删除拆装单(含成本回退) | |
| 441 | + /// 出库方:恢复 wt_sp_cost.sl | |
| 442 | + /// 入库方:反向加权平均回退 cbj 和 sl | |
| 438 | 443 | /// </summary> |
| 439 | 444 | /// <returns></returns> |
| 440 | 445 | [HttpDelete("{id}")] |
| ... | ... | @@ -444,22 +449,19 @@ namespace NCC.Extend.WtCzd |
| 444 | 449 | _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); |
| 445 | 450 | try |
| 446 | 451 | { |
| 447 | - //开启事务 | |
| 448 | 452 | _db.BeginTran(); |
| 453 | + | |
| 454 | + // 回退成本 | |
| 455 | + await RollbackSpCostOnCzdDelete(entity); | |
| 449 | 456 | |
| 450 | - //删除拆装单记录 | |
| 451 | 457 | await _db.Deleteable<WtCzdEntity>().Where(d => d.Id == id).ExecuteCommandAsync(); |
| 452 | - | |
| 453 | - //清空子表数据 | |
| 454 | 458 | await _db.Deleteable<WtCzdmxEntity>().Where(u => u.Djbh == id).ExecuteCommandAsync(); |
| 455 | 459 | await _db.Deleteable<WtCzdmx2Entity>().Where(u => u.Djbh == id).ExecuteCommandAsync(); |
| 456 | 460 | |
| 457 | - //关闭事务 | |
| 458 | 461 | _db.CommitTran(); |
| 459 | 462 | } |
| 460 | 463 | catch (Exception) |
| 461 | 464 | { |
| 462 | - //回滚事务 | |
| 463 | 465 | _db.RollbackTran(); |
| 464 | 466 | throw NCCException.Oh(ErrorCode.COM1002); |
| 465 | 467 | } |
| ... | ... | @@ -639,6 +641,259 @@ namespace NCC.Extend.WtCzd |
| 639 | 641 | return nextNumber.ToString().PadLeft(8, '0'); |
| 640 | 642 | } |
| 641 | 643 | |
| 644 | + #region P4: wt_sp_cost 成本表联动 | |
| 645 | + | |
| 646 | + /// <summary> | |
| 647 | + /// 拆装单创建时自动维护 wt_sp_cost | |
| 648 | + /// 出库明细:快照成本价 + 减少在库数量 | |
| 649 | + /// 入库明细:按出库总成本分摊入库 + 增加数量并更新加权平均成本 | |
| 650 | + /// </summary> | |
| 651 | + private async Task UpdateSpCostOnCzdCreate(WtCzdEntity entity, List<WtCzdmxEntity> outList, List<WtCzdmx2Entity> inList) | |
| 652 | + { | |
| 653 | + try | |
| 654 | + { | |
| 655 | + // 出库明细:从 wt_sp_cost 快照成本,并减少数量 | |
| 656 | + decimal totalOutCost = 0; | |
| 657 | + if (outList != null) | |
| 658 | + { | |
| 659 | + foreach (var item in outList) | |
| 660 | + { | |
| 661 | + if (string.IsNullOrEmpty(item.Spbh) || (item.Sl ?? 0) <= 0) continue; | |
| 662 | + var warehouseId = !string.IsNullOrEmpty(item.Chck) ? item.Chck : entity.Chck; | |
| 663 | + if (string.IsNullOrEmpty(warehouseId)) continue; | |
| 664 | + | |
| 665 | + var costResult = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 666 | + "SELECT cbj, sl FROM wt_sp_cost WHERE spbh = @spbh AND ck = @ck LIMIT 1", | |
| 667 | + new { spbh = item.Spbh, ck = warehouseId }); | |
| 668 | + | |
| 669 | + if (costResult != null && costResult.Count > 0) | |
| 670 | + { | |
| 671 | + decimal cbj = Convert.ToDecimal(costResult[0].cbj); | |
| 672 | + int oldSl = Convert.ToInt32(costResult[0].sl); | |
| 673 | + int qty = item.Sl ?? 0; | |
| 674 | + | |
| 675 | + if (item.Cbdj == 0) item.Cbdj = cbj; | |
| 676 | + if (item.Cbje == 0) item.Cbje = Math.Round(cbj * qty, 2); | |
| 677 | + totalOutCost += item.Cbje; | |
| 678 | + | |
| 679 | + await _db.Ado.ExecuteCommandAsync( | |
| 680 | + "UPDATE wt_czdmx SET cbdj = @cbdj, cbje = @cbje WHERE F_Id = @id", | |
| 681 | + new { cbdj = item.Cbdj, cbje = item.Cbje, id = item.Id }); | |
| 682 | + | |
| 683 | + int newSl = Math.Max(oldSl - qty, 0); | |
| 684 | + await _db.Ado.ExecuteCommandAsync( | |
| 685 | + "UPDATE wt_sp_cost SET sl = @sl, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 686 | + new { sl = newSl, spbh = item.Spbh, ck = warehouseId }); | |
| 687 | + } | |
| 688 | + } | |
| 689 | + } | |
| 690 | + | |
| 691 | + // 入库明细:按出库成本分摊,更新加权平均成本 | |
| 692 | + if (inList != null && inList.Count > 0) | |
| 693 | + { | |
| 694 | + int totalInQty = inList.Where(i => (i.Sl ?? 0) > 0).Sum(i => i.Sl ?? 0); | |
| 695 | + | |
| 696 | + foreach (var item in inList) | |
| 697 | + { | |
| 698 | + if (string.IsNullOrEmpty(item.Spbh) || (item.Sl ?? 0) <= 0) continue; | |
| 699 | + var warehouseId = !string.IsNullOrEmpty(item.Chck) ? item.Chck : entity.Rhck; | |
| 700 | + if (string.IsNullOrEmpty(warehouseId)) continue; | |
| 701 | + | |
| 702 | + int qty = item.Sl ?? 0; | |
| 703 | + decimal inCostPerUnit = totalInQty > 0 && totalOutCost > 0 | |
| 704 | + ? Math.Round(totalOutCost / totalInQty, 2) | |
| 705 | + : item.Cbdj; | |
| 706 | + | |
| 707 | + if (item.Cbdj == 0 && inCostPerUnit > 0) | |
| 708 | + { | |
| 709 | + item.Cbdj = inCostPerUnit; | |
| 710 | + item.Cbje = Math.Round(inCostPerUnit * qty, 2); | |
| 711 | + await _db.Ado.ExecuteCommandAsync( | |
| 712 | + "UPDATE wt_czdmx_2 SET cbdj = @cbdj, cbje = @cbje WHERE F_Id = @id", | |
| 713 | + new { cbdj = item.Cbdj, cbje = item.Cbje, id = item.Id }); | |
| 714 | + } | |
| 715 | + | |
| 716 | + var existing = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 717 | + "SELECT cbj, sl FROM wt_sp_cost WHERE spbh = @spbh AND ck = @ck LIMIT 1", | |
| 718 | + new { spbh = item.Spbh, ck = warehouseId }); | |
| 719 | + | |
| 720 | + if (existing != null && existing.Count > 0) | |
| 721 | + { | |
| 722 | + decimal oldCbj = Convert.ToDecimal(existing[0].cbj); | |
| 723 | + int oldSl = Convert.ToInt32(existing[0].sl); | |
| 724 | + int newSl = oldSl + qty; | |
| 725 | + decimal newCbj = newSl > 0 | |
| 726 | + ? Math.Round((oldCbj * oldSl + inCostPerUnit * qty) / newSl, 2) | |
| 727 | + : inCostPerUnit; | |
| 728 | + | |
| 729 | + await _db.Ado.ExecuteCommandAsync( | |
| 730 | + "UPDATE wt_sp_cost SET cbj = @cbj, sl = @sl, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 731 | + new { cbj = newCbj, sl = newSl, spbh = item.Spbh, ck = warehouseId }); | |
| 732 | + } | |
| 733 | + else if (inCostPerUnit > 0) | |
| 734 | + { | |
| 735 | + await _db.Ado.ExecuteCommandAsync( | |
| 736 | + "INSERT INTO wt_sp_cost (F_Id, spbh, ck, cbj, sl, update_time) VALUES (@id, @spbh, @ck, @cbj, @sl, NOW())", | |
| 737 | + new { id = $"{item.Spbh}_{warehouseId}", spbh = item.Spbh, ck = warehouseId, cbj = inCostPerUnit, sl = qty }); | |
| 738 | + } | |
| 739 | + } | |
| 740 | + } | |
| 741 | + } | |
| 742 | + catch (Exception ex) | |
| 743 | + { | |
| 744 | + Console.WriteLine($"拆装单更新成本表失败(不影响主业务): {ex.Message}"); | |
| 745 | + } | |
| 746 | + } | |
| 747 | + | |
| 748 | + /// <summary> | |
| 749 | + /// 拆装单删除时回退 wt_sp_cost | |
| 750 | + /// 出库方:恢复 sl(cbj 不变) | |
| 751 | + /// 入库方:反向加权平均回退 cbj 和 sl | |
| 752 | + /// </summary> | |
| 753 | + private async Task RollbackSpCostOnCzdDelete(WtCzdEntity entity) | |
| 754 | + { | |
| 755 | + var id = entity.Id; | |
| 756 | + | |
| 757 | + // 出库明细:恢复 sl | |
| 758 | + var outList = await _db.Queryable<WtCzdmxEntity>() | |
| 759 | + .Where(d => d.Djbh == id).ToListAsync(); | |
| 760 | + foreach (var item in outList) | |
| 761 | + { | |
| 762 | + if (string.IsNullOrEmpty(item.Spbh) || (item.Sl ?? 0) <= 0) continue; | |
| 763 | + var warehouseId = !string.IsNullOrEmpty(item.Chck) ? item.Chck : entity.Chck; | |
| 764 | + if (string.IsNullOrEmpty(warehouseId)) continue; | |
| 765 | + | |
| 766 | + int qty = item.Sl ?? 0; | |
| 767 | + await _db.Ado.ExecuteCommandAsync( | |
| 768 | + "UPDATE wt_sp_cost SET sl = sl + @qty, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 769 | + new { qty, spbh = item.Spbh, ck = warehouseId }); | |
| 770 | + } | |
| 771 | + | |
| 772 | + // 入库明细:反向加权平均 | |
| 773 | + var inList = await _db.Queryable<WtCzdmx2Entity>() | |
| 774 | + .Where(d => d.Djbh == id).ToListAsync(); | |
| 775 | + foreach (var item in inList) | |
| 776 | + { | |
| 777 | + if (string.IsNullOrEmpty(item.Spbh) || (item.Sl ?? 0) <= 0) continue; | |
| 778 | + var warehouseId = !string.IsNullOrEmpty(item.Chck) ? item.Chck : entity.Rhck; | |
| 779 | + if (string.IsNullOrEmpty(warehouseId)) continue; | |
| 780 | + | |
| 781 | + int qty = item.Sl ?? 0; | |
| 782 | + decimal inCostPerUnit = item.Cbdj; | |
| 783 | + | |
| 784 | + var existing = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 785 | + "SELECT cbj, sl FROM wt_sp_cost WHERE spbh = @spbh AND ck = @ck LIMIT 1", | |
| 786 | + new { spbh = item.Spbh, ck = warehouseId }); | |
| 787 | + | |
| 788 | + if (existing != null && existing.Count > 0) | |
| 789 | + { | |
| 790 | + decimal currentCbj = Convert.ToDecimal(existing[0].cbj); | |
| 791 | + int currentSl = Convert.ToInt32(existing[0].sl); | |
| 792 | + int newSl = currentSl - qty; | |
| 793 | + | |
| 794 | + decimal newCbj = 0; | |
| 795 | + if (newSl > 0) | |
| 796 | + { | |
| 797 | + newCbj = Math.Round((currentCbj * currentSl - inCostPerUnit * qty) / newSl, 2); | |
| 798 | + } | |
| 799 | + else | |
| 800 | + { | |
| 801 | + newSl = 0; | |
| 802 | + } | |
| 803 | + | |
| 804 | + await _db.Ado.ExecuteCommandAsync( | |
| 805 | + "UPDATE wt_sp_cost SET cbj = @cbj, sl = @sl, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 806 | + new { cbj = newCbj, sl = newSl, spbh = item.Spbh, ck = warehouseId }); | |
| 807 | + } | |
| 808 | + } | |
| 809 | + } | |
| 810 | + | |
| 811 | + #endregion | |
| 812 | + | |
| 813 | + /// <summary> | |
| 814 | + /// 获取商品成本单价 | |
| 815 | + /// </summary> | |
| 816 | + /// <remarks> | |
| 817 | + /// 优先从最近一次采购入库单获取单价,若无则用商品档案零售价兜底 | |
| 818 | + /// </remarks> | |
| 819 | + /// <param name="input">包含 ProductId(商品ID)和 WarehouseId(仓库/门店ID)</param> | |
| 820 | + /// <returns>返回 success、data(成本价)、source(价格来源)</returns> | |
| 821 | + [HttpPost("GetProductCost")] | |
| 822 | + public async Task<dynamic> GetProductCost([FromBody] GetProductCostInput input) | |
| 823 | + { | |
| 824 | + if (string.IsNullOrWhiteSpace(input?.ProductId)) | |
| 825 | + return new { success = false, data = 0m, source = "", msg = "商品ID不能为空" }; | |
| 826 | + | |
| 827 | + // 优先从 wt_sp_cost 成本表取(加权平均成本价) | |
| 828 | + var costSql = @"SELECT cbj FROM wt_sp_cost WHERE spbh = @spbh AND ck = @ck AND cbj > 0 LIMIT 1"; | |
| 829 | + var costResult = await _db.Ado.SqlQueryAsync<decimal?>(costSql, | |
| 830 | + new { spbh = input.ProductId, ck = input.WarehouseId ?? "" }); | |
| 831 | + var costPrice = costResult?.FirstOrDefault(); | |
| 832 | + | |
| 833 | + if (costPrice.HasValue && costPrice.Value > 0) | |
| 834 | + return new { success = true, data = costPrice.Value, source = "成本价表" }; | |
| 835 | + | |
| 836 | + // 兜底:从最近采购入库单取 | |
| 837 | + var sql = @"SELECT mx.dj FROM wt_xsckd_mx mx | |
| 838 | + INNER JOIN wt_xsckd zd ON mx.djbh = zd.F_Id | |
| 839 | + WHERE mx.spbh = @spbh AND zd.djlx = '采购入库单' | |
| 840 | + ORDER BY zd.djrq DESC LIMIT 1"; | |
| 841 | + var result = await _db.Ado.SqlQueryAsync<decimal?>(sql, new { spbh = input.ProductId }); | |
| 842 | + var price = result?.FirstOrDefault(); | |
| 843 | + | |
| 844 | + if (price.HasValue) | |
| 845 | + return new { success = true, data = price.Value, source = "采购入库单" }; | |
| 846 | + | |
| 847 | + // 再兜底:从商品档案取零售价 | |
| 848 | + var productSql = @"SELECT F_Lsj FROM wt_sp WHERE F_Id = @spbh LIMIT 1"; | |
| 849 | + var lsjResult = await _db.Ado.SqlQueryAsync<decimal?>(productSql, new { spbh = input.ProductId }); | |
| 850 | + var lsj = lsjResult?.FirstOrDefault(); | |
| 851 | + | |
| 852 | + if (lsj.HasValue && lsj.Value > 0) | |
| 853 | + return new { success = true, data = lsj.Value, source = "商品零售价" }; | |
| 854 | + | |
| 855 | + return new { success = true, data = 0m, source = "默认" }; | |
| 856 | + } | |
| 857 | + | |
| 858 | + /// <summary> | |
| 859 | + /// 查询库存数量 | |
| 860 | + /// </summary> | |
| 861 | + /// <remarks> | |
| 862 | + /// 通过门店ID查找关联仓库,再统计序列号表中在库数量 | |
| 863 | + /// </remarks> | |
| 864 | + /// <param name="input">包含 warehouseId(门店ID)、productId(商品ID)、serialNumber(可选序列号)</param> | |
| 865 | + /// <returns>返回 success、data(库存数量)、msg</returns> | |
| 866 | + [HttpPost("QueryStock")] | |
| 867 | + public async Task<dynamic> QueryStock([FromBody] QueryStockInput input) | |
| 868 | + { | |
| 869 | + if (string.IsNullOrWhiteSpace(input?.ProductId)) | |
| 870 | + return new { success = false, data = 0, msg = "商品ID不能为空" }; | |
| 871 | + | |
| 872 | + var sql = @"SELECT COUNT(*) FROM wt_serial_number | |
| 873 | + WHERE TRIM(spbh) = TRIM(@productId) | |
| 874 | + AND TRIM(in_warehouse) = TRIM(@warehouseId) | |
| 875 | + AND status = 0"; | |
| 876 | + var parameters = new List<SugarParameter> | |
| 877 | + { | |
| 878 | + new SugarParameter("@productId", input.ProductId), | |
| 879 | + new SugarParameter("@warehouseId", input.WarehouseId ?? "") | |
| 880 | + }; | |
| 881 | + | |
| 882 | + if (!string.IsNullOrWhiteSpace(input.SerialNumber)) | |
| 883 | + { | |
| 884 | + sql = @"SELECT COUNT(*) FROM wt_serial_number | |
| 885 | + WHERE TRIM(spbh) = TRIM(@productId) | |
| 886 | + AND TRIM(in_warehouse) = TRIM(@warehouseId) | |
| 887 | + AND status = 0 | |
| 888 | + AND serial_number = @serialNumber"; | |
| 889 | + parameters.Add(new SugarParameter("@serialNumber", input.SerialNumber)); | |
| 890 | + } | |
| 891 | + | |
| 892 | + var stockCount = await _db.Ado.GetIntAsync(sql, parameters); | |
| 893 | + | |
| 894 | + return new { success = true, data = stockCount, msg = "查询成功" }; | |
| 895 | + } | |
| 896 | + | |
| 642 | 897 | /// <summary> |
| 643 | 898 | /// 获取商品的可用序列号列表(用于出库选择) |
| 644 | 899 | /// </summary> | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtTjdService.cs
0 → 100644
| 1 | +using NCC.Common.Core.Manager; | |
| 2 | +using NCC.Common.Extension; | |
| 3 | +using NCC.Common.Filter; | |
| 4 | +using NCC.Dependency; | |
| 5 | +using NCC.DynamicApiController; | |
| 6 | +using NCC.FriendlyException; | |
| 7 | +using NCC.Extend.Interfaces.WtTjd; | |
| 8 | +using Mapster; | |
| 9 | +using Microsoft.AspNetCore.Mvc; | |
| 10 | +using SqlSugar; | |
| 11 | +using System; | |
| 12 | +using System.Collections.Generic; | |
| 13 | +using System.Linq; | |
| 14 | +using System.Threading.Tasks; | |
| 15 | +using NCC.Extend.Entitys; | |
| 16 | +using NCC.Extend.Entitys.Dto.WtTjd; | |
| 17 | +using Yitter.IdGenerator; | |
| 18 | +using NCC.JsonSerialization; | |
| 19 | +using NCC.System.Entitys.Permission; | |
| 20 | + | |
| 21 | +namespace NCC.Extend.WtTjd | |
| 22 | +{ | |
| 23 | + /// <summary> | |
| 24 | + /// 调价单服务 | |
| 25 | + /// </summary> | |
| 26 | + [ApiDescriptionSettings(Tag = "Extend", Name = "WtTjd", Order = 200)] | |
| 27 | + [Route("api/Extend/[controller]")] | |
| 28 | + public class WtTjdService : IWtTjdService, IDynamicApiController, ITransient | |
| 29 | + { | |
| 30 | + private readonly ISqlSugarRepository<WtTjdEntity> _wtTjdRepository; | |
| 31 | + private readonly ISqlSugarRepository<WtTjdMxEntity> _wtTjdMxRepository; | |
| 32 | + private readonly ISqlSugarRepository<WtSpCostEntity> _wtSpCostRepository; | |
| 33 | + private readonly SqlSugarScope _db; | |
| 34 | + private readonly IUserManager _userManager; | |
| 35 | + | |
| 36 | + /// <summary> | |
| 37 | + /// 初始化一个<see cref="WtTjdService"/>类型的新实例 | |
| 38 | + /// </summary> | |
| 39 | + public WtTjdService( | |
| 40 | + ISqlSugarRepository<WtTjdEntity> wtTjdRepository, | |
| 41 | + ISqlSugarRepository<WtTjdMxEntity> wtTjdMxRepository, | |
| 42 | + ISqlSugarRepository<WtSpCostEntity> wtSpCostRepository, | |
| 43 | + IUserManager userManager) | |
| 44 | + { | |
| 45 | + _wtTjdRepository = wtTjdRepository; | |
| 46 | + _wtTjdMxRepository = wtTjdMxRepository; | |
| 47 | + _wtSpCostRepository = wtSpCostRepository; | |
| 48 | + _db = _wtTjdRepository.Context; | |
| 49 | + _userManager = userManager; | |
| 50 | + } | |
| 51 | + | |
| 52 | + #region 基础 CRUD | |
| 53 | + | |
| 54 | + /// <summary> | |
| 55 | + /// 获取调价单详情 | |
| 56 | + /// </summary> | |
| 57 | + /// <param name="id">单据编号</param> | |
| 58 | + /// <returns>调价单主表及明细</returns> | |
| 59 | + [HttpGet("{id}")] | |
| 60 | + public async Task<dynamic> GetInfo(string id) | |
| 61 | + { | |
| 62 | + var entity = await _db.Queryable<WtTjdEntity>().FirstAsync(p => p.Id == id); | |
| 63 | + if (entity == null) | |
| 64 | + throw NCCException.Bah("调价单不存在"); | |
| 65 | + | |
| 66 | + var output = entity.Adapt<WtTjdInfoOutput>(); | |
| 67 | + | |
| 68 | + var mxList = await _db.Queryable<WtTjdMxEntity>().Where(w => w.Djbh == id).ToListAsync(); | |
| 69 | + output.wtTjdMxList = mxList.Adapt<List<WtTjdMxInfoOutput>>(); | |
| 70 | + | |
| 71 | + return output; | |
| 72 | + } | |
| 73 | + | |
| 74 | + /// <summary> | |
| 75 | + /// 获取调价单列表(分页) | |
| 76 | + /// </summary> | |
| 77 | + /// <param name="input">查询参数</param> | |
| 78 | + /// <returns>分页列表</returns> | |
| 79 | + [HttpGet("")] | |
| 80 | + public async Task<dynamic> GetList([FromQuery] WtTjdListQueryInput input) | |
| 81 | + { | |
| 82 | + var sidx = input.sidx == null ? "id" : input.sidx; | |
| 83 | + List<string> queryDjrq = input.djrq != null ? input.djrq.Split(',').ToObeject<List<string>>() : null; | |
| 84 | + DateTime? startDjrq = queryDjrq != null ? Ext.GetDateTime(queryDjrq.First()) : null; | |
| 85 | + DateTime? endDjrq = queryDjrq != null ? Ext.GetDateTime(queryDjrq.Last()) : null; | |
| 86 | + | |
| 87 | + var data = await _db.Queryable<WtTjdEntity>() | |
| 88 | + .WhereIF(!string.IsNullOrEmpty(input.code), p => p.Id.Contains(input.code)) | |
| 89 | + .WhereIF(queryDjrq != null, | |
| 90 | + p => p.Djrq >= new DateTime(startDjrq.ToDate().Year, startDjrq.ToDate().Month, | |
| 91 | + startDjrq.ToDate().Day, 0, 0, 0)) | |
| 92 | + .WhereIF(queryDjrq != null, | |
| 93 | + p => p.Djrq <= new DateTime(endDjrq.ToDate().Year, endDjrq.ToDate().Month, | |
| 94 | + endDjrq.ToDate().Day, 23, 59, 59)) | |
| 95 | + .WhereIF(!string.IsNullOrEmpty(input.ck), p => p.Ck.Equals(input.ck)) | |
| 96 | + .WhereIF(!string.IsNullOrEmpty(input.djzt), p => p.Djzt.Equals(input.djzt)) | |
| 97 | + .Select(it => new WtTjdListOutput | |
| 98 | + { | |
| 99 | + id = it.Id, | |
| 100 | + djrq = it.Djrq, | |
| 101 | + ck = SqlFunc.Subqueryable<WtCkEntity>().Where(u => u.Id == it.Ck).Select(u => u.Mdmc), | |
| 102 | + jsr = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.Jsr).Select(u => u.RealName), | |
| 103 | + zdr = SqlFunc.Subqueryable<UserEntity>().Where(u => u.Id == it.Zdr).Select(u => u.RealName), | |
| 104 | + djzt = it.Djzt, | |
| 105 | + bz = it.Bz | |
| 106 | + }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize); | |
| 107 | + | |
| 108 | + return PageResult<WtTjdListOutput>.SqlSugarPageResult(data); | |
| 109 | + } | |
| 110 | + | |
| 111 | + /// <summary> | |
| 112 | + /// 新建调价单 | |
| 113 | + /// </summary> | |
| 114 | + /// <param name="input">创建参数</param> | |
| 115 | + /// <returns>新建的单据编号</returns> | |
| 116 | + [HttpPost("")] | |
| 117 | + public async Task<dynamic> Create([FromBody] WtTjdCrInput input) | |
| 118 | + { | |
| 119 | + var userInfo = await _userManager.GetUserInfo(); | |
| 120 | + var entity = input.Adapt<WtTjdEntity>(); | |
| 121 | + | |
| 122 | + var today = DateTime.Now.ToString("yyyyMMdd"); | |
| 123 | + string prefix = "TJD"; | |
| 124 | + | |
| 125 | + var maxId = await _db.Queryable<WtTjdEntity>() | |
| 126 | + .Where(x => x.Id.StartsWith(prefix + today)) | |
| 127 | + .OrderBy(x => x.Id, OrderByType.Desc) | |
| 128 | + .Select(x => x.Id) | |
| 129 | + .FirstAsync(); | |
| 130 | + | |
| 131 | + int serial = 1; | |
| 132 | + if (!string.IsNullOrEmpty(maxId) && maxId.Length >= prefix.Length + 8 + 4) | |
| 133 | + { | |
| 134 | + var serialStr = maxId.Substring(prefix.Length + 8, 4); | |
| 135 | + int.TryParse(serialStr, out serial); | |
| 136 | + serial++; | |
| 137 | + } | |
| 138 | + | |
| 139 | + try | |
| 140 | + { | |
| 141 | + _db.BeginTran(); | |
| 142 | + int tryCount = 0; | |
| 143 | + while (true) | |
| 144 | + { | |
| 145 | + try | |
| 146 | + { | |
| 147 | + entity.Id = $"{prefix}{today}{serial.ToString("D4")}"; | |
| 148 | + entity.Djrq = DateTime.Now; | |
| 149 | + entity.Zdr = userInfo.userId; | |
| 150 | + entity.Djzt = "草稿"; | |
| 151 | + | |
| 152 | + var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync(); | |
| 153 | + | |
| 154 | + if (input.wtTjdMxList != null && input.wtTjdMxList.Count > 0) | |
| 155 | + { | |
| 156 | + var mxEntityList = input.wtTjdMxList.Adapt<List<WtTjdMxEntity>>(); | |
| 157 | + foreach (var item in mxEntityList) | |
| 158 | + { | |
| 159 | + item.Id = YitIdHelper.NextId().ToString(); | |
| 160 | + item.Djbh = newEntity.Id; | |
| 161 | + item.Ck = entity.Ck; | |
| 162 | + } | |
| 163 | + await _db.Insertable(mxEntityList).ExecuteCommandAsync(); | |
| 164 | + } | |
| 165 | + | |
| 166 | + _db.CommitTran(); | |
| 167 | + return new { id = newEntity.Id, msg = "创建成功" }; | |
| 168 | + } | |
| 169 | + catch (Exception ex) | |
| 170 | + { | |
| 171 | + if (ex.Message.Contains("Duplicate entry") && tryCount < 10) | |
| 172 | + { | |
| 173 | + serial++; | |
| 174 | + tryCount++; | |
| 175 | + continue; | |
| 176 | + } | |
| 177 | + _db.RollbackTran(); | |
| 178 | + throw NCCException.Bah($"保存失败: {ex.Message}"); | |
| 179 | + } | |
| 180 | + } | |
| 181 | + } | |
| 182 | + catch (Exception ex) | |
| 183 | + { | |
| 184 | + _db.RollbackTran(); | |
| 185 | + throw NCCException.Bah($"保存失败: {ex.Message}"); | |
| 186 | + } | |
| 187 | + } | |
| 188 | + | |
| 189 | + /// <summary> | |
| 190 | + /// 更新调价单(仅草稿状态可修改) | |
| 191 | + /// </summary> | |
| 192 | + /// <param name="id">单据编号</param> | |
| 193 | + /// <param name="input">更新参数</param> | |
| 194 | + /// <returns></returns> | |
| 195 | + [HttpPut("{id}")] | |
| 196 | + public async Task Update(string id, [FromBody] WtTjdUpInput input) | |
| 197 | + { | |
| 198 | + var existing = await _db.Queryable<WtTjdEntity>().FirstAsync(p => p.Id == id); | |
| 199 | + if (existing == null) | |
| 200 | + throw NCCException.Bah("调价单不存在"); | |
| 201 | + if (existing.Djzt != "草稿") | |
| 202 | + throw NCCException.Bah("只有草稿状态的调价单才能修改"); | |
| 203 | + | |
| 204 | + var entity = input.Adapt<WtTjdEntity>(); | |
| 205 | + entity.Id = id; | |
| 206 | + | |
| 207 | + try | |
| 208 | + { | |
| 209 | + _db.BeginTran(); | |
| 210 | + | |
| 211 | + await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); | |
| 212 | + | |
| 213 | + await _db.Deleteable<WtTjdMxEntity>().Where(u => u.Djbh == id).ExecuteCommandAsync(); | |
| 214 | + | |
| 215 | + if (input.wtTjdMxList != null && input.wtTjdMxList.Count > 0) | |
| 216 | + { | |
| 217 | + var mxEntityList = input.wtTjdMxList.Adapt<List<WtTjdMxEntity>>(); | |
| 218 | + foreach (var item in mxEntityList) | |
| 219 | + { | |
| 220 | + item.Id = YitIdHelper.NextId().ToString(); | |
| 221 | + item.Djbh = id; | |
| 222 | + item.Ck = entity.Ck ?? existing.Ck; | |
| 223 | + } | |
| 224 | + await _db.Insertable(mxEntityList).ExecuteCommandAsync(); | |
| 225 | + } | |
| 226 | + | |
| 227 | + _db.CommitTran(); | |
| 228 | + } | |
| 229 | + catch (Exception ex) | |
| 230 | + { | |
| 231 | + _db.RollbackTran(); | |
| 232 | + throw NCCException.Bah($"更新失败: {ex.Message}"); | |
| 233 | + } | |
| 234 | + } | |
| 235 | + | |
| 236 | + /// <summary> | |
| 237 | + /// 删除调价单(仅草稿状态可删除) | |
| 238 | + /// </summary> | |
| 239 | + /// <param name="id">单据编号</param> | |
| 240 | + /// <returns></returns> | |
| 241 | + [HttpDelete("{id}")] | |
| 242 | + public async Task Delete(string id) | |
| 243 | + { | |
| 244 | + var existing = await _db.Queryable<WtTjdEntity>().FirstAsync(p => p.Id == id); | |
| 245 | + if (existing == null) | |
| 246 | + throw NCCException.Bah("调价单不存在"); | |
| 247 | + if (existing.Djzt != "草稿") | |
| 248 | + throw NCCException.Bah("只有草稿状态的调价单才能删除"); | |
| 249 | + | |
| 250 | + try | |
| 251 | + { | |
| 252 | + _db.BeginTran(); | |
| 253 | + | |
| 254 | + await _db.Deleteable<WtTjdEntity>().Where(d => d.Id == id).ExecuteCommandAsync(); | |
| 255 | + await _db.Deleteable<WtTjdMxEntity>().Where(u => u.Djbh == id).ExecuteCommandAsync(); | |
| 256 | + | |
| 257 | + _db.CommitTran(); | |
| 258 | + } | |
| 259 | + catch (Exception) | |
| 260 | + { | |
| 261 | + _db.RollbackTran(); | |
| 262 | + throw NCCException.Bah("删除失败"); | |
| 263 | + } | |
| 264 | + } | |
| 265 | + | |
| 266 | + #endregion | |
| 267 | + | |
| 268 | + #region 业务操作 | |
| 269 | + | |
| 270 | + /// <summary> | |
| 271 | + /// 审核调价单:更新状态为已审核,并将调后单价写入商品成本表 | |
| 272 | + /// </summary> | |
| 273 | + /// <param name="id">单据编号</param> | |
| 274 | + /// <returns></returns> | |
| 275 | + [HttpPost("Actions/Audit/{id}")] | |
| 276 | + public async Task<dynamic> Audit(string id) | |
| 277 | + { | |
| 278 | + var entity = await _db.Queryable<WtTjdEntity>().FirstAsync(p => p.Id == id); | |
| 279 | + if (entity == null) | |
| 280 | + throw NCCException.Bah("调价单不存在"); | |
| 281 | + if (entity.Djzt == "已审核") | |
| 282 | + throw NCCException.Bah("该调价单已审核,请勿重复操作"); | |
| 283 | + | |
| 284 | + var mxList = await _db.Queryable<WtTjdMxEntity>().Where(w => w.Djbh == id).ToListAsync(); | |
| 285 | + if (mxList == null || mxList.Count == 0) | |
| 286 | + throw NCCException.Bah("调价单明细为空,无法审核"); | |
| 287 | + | |
| 288 | + try | |
| 289 | + { | |
| 290 | + _db.BeginTran(); | |
| 291 | + | |
| 292 | + await _db.Updateable<WtTjdEntity>() | |
| 293 | + .SetColumns(it => it.Djzt == "已审核") | |
| 294 | + .Where(it => it.Id == id) | |
| 295 | + .ExecuteCommandAsync(); | |
| 296 | + | |
| 297 | + foreach (var mx in mxList) | |
| 298 | + { | |
| 299 | + var costRecord = await _db.Queryable<WtSpCostEntity>() | |
| 300 | + .FirstAsync(c => c.Spbh == mx.Spbh && c.Ck == mx.Ck); | |
| 301 | + | |
| 302 | + if (costRecord != null) | |
| 303 | + { | |
| 304 | + await _db.Updateable<WtSpCostEntity>() | |
| 305 | + .SetColumns(it => new WtSpCostEntity | |
| 306 | + { | |
| 307 | + Cbj = mx.Thdj, | |
| 308 | + UpdateTime = DateTime.Now | |
| 309 | + }) | |
| 310 | + .Where(it => it.Id == costRecord.Id) | |
| 311 | + .ExecuteCommandAsync(); | |
| 312 | + } | |
| 313 | + else | |
| 314 | + { | |
| 315 | + var newCost = new WtSpCostEntity | |
| 316 | + { | |
| 317 | + Id = YitIdHelper.NextId().ToString(), | |
| 318 | + Spbh = mx.Spbh, | |
| 319 | + Ck = mx.Ck, | |
| 320 | + Cbj = mx.Thdj, | |
| 321 | + Sl = mx.Sl, | |
| 322 | + UpdateTime = DateTime.Now | |
| 323 | + }; | |
| 324 | + await _db.Insertable(newCost).ExecuteCommandAsync(); | |
| 325 | + } | |
| 326 | + } | |
| 327 | + | |
| 328 | + _db.CommitTran(); | |
| 329 | + return new { msg = "审核成功" }; | |
| 330 | + } | |
| 331 | + catch (Exception ex) | |
| 332 | + { | |
| 333 | + _db.RollbackTran(); | |
| 334 | + throw NCCException.Bah($"审核失败: {ex.Message}"); | |
| 335 | + } | |
| 336 | + } | |
| 337 | + | |
| 338 | + /// <summary> | |
| 339 | + /// 反审调价单:将 wt_sp_cost.cbj 恢复为调前单价,状态回退为草稿 | |
| 340 | + /// </summary> | |
| 341 | + /// <param name="id">单据编号</param> | |
| 342 | + /// <returns></returns> | |
| 343 | + [HttpPost("Actions/ReverseAudit/{id}")] | |
| 344 | + public async Task<dynamic> ReverseAudit(string id) | |
| 345 | + { | |
| 346 | + var entity = await _db.Queryable<WtTjdEntity>().FirstAsync(p => p.Id == id); | |
| 347 | + if (entity == null) | |
| 348 | + throw NCCException.Bah("调价单不存在"); | |
| 349 | + if (entity.Djzt != "已审核") | |
| 350 | + throw NCCException.Bah("只有已审核的调价单才能反审"); | |
| 351 | + | |
| 352 | + var mxList = await _db.Queryable<WtTjdMxEntity>().Where(w => w.Djbh == id).ToListAsync(); | |
| 353 | + if (mxList == null || mxList.Count == 0) | |
| 354 | + throw NCCException.Bah("调价单明细为空,无法反审"); | |
| 355 | + | |
| 356 | + try | |
| 357 | + { | |
| 358 | + _db.BeginTran(); | |
| 359 | + | |
| 360 | + foreach (var mx in mxList) | |
| 361 | + { | |
| 362 | + var costRecord = await _db.Queryable<WtSpCostEntity>() | |
| 363 | + .FirstAsync(c => c.Spbh == mx.Spbh && c.Ck == mx.Ck); | |
| 364 | + | |
| 365 | + if (costRecord != null) | |
| 366 | + { | |
| 367 | + await _db.Updateable<WtSpCostEntity>() | |
| 368 | + .SetColumns(it => new WtSpCostEntity | |
| 369 | + { | |
| 370 | + Cbj = mx.Tqdj, | |
| 371 | + UpdateTime = DateTime.Now | |
| 372 | + }) | |
| 373 | + .Where(it => it.Id == costRecord.Id) | |
| 374 | + .ExecuteCommandAsync(); | |
| 375 | + } | |
| 376 | + } | |
| 377 | + | |
| 378 | + await _db.Updateable<WtTjdEntity>() | |
| 379 | + .SetColumns(it => it.Djzt == "草稿") | |
| 380 | + .Where(it => it.Id == id) | |
| 381 | + .ExecuteCommandAsync(); | |
| 382 | + | |
| 383 | + _db.CommitTran(); | |
| 384 | + return new { msg = "反审成功,调价单已恢复为草稿状态" }; | |
| 385 | + } | |
| 386 | + catch (Exception ex) | |
| 387 | + { | |
| 388 | + _db.RollbackTran(); | |
| 389 | + throw NCCException.Bah($"反审失败: {ex.Message}"); | |
| 390 | + } | |
| 391 | + } | |
| 392 | + | |
| 393 | + /// <summary> | |
| 394 | + /// 按仓库加载商品列表(含成本价和库存) | |
| 395 | + /// </summary> | |
| 396 | + /// <param name="ck">仓库ID</param> | |
| 397 | + /// <returns>商品列表</returns> | |
| 398 | + [HttpGet("Actions/LoadProducts")] | |
| 399 | + public async Task<dynamic> LoadProducts([FromQuery] string ck) | |
| 400 | + { | |
| 401 | + if (string.IsNullOrEmpty(ck)) | |
| 402 | + throw NCCException.Bah("仓库ID不能为空"); | |
| 403 | + | |
| 404 | + var products = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 405 | + @"SELECT sc.spbh, sp.F_Spbm as spbm, sp.F_Spmc as spmc, sc.sl, sc.cbj as tqdj | |
| 406 | + FROM wt_sp_cost sc | |
| 407 | + INNER JOIN wt_sp sp ON sc.spbh = sp.F_Id | |
| 408 | + WHERE sc.ck = @ck AND sc.sl > 0 | |
| 409 | + ORDER BY sp.F_Spmc", | |
| 410 | + new { ck = ck }); | |
| 411 | + | |
| 412 | + if (products == null || products.Count == 0) | |
| 413 | + { | |
| 414 | + products = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 415 | + @"SELECT sn.spbh, '' as spbm, sn.spmc, COUNT(*) as sl, 0 as tqdj | |
| 416 | + FROM wt_serial_number sn | |
| 417 | + WHERE sn.in_warehouse = @ck AND sn.status = 0 | |
| 418 | + GROUP BY sn.spbh, sn.spmc", | |
| 419 | + new { ck = ck }); | |
| 420 | + } | |
| 421 | + | |
| 422 | + return products; | |
| 423 | + } | |
| 424 | + | |
| 425 | + /// <summary> | |
| 426 | + /// 查询指定商品在指定仓库的成本价 | |
| 427 | + /// </summary> | |
| 428 | + /// <param name="spbh">商品编号</param> | |
| 429 | + /// <param name="ck">仓库ID</param> | |
| 430 | + /// <returns>成本价</returns> | |
| 431 | + [HttpGet("Actions/GetCost")] | |
| 432 | + public async Task<dynamic> GetCost([FromQuery] string spbh, [FromQuery] string ck) | |
| 433 | + { | |
| 434 | + if (string.IsNullOrEmpty(spbh) || string.IsNullOrEmpty(ck)) | |
| 435 | + return new { cbj = 0m }; | |
| 436 | + | |
| 437 | + var cost = await _db.Queryable<WtSpCostEntity>() | |
| 438 | + .FirstAsync(c => c.Spbh == spbh && c.Ck == ck); | |
| 439 | + | |
| 440 | + return new { cbj = cost?.Cbj ?? 0m, sl = cost?.Sl ?? 0 }; | |
| 441 | + } | |
| 442 | + | |
| 443 | + /// <summary> | |
| 444 | + /// 分页查询成本价列表 | |
| 445 | + /// </summary> | |
| 446 | + /// <param name="keyword">商品名称或编码关键字</param> | |
| 447 | + /// <param name="ck">仓库ID</param> | |
| 448 | + /// <param name="currentPage">当前页码</param> | |
| 449 | + /// <param name="pageSize">每页条数</param> | |
| 450 | + /// <returns>成本价分页列表</returns> | |
| 451 | + [HttpGet("Actions/GetCostList")] | |
| 452 | + public async Task<dynamic> GetCostList([FromQuery] string keyword, [FromQuery] string ck, [FromQuery] int currentPage = 1, [FromQuery] int pageSize = 20) | |
| 453 | + { | |
| 454 | + var whereClauses = new List<string>(); | |
| 455 | + if (!string.IsNullOrEmpty(keyword)) | |
| 456 | + whereClauses.Add("(sp.F_Spmc LIKE @keyword OR sp.F_Spbm LIKE @keyword)"); | |
| 457 | + if (!string.IsNullOrEmpty(ck)) | |
| 458 | + whereClauses.Add("sc.ck = @ck"); | |
| 459 | + | |
| 460 | + var whereStr = whereClauses.Count > 0 ? "WHERE " + string.Join(" AND ", whereClauses) : ""; | |
| 461 | + var offset = (currentPage - 1) * pageSize; | |
| 462 | + | |
| 463 | + var queryParams = new { keyword = $"%{keyword}%", ck = ck, offset = offset, pageSize = pageSize }; | |
| 464 | + | |
| 465 | + var countSql = $@"SELECT COUNT(*) | |
| 466 | + FROM wt_sp_cost sc | |
| 467 | + LEFT JOIN wt_sp sp ON sc.spbh = sp.F_Id | |
| 468 | + LEFT JOIN wt_ck ck_t ON sc.ck = ck_t.F_Id | |
| 469 | + {whereStr}"; | |
| 470 | + | |
| 471 | + var dataSql = $@"SELECT sc.F_Id as id, sc.spbh, sp.F_Spbm as spbm, sp.F_Spmc as spmc, | |
| 472 | + sc.ck, ck_t.F_mdmc as ckmc, sc.cbj, sc.sl, | |
| 473 | + ROUND(sc.cbj * sc.sl, 2) as cbje, sc.update_time as updateTime | |
| 474 | + FROM wt_sp_cost sc | |
| 475 | + LEFT JOIN wt_sp sp ON sc.spbh = sp.F_Id | |
| 476 | + LEFT JOIN wt_ck ck_t ON sc.ck = ck_t.F_Id | |
| 477 | + {whereStr} | |
| 478 | + ORDER BY sp.F_Spmc | |
| 479 | + LIMIT @offset, @pageSize"; | |
| 480 | + | |
| 481 | + var total = await _db.Ado.GetIntAsync(countSql, queryParams); | |
| 482 | + var data = await _db.Ado.SqlQueryAsync<dynamic>(dataSql, queryParams); | |
| 483 | + | |
| 484 | + return new { list = data, pagination = new { total, pageSize, currentPage } }; | |
| 485 | + } | |
| 486 | + | |
| 487 | + /// <summary> | |
| 488 | + /// 加载某仓库所有商品(含在库数量和成本价,用于全部商品调价) | |
| 489 | + /// </summary> | |
| 490 | + /// <param name="ck">仓库ID</param> | |
| 491 | + /// <returns>商品列表</returns> | |
| 492 | + [HttpGet("Actions/LoadAllProducts")] | |
| 493 | + public async Task<dynamic> LoadAllProducts([FromQuery] string ck) | |
| 494 | + { | |
| 495 | + if (string.IsNullOrEmpty(ck)) | |
| 496 | + throw NCCException.Bah("仓库ID不能为空"); | |
| 497 | + | |
| 498 | + var products = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 499 | + @"SELECT sn.spbh, sp.F_Spbm as spbm, sn.spmc, | |
| 500 | + COUNT(CASE WHEN sn.status = 0 THEN 1 END) as sl, | |
| 501 | + IFNULL(sc.cbj, 0) as tqdj | |
| 502 | + FROM wt_serial_number sn | |
| 503 | + LEFT JOIN wt_sp sp ON sn.spbh = sp.F_Id | |
| 504 | + LEFT JOIN wt_sp_cost sc ON sn.spbh = sc.spbh AND sc.ck = @ck | |
| 505 | + WHERE sn.in_warehouse = @ck | |
| 506 | + GROUP BY sn.spbh, sn.spmc, sp.F_Spbm, sc.cbj | |
| 507 | + ORDER BY sn.spmc", | |
| 508 | + new { ck = ck }); | |
| 509 | + | |
| 510 | + return products; | |
| 511 | + } | |
| 512 | + | |
| 513 | + #endregion | |
| 514 | + } | |
| 515 | +} | ... | ... |
Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtXsckdService.cs
| ... | ... | @@ -351,12 +351,23 @@ namespace NCC.Extend.WtXsckd |
| 351 | 351 | //开启事务 |
| 352 | 352 | _db.BeginTran(); |
| 353 | 353 | int tryCount = 0; |
| 354 | + // 如果前端传了预生成的单号且格式正确,优先使用 | |
| 355 | + bool usePreGeneratedId = !string.IsNullOrEmpty(input.id) | |
| 356 | + && input.id.StartsWith(prefix) | |
| 357 | + && input.id.Contains(today); | |
| 354 | 358 | while (true) |
| 355 | 359 | { |
| 356 | 360 | try |
| 357 | 361 | { |
| 358 | - // 生成单号逻辑 | |
| 359 | - entity.Id = $"{prefix}{today}{serial.ToString("D4")}"; | |
| 362 | + if (usePreGeneratedId) | |
| 363 | + { | |
| 364 | + entity.Id = input.id; | |
| 365 | + usePreGeneratedId = false; // 冲突重试时走自增逻辑 | |
| 366 | + } | |
| 367 | + else | |
| 368 | + { | |
| 369 | + entity.Id = $"{prefix}{today}{serial.ToString("D4")}"; | |
| 370 | + } | |
| 360 | 371 | entity.Djrq = DateTime.Now; |
| 361 | 372 | // ✅ 采购入库单:默认状态为"待审核" |
| 362 | 373 | if (input.djlx == "采购入库单" && string.IsNullOrEmpty(entity.Djzt)) |
| ... | ... | @@ -575,6 +586,9 @@ namespace NCC.Extend.WtXsckd |
| 575 | 586 | await GenerateCommissionRecords(newEntity.Id, wtXsckdMxEntityList, input.jsr); |
| 576 | 587 | } |
| 577 | 588 | |
| 589 | + // ========== P4: 自动维护 wt_sp_cost 成本表 ========== | |
| 590 | + await UpdateSpCostOnCreate(input.djlx, entity, wtXsckdMxEntityList); | |
| 591 | + | |
| 578 | 592 | //关闭事务 |
| 579 | 593 | _db.CommitTran(); |
| 580 | 594 | |
| ... | ... | @@ -714,6 +728,60 @@ namespace NCC.Extend.WtXsckd |
| 714 | 728 | } |
| 715 | 729 | |
| 716 | 730 | /// <summary> |
| 731 | + /// 预生成单据编号(新建时前端展示用) | |
| 732 | + /// </summary> | |
| 733 | + /// <param name="djlx">单据类型</param> | |
| 734 | + [HttpGet("Actions/GenerateBillNo")] | |
| 735 | + public async Task<dynamic> GenerateBillNo([FromQuery] string djlx) | |
| 736 | + { | |
| 737 | + var today = DateTime.Now.ToString("yyyyMMdd"); | |
| 738 | + string prefix = "CHD"; | |
| 739 | + if (!string.IsNullOrEmpty(djlx)) | |
| 740 | + { | |
| 741 | + if (djlx.Contains("采购入库单")) prefix = "RK"; | |
| 742 | + else if (djlx.Contains("预售出库单")) prefix = "YC"; | |
| 743 | + else if (djlx.Contains("预售退货单")) prefix = "YT"; | |
| 744 | + else if (djlx.Contains("采购退货单")) prefix = "CT"; | |
| 745 | + else if (djlx.Contains("销售出库单")) prefix = "CHD"; | |
| 746 | + else if (djlx.Contains("委托代销发货单")) prefix = "WF"; | |
| 747 | + else if (djlx.Contains("委托代销退货单")) prefix = "WT"; | |
| 748 | + else if (djlx.Contains("委托代销结算单")) prefix = "WJ"; | |
| 749 | + else if (djlx.Contains("现金费用单")) prefix = "XF"; | |
| 750 | + else if (djlx.Contains("其他收入单")) prefix = "QT"; | |
| 751 | + else if (djlx.Contains("盘点单")) prefix = "PDD"; | |
| 752 | + else if (djlx.Contains("报损单")) prefix = "BSD"; | |
| 753 | + else if (djlx.Contains("同价调拨单")) prefix = "TJD"; | |
| 754 | + else if (djlx.Contains("变价调拨单")) prefix = "BJD"; | |
| 755 | + else if (djlx.Contains("获赠单")) prefix = "HZD"; | |
| 756 | + else if (djlx.Contains("报溢单")) prefix = "BYD"; | |
| 757 | + } | |
| 758 | + | |
| 759 | + var allTodayOrders = await _db.Queryable<WtXsckdEntity>() | |
| 760 | + .Where(x => x.Id.StartsWith(prefix) && x.Id.Contains(today)) | |
| 761 | + .Select(x => x.Id) | |
| 762 | + .ToListAsync(); | |
| 763 | + | |
| 764 | + int serial = 1; | |
| 765 | + if (allTodayOrders.Count > 0) | |
| 766 | + { | |
| 767 | + var serialNumbers = new List<int>(); | |
| 768 | + foreach (var orderId in allTodayOrders) | |
| 769 | + { | |
| 770 | + if (orderId.Length >= 4) | |
| 771 | + { | |
| 772 | + var serialStr = orderId.Substring(orderId.Length - 4); | |
| 773 | + if (int.TryParse(serialStr, out int parsedSerial)) | |
| 774 | + serialNumbers.Add(parsedSerial); | |
| 775 | + } | |
| 776 | + } | |
| 777 | + if (serialNumbers.Count > 0) | |
| 778 | + serial = serialNumbers.Max() + 1; | |
| 779 | + } | |
| 780 | + | |
| 781 | + return new { billNo = $"{prefix}{today}{serial.ToString("D4")}" }; | |
| 782 | + } | |
| 783 | + | |
| 784 | + /// <summary> | |
| 717 | 785 | /// 导出销售出库单 |
| 718 | 786 | /// </summary> |
| 719 | 787 | /// <param name="input">请求参数</param> |
| ... | ... | @@ -770,7 +838,7 @@ namespace NCC.Extend.WtXsckd |
| 770 | 838 | } |
| 771 | 839 | |
| 772 | 840 | /// <summary> |
| 773 | - /// 批量删除销售出库单 | |
| 841 | + /// 批量删除销售出库单(含成本回退) | |
| 774 | 842 | /// </summary> |
| 775 | 843 | /// <param name="ids">主键数组</param> |
| 776 | 844 | /// <returns></returns> |
| ... | ... | @@ -782,19 +850,21 @@ namespace NCC.Extend.WtXsckd |
| 782 | 850 | { |
| 783 | 851 | try |
| 784 | 852 | { |
| 785 | - //开启事务 | |
| 786 | 853 | _db.BeginTran(); |
| 787 | - //批量删除销售出库单 | |
| 788 | - await _db.Deleteable<WtXsckdEntity>().In(d => d.Id,ids).ExecuteCommandAsync(); | |
| 789 | 854 | |
| 790 | - //清空子表数据 | |
| 791 | - await _db.Deleteable<WtXsckdMxEntity>().In(u => u.Djbh,ids).ExecuteCommandAsync(); | |
| 792 | - //关闭事务 | |
| 855 | + // 逐单回退成本 | |
| 856 | + foreach (var entity in entitys) | |
| 857 | + { | |
| 858 | + await RollbackSpCostOnDelete(entity); | |
| 859 | + } | |
| 860 | + | |
| 861 | + await _db.Deleteable<WtXsckdEntity>().In(d => d.Id, ids).ExecuteCommandAsync(); | |
| 862 | + await _db.Deleteable<WtXsckdMxEntity>().In(u => u.Djbh, ids).ExecuteCommandAsync(); | |
| 863 | + | |
| 793 | 864 | _db.CommitTran(); |
| 794 | 865 | } |
| 795 | 866 | catch (Exception) |
| 796 | 867 | { |
| 797 | - //回滚事务 | |
| 798 | 868 | _db.RollbackTran(); |
| 799 | 869 | throw NCCException.Oh(ErrorCode.COM1002); |
| 800 | 870 | } |
| ... | ... | @@ -866,7 +936,10 @@ namespace NCC.Extend.WtXsckd |
| 866 | 936 | } |
| 867 | 937 | |
| 868 | 938 | /// <summary> |
| 869 | - /// 删除销售出库单 | |
| 939 | + /// 删除销售出库单(含成本回退) | |
| 940 | + /// 采购入库单(已审核):检查序列号→删除→回退成本 | |
| 941 | + /// 销售/预售出库单:恢复 wt_sp_cost.sl | |
| 942 | + /// 销售退货单:减少 wt_sp_cost.sl | |
| 870 | 943 | /// </summary> |
| 871 | 944 | /// <returns></returns> |
| 872 | 945 | [HttpDelete("{id}")] |
| ... | ... | @@ -876,28 +949,62 @@ namespace NCC.Extend.WtXsckd |
| 876 | 949 | _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); |
| 877 | 950 | try |
| 878 | 951 | { |
| 879 | - //开启事务 | |
| 880 | 952 | _db.BeginTran(); |
| 881 | - | |
| 882 | - //删除销售出库单记录 | |
| 883 | - await _db.Deleteable<WtXsckdEntity>().Where(d => d.Id == id).ExecuteCommandAsync(); | |
| 884 | 953 | |
| 885 | - //清空子表数据 | |
| 954 | + // 删除前回退成本 | |
| 955 | + await RollbackSpCostOnDelete(entity); | |
| 956 | + | |
| 957 | + await _db.Deleteable<WtXsckdEntity>().Where(d => d.Id == id).ExecuteCommandAsync(); | |
| 886 | 958 | await _db.Deleteable<WtXsckdMxEntity>().Where(u => u.Djbh == id).ExecuteCommandAsync(); |
| 887 | 959 | |
| 888 | - //关闭事务 | |
| 889 | 960 | _db.CommitTran(); |
| 890 | 961 | } |
| 891 | 962 | catch (Exception) |
| 892 | 963 | { |
| 893 | - //回滚事务 | |
| 894 | 964 | _db.RollbackTran(); |
| 895 | 965 | throw NCCException.Oh(ErrorCode.COM1002); |
| 896 | 966 | } |
| 897 | 967 | } |
| 898 | 968 | |
| 899 | - | |
| 900 | - | |
| 969 | + /// <summary> | |
| 970 | + /// 根据商品+仓库查询已审核的采购入库单列表(用于采购退货单选择入库编号) | |
| 971 | + /// </summary> | |
| 972 | + [HttpGet("GetPurchaseInboundOrders")] | |
| 973 | + public async Task<dynamic> GetPurchaseInboundOrders(string spbh, string ck) | |
| 974 | + { | |
| 975 | + if (string.IsNullOrWhiteSpace(spbh) || string.IsNullOrWhiteSpace(ck)) | |
| 976 | + { | |
| 977 | + return new { code = 200, data = new List<object>() }; | |
| 978 | + } | |
| 979 | + | |
| 980 | + // 兼容两种传参:ck 可能是仓库ID,也可能是门店ID(F_ssmd) | |
| 981 | + var warehouseIds = await _db.Ado.SqlQueryAsync<string>( | |
| 982 | + "SELECT F_Id FROM wt_ck WHERE F_Id = @ck OR F_ssmd = @ck", | |
| 983 | + new { ck }); | |
| 984 | + if (warehouseIds == null || warehouseIds.Count == 0) | |
| 985 | + { | |
| 986 | + warehouseIds = new List<string> { ck }; | |
| 987 | + } | |
| 988 | + | |
| 989 | + var warehouseParams = warehouseIds | |
| 990 | + .Select((id, idx) => new SugarParameter($"@ck{idx}", id)) | |
| 991 | + .ToList(); | |
| 992 | + var inSql = string.Join(",", warehouseParams.Select(p => p.ParameterName)); | |
| 993 | + | |
| 994 | + var sql = $@"SELECT d.F_Id as orderid, d.djrq, mx.dj, mx.sl, mx.spmc | |
| 995 | + FROM wt_xsckd d | |
| 996 | + INNER JOIN wt_xsckd_mx mx ON mx.djbh = d.F_Id | |
| 997 | + WHERE d.djlx = '采购入库单' AND d.djzt = '已审核' | |
| 998 | + AND mx.spbh = @spbh | |
| 999 | + AND (mx.rkck IN ({inSql}) OR ((mx.rkck IS NULL OR mx.rkck = '') AND d.rkck IN ({inSql}))) | |
| 1000 | + ORDER BY d.F_Id DESC"; | |
| 1001 | + | |
| 1002 | + var sqlParams = new List<SugarParameter> { new SugarParameter("@spbh", spbh) }; | |
| 1003 | + sqlParams.AddRange(warehouseParams); | |
| 1004 | + var list = await _db.Ado.SqlQueryAsync<dynamic>(sql, sqlParams.ToArray()); | |
| 1005 | + return new { code = 200, data = list }; | |
| 1006 | + } | |
| 1007 | + | |
| 901 | 1008 | [HttpGet("GetProductSummary")] |
| 902 | 1009 | public async Task<dynamic> GetProductSummary(string productName = null, string settleUnit = null, string contactUnit = null, string agent = null, string warehouse = null, string billType = null, string startDate = null, string endDate = null) |
| 903 | 1010 | { |
| ... | ... | @@ -1749,10 +1856,11 @@ ORDER BY t.`商品编号`;"; |
| 1749 | 1856 | entity.Shr1 = userId; |
| 1750 | 1857 | await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr1 }).ExecuteCommandAsync(); |
| 1751 | 1858 | |
| 1752 | - // 采购入库单最终审核通过后生成序列号 | |
| 1859 | + // 采购入库单最终审核通过后生成序列号 + 更新成本 | |
| 1753 | 1860 | if (entity.Djlx == "采购入库单") |
| 1754 | 1861 | { |
| 1755 | 1862 | await GenerateSerialNumbersForApproval(entity, id); |
| 1863 | + await UpdateSpCostOnPurchaseApproval(entity, id); | |
| 1756 | 1864 | } |
| 1757 | 1865 | |
| 1758 | 1866 | _db.CommitTran(); |
| ... | ... | @@ -1767,10 +1875,11 @@ ORDER BY t.`商品编号`;"; |
| 1767 | 1875 | entity.Shr = userId; |
| 1768 | 1876 | await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr2 }).ExecuteCommandAsync(); |
| 1769 | 1877 | |
| 1770 | - // 采购入库单最终审核通过后生成序列号 | |
| 1878 | + // 采购入库单最终审核通过后生成序列号 + 更新成本 | |
| 1771 | 1879 | if (entity.Djlx == "采购入库单") |
| 1772 | 1880 | { |
| 1773 | 1881 | await GenerateSerialNumbersForApproval(entity, id); |
| 1882 | + await UpdateSpCostOnPurchaseApproval(entity, id); | |
| 1774 | 1883 | } |
| 1775 | 1884 | |
| 1776 | 1885 | _db.CommitTran(); |
| ... | ... | @@ -1859,6 +1968,7 @@ ORDER BY t.`商品编号`;"; |
| 1859 | 1968 | /// <summary> |
| 1860 | 1969 | /// 反审单据:将"已审核"或"一级已审"状态回退为"待审核",清空审核人字段 |
| 1861 | 1970 | /// 一级/二级审核人均可执行反审操作 |
| 1971 | + /// 采购入库单反审时需检查序列号使用情况并回退成本 | |
| 1862 | 1972 | /// </summary> |
| 1863 | 1973 | [HttpPost("ReverseApproval/{id}")] |
| 1864 | 1974 | public async Task<dynamic> ReverseApproval(string id) |
| ... | ... | @@ -1887,15 +1997,49 @@ ORDER BY t.`商品编号`;"; |
| 1887 | 1997 | if (!isAuthorized) |
| 1888 | 1998 | return new { success = false, message = "您没有反审权限,只有一级或二级审核人可以反审" }; |
| 1889 | 1999 | |
| 1890 | - entity.Djzt = "待审核"; | |
| 1891 | - entity.Shr = null; | |
| 1892 | - entity.Shr1 = null; | |
| 1893 | - entity.Shr2 = null; | |
| 1894 | - await _db.Updateable(entity) | |
| 1895 | - .UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr1, x.Shr2 }) | |
| 1896 | - .ExecuteCommandAsync(); | |
| 2000 | + _db.BeginTran(); | |
| 2001 | + try | |
| 2002 | + { | |
| 2003 | + // 采购入库单反审:检查序列号+回退成本 | |
| 2004 | + if (entity.Djlx == "采购入库单") | |
| 2005 | + { | |
| 2006 | + var serialNumbers = await _db.Queryable<WtSerialNumberEntity>() | |
| 2007 | + .Where(s => s.InDjbh == id) | |
| 2008 | + .ToListAsync(); | |
| 2009 | + | |
| 2010 | + if (serialNumbers.Any(s => s.Status != 0)) | |
| 2011 | + { | |
| 2012 | + _db.RollbackTran(); | |
| 2013 | + return new { success = false, message = "该入库单的商品已有出库记录,无法反审" }; | |
| 2014 | + } | |
| 2015 | + | |
| 2016 | + // 删除未使用的序列号 | |
| 2017 | + if (serialNumbers.Any()) | |
| 2018 | + { | |
| 2019 | + var snIds = serialNumbers.Select(s => s.Id).ToList(); | |
| 2020 | + await _db.Deleteable<WtSerialNumberEntity>().In(s => s.Id, snIds).ExecuteCommandAsync(); | |
| 2021 | + } | |
| 2022 | + | |
| 2023 | + // 回退 wt_sp_cost | |
| 2024 | + await RollbackSpCostForPurchase(entity, id); | |
| 2025 | + } | |
| 2026 | + | |
| 2027 | + entity.Djzt = "待审核"; | |
| 2028 | + entity.Shr = null; | |
| 2029 | + entity.Shr1 = null; | |
| 2030 | + entity.Shr2 = null; | |
| 2031 | + await _db.Updateable(entity) | |
| 2032 | + .UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr1, x.Shr2 }) | |
| 2033 | + .ExecuteCommandAsync(); | |
| 1897 | 2034 | |
| 1898 | - return new { success = true, message = "反审成功,单据已恢复为待审核状态" }; | |
| 2035 | + _db.CommitTran(); | |
| 2036 | + return new { success = true, message = "反审成功,单据已恢复为待审核状态" }; | |
| 2037 | + } | |
| 2038 | + catch (Exception ex) | |
| 2039 | + { | |
| 2040 | + _db.RollbackTran(); | |
| 2041 | + return new { success = false, message = $"反审失败: {ex.Message}" }; | |
| 2042 | + } | |
| 1899 | 2043 | } |
| 1900 | 2044 | catch (Exception ex) |
| 1901 | 2045 | { |
| ... | ... | @@ -2587,5 +2731,369 @@ ORDER BY t.`商品编号`;"; |
| 2587 | 2731 | // 不抛出异常,避免影响主业务流程 |
| 2588 | 2732 | } |
| 2589 | 2733 | } |
| 2734 | + | |
| 2735 | + #region P4: wt_sp_cost 成本表自动维护 | |
| 2736 | + | |
| 2737 | + /// <summary> | |
| 2738 | + /// 采购入库单审核通过后,按加权平均法更新成本表 | |
| 2739 | + /// 从数据库重新读取明细(保证使用编辑后的最终数据) | |
| 2740 | + /// </summary> | |
| 2741 | + private async Task UpdateSpCostOnPurchaseApproval(WtXsckdEntity entity, string id) | |
| 2742 | + { | |
| 2743 | + try | |
| 2744 | + { | |
| 2745 | + var warehouseId = entity.Rkck; | |
| 2746 | + if (string.IsNullOrEmpty(warehouseId)) return; | |
| 2747 | + | |
| 2748 | + var detailList = await _db.Queryable<WtXsckdMxEntity>() | |
| 2749 | + .Where(d => d.Djbh == id).ToListAsync(); | |
| 2750 | + | |
| 2751 | + foreach (var detail in detailList) | |
| 2752 | + { | |
| 2753 | + if (string.IsNullOrEmpty(detail.Spbh)) continue; | |
| 2754 | + if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; | |
| 2755 | + | |
| 2756 | + var detailWarehouse = !string.IsNullOrEmpty(detail.Rkck) ? detail.Rkck : warehouseId; | |
| 2757 | + decimal purchasePrice = detail.Dj; | |
| 2758 | + | |
| 2759 | + var existing = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 2760 | + "SELECT cbj, sl FROM wt_sp_cost WHERE spbh = @spbh AND ck = @ck LIMIT 1", | |
| 2761 | + new { spbh = detail.Spbh, ck = detailWarehouse }); | |
| 2762 | + | |
| 2763 | + if (existing != null && existing.Count > 0) | |
| 2764 | + { | |
| 2765 | + decimal oldCbj = Convert.ToDecimal(existing[0].cbj); | |
| 2766 | + int oldSl = Convert.ToInt32(existing[0].sl); | |
| 2767 | + int newSl = oldSl + qty; | |
| 2768 | + decimal newCbj = newSl > 0 | |
| 2769 | + ? Math.Round((oldCbj * oldSl + purchasePrice * qty) / newSl, 2) | |
| 2770 | + : purchasePrice; | |
| 2771 | + | |
| 2772 | + await _db.Ado.ExecuteCommandAsync( | |
| 2773 | + "UPDATE wt_sp_cost SET cbj = @cbj, sl = @sl, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 2774 | + new { cbj = newCbj, sl = newSl, spbh = detail.Spbh, ck = detailWarehouse }); | |
| 2775 | + } | |
| 2776 | + else | |
| 2777 | + { | |
| 2778 | + await _db.Ado.ExecuteCommandAsync( | |
| 2779 | + "INSERT INTO wt_sp_cost (F_Id, spbh, ck, cbj, sl, update_time) VALUES (@id, @spbh, @ck, @cbj, @sl, NOW())", | |
| 2780 | + new { id = $"{detail.Spbh}_{detailWarehouse}", spbh = detail.Spbh, ck = detailWarehouse, cbj = purchasePrice, sl = qty }); | |
| 2781 | + } | |
| 2782 | + } | |
| 2783 | + } | |
| 2784 | + catch (Exception ex) | |
| 2785 | + { | |
| 2786 | + Console.WriteLine($"采购入库审核更新成本失败(不影响主业务): {ex.Message}"); | |
| 2787 | + } | |
| 2788 | + } | |
| 2789 | + | |
| 2790 | + /// <summary> | |
| 2791 | + /// 创建单据时自动维护 wt_sp_cost(在事务内调用) | |
| 2792 | + /// 采购入库 → 审核通过后才更新(见 UpdateSpCostOnPurchaseApproval) | |
| 2793 | + /// 销售/预售出库 → 快照成本到明细 + 减少数量 | |
| 2794 | + /// 销售退货 → 恢复数量(成本不变) | |
| 2795 | + /// </summary> | |
| 2796 | + private async Task UpdateSpCostOnCreate(string djlx, WtXsckdEntity entity, List<WtXsckdMxEntity> mxList) | |
| 2797 | + { | |
| 2798 | + if (mxList == null || mxList.Count == 0) return; | |
| 2799 | + | |
| 2800 | + try | |
| 2801 | + { | |
| 2802 | + if (djlx == "销售出库单" || djlx == "预售出库单") | |
| 2803 | + { | |
| 2804 | + var outStoreId = entity.Cjck; | |
| 2805 | + if (string.IsNullOrEmpty(outStoreId)) return; | |
| 2806 | + | |
| 2807 | + var warehouseIds = await _db.Queryable<WtCkEntity>() | |
| 2808 | + .Where(c => c.Ssmd == outStoreId) | |
| 2809 | + .Select(c => c.Id) | |
| 2810 | + .ToListAsync(); | |
| 2811 | + if (warehouseIds == null || warehouseIds.Count == 0) return; | |
| 2812 | + | |
| 2813 | + foreach (var detail in mxList) | |
| 2814 | + { | |
| 2815 | + if (string.IsNullOrEmpty(detail.Spbh)) continue; | |
| 2816 | + if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; | |
| 2817 | + | |
| 2818 | + var costResult = await _db.Ado.SqlQueryAsync<decimal?>( | |
| 2819 | + $"SELECT cbj FROM wt_sp_cost WHERE spbh = @spbh AND ck IN ({string.Join(",", warehouseIds.Select((_, i) => $"@ck{i}"))}) AND cbj > 0 LIMIT 1", | |
| 2820 | + warehouseIds.Select((id, i) => new SugarParameter($"@ck{i}", id)) | |
| 2821 | + .Append(new SugarParameter("@spbh", detail.Spbh)).ToArray()); | |
| 2822 | + | |
| 2823 | + decimal costPrice = costResult?.FirstOrDefault() ?? 0; | |
| 2824 | + | |
| 2825 | + if (costPrice > 0) | |
| 2826 | + { | |
| 2827 | + await _db.Ado.ExecuteCommandAsync( | |
| 2828 | + "UPDATE wt_xsckd_mx SET cbdj = @cbdj, cbje = @cbje WHERE F_Id = @id", | |
| 2829 | + new { cbdj = costPrice, cbje = Math.Round(costPrice * qty, 2), id = detail.Id }); | |
| 2830 | + } | |
| 2831 | + | |
| 2832 | + foreach (var whId in warehouseIds) | |
| 2833 | + { | |
| 2834 | + var actualCount = await _db.Ado.GetIntAsync( | |
| 2835 | + "SELECT COUNT(*) FROM wt_serial_number WHERE spbh = @spbh AND in_warehouse = @ck AND status = 0", | |
| 2836 | + new SugarParameter[] { new SugarParameter("@spbh", detail.Spbh), new SugarParameter("@ck", whId) }); | |
| 2837 | + await _db.Ado.ExecuteCommandAsync( | |
| 2838 | + "UPDATE wt_sp_cost SET sl = @sl, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 2839 | + new { sl = actualCount, spbh = detail.Spbh, ck = whId }); | |
| 2840 | + } | |
| 2841 | + } | |
| 2842 | + } | |
| 2843 | + else if (djlx == "销售退货单") | |
| 2844 | + { | |
| 2845 | + var returnWarehouseId = entity.Rkck; | |
| 2846 | + if (string.IsNullOrEmpty(returnWarehouseId)) return; | |
| 2847 | + | |
| 2848 | + foreach (var detail in mxList) | |
| 2849 | + { | |
| 2850 | + if (string.IsNullOrEmpty(detail.Spbh)) continue; | |
| 2851 | + if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; | |
| 2852 | + | |
| 2853 | + var existing = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 2854 | + "SELECT sl FROM wt_sp_cost WHERE spbh = @spbh AND ck = @ck LIMIT 1", | |
| 2855 | + new { spbh = detail.Spbh, ck = returnWarehouseId }); | |
| 2856 | + | |
| 2857 | + if (existing != null && existing.Count > 0) | |
| 2858 | + { | |
| 2859 | + await _db.Ado.ExecuteCommandAsync( | |
| 2860 | + "UPDATE wt_sp_cost SET sl = sl + @qty, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 2861 | + new { qty, spbh = detail.Spbh, ck = returnWarehouseId }); | |
| 2862 | + } | |
| 2863 | + } | |
| 2864 | + } | |
| 2865 | + else if (djlx == "采购退货单") | |
| 2866 | + { | |
| 2867 | + // 采购退货:用退货明细中的单价(即原始采购价)做反向加权平均 | |
| 2868 | + var outStoreId = entity.Cjck; | |
| 2869 | + if (string.IsNullOrEmpty(outStoreId)) return; | |
| 2870 | + | |
| 2871 | + var warehouseIds = await _db.Queryable<WtCkEntity>() | |
| 2872 | + .Where(c => c.Ssmd == outStoreId || c.Id == outStoreId) | |
| 2873 | + .Select(c => c.Id) | |
| 2874 | + .ToListAsync(); | |
| 2875 | + if (warehouseIds == null || warehouseIds.Count == 0) | |
| 2876 | + { | |
| 2877 | + warehouseIds = new List<string> { outStoreId }; | |
| 2878 | + } | |
| 2879 | + | |
| 2880 | + foreach (var detail in mxList) | |
| 2881 | + { | |
| 2882 | + if (string.IsNullOrEmpty(detail.Spbh)) continue; | |
| 2883 | + if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; | |
| 2884 | + | |
| 2885 | + decimal returnPrice = detail.Dj; | |
| 2886 | + | |
| 2887 | + foreach (var whId in warehouseIds) | |
| 2888 | + { | |
| 2889 | + var existing = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 2890 | + "SELECT cbj, sl FROM wt_sp_cost WHERE spbh = @spbh AND ck = @ck LIMIT 1", | |
| 2891 | + new { spbh = detail.Spbh, ck = whId }); | |
| 2892 | + | |
| 2893 | + if (existing != null && existing.Count > 0) | |
| 2894 | + { | |
| 2895 | + decimal currentCbj = Convert.ToDecimal(existing[0].cbj); | |
| 2896 | + int currentSl = Convert.ToInt32(existing[0].sl); | |
| 2897 | + int newSl = currentSl - qty; | |
| 2898 | + | |
| 2899 | + decimal newCbj = 0; | |
| 2900 | + if (newSl > 0) | |
| 2901 | + { | |
| 2902 | + newCbj = Math.Round((currentCbj * currentSl - returnPrice * qty) / newSl, 2); | |
| 2903 | + } | |
| 2904 | + else | |
| 2905 | + { | |
| 2906 | + newSl = 0; | |
| 2907 | + } | |
| 2908 | + | |
| 2909 | + await _db.Ado.ExecuteCommandAsync( | |
| 2910 | + "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 }); | |
| 2912 | + | |
| 2913 | + await _db.Ado.ExecuteCommandAsync( | |
| 2914 | + "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 }); | |
| 2916 | + | |
| 2917 | + break; | |
| 2918 | + } | |
| 2919 | + } | |
| 2920 | + } | |
| 2921 | + } | |
| 2922 | + } | |
| 2923 | + catch (Exception ex) | |
| 2924 | + { | |
| 2925 | + Console.WriteLine($"更新成本表失败(不影响主业务): {ex.Message}"); | |
| 2926 | + } | |
| 2927 | + } | |
| 2928 | + | |
| 2929 | + /// <summary> | |
| 2930 | + /// 采购入库单反审/删除时回退成本:反向加权平均 | |
| 2931 | + /// </summary> | |
| 2932 | + private async Task RollbackSpCostForPurchase(WtXsckdEntity entity, string id) | |
| 2933 | + { | |
| 2934 | + var warehouseId = entity.Rkck; | |
| 2935 | + if (string.IsNullOrEmpty(warehouseId)) return; | |
| 2936 | + | |
| 2937 | + var detailList = await _db.Queryable<WtXsckdMxEntity>() | |
| 2938 | + .Where(d => d.Djbh == id).ToListAsync(); | |
| 2939 | + | |
| 2940 | + foreach (var detail in detailList) | |
| 2941 | + { | |
| 2942 | + if (string.IsNullOrEmpty(detail.Spbh)) continue; | |
| 2943 | + if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; | |
| 2944 | + | |
| 2945 | + var detailWarehouse = !string.IsNullOrEmpty(detail.Rkck) ? detail.Rkck : warehouseId; | |
| 2946 | + decimal purchasePrice = detail.Dj; | |
| 2947 | + | |
| 2948 | + var existing = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 2949 | + "SELECT cbj, sl FROM wt_sp_cost WHERE spbh = @spbh AND ck = @ck LIMIT 1", | |
| 2950 | + new { spbh = detail.Spbh, ck = detailWarehouse }); | |
| 2951 | + | |
| 2952 | + if (existing != null && existing.Count > 0) | |
| 2953 | + { | |
| 2954 | + decimal currentCbj = Convert.ToDecimal(existing[0].cbj); | |
| 2955 | + int currentSl = Convert.ToInt32(existing[0].sl); | |
| 2956 | + int newSl = currentSl - qty; | |
| 2957 | + | |
| 2958 | + decimal newCbj = 0; | |
| 2959 | + if (newSl > 0) | |
| 2960 | + { | |
| 2961 | + newCbj = Math.Round((currentCbj * currentSl - purchasePrice * qty) / newSl, 2); | |
| 2962 | + } | |
| 2963 | + else | |
| 2964 | + { | |
| 2965 | + newSl = 0; | |
| 2966 | + } | |
| 2967 | + | |
| 2968 | + await _db.Ado.ExecuteCommandAsync( | |
| 2969 | + "UPDATE wt_sp_cost SET cbj = @cbj, sl = @sl, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 2970 | + new { cbj = newCbj, sl = newSl, spbh = detail.Spbh, ck = detailWarehouse }); | |
| 2971 | + } | |
| 2972 | + } | |
| 2973 | + } | |
| 2974 | + | |
| 2975 | + /// <summary> | |
| 2976 | + /// 删除单据时根据单据类型回退 wt_sp_cost | |
| 2977 | + /// 采购入库单(已审核):检查序列号→删除→回退成本 | |
| 2978 | + /// 销售/预售出库单:恢复sl | |
| 2979 | + /// 销售退货单:减少sl | |
| 2980 | + /// </summary> | |
| 2981 | + private async Task RollbackSpCostOnDelete(WtXsckdEntity entity) | |
| 2982 | + { | |
| 2983 | + var id = entity.Id; | |
| 2984 | + var djlx = entity.Djlx; | |
| 2985 | + | |
| 2986 | + if (djlx == "采购入库单" && entity.Djzt == "已审核") | |
| 2987 | + { | |
| 2988 | + // 检查序列号是否被使用 | |
| 2989 | + var serialNumbers = await _db.Queryable<WtSerialNumberEntity>() | |
| 2990 | + .Where(s => s.InDjbh == id) | |
| 2991 | + .ToListAsync(); | |
| 2992 | + | |
| 2993 | + if (serialNumbers.Any(s => s.Status != 0)) | |
| 2994 | + throw new Exception("该入库单的商品已有出库记录,无法删除"); | |
| 2995 | + | |
| 2996 | + if (serialNumbers.Any()) | |
| 2997 | + { | |
| 2998 | + var snIds = serialNumbers.Select(s => s.Id).ToList(); | |
| 2999 | + await _db.Deleteable<WtSerialNumberEntity>().In(s => s.Id, snIds).ExecuteCommandAsync(); | |
| 3000 | + } | |
| 3001 | + | |
| 3002 | + await RollbackSpCostForPurchase(entity, id); | |
| 3003 | + } | |
| 3004 | + else if (djlx == "销售出库单" || djlx == "预售出库单") | |
| 3005 | + { | |
| 3006 | + var outStoreId = entity.Cjck; | |
| 3007 | + if (string.IsNullOrEmpty(outStoreId)) return; | |
| 3008 | + | |
| 3009 | + var warehouseIds = await _db.Queryable<WtCkEntity>() | |
| 3010 | + .Where(c => c.Ssmd == outStoreId) | |
| 3011 | + .Select(c => c.Id) | |
| 3012 | + .ToListAsync(); | |
| 3013 | + if (warehouseIds == null || warehouseIds.Count == 0) return; | |
| 3014 | + | |
| 3015 | + var mxList = await _db.Queryable<WtXsckdMxEntity>() | |
| 3016 | + .Where(d => d.Djbh == id).ToListAsync(); | |
| 3017 | + | |
| 3018 | + foreach (var detail in mxList) | |
| 3019 | + { | |
| 3020 | + if (string.IsNullOrEmpty(detail.Spbh)) continue; | |
| 3021 | + if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; | |
| 3022 | + | |
| 3023 | + foreach (var whId in warehouseIds) | |
| 3024 | + { | |
| 3025 | + await _db.Ado.ExecuteCommandAsync( | |
| 3026 | + "UPDATE wt_sp_cost SET sl = sl + @qty, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 3027 | + new { qty, spbh = detail.Spbh, ck = whId }); | |
| 3028 | + } | |
| 3029 | + } | |
| 3030 | + } | |
| 3031 | + else if (djlx == "销售退货单") | |
| 3032 | + { | |
| 3033 | + var returnWarehouseId = entity.Rkck; | |
| 3034 | + if (string.IsNullOrEmpty(returnWarehouseId)) return; | |
| 3035 | + | |
| 3036 | + var mxList = await _db.Queryable<WtXsckdMxEntity>() | |
| 3037 | + .Where(d => d.Djbh == id).ToListAsync(); | |
| 3038 | + | |
| 3039 | + foreach (var detail in mxList) | |
| 3040 | + { | |
| 3041 | + if (string.IsNullOrEmpty(detail.Spbh)) continue; | |
| 3042 | + if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; | |
| 3043 | + | |
| 3044 | + await _db.Ado.ExecuteCommandAsync( | |
| 3045 | + "UPDATE wt_sp_cost SET sl = sl - @qty, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 3046 | + new { qty, spbh = detail.Spbh, ck = returnWarehouseId }); | |
| 3047 | + } | |
| 3048 | + } | |
| 3049 | + else if (djlx == "采购退货单") | |
| 3050 | + { | |
| 3051 | + // 采购退货单删除:恢复成本(反向操作,相当于再入库) | |
| 3052 | + var outStoreId = entity.Cjck; | |
| 3053 | + if (string.IsNullOrEmpty(outStoreId)) return; | |
| 3054 | + | |
| 3055 | + var warehouseIds = await _db.Queryable<WtCkEntity>() | |
| 3056 | + .Where(c => c.Ssmd == outStoreId || c.Id == outStoreId) | |
| 3057 | + .Select(c => c.Id) | |
| 3058 | + .ToListAsync(); | |
| 3059 | + if (warehouseIds == null || warehouseIds.Count == 0) | |
| 3060 | + warehouseIds = new List<string> { outStoreId }; | |
| 3061 | + | |
| 3062 | + var mxList = await _db.Queryable<WtXsckdMxEntity>() | |
| 3063 | + .Where(d => d.Djbh == id).ToListAsync(); | |
| 3064 | + | |
| 3065 | + foreach (var detail in mxList) | |
| 3066 | + { | |
| 3067 | + if (string.IsNullOrEmpty(detail.Spbh)) continue; | |
| 3068 | + if (!int.TryParse(detail.Sl, out int qty) || qty <= 0) continue; | |
| 3069 | + | |
| 3070 | + decimal returnPrice = detail.Dj; | |
| 3071 | + | |
| 3072 | + foreach (var whId in warehouseIds) | |
| 3073 | + { | |
| 3074 | + var existing = await _db.Ado.SqlQueryAsync<dynamic>( | |
| 3075 | + "SELECT cbj, sl FROM wt_sp_cost WHERE spbh = @spbh AND ck = @ck LIMIT 1", | |
| 3076 | + new { spbh = detail.Spbh, ck = whId }); | |
| 3077 | + | |
| 3078 | + if (existing != null && existing.Count > 0) | |
| 3079 | + { | |
| 3080 | + decimal currentCbj = Convert.ToDecimal(existing[0].cbj); | |
| 3081 | + int currentSl = Convert.ToInt32(existing[0].sl); | |
| 3082 | + int newSl = currentSl + qty; | |
| 3083 | + decimal newCbj = newSl > 0 | |
| 3084 | + ? Math.Round((currentCbj * currentSl + returnPrice * qty) / newSl, 2) | |
| 3085 | + : returnPrice; | |
| 3086 | + | |
| 3087 | + await _db.Ado.ExecuteCommandAsync( | |
| 3088 | + "UPDATE wt_sp_cost SET cbj = @cbj, sl = @sl, update_time = NOW() WHERE spbh = @spbh AND ck = @ck", | |
| 3089 | + new { cbj = newCbj, sl = newSl, spbh = detail.Spbh, ck = whId }); | |
| 3090 | + break; | |
| 3091 | + } | |
| 3092 | + } | |
| 3093 | + } | |
| 3094 | + } | |
| 3095 | + } | |
| 3096 | + | |
| 3097 | + #endregion | |
| 2590 | 3098 | } |
| 2591 | 3099 | } | ... | ... |