订单异议问题修复,跟进合同优化

This commit is contained in:
朱昊天
2026-05-22 18:00:00 +08:00
parent 7686d70e59
commit 4408e80e1c
8 changed files with 265 additions and 15 deletions

View File

@@ -52,6 +52,11 @@ public class GearReturnExchange extends BaseEntity {
* 涉及金额
*/
private BigDecimal amount;
/**
* 销售员ID
*/
private Long salesmanId;
/**
* 删除标志0=正常1=已删除)
*/

View File

@@ -58,6 +58,11 @@ public class GearReturnExchangeBo extends BaseEntity {
*/
private BigDecimal amount;
/**
* 销售员ID
*/
private Long salesmanId;
//订单id
private Long orderId;

View File

@@ -41,6 +41,16 @@ public class GearReturnExchangeVo {
@ExcelProperty(value = "订单ID")
private Long orderId;
/**
* 销售员ID
*/
private Long salesmanId;
/**
* 销售员姓名
*/
private String salesmanName;
/**
* 客户ID
*/

View File

@@ -12,7 +12,11 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.gear.oa.domain.bo.GearReturnExchangeBo;
import com.gear.oa.domain.vo.GearReturnExchangeVo;
import com.gear.oa.domain.GearOrder;
import com.gear.oa.domain.GearOrderDetail;
import com.gear.oa.domain.GearReturnExchange;
import com.gear.oa.mapper.GearOrderDetailMapper;
import com.gear.oa.mapper.GearOrderMapper;
import com.gear.oa.mapper.GearReturnExchangeMapper;
import com.gear.oa.service.IGearReturnExchangeService;
@@ -31,6 +35,8 @@ import java.util.Collection;
public class GearReturnExchangeServiceImpl implements IGearReturnExchangeService {
private final GearReturnExchangeMapper baseMapper;
private final GearOrderDetailMapper orderDetailMapper;
private final GearOrderMapper orderMapper;
/**
* 查询退换货管理
@@ -103,6 +109,9 @@ public class GearReturnExchangeServiceImpl implements IGearReturnExchangeService
*/
@Override
public Boolean insertByBo(GearReturnExchangeBo bo) {
if (bo.getSalesmanId() == null) {
bo.setSalesmanId(resolveSalesmanIdByOrderDetail(bo.getOrderDetailId()));
}
GearReturnExchange add = BeanUtil.toBean(bo, GearReturnExchange.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
@@ -117,11 +126,29 @@ public class GearReturnExchangeServiceImpl implements IGearReturnExchangeService
*/
@Override
public Boolean updateByBo(GearReturnExchangeBo bo) {
if (bo.getSalesmanId() == null) {
bo.setSalesmanId(resolveSalesmanIdByOrderDetail(bo.getOrderDetailId()));
}
GearReturnExchange update = BeanUtil.toBean(bo, GearReturnExchange.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
private Long resolveSalesmanIdByOrderDetail(Long orderDetailId) {
if (orderDetailId == null) {
return null;
}
GearOrderDetail detail = orderDetailMapper.selectById(orderDetailId);
if (detail == null || detail.getOrderId() == null) {
return null;
}
GearOrder order = orderMapper.selectById(detail.getOrderId());
if (order == null) {
return null;
}
return order.getSalesmanId();
}
/**
* 保存前的数据校验
*/

View File

@@ -12,6 +12,7 @@
<result property="reason" column="reason"/>
<result property="status" column="status"/>
<result property="amount" column="amount"/>
<result property="salesmanId" column="salesman_id"/>
<result property="delFlag" column="del_flag"/>
<result property="createTime" column="create_time"/>
<result property="createBy" column="create_by"/>
@@ -28,12 +29,17 @@
gre.reason,
gre.status,
gre.amount,
(CASE
WHEN o.salesman_id IS NOT NULL THEN o.salesman_id
ELSE gre.salesman_id
END) AS salesmanId,
gre.del_flag,
gre.create_time,
gre.create_by,
gre.update_time,
gre.update_by,
god.order_id,
gs.name AS salesmanName,
gp.product_id AS productId,
gp.product_code AS productCode,
gp.product_name AS productName,
@@ -44,6 +50,8 @@
god.no_tax_price AS noTaxPrice
FROM gear_return_exchange gre
LEFT JOIN gear_order_detail god ON gre.order_detail_id = god.detail_id
LEFT JOIN gear_order o ON god.order_id = o.order_id AND o.del_flag = 0
LEFT JOIN gear_salesman gs ON o.salesman_id = gs.salesman_id AND gs.del_flag = 0
LEFT JOIN gear_product gp ON god.product_id = gp.product_id
LEFT JOIN gear_customer gc ON gre.customer_id = gc.customer_id
${ew.customSqlSegment}

View File

@@ -354,14 +354,17 @@
<el-col :span="24">
<el-form-item label="产品" prop="productId">
<!-- 仅允许选择成品product避免半成品进入发货 -->
<el-select v-model="orderDetailAddForm.productId" filterable clearable placeholder="请选择产品" style="width: 100%" @change="handleOrderDetailProductPicked">
<el-option
v-for="p in productBaseOptions"
:key="p.productId"
:label="`${p.productName}${p.spec ? ' ' + p.spec : ''}${p.model ? ' ' + p.model : ''}`"
:value="p.productId"
/>
</el-select>
<div style="display:flex; align-items:center; gap: 10px; width: 100%;">
<el-select v-model="orderDetailAddForm.productId" filterable clearable placeholder="请选择产品" style="flex: 1; min-width: 0;" @change="handleOrderDetailProductPicked">
<el-option
v-for="p in productBaseOptions"
:key="p.productId"
:label="`${p.productName}${p.spec ? ' ' + p.spec : ''}${p.model ? ' ' + p.model : ''}`"
:value="p.productId"
/>
</el-select>
<el-button plain type="primary" @click="openQuickAddProduct">新增成品</el-button>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
@@ -392,6 +395,42 @@
</template>
</el-dialog>
<el-dialog title="新增成品" v-model="productQuickAddOpen" width="680px" append-to-body>
<el-form ref="productQuickAddRef" :model="productQuickAddForm" label-width="90px">
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="成品名称" prop="productName">
<el-input v-model="productQuickAddForm.productName" placeholder="请输入成品名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="规格" prop="spec">
<el-input v-model="productQuickAddForm.spec" placeholder="规格" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="型号" prop="model">
<el-input v-model="productQuickAddForm.model" placeholder="型号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单价" prop="unitPrice">
<el-input-number v-model="productQuickAddForm.unitPrice" :controls="false" :min="0" :precision="4" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="productQuickAddForm.remark" type="textarea" :rows="3" placeholder="备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="productQuickAddOpen = false">取消</el-button>
<el-button type="primary" :loading="productQuickAddLoading" @click="submitQuickAddProduct">确定</el-button>
</template>
</el-dialog>
<el-tab-pane label="财务状态" name="finance">
<div class="mt-4 finance-section">
<div class="finance-title-row">
@@ -686,7 +725,7 @@ import Receive from './receive.vue';
import { listSalesman } from "@/api/oms/salesman";
import { listShippingOrder, addShippingOrder, delShippingOrder } from "@/api/oms/shippingOrder";
import { listOrderDetail, addOrderDetail, updateOrderDetail } from "@/api/oms/orderDetail";
import { listProductBase } from "@/api/mat/product";
import { listProductBase, addProduct } from "@/api/mat/product";
import { sumStockQuantityByItemIds } from "@/api/wms/stock";
import { listReceivable } from "@/api/finance/receivable";
import { listOrderProduction, initOrderProduction, updateOrderProduction } from "@/api/oms/orderProduction";
@@ -830,6 +869,15 @@ export default {
model: "",
remark: ""
},
productQuickAddOpen: false,
productQuickAddLoading: false,
productQuickAddForm: {
productName: "",
spec: "",
model: "",
unitPrice: 0,
remark: ""
},
productionLoading: false,
productionInitLoading: false,
productionOnlyProduct: true,
@@ -1696,6 +1744,57 @@ export default {
});
},
openQuickAddProduct() {
this.productQuickAddForm = {
productName: "",
spec: "",
model: "",
unitPrice: 0,
remark: ""
};
this.productQuickAddOpen = true;
},
submitQuickAddProduct() {
const name = this.productQuickAddForm && this.productQuickAddForm.productName != null
? String(this.productQuickAddForm.productName).trim()
: "";
if (!name) {
this.$modal.msgError("请输入成品名称");
return;
}
this.productQuickAddLoading = true;
const payload = {
productName: name,
spec: this.productQuickAddForm.spec != null ? String(this.productQuickAddForm.spec).trim() : "",
model: this.productQuickAddForm.model != null ? String(this.productQuickAddForm.model).trim() : "",
unitPrice: this.productQuickAddForm.unitPrice != null ? this.productQuickAddForm.unitPrice : 0,
remark: this.productQuickAddForm.remark != null ? String(this.productQuickAddForm.remark).trim() : "",
productType: "product"
};
addProduct(payload).then(() => {
this.$modal.msgSuccess("已新增成品");
this.productQuickAddOpen = false;
return listProductBase({ pageNum: 1, pageSize: 9999, productType: "product" });
}).then(res => {
this.productBaseOptions = (res && res.rows) ? res.rows : [];
const list = Array.isArray(this.productBaseOptions) ? this.productBaseOptions : [];
const hit = list.find(p => {
if (!p) return false;
const n = p.productName != null ? String(p.productName).trim() : "";
const s = p.spec != null ? String(p.spec).trim() : "";
const m = p.model != null ? String(p.model).trim() : "";
return n === payload.productName && s === payload.spec && m === payload.model;
});
if (hit && hit.productId != null) {
this.orderDetailAddForm.productId = hit.productId;
this.handleOrderDetailProductPicked(hit.productId);
}
}).finally(() => {
this.productQuickAddLoading = false;
});
},
/** 订单明细:选中产品后自动带出单位/单价(不涉及库存) */
handleOrderDetailProductPicked(productId) {
const list = Array.isArray(this.productBaseOptions) ? this.productBaseOptions : [];

View File

@@ -68,12 +68,32 @@
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="returnExchangeRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="订单明细" prop="orderDetailId">
<el-select v-model="form.orderDetailId" placeholder="请选择订单明细">
<el-option v-for="item in orderDetailList" :key="item.detailId" :value="item.detailId">
<ProductInfo :productId="item.productId" />
<el-select v-model="form.orderDetailId" placeholder="请选择订单明细" filterable>
<el-option
v-for="item in orderDetailList"
:key="item.detailId"
:value="item.detailId"
:label="orderDetailLabel(item)"
>
<div style="display:flex; align-items:center; justify-content:space-between; gap: 12px; width: 100%;">
<div style="flex: 1; min-width: 0;">
<div style="font-weight: 600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
{{ item.productName || '-' }}
</div>
<div style="color:#909399; font-size: 12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
{{ item.productCode || '-' }}
</div>
</div>
<div style="color:#606266; font-size: 12px; flex: none; white-space:nowrap;">
{{ (item.spec || '-') + (item.model ? (' / ' + item.model) : '') }}
</div>
</div>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="销售员">
<el-input :model-value="orderSalesmanName" disabled placeholder="-" />
</el-form-item>
<el-form-item label="客户ID" prop="customerId">
<CustomerSelect v-model="form.customerId" />
</el-form-item>
@@ -100,8 +120,8 @@
<script setup name="ReturnExchange">
import { listReturnExchange, getReturnExchange, delReturnExchange, addReturnExchange, updateReturnExchange } from "@/api/oa/returnExchange";
import { listOrderDetail } from "@/api/oms/orderDetail";
import { getOrder } from "@/api/oms/order";
import CustomerSelect from '@/components/CustomerSelect/index.vue';
import ProductInfo from '@/components/Renderer/ProductInfo.vue';
const { proxy } = getCurrentInstance();
@@ -123,6 +143,23 @@ const multiple = ref(true);
const total = ref(0);
const title = ref("");
const orderInfo = ref(null);
const orderSalesmanName = computed(() => {
const o = orderInfo.value;
if (!o) return "";
return o.salesManager || "-";
});
function orderDetailLabel(item) {
if (!item) return '-';
const name = item.productName || '-';
const code = item.productCode ? `(${item.productCode})` : '';
const spec = item.spec || '';
const model = item.model || '';
const sm = [spec, model].filter(Boolean).join('/');
return `${name}${code}${sm ? ' ' + sm : ''}`;
}
const formatterTime = (time) => {
return proxy.parseTime(time, '{y}-{m}-{d}')
}
@@ -148,9 +185,22 @@ const { queryParams, form, rules } = toRefs(data);
watch(() => props.orderId, () => {
getOrderDetailList();
fetchOrderInfo();
getList();
}, { immediate: true });
function fetchOrderInfo() {
if (!props.orderId) {
orderInfo.value = null;
return;
}
getOrder(props.orderId).then(res => {
orderInfo.value = res && res.data ? res.data : null;
}).catch(() => {
orderInfo.value = null;
});
}
/** 查询退换货管理列表 */
function getList() {
loading.value = true;
@@ -186,6 +236,7 @@ function reset() {
reason: null,
status: null,
amount: null,
salesmanId: null,
delFlag: null,
createTime: null,
createBy: null,
@@ -217,6 +268,14 @@ function handleSelectionChange(selection) {
/** 新增按钮操作 */
function handleAdd() {
reset();
if (orderInfo.value) {
if (orderInfo.value.customerId != null) {
form.value.customerId = orderInfo.value.customerId;
}
if (orderInfo.value.salesmanId != null) {
form.value.salesmanId = orderInfo.value.salesmanId;
}
}
open.value = true;
title.value = "添加退换货管理";
}
@@ -229,6 +288,9 @@ function handleUpdate(row) {
getReturnExchange(_returnExchangeId).then(response => {
loading.value = false;
form.value = response.data;
if (form.value && form.value.salesmanId == null && orderInfo.value && orderInfo.value.salesmanId != null) {
form.value.salesmanId = orderInfo.value.salesmanId;
}
open.value = true;
title.value = "修改退换货管理";
});

View File

@@ -110,7 +110,13 @@
<el-table v-loading="orderLoading" :data="orderList">
<el-table-column label="订单编号" prop="orderCode" min-width="160" />
<el-table-column label="客户" prop="customerName" min-width="200" />
<el-table-column label="状态" prop="orderStatus" width="120" />
<el-table-column label="状态" width="120" align="center">
<template #default="scope">
<el-tag :type="orderStatusTagType(scope.row.orderStatus)" size="small">
{{ orderStatusLabel(scope.row.orderStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime" width="180" />
</el-table>
@@ -240,7 +246,13 @@
<el-table-column label="发货时间" prop="shipTime" width="180" />
<el-table-column label="物流公司" prop="logisticsCompany" min-width="140" />
<el-table-column label="运单号" prop="logisticsNo" min-width="180" />
<el-table-column label="状态" prop="status" width="120" />
<el-table-column label="状态" width="120" align="center">
<template #default="scope">
<el-tag :type="shippingStatusTagType(scope.row.status)" size="small">
{{ shippingStatusLabel(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
</el-table>
</div>
@@ -633,6 +645,28 @@ export default {
return "info";
},
shippingStatusLabel(status) {
const n = Number(status);
if (Number.isFinite(n)) {
if (n === 0) return "未发货";
if (n === 1) return "已打印";
if (n === 2) return "已发货";
if (n === 3) return "已完成";
}
const t = String(status ?? "");
return t || "-";
},
shippingStatusTagType(status) {
const n = Number(status);
if (Number.isFinite(n)) {
if (n === 3) return "success";
if (n === 2) return "primary";
if (n === 1) return "warning";
}
return "info";
},
loadShippingOrders() {
if (!this.selectedSalesmanId) return;
this.shippingLoading = true;