初始代码

This commit is contained in:
hhh
2026-04-02 10:38:23 +08:00
parent d8b4140f50
commit aed67ce1fd
1937 changed files with 447678 additions and 1 deletions

View File

@@ -0,0 +1,655 @@
# 生产流程新增成品入库流程
## 需求概述
在销售订单页面新增"生产入库"功能,实现从生产完成到成品入库的流程闭环。
## 业务流程
### 当前流程
```
待生产(A) → 生产中(B) → 生产完成(F) → 已发货(C)
```
### 新增流程
```
待生产(A) → 生产中(B) → 生产完成(F) → 已入库(G) → 部分发货(E)/已发货(C)
```
## 功能详细设计
### 1. 数据字典修改
#### 1.1 新增销售订单状态
`sys_dict_data` 表中新增状态:
| 字段 | 值 |
|------|-----|
| dict_label | 已入库 |
| dict_value | G |
| dict_type | salorder_status |
| dict_sort | 5 |
| css_class | success |
| remark | 生产完成后已入库,待发货 |
**SQL语句**
```sql
-- 注意ID 需要根据实际数据库中的最大ID来设置这里假设使用 206 之后的ID
INSERT INTO `sys_dict_data`
VALUES (NULL, 7, '已入库', 'G', 'salorder_status', NULL, 'success', 'N', '0',
'admin', NOW(), '', NULL, '生产完成后已入库,待发货');
```
**说明:**
- 当前数据库中已有状态A(待生产)、B(生产中)、C(已发货)、D(已关闭)、E(部分发货)、F(生产完成)
- 新增 G(已入库) 状态dict_sort 设为 7在F之后
### 2. 前端页面修改
#### 2.1 销售订单列表页面 (`index.vue`)
**新增按钮位置:**
在"销售出库"按钮后面添加"生产入库"按钮
```vue
<el-col :span="1.5">
<el-button
plain
style="color: #0E7C7B; background-color: #A7E8BD"
icon="el-icon-download"
size="mini"
@click="openManufactureIntoForm()"
:disabled="multiple"
>生产入库</el-button>
</el-col>
```
**按钮启用条件:**
- 必须选中至少一条订单明细
- 选中的订单明细状态必须为 `F`(生产完成)
#### 2.2 生产入库弹窗设计
**弹窗标题:** 生产入库
**表单字段:**
| 字段名 | 字段说明 | 数据来源 | 是否必填 | 备注 |
|--------|----------|----------|----------|------|
| 入库单编号 | number | 自动生成 | 是 | 禁用输入 |
| 入库日期 | intoDate | 默认当前日期 | 是 | 可修改 |
| 交货人 | delivererId | 手动选择 | 是 | 下拉选择 |
| 仓库 | warehouseId | 手动选择 | 是 | 下拉选择 |
| 备注 | remark | 手动输入 | 否 | 文本域 |
**明细表格字段:**
| 字段名 | 字段说明 | 数据来源 | 是否必填 | 备注 |
|--------|----------|----------|----------|------|
| 序号 | sort | 自动生成 | 是 | 禁用 |
| 产品名 | materialName | 销售订单 | 是 | 禁用 |
| 产品编号 | materialNumber | 销售订单 | 是 | 禁用 |
| 规格型号 | specification | 销售订单 | 否 | 可修改 |
| 单位 | materialUnitName | 销售订单 | 是 | 禁用 |
| 数量 | quantity | 销售订单 | 是 | 可修改 |
| 批次编号 | batchNumber | 手动输入 | 否 | 可输入 |
| 生产日期 | manufactureDate | 默认当前日期 | 否 | 可修改 |
### 3. 后端接口设计
#### 3.1 新增接口
**接口路径:** `/sale/saleOrder/createManufactureInto`
**请求方法:** POST
**请求参数:**
```json
{
"saleOrderEntryIds": [1, 2, 3], // 销售订单明细ID数组
"manufactureInto": {
"intoDate": "2025-11-21",
"delivererId": 1,
"delivererName": "张三",
"warehouseId": 1,
"warehouseNumber": "WH001",
"warehouseName": "成品仓",
"remark": "备注信息"
}
}
```
**返回结果:**
```json
{
"code": 200,
"msg": "生产入库成功",
"data": {
"manufactureIntoId": 123,
"number": "MI202511210001"
}
}
```
#### 3.2 业务逻辑
1. **数据验证**
- 验证所有订单明细状态必须为 `F`(生产完成)
- 验证仓库、交货人等必填字段
2. **创建完工入库单**
- 生成入库单编号
- 创建入库单主表记录
- 根据销售订单明细创建入库单明细
3. **更新销售订单状态**
- 将选中的销售订单明细状态从 `F` 更新为 `G`(已入库)
- 检查主表所有明细状态,如果全部为 `G`,则更新主表状态为 `G`
4. **库存处理**
- 调用库存模块接口,增加成品库存
### 4. 状态流转规则
#### 4.1 允许的状态流转
```
A(待生产) → B(生产中) [工序排产]
B(生产中) → F(生产完成) [工单完成]
F(生产完成) → G(已入库) [生产入库] ✨新增
G(已入库) → E(部分发货) [销售出库-部分]
G(已入库) → C(已发货) [销售出库-全部]
E(部分发货) → C(已发货) [销售出库-剩余]
任意状态 → D(已关闭) [手动关闭]
```
#### 4.2 状态验证规则
- **生产入库按钮**:只能对状态为 `F` 的订单明细操作
- **销售出库按钮**:只能对状态为 `G` 的订单明细操作(需修改原有逻辑)
- **发货完成按钮**:只能对状态为 `G``E` 的订单明细操作
### 5. 前端方法实现
#### 5.1 打开生产入库弹窗
```javascript
openManufactureIntoForm() {
// 验证选中的订单明细状态
const selectedEntries = this.saleOrderList.filter(item =>
this.entryIds.includes(item.id)
);
// 检查是否都是生产完成状态
const hasInvalidStatus = selectedEntries.some(item => item.status !== 'F');
if (hasInvalidStatus) {
this.$modal.msgWarning("只能对生产完成(F)状态的订单进行入库操作!");
return;
}
// 跳转到生产入库页面携带订单明细ID
this.$router.push({
path: "/mes/manufactureInto-add/index",
query: { saleOrderEntryIds: this.entryIds.join(',') }
});
}
```
#### 5.2 完工入库单页面自动填充
`manufactureInto/form.vue``created()` 方法中:
```javascript
created() {
// 检查是否从销售订单跳转过来
const saleOrderEntryIds = this.$route.query.saleOrderEntryIds;
if (saleOrderEntryIds) {
this.loadFromSaleOrder(saleOrderEntryIds);
}
// 原有逻辑...
}
async loadFromSaleOrder(entryIds) {
try {
// 调用接口获取销售订单明细
const { listEntryByIds } = await import("@/api/mes/sale/saleOrder");
const response = await listEntryByIds({ ids: entryIds });
const saleOrderEntries = response.data;
// 自动填充明细数据
this.form.manufactureIntoEntryList = saleOrderEntries.map((entry, index) => ({
sort: index + 1,
materialId: entry.materialId,
materialName: entry.materialName,
materialNumber: entry.materialNumber,
specification: entry.materialSpecification,
materialUnitId: entry.unitId,
materialUnitName: entry.unitName,
quantity: entry.quantity,
batchNumber: '', // 需要手动填写
manufactureDate: new Date(), // 默认当前日期
saleOrderEntryId: entry.id // 保存关联关系
}));
this.maxIndex = saleOrderEntries.length;
} catch (error) {
console.error('加载销售订单数据失败:', error);
this.$modal.msgError("加载订单数据失败:" + error.message);
}
}
```
### 6. 后端实现要点
#### 6.1 Service层新增方法
**接口定义:** `ISalOrderService.java`
```java
/**
* 从销售订单创建生产入库单
*
* @param entryIds 销售订单明细ID数组
* @param manufactureInto 入库单信息
* @return 结果
*/
AjaxResult createManufactureIntoFromSaleOrder(String entryIds, ManufactureInto manufactureInto);
```
**实现逻辑:** `SalOrderServiceImpl.java`
```java
@Override
@Transactional
public AjaxResult createManufactureIntoFromSaleOrder(String entryIds, ManufactureInto manufactureInto) {
// 1. 查询销售订单明细
String[] ids = entryIds.split(",");
List<SalOrderEntry> entries = salOrderEntryMapper.selectByIds(ids);
// 2. 验证状态
for (SalOrderEntry entry : entries) {
if (!"F".equals(entry.getStatus())) {
throw new ServiceException("订单明细【" + entry.getMaterialName() + "】状态不是生产完成,无法入库");
}
}
// 3. 创建入库单
manufactureIntoService.insertManufactureInto(manufactureInto);
// 4. 更新销售订单状态为已入库(G)
for (String id : ids) {
SalOrderEntry entry = new SalOrderEntry();
entry.setId(Long.parseLong(id));
entry.setStatus("G");
salOrderEntryMapper.updateSalOrderEntry(entry);
}
// 5. 更新主表状态
updateMainOrderStatus(entries);
return AjaxResult.success("生产入库成功", manufactureInto);
}
```
#### 6.2 Controller层新增接口
**文件:** `SalOrderController.java`
```java
/**
* 从销售订单创建生产入库单
*/
@PreAuthorize("@ss.hasPermi('sale:saleOrder:manufactureInto')")
@Log(title = "销售订单-生产入库", businessType = BusinessType.INSERT)
@PostMapping("/createManufactureInto")
public AjaxResult createManufactureInto(@RequestBody Map<String, Object> params) {
String entryIds = (String) params.get("saleOrderEntryIds");
ManufactureInto manufactureInto = JSON.parseObject(
JSON.toJSONString(params.get("manufactureInto")),
ManufactureInto.class
);
return salOrderService.createManufactureIntoFromSaleOrder(entryIds, manufactureInto);
}
```
### 7. 数据库修改
#### 7.1 完工入库单明细表添加关联字段
```sql
ALTER TABLE `wm_manufacture_into_entry`
ADD COLUMN `sale_order_entry_id` BIGINT(20) NULL COMMENT '销售订单明细ID' AFTER `manufacture_date`;
ALTER TABLE `wm_manufacture_into_entry`
ADD INDEX `idx_sale_order_entry_id` (`sale_order_entry_id`);
```
### 8. 权限配置
#### 8.1 新增权限标识
在系统管理 → 菜单管理中,为销售订单菜单添加按钮权限:
| 权限名称 | 权限标识 | 备注 |
|---------|---------|------|
| 生产入库 | sale:saleOrder:manufactureInto | 销售订单-生产入库按钮 |
### 9. 测试用例
#### 9.1 正常流程测试
1. 创建销售订单
2. 进行工序排产,生成工单
3. 完成工单报工,订单状态变为 `F`
4. 选中状态为 `F` 的订单,点击"生产入库"
5. 验证弹窗数据自动填充正确
6. 填写必填项(交货人、仓库)
7. 提交入库单
8. 验证订单状态变为 `G`
9. 验证库存增加
10. 进行销售出库
11. 验证订单状态变为 `C``E`
#### 9.2 异常流程测试
1. **状态验证**:选中非 `F` 状态的订单,点击生产入库,应提示错误
2. **必填项验证**:不填交货人或仓库,应提示错误
3. **并发测试**:同一订单同时进行入库操作,应避免重复入库
4. **回滚测试**:入库失败时,订单状态不应变更
### 10. 注意事项
1. **状态流转限制**
- 必须严格按照 F → G → E/C 的顺序流转
- 不允许跳过 G 状态直接从 F 到 C
2. **库存处理**
- 生产入库时增加成品库存
- 销售出库时减少成品库存
- 需要考虑库存不足的情况
3. **数据一致性**
- 入库单与销售订单的关联关系必须准确
- 状态更新需要事务保证
4. **用户体验**
- 自动填充数据减少手工录入
- 明确的状态提示和错误信息
- 按钮的启用/禁用状态要准确
### 11. 实施步骤
1.**数据库修改**
- ✅ 添加字典数据 (已创建SQL文件)
- ✅ 修改入库单明细表结构 (已创建SQL文件)
2.**后端开发**
- ✅ Service层方法实现
- ✅ 状态流转逻辑
- ✅ 实体类字段添加
3.**前端开发**
- ✅ 销售订单页面添加按钮
- ✅ 完工入库单页面自动填充逻辑
- ✅ 销售出库状态验证更新
4.**测试验证**
- ⏳ 数据库脚本执行
- ⏳ 功能测试
- ⏳ 用户验收测试
5.**上线部署**
- ⏳ 数据库脚本执行
- ⏳ 代码部署
- ⏳ 权限配置
---
## 已完成工作 (2025-11-21)
### 1. 数据库脚本 ✅
#### 1.1 字典数据
**文件:** `2025-11-21_02_周启威_字典新增销售订单状态.sql`
```sql
INSERT INTO `sys_dict_data`
VALUES (NULL, 7, '已入库', 'G', 'salorder_status', NULL, 'success', 'N', '0',
'admin', NOW(), '', NULL, '生产完成后已入库,待发货');
```
#### 1.2 表结构修改
**文件:** `2025-11-21_03_周启威_入库单明细表添加销售订单关联字段.sql`
```sql
ALTER TABLE `wm_manufacture_into_entry`
ADD COLUMN `sale_order_entry_id` BIGINT(20) NULL COMMENT '销售订单明细ID' AFTER `manufacture_date`;
ALTER TABLE `wm_manufacture_into_entry`
ADD INDEX `idx_sale_order_entry_id` (`sale_order_entry_id`);
```
### 2. 后端实现 ✅
#### 2.1 实体类修改
**文件:** `ManufactureIntoEntry.java`
新增字段:
```java
/** 销售订单明细ID */
@Excel(name = "销售订单明细ID")
private Long saleOrderEntryId;
```
#### 2.2 Service层实现
**文件:** `ManufactureIntoServiceImpl.java`
**修改内容:**
1. 注入 `SalOrderEntryMapper`
2.`insertManufactureInto()` 方法中添加状态更新调用
3. 新增 `updateSaleOrderStatus()` 方法
**核心代码:**
```java
/**
* 更新销售订单状态为已入库(G)
*/
private void updateSaleOrderStatus(ManufactureInto manufactureInto) {
List<ManufactureIntoEntry> entryList = manufactureInto.getManufactureIntoEntryList();
if (StringUtils.isNotNull(entryList)) {
for (ManufactureIntoEntry entry : entryList) {
if (entry.getSaleOrderEntryId() != null) {
SalOrderEntry salOrderEntry = new SalOrderEntry();
salOrderEntry.setId(entry.getSaleOrderEntryId());
salOrderEntry.setStatus("G"); // G=已入库
salOrderEntryMapper.updateById(salOrderEntry);
}
}
}
}
```
### 3. 前端实现 ✅
#### 3.1 销售订单列表页面
**文件:** `mes-ui/src/views/mes/sale/saleOrder/index.vue`
**修改内容:**
1. **新增"生产入库"按钮**
```vue
<el-col :span="1.5">
<el-button
plain
style="color: #0E7C7B; background-color: #A7E8BD"
icon="el-icon-download"
size="mini"
@click="openManufactureIntoForm()"
:disabled="multiple"
>生产入库</el-button>
</el-col>
```
2. **新增 `openManufactureIntoForm()` 方法**
```javascript
openManufactureIntoForm(){
const selectedEntries = this.saleOrderList.filter(item =>
this.entryIds.includes(item.id)
);
const hasInvalidStatus = selectedEntries.some(item => item.status !== 'F');
if (hasInvalidStatus) {
this.$modal.msgWarning("只能对生产完成(F)状态的订单进行入库操作!");
return;
}
this.$router.push({
path: "/wm/manufactureInto-add/index",
query: { saleOrderEntryIds: this.entryIds.join(',') }
});
}
```
3. **修改 `openSalOutForm()` 方法**
```javascript
// 必须是G(已入库)或E(部分发货)状态才能发货
if(saleOrder[0].status !="G" && saleOrder[0].status !="E"){
this.$modal.msgWarning("只能对已入库(G)或部分发货(E)状态的订单进行发货操作!请重新勾选!")
return;
}
```
#### 3.2 完工入库单表单页面
**文件:** `mes-ui/src/views/mes/warehouse/manufactureInto/form.vue`
**修改内容:**
1. **修改 `created()` 方法**
```javascript
created() {
this.formType=false;
this.getUserList();
this.getWarehouseList();
// 检查是否从销售订单跳转过来
const saleOrderEntryIds = this.$route.query.saleOrderEntryIds;
if (saleOrderEntryIds) {
this.loadFromSaleOrder(saleOrderEntryIds);
}
const id = this.$route.params && this.$route.params.id;
if ( typeof(id) !='undefined' ){
this.getForm(id);
}
}
```
2. **新增 `loadFromSaleOrder()` 方法**
```javascript
async loadFromSaleOrder(entryIds) {
try {
this.$modal.loading("正在加载订单数据...");
const { listEntryByIds } = await import("@/api/mes/sale/saleOrder");
const response = await listEntryByIds({ ids: entryIds });
const saleOrderEntries = response.data;
if (!saleOrderEntries || saleOrderEntries.length === 0) {
this.$modal.msgError("未找到销售订单数据");
return;
}
this.form.manufactureIntoEntryList = saleOrderEntries.map((entry, index) => ({
sort: index + 1,
materialId: entry.materialId,
materialName: entry.materialName,
materialNumber: entry.materialNumber,
specification: entry.materialSpecification,
materialUnitId: entry.unitId,
materialUnitName: entry.unitName,
quantity: entry.quantity,
batchNumber: '',
manufactureDate: new Date().format("yyyy-MM-dd"),
saleOrderEntryId: entry.id
}));
this.maxIndex = saleOrderEntries.length;
this.$modal.closeLoading();
this.$modal.msgSuccess(`已自动填充 ${saleOrderEntries.length} 条订单明细数据`);
} catch (error) {
this.$modal.closeLoading();
console.error('加载销售订单数据失败:', error);
this.$modal.msgError("加载订单数据失败:" + (error.message || "未知错误"));
}
}
```
---
## 测试要点
### 功能测试清单
1.**生产入库按钮**
- 按钮位置正确(工序排产和销售出库之间)
- 按钮样式正确(绿色系)
- 未选中订单时按钮禁用
2.**状态验证**
- 选中非F状态订单提示错误
- 选中F状态订单正常跳转
3.**数据自动填充**
- 明细数据自动填充
- 字段映射正确
- 关联ID保存正确
4.**状态流转**
- 入库后订单状态变为G
- 销售出库只能选G或E状态
- 状态显示正确
### 待测试项
- [ ] 执行数据库脚本
- [ ] 完整流程测试F → G → E/C
- [ ] 并发测试
- [ ] 异常回滚测试
---
## 附录
### A. 相关文件清单
**前端文件:**
- `mes-ui/src/views/mes/sale/saleOrder/index.vue` - 销售订单列表页
- `mes-ui/src/views/mes/warehouse/manufactureInto/form.vue` - 完工入库单表单
- `mes-ui/src/api/mes/sale/saleOrder.js` - 销售订单API
**后端文件:**
- `yjh-mes/src/main/java/cn/sourceplan/sale/service/ISalOrderService.java` - Service接口
- `yjh-mes/src/main/java/cn/sourceplan/sale/service/impl/SalOrderServiceImpl.java` - Service实现
- `yjh-mes/src/main/java/cn/sourceplan/sale/controller/SalOrderController.java` - Controller
**数据库文件:**
- `.sql/upgrade_add_manufacture_into_status.sql` - 升级脚本
### B. 状态码对照表
| 状态码 | 状态名称 | 颜色 | 说明 |
|-------|---------|------|------|
| A | 待生产 | info (灰色) | 订单已创建,待排产 |
| B | 生产中 | warning (橙色) | 已排产,生产中 |
| F | 生产完成 | success (绿色) | 工单已完成,待入库 |
| G | 已入库 | success (绿色) | 已完成入库,待发货 ✨新增 |
| E | 部分发货 | primary (蓝色) | 已部分发货 |
| C | 已发货 | success (绿色) | 已全部发货 |
| D | 已关闭 | danger (红色) | 订单已关闭 |
---
**文档版本:** v1.0
**创建日期:** 2025-11-21
**创建人:** Cascade AI
**最后更新:** 2025-11-21