feat(example): 优化生产订单生成功能

This commit is contained in:
zxy 2026-04-20 11:44:42 +08:00
parent cf5b0c3ca2
commit 9592f36871
9 changed files with 312 additions and 92 deletions

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import jnpf.entity.OrderDetailEntity;
import jnpf.model.order.OrderItemWithOrderPagination;
import jnpf.model.order.OrderItemWithOrderVO;
import jnpf.model.orderdetail.OrderDetailForm;
import jnpf.model.orderdetail.OrderDetailPagination;
import java.util.List;
@ -39,4 +40,6 @@ public interface OrderDetailService extends IService<OrderDetailEntity> {
List<OrderItemWithOrderVO> getOrderItemWithOrderByIds(List<String> orderIds);
String close(OrderDetailForm proOrderForm);
}

View File

@ -35,4 +35,7 @@ public interface ProOrderService extends IService<ProOrderEntity> {
ProOrderVO getInfoById(Integer id);
void generate(ExampleOrderForm proOrderForm);
String generateCheck(ExampleOrderForm proOrderForm);
}

View File

@ -7,6 +7,7 @@ import jnpf.entity.OrderDetailEntity;
import jnpf.mapper.OrderDetailMapper;
import jnpf.model.order.OrderItemWithOrderPagination;
import jnpf.model.order.OrderItemWithOrderVO;
import jnpf.model.orderdetail.OrderDetailForm;
import jnpf.model.orderdetail.OrderDetailPagination;
import jnpf.service.OrderDetailService;
import org.springframework.stereotype.Service;
@ -49,4 +50,18 @@ public class OrderDetailServiceImpl extends ServiceImpl<OrderDetailMapper, Order
public List<OrderItemWithOrderVO> getOrderItemWithOrderByIds(List<String> orderIds) {
return this.baseMapper.getOrderItemWithOrderByIds(orderIds);
}
@Override
public String close(OrderDetailForm proOrderForm) {
OrderDetailEntity detailEntity = this.getById(proOrderForm.getId());
if (detailEntity == null) {
return "该订单产品信息不存在,请刷新数据!";
}
if ("9".equals(detailEntity.getOrdItemStatus())) {
return "该订单产品信息已关闭,请刷新数据!";
}
detailEntity.setOrdItemStatus("9");
this.updateById(detailEntity);
return null;
}
}

View File

@ -108,22 +108,27 @@ public class ProOrderServiceImpl extends ServiceImpl<ProOrderMapper, ProOrderEnt
String orderNo = generaterSwapUtil.getBillNumber("scdd", false);
proOrder.setProNo(orderNo);
proOrder.setProDate(orderForm.getOrdDate());
proOrder.setProDate(orderForm.getProDate());
proOrder.setPlanBgDate(orderForm.getPlanBgDate());
proOrder.setPlanEndDate(orderForm.getPlanEndDate());
proOrder.setPlanQty(orderForm.getPlanQty());
proOrder.setMaterialCode(orderForm.getMaterialCode());
proOrder.setMaterialName(orderForm.getMaterialName());
// proOrder.setMaterialId(Integer.valueOf(orderForm.getMaterialId()));
proOrder.setSpec(orderForm.getSpec());
proOrder.setUnit(orderForm.getUnit());
proOrder.setProcessFlow(orderForm.getProcessFlow());
proOrder.setPlanStatus("0");
proOrder.setIsAllLine(orderForm.getIsAllLine());
// 物料信息从订单明细中获取
List<OrderDetailForm> orderItems = orderForm.getOrderItems();
OrderDetailForm orderDetailForm = orderItems.stream().findFirst().get();
if (orderDetailForm != null) {
OrderDetailEntity itemEntity = orderDetailService.getById(orderDetailForm.getItemId());
proOrder.setMaterialId(itemEntity.getMaterialId());
proOrder.setMaterialCode(itemEntity.getMaterialCode());
proOrder.setMaterialName(itemEntity.getMaterialName());
proOrder.setSpec(itemEntity.getSpec());
proOrder.setUnit(itemEntity.getUnit());
}
this.save(proOrder);
List<OrderDetailForm> orderItems = orderForm.getOrderItems();
for (OrderDetailForm orderItem : orderItems) {
OrderDetailEntity detailEntity = orderDetailService.getById(orderItem.getItemId());
ExampleOrderEntity orderEntity = orderService.getById(detailEntity.getSaleOrdId());
@ -161,4 +166,27 @@ public class ProOrderServiceImpl extends ServiceImpl<ProOrderMapper, ProOrderEnt
proOrderLineService.save(lineEntity);
}
}
@Override
public String generateCheck(ExampleOrderForm proOrderForm) {
// 校验订单状态
List<OrderDetailForm> orderItems = proOrderForm.getOrderItems();
for (OrderDetailForm orderItem : orderItems) {
OrderDetailEntity detailEntity = orderDetailService.getById(orderItem.getItemId());
if (detailEntity == null) {
return "选择订单不存在,请刷新页面!";
}
ExampleOrderEntity orderEntity = orderService.getById(detailEntity.getSaleOrdId());
if (orderEntity == null) {
return "选择订单不存在,请刷新页面!";
}
if (!"3".equals(orderEntity.getOrdStatus())) {
return "选择订单(" + orderEntity.getSaleOrdNo() + " )非审批状态,请刷新界面";
}
if ("2".equals(detailEntity.getOrdItemStatus()) || "9".equals(detailEntity.getOrdItemStatus())) {
return "选择订单(" + orderEntity.getSaleOrdNo() + " )已关闭或全部转产,请刷新界面";
}
}
return null;
}
}

