Browse Source

2026-09-30

销售报价明细增加“保税料”、“BOM税率”和“保税”
master
fengyuan_yang 1 month ago
parent
commit
70261dd9e5
  1. 4
      src/api/quote/quoteDetailBom.js
  2. 61
      src/views/modules/quote/detail/primary/quoteDetailBom.vue
  3. 15
      src/views/modules/quote/detail/primary/quoteDetailCost.vue
  4. 144
      src/views/modules/quote/detail/quoteDetail.vue

4
src/api/quote/quoteDetailBom.js

@ -2,4 +2,8 @@ import {createAPI} from "../../utils/httpRequest";
export const queryQuoteDetailBom = (data) => createAPI("/quote/detail/bom", "post", data);
export const listQuoteDetailBomBonded = (data) => createAPI("/quote/detail/bom/listBonded", "post", data);
export const saveQuoteDetailBomBonded = (data) => createAPI("/quote/detail/bom/saveBonded", "post", data);
export const checkZeroUnitPrice = (data) => createAPI("/quote/detail/bom/checkZeroPrice", "post", data);

61
src/views/modules/quote/detail/primary/quoteDetailBom.vue

@ -14,6 +14,10 @@ export default {
type: Object,
required: true
},
bondedDraft: {
type: Object,
default: null
}
},
model: {
prop: "quoteDetail",
@ -94,6 +98,23 @@ export default {
status: true,
fixed: '',
columnWidth: 160,
}, {
userId: this.$store.state.user.name,
functionId: 5011,
serialNumber: '5011Table4BondedFlag',
tableId: "5011Table4",
tableName: "报价材料信息",
columnProp: 'bondedFlag',
headerAlign: "center",
align: "center",
columnLabel: '保税',
columnHidden: false,
columnImage: false,
columnSortable: false,
sortLv: 0,
status: true,
fixed: '',
columnWidth: 70,
}, {
userId: this.$store.state.user.name,
functionId: 5011,
@ -389,7 +410,7 @@ export default {
this.queryLoading = true;
queryQuoteDetailBom(params).then(({data}) => {
if (data && data.code === 0) {
this.dataList = data.rows;
this.dataList = this.mergeBondedDraft(data.rows);
} else {
this.$message.warning(data.msg);
}
@ -527,7 +548,8 @@ export default {
quoteDetailItemNo: this.quoteDetail.itemNo,
versionNo:this.quoteDetail.versionNo,
recalculateCost: this.recalculateCost, // 是否重新计算成本
bondedFlag: this.quoteDetail.bondedFlag,
bondedTaxRate: this.quoteDetail.bondedTaxRate,
}
if (node) {
params.id = node.id
@ -542,6 +564,7 @@ export default {
this.$message.success(data.msg);
this.versionVisible = false;
this.recalculateCost = false; // 重置勾选状态
this.$emit('bonded-draft-reset')
this.handleQueryQuoteDetailBomTree();
} else {
this.$message.warning(data.msg);
@ -619,6 +642,30 @@ export default {
},
finishDownload() {
this.exportLoading = false
},
bondedDisabled() {
return this.quoteDetail.showModalFlag
|| this.quoteDetail.status === '已完成'
|| this.quoteDetail.bondedFlag !== 'Y'
},
mergeBondedDraft(rows) {
if (!this.bondedDraft || !rows) {
return rows || []
}
return rows.map(row => {
const flag = this.bondedDraft[row.id]
if (flag == null) {
return row
}
return {
...row,
bondedFlag: flag
}
})
},
handleBondedLineChange(row, checked) {
this.$set(row, 'bondedFlag', checked ? 'Y' : 'N')
this.$emit('bonded-change', row, checked)
}
},
created() {
@ -632,6 +679,9 @@ export default {
},
isAllBom(newVal, oldVal) {
this.handleQueryQuoteDetailBom();
},
bondedDraft() {
this.dataList = this.mergeBondedDraft(this.dataList)
}
}
}
@ -689,6 +739,13 @@ export default {
<template v-if="item.columnProp === 'partNo'">
<el-link @click="handleJumpToBom(scope.row)">{{ scope.row[item.columnProp] }}</el-link>
</template>
<template v-else-if="item.columnProp === 'bondedFlag'">
<el-checkbox
:value="scope.row.bondedFlag === 'Y'"
:disabled="bondedDisabled()"
@change="handleBondedLineChange(scope.row, $event)">
</el-checkbox>
</template>
<template v-else-if="item.columnProp === 'calcUnitCost'">
<span>{{ getCalcUnitCost(scope.row) }}</span>
</template>

15
src/views/modules/quote/detail/primary/quoteDetailCost.vue

@ -11,6 +11,10 @@ export default {
type: Object,
required: true
},
bondedDraft: {
type: Object,
default: null
}
},
model: {
prop: "quoteDetail",
@ -167,8 +171,16 @@ export default {
this.computeCurrencyTotalCost();
},
handleQueryAllCost(){
let bondedLines = null
if (this.bondedDraft) {
bondedLines = Object.keys(this.bondedDraft).map(id => ({
id: Number(id),
bondedFlag: this.bondedDraft[id]
}))
}
let params = {
...this.quoteDetail
...this.quoteDetail,
bondedLines
}
this.computeLoading = true
queryQuoteDetailAllCost(params).then(({data})=>{
@ -194,6 +206,7 @@ export default {
this.quoteDetail.otherCost = data.row.otherCost;
this.computedTotalPartCost();// 重新计算成本
this.$emit('calculated')
}else {
this.$message.warning(data.msg);
}

144
src/views/modules/quote/detail/quoteDetail.vue

@ -13,6 +13,7 @@ import {getProjectPartList} from "../../../../api/project/project";
import QuoteDetailCost from "./primary/quoteDetailCost.vue";
import QuoteDetailTool from "./primary/quoteDetailTool.vue";
import QuoteDetailBom from "./primary/quoteDetailBom.vue";
import {listQuoteDetailBomBonded, saveQuoteDetailBomBonded} from "../../../../api/quote/quoteDetailBom";
import QuoteDetailRouting from "./primary/quoteDetailRouting.vue";
import QuoteDetailOtherCost from "./primary/quoteDetailOtherCost.vue";
import DictDataSelect from "../../sys/dict-data-select.vue";
@ -130,6 +131,10 @@ export default {
saveVisible:false,
showModalFlag:false,
showFlagChecked:false,
bondedChecked:false,
bomBondedDraft: null,
bondedManualOverride: {},
bondedLoadSeq: 0,
saveQuoteDetailRules:{
partNo: [{required: true, message: '请输入物料编码', trigger: ['blur','change']}],
partDesc: [{required: true, message: '请输入物料名称', trigger: ['blur','change']}],
@ -793,6 +798,12 @@ export default {
}
// 设置勾选状态
this.showFlagChecked = row.showFlag === 'Y'
this.bomBondedDraft = null
this.bondedManualOverride = {}
this.bondedChecked = row.bondedFlag === 'Y'
if (row.bondedFlag !== 'Y') {
this.$set(this.saveQuoteDetail, 'bondedTaxRate', 0)
}
}else {
this.saveQuoteDetail = {
...this.quoteDetail,
@ -801,6 +812,8 @@ export default {
active:'Y',
qty:1,
isDetail:false,
bondedFlag:'N',
bondedTaxRate:0,
}
if (this.quoteGroupDetail){
this.saveQuoteDetail.partNo = this.quoteGroupDetail.partNo
@ -809,8 +822,11 @@ export default {
this.$nextTick(()=>{
this.saveQuoteDetail.projectNo = this.quote.projectNo
})
// 新建时默认勾选
// 新建时默认勾选询价展示,保税料默认不勾选
this.showFlagChecked = true
this.bomBondedDraft = null
this.bondedManualOverride = {}
this.bondedChecked = false
}
this.saveQuoteDetail.showModalFlag = false
this.activeName = 'bom';
@ -824,6 +840,12 @@ export default {
this.saveQuoteDetail.showModalFlag = true
// 设置勾选状态
this.showFlagChecked = row.showFlag === 'Y'
this.bomBondedDraft = null
this.bondedManualOverride = {}
this.bondedChecked = row.bondedFlag === 'Y'
if (row.bondedFlag !== 'Y') {
this.$set(this.saveQuoteDetail, 'bondedTaxRate', 0)
}
this.activeName = 'bom';
this.saveVisible = true;
this.showModalFlag = true;
@ -884,6 +906,93 @@ export default {
this.showFlagChecked = !val
})
},
isBondedCandidate(row) {
const partNo = row.componentPart || ''
return row.status === 'Y'
&& partNo.indexOf('DF') >= 0
&& (row.partType === 'Purchased' || row.partType === 'Purchased (raw)')
},
handleBondedChange(val) {
this.bondedChecked = val
this.bondedManualOverride = {}
this.$set(this.saveQuoteDetail, 'bondedFlag', val ? 'Y' : 'N')
this.$set(this.saveQuoteDetail, 'bondedTaxRate', val ? 13 : 0)
if (!this.saveQuoteDetail.id) {
return
}
const requestSeq = ++this.bondedLoadSeq
listQuoteDetailBomBonded({quoteDetailId: this.saveQuoteDetail.id}).then(({data}) => {
if (requestSeq !== this.bondedLoadSeq) {
return
}
if (!(data && data.code === 0)) {
this.$message.warning((data && data.msg) || '读取材料失败')
return
}
const draft = {}
;(data.rows || []).forEach(row => {
const manualFlag = this.bondedManualOverride[row.id]
if (manualFlag) {
draft[row.id] = manualFlag
} else {
draft[row.id] = val && this.isBondedCandidate(row) ? 'Y' : 'N'
}
})
this.bomBondedDraft = draft
}).catch((error) => {
this.$message.error(error)
})
},
handleBomBondedChange(row, checked) {
const flag = checked ? 'Y' : 'N'
if (!this.bondedManualOverride) {
this.bondedManualOverride = {}
}
this.$set(this.bondedManualOverride, row.id, flag)
if (!this.bomBondedDraft) {
this.bomBondedDraft = {}
}
this.$set(this.bomBondedDraft, row.id, flag)
},
syncOpenDetailToList() {
if (!this.saveQuoteDetail || !this.saveQuoteDetail.id || !this.dataList) {
return
}
const index = this.dataList.findIndex(item => item.id === this.saveQuoteDetail.id)
if (index < 0) {
return
}
this.$set(this.dataList, index, {
...this.dataList[index],
bondedFlag: this.saveQuoteDetail.bondedFlag,
bondedTaxRate: this.saveQuoteDetail.bondedTaxRate
})
},
handleCostSaved() {
this.syncOpenDetailToList()
this.handleQueryQuoteDetail()
},
handleDetailDialogClose() {
this.handleQueryQuoteDetail()
},
saveBomBondedDraft() {
if (!this.bomBondedDraft || !this.saveQuoteDetail.id) {
return Promise.resolve()
}
const lines = Object.keys(this.bomBondedDraft).map(id => ({
id: Number(id),
bondedFlag: this.bomBondedDraft[id]
}))
if (!lines.length) {
return Promise.resolve()
}
return saveQuoteDetailBomBonded(lines).then(({data}) => {
if (!(data && data.code === 0)) {
return Promise.reject((data && data.msg) || '保税保存失败')
}
this.bomBondedDraft = null
})
},
handleDeleteQuoteDetail(row){
if (!this.canOperateQuoteDetail()) {
this.$message.warning('审批中状态下仅当前审批人可删除')
@ -1023,6 +1132,7 @@ export default {
...data.row,
}
this.showFlagChecked = this.saveQuoteDetail.showFlag === 'Y'
this.bondedChecked = this.saveQuoteDetail.bondedFlag === 'Y'
}else {
this.saveVisible = false;
}
@ -1045,9 +1155,13 @@ export default {
}
updateQuoteDetail(params).then(({data})=>{
if (data && data.code === 0){
this.$message.success(data.msg);
this.handleQueryQuoteDetail();
this.saveVisible = false;
this.saveBomBondedDraft().then(() => {
this.$message.success(data.msg);
this.handleQueryQuoteDetail();
this.saveVisible = false;
}).catch((error) => {
this.$message.error(error);
})
}else {
this.$message.warning(data.msg);
}
@ -1352,7 +1466,7 @@ export default {
</el-table-column>
</el-table>
<el-dialog :title="`报价明细`" top="10vh" v-drag :visible.sync="saveVisible" append-to-body :width="`${saveQuoteDetail.id?1200:600}px`" :close-on-click-modal="false">
<el-dialog :title="`报价明细`" top="10vh" v-drag :visible.sync="saveVisible" append-to-body :width="`${saveQuoteDetail.id?1200:600}px`" :close-on-click-modal="false" @close="handleDetailDialogClose">
<el-form :model="saveQuoteDetail" ref="saveQuoteDetailForm" :rules="saveQuoteDetailRules" label-position="top" v-if="!saveQuoteDetail.id">
<el-row :gutter="20">
<el-col :span="8">
@ -1428,7 +1542,21 @@ export default {
<el-form-item label=" " :show-message="false">
<el-button v-if="saveQuoteDetail.id && isAuth('5011:detail:dataEntry')" type="warning" @click="handleDataEntry">数据录入</el-button> </el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="18">
<el-form-item label=" " :show-message="false"></el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label=" " :show-message="false">
<el-checkbox v-model="bondedChecked" :disabled="showModalFlag || saveQuoteDetail.status === '已完成'" @change="handleBondedChange">保税料</el-checkbox>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label="BOM税率" :show-message="false">
<el-input-number v-model="saveQuoteDetail.bondedTaxRate" :controls="false" :precision="2" :min="0" :step="0.01" :disabled="showModalFlag || saveQuoteDetail.status === '已完成' || !bondedChecked" style="width: 100%"></el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
@ -1441,7 +1569,7 @@ export default {
<el-tabs v-model="activeName" v-if="saveQuoteDetail.id" @tab-click="handleClickTab">
<el-tab-pane label="材料" name="bom">
<quote-detail-bom v-if="saveVisible && activeName === 'bom'" @close="handleClose" v-model:quoteDetail="saveQuoteDetail"></quote-detail-bom>
<quote-detail-bom v-if="saveVisible && activeName === 'bom'" @close="handleClose" @bonded-change="handleBomBondedChange" @bonded-draft-reset="bomBondedDraft = null" :bonded-draft="bomBondedDraft" v-model:quoteDetail="saveQuoteDetail"></quote-detail-bom>
</el-tab-pane>
<el-tab-pane label="工艺" name="routing">
<quote-detail-routing ref="routing" v-if="saveVisible && activeName === 'routing'" @close="handleClose" v-model:quoteDetail="saveQuoteDetail"></quote-detail-routing>
@ -1459,7 +1587,7 @@ export default {
<quote-detail-other v-if="saveVisible && activeName === 'other'" v-model:quoteDetail="saveQuoteDetail"></quote-detail-other>
</el-tab-pane>
<el-tab-pane label="成本&价格" name="cost">
<quote-detail-cost v-if="saveVisible && activeName === 'cost'" ref="cost" v-model:quoteDetail="saveQuoteDetail"></quote-detail-cost>
<quote-detail-cost v-if="saveVisible && activeName === 'cost'" ref="cost" :bonded-draft="bomBondedDraft" @calculated="handleCostSaved" v-model:quoteDetail="saveQuoteDetail"></quote-detail-cost>
</el-tab-pane>
</el-tabs>
<div slot="footer" class="dialog-footer">

Loading…
Cancel
Save