View File

@ -11,6 +11,8 @@ import jnpf.base.vo.PageListVO;
import jnpf.base.vo.PaginationVO;
import jnpf.constant.MsgCode;
import jnpf.model.order.*;
import jnpf.model.orderdetail.OrderDetailForm;
import jnpf.service.OrderDetailService;
import jnpf.service.ProOrderService;
import jnpf.util.JsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
@ -34,6 +36,10 @@ public class ProOrderController {
@Autowired
private ProOrderService proOrderService;
@Autowired
private OrderDetailService orderDetailService;
/**
* 列表
*
@ -71,7 +77,7 @@ public class ProOrderController {
* @param proOrderForm 实体模型
* @return
*/
@Operation(summary = "生成生产订单")
@Operation(summary = "生成生产订单校验")
@SaCheckPermission("example.proOrder")
@Parameter(name = "proOrderForm", description = "实体模型", required = true)
@PostMapping("/generate")
@ -80,6 +86,32 @@ public class ProOrderController {
return ActionResult.success(MsgCode.SU001.get());
}
@Operation(summary = "生成生产订单")
@SaCheckPermission("example.proOrder")
@Parameter(name = "proOrderForm", description = "实体模型", required = true)
@PostMapping("/generateCheck")
public ActionResult<ProOrderForm> generateCheck(@RequestBody ExampleOrderForm proOrderForm) {
String message = proOrderService.generateCheck(proOrderForm);
if (ObjectUtil.isNotEmpty(message)) {
return ActionResult.fail(message);
}
return ActionResult.success(MsgCode.SU001.get());
}
// 关闭订单
@Operation(summary = "关闭生产订单")
@SaCheckPermission("example.proOrder")
@Parameter(name = "proOrderForm", description = "实体模型", required = true)
@PostMapping("/close")
public ActionResult<ProOrderForm> close(@RequestBody OrderDetailForm proOrderForm) {
String message = orderDetailService.close(proOrderForm);
if (ObjectUtil.isNotEmpty(message)) {
return ActionResult.fail(message);
}
return ActionResult.success(MsgCode.SU001.get());
}
/**
* 修改
*

View File

@ -138,21 +138,12 @@ public class ExampleOrderForm {
private BigDecimal planQty;
private String materialName;
private String materialCode;
private String materialId;
private String spec;
private String unit;
private Date proDate;
private String processFlow;
private String isAllLine;
private List<OrderDetailForm> orderItems;
private List<OrderLineForm> proLines;

View File

@ -7,10 +7,9 @@
<el-button @click="resetProLine">重置</el-button>
</div>
<el-table
<JNPF-table
ref="proLineTable"
:data="proLineList"
border
:height="300"
@selection-change="handleSelectionChange"
>
@ -18,24 +17,19 @@
<el-table-column prop="proLineCd" label="产线编码" align="center" />
<el-table-column prop="proLineName" label="产线名称" align="center" />
<el-table-column prop="remark" label="备注" align="center" />
</el-table>
</JNPF-table>
<div class="pagination-container">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="page.currentPage"
:page-sizes="[10, 20, 50]"
:page-size="page.pageSize"
:total="page.total"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
<pagination
:total="page.total"
:page.sync="page.currentPage"
:limit.sync="page.pageSize"
@pagination="loadProLineList"
/>
</div>
<template slot="footer" class="dialog-footer">
<el-button type="primary" @click="confirmSelection"> </el-button>
<el-button @click="visible = false"> </el-button>
<el-button @click="handleClose"> </el-button>
</template>
</el-dialog>
</template>
@ -126,15 +120,7 @@ export default {
this.loadProLineList();
},
handleSizeChange(val) {
this.page.pageSize = val;
this.loadProLineList();
},
handleCurrentChange(val) {
this.page.currentPage = val;
this.loadProLineList();
},
handleSelectionChange(val) {
this.selectedLines = val;
@ -156,6 +142,11 @@ export default {
this.$emit("confirm", selectedData);
this.$emit("update:visible", false);
this.selectedLines = [];
},
handleClose() {
this.$emit("update:visible", false);
this.selectedLines = [];
}
}
};
@ -177,11 +168,7 @@ export default {
width: 200px;
}
.pagination-container {
display: flex;
justify-content: center;
margin-top: 16px;
}
.dialog-footer {
display: flex;

View File

@ -1,11 +1,11 @@
<template>
<div class="generate-order-container">
<div class="page-header">
<!-- <div class="page-header">
<el-button @click="goBack" class="back-btn">
<i class="el-icon-arrow-left"></i> 返回
</el-button>
<h2 class="page-title">生成订单</h2>
</div>
</div> -->
<div class="main-content">
<div class="section">
@ -13,7 +13,7 @@
<span class="section-title">基础信息</span>
</div>
<div class="section-body">
<el-form :model="baseForm" label-width="120px" class="base-form">
<el-form ref="formRef" :model="baseForm" :rules="dataRule" label-width="120px" class="base-form">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="订单编号" prop="orderNo">
@ -182,13 +182,21 @@
</el-button>
</div>
<div class="section-body">
<div v-if="!allocateQtyMatch && lineList.length > 0" style="color: #e6a23c; margin-bottom: 10px; font-size: 14px;">
<i class="el-icon-warning"></i> 产线分配数量不等于计划数量请确认
</div>
<el-table :data="lineList" border :height="200">
<el-table-column prop="index" label="序号" align="center" type="index" />
<el-table-column prop="lineCode" label="产线编码" align="center" />
<el-table-column prop="lineName" label="产线名称" align="center" />
<el-table-column prop="allocateQty" label="分配数量(件)" align="center">
<el-table-column prop="allocateQty" label="分配数量*" align="center">
<template slot-scope="scope">
<el-input v-model.number="scope.row.allocateQty" placeholder="分配数量" />
<el-input
v-model="scope.row.allocateQty"
placeholder="分配数量"
style="width: 120px;"
@input="handleAllocateQtyInput(scope.row)"
/>
</template>
</el-table-column>
<el-table-column prop="completedQty" label="已完成数量" align="center">
@ -251,7 +259,7 @@ export default {
lineModalVisible: false,
baseForm: {
orderNo: "",
proDate: new Date(),
proDate: "",
orderStatus: "0",
materialName: "",
spec: "",
@ -268,7 +276,32 @@ export default {
orderLoading: false,
selectedLineCodes: [],
processFlowList: [],
processFlowLoading: false
processFlowLoading: false,
allocateQtyMatch: true,
dataRule: {
proDate: [
{required: true, message: "请选择订单日期", trigger: "change"}
],
orderStatus: [
{required: true, message: "请选择订单状态", trigger: "change"}
],
materialName: [
{required: true, message: "请输入产品名称", trigger: "blur"}
],
spec: [
{required: true, message: "请输入规格型号", trigger: "blur"}
],
planBgDate: [
{required: true, message: "请选择开始日期", trigger: "change"}
],
planEndDate: [
{required: true, message: "请选择结束日期", trigger: "change"}
],
processFlow: [
{required: true, message: "请选择工艺流程", trigger: "change"}
]
}
};
},
computed: {
@ -356,7 +389,7 @@ export default {
handlePlanQtyInput(row) {
if (row.planQty > row.remainingQty) {
row.planQty = row.remainingQty;
this.$message.warning('转生产订单数量大于生产数量,请确认!');
this.$message.warning('转生产订单数量大于剩余数量,请确认!');
}
if (row.planQty < 0) {
row.planQty = 0;
@ -370,6 +403,35 @@ export default {
this.baseForm.planQty = total;
},
handleAllocateQtyInput(row) {
let value = row.allocateQty;
if (!value) return;
value = value.toString().replace(/[^\d.]/g, '');
value = value.replace(/\.{2,}/g, '.');
value = value.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.');
const parts = value.split('.');
if (parts.length > 2) {
value = parts[0] + '.' + parts[1].slice(0, 2);
} else if (parts.length === 2 && parts[1].length > 2) {
value = parts[0] + '.' + parts[1].slice(0, 2);
}
if (value.startsWith('.')) {
value = '0' + value;
}
row.allocateQty = value;
this.checkAllocateQtyTotal();
},
checkAllocateQtyTotal() {
const totalPlanQty = this.orderList.reduce((sum, item) => sum + Number(item.planQty || 0), 0);
const totalAllocateQty = this.lineList.reduce((sum, item) => sum + Number(item.allocateQty || 0), 0);
if (totalPlanQty !== totalAllocateQty && totalAllocateQty > 0) {
this.allocateQtyMatch = false;
} else {
this.allocateQtyMatch = true;
}
},
getProStatusText(status) {
const map = { '0': '正常', '1': '部分转生产', '2': '全部转生产' };
return map[status] || status;
@ -395,6 +457,38 @@ export default {
handleisAllLineChange(val) {
this.baseForm.isAllLine = val ? "1" : "0";
if (val) {
this.lineList = [];
request({
url: "/api/example/proLine/getSelectList",
method: "get"
}).then(res => {
if (res.code === 200) {
const lines = res.data || [];
if (lines.length > 10) {
this.$message.warning("产线数量超过10条请手动选择部分产线");
this.baseForm.isAllLine = "0";
return;
}
lines.forEach(line => {
this.lineList.push({
id: line.id,
lineCode: line.lineCode,
lineName: line.lineName,
allocateQty: null,
completedQty: null,
planStartDate: "",
planEndDate: "",
remark: ""
});
});
this.selectedLineCodes = this.lineList.map(item => item.lineCode);
}
});
} else {
this.lineList = [];
this.selectedLineCodes = [];
}
},
handleProLineConfirm(selectedLines) {
@ -402,14 +496,14 @@ export default {
const exists = this.lineList.some(item => item.lineCode === line.lineCode);
if (!exists) {
this.lineList.push({
id: line.id,
id: line.lineId,
lineCode: line.lineCode,
lineName: line.lineName,
allocateQty: null,
completedQty: null,
planStartDate: "",
planEndDate: "",
remark: ""
remark: line.remark || ""
});
}
});
@ -427,35 +521,57 @@ export default {
},
save() {
if (!this.validateProLine()) return;
if (!this.validateOrderPlanQty()) return;
const submitData = this.prepareSubmitData();
submitData.orderStatus = "0";
request({
url: "/api/example/proOrder/generate",
method: "post",
data: submitData
}).then(res => {
if (res.code === 200) {
this.$message.success("保存成功");
this.goBack();
this.$refs.formRef.validate((valid) => {
if (valid) {
if (!this.validateOrderPlanQty()) return;
if (this.lineList.length > 0 && !this.validateAllocateQty()) return;
const submitData = this.prepareSubmitData();
submitData.orderStatus = "0";
this.generateCheck(submitData).then(() => {
this.doGenerate(submitData);
}).catch(() => {
return;
});
}
});
},
submit() {
if (!this.validateProLine()) return;
if (!this.validateOrderPlanQty()) return;
if (!this.validateAllocateQty()) return;
const submitData = this.prepareSubmitData();
submitData.orderStatus = "1";
if (!this.lineList || this.lineList.length === 0) {
this.$message.warning("生产订单下发必须指定产线,请确认!");
return;
}
},
generateCheck(submitData) {
return new Promise((resolve, reject) => {
request({
url: "/api/example/proOrder/generateCheck",
method: "post",
data: submitData
}).then(res => {
if (res.code === 200) {
resolve();
} else {
this.$message.error(res.msg || "检查失败");
reject(res.msg);
}
}).catch(err => {
this.$message.error("检查接口调用失败");
reject(err);
});
});
},
doGenerate(submitData) {
request({
url: "/api/example/proOrder/generate",
method: "post",
data: submitData
}).then(res => {
if (res.code === 200) {
this.$message.success("下发成功");
const message = submitData.orderStatus === "0" ? "保存成功" : "下发成功";
this.$message.success(message);
this.goBack();
}
});
@ -484,21 +600,42 @@ export default {
},
validateAllocateQty() {
const totalPlanQty = this.orderList.reduce((sum, item) => sum + Number(item.planQty || 0), 0);
const totalAllocateQty = this.lineList.reduce((sum, item) => sum + Number(item.allocateQty || 0), 0);
if (this.lineList.length === 0) {
return true;
}
if (totalPlanQty !== totalAllocateQty) {
this.$message.warning("产线分配数量不等于计划数量,请确认!");
let hasValidAllocateQty = false;
for (let item of this.lineList) {
const allocateQty = Number(item.allocateQty);
if (!isNaN(allocateQty) && allocateQty > 0) {
hasValidAllocateQty = true;
break;
}
}
if (!hasValidAllocateQty) {
this.$message.error("至少需要有一条产线设置有效的分配数量");
return false;
}
for (let item of this.lineList) {
if (!item.allocateQty || item.allocateQty <= 0) {
this.$message.error("产线分配数量不能为空且必须大于0");
const allocateQty = Number(item.allocateQty);
if (!isNaN(allocateQty) && allocateQty > 0) {
continue;
} else if (item.allocateQty !== null && item.allocateQty !== undefined && item.allocateQty !== '') {
this.$message.error("产线分配数量必须大于0");
return false;
}
}
const totalPlanQty = this.orderList.reduce((sum, item) => sum + Number(item.planQty || 0), 0);
const totalAllocateQty = this.lineList.reduce((sum, item) => sum + Number(item.allocateQty || 0), 0);
if (Math.abs(totalPlanQty - totalAllocateQty) > 0.0001) {
this.$message.warning("产线分配数量不等于计划数量,请确认!");
return false;
}
return true;
},
@ -522,14 +659,10 @@ export default {
return {
...this.baseForm,
planBgDate: this.baseForm.startDate,
planEndDate: this.baseForm.endDate,
planBgDate: this.baseForm.planBgDate,
planEndDate: this.baseForm.planEndDate,
processFlow: this.baseForm.processFlow,
materialName: firstItem.materialName || '',
materialCode: firstItem.materialCode || '',
spec: firstItem.spec || '',
unit: firstItem.unit || '',
materialId: firstItem.materialId || firstItem.id || '',
proDate: this.baseForm.proDate ? new Date(this.baseForm.proDate).toISOString() : '',
orderItems: orderItems,
proLines: proLines
};
@ -568,6 +701,7 @@ export default {
.main-content {
flex: 1;
padding: 24px;
padding-bottom: 80px;
overflow-y: auto;
}
@ -607,12 +741,17 @@ export default {
}
.page-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 16px 24px;
display: flex;
justify-content: flex-end;
gap: 16px;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
z-index: 100;
}
.page-footer el-button {

View File

@ -125,7 +125,7 @@
</el-table-column>
<el-table-column label="操作" fixed="right" align="center" width="150">
<template slot-scope="scope">
<!-- <el-button type="text" @click="handleDetail(scope.row)">详情</el-button> -->
<el-button type="text" @click="handleClose(scope.row)"> </el-button>
</template>
</el-table-column>
</JNPF-table>
@ -264,7 +264,7 @@ export default {
generateOrder() {
const selectedRows = this.selectedRows;
if (!selectedRows || selectedRows.length === 0) {
this.$message.warning("请选择要生成订单的记录");
this.$message.warning("请选择需要转生产的销售订单信息,请确认!");
return;
}
@ -272,12 +272,12 @@ export default {
const specs = [...new Set(selectedRows.map(row => row.spec))];
if (materialNames.length > 1) {
this.$message.error("只能选择同一产品名称的记录生成订单");
this.$message.error("必须选择产品名称和规格型号一致的产品,请确认!");
return;
}
if (specs.length > 1) {
this.$message.error("只能选择同一规格型号的记录生成订单");
this.$message.error("必须选择产品名称和规格型号一致的产品,请确认!");
return;
}
@ -305,6 +305,28 @@ export default {
getLabel(type, value) {
return getLabel(type, value);
},
handleClose(row) {
this.$confirm('确定要关闭该订单吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
request({
url: "/api/example/proOrder/close",
method: "post",
data: { id: row.itemId }
}).then(res => {
if (res.code === 200) {
this.$message.success("关闭成功");
this.initData();
}
}).catch(() => {
this.$message.error("关闭失败");
});
}).catch(() => {
this.$message.info('已取消关闭');
});
},
},
};
</script>