生产环节优化
This commit is contained in:
67
.agents/skills/karpathy-guidelines/SKILL.md
Normal file
67
.agents/skills/karpathy-guidelines/SKILL.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
name: karpathy-guidelines
|
||||||
|
description: Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria.
|
||||||
|
license: MIT
|
||||||
|
---
|
||||||
|
|
||||||
|
# Karpathy Guidelines
|
||||||
|
|
||||||
|
Behavioral guidelines to reduce common LLM coding mistakes, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls.
|
||||||
|
|
||||||
|
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
||||||
|
|
||||||
|
## 1. Think Before Coding
|
||||||
|
|
||||||
|
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
||||||
|
|
||||||
|
Before implementing:
|
||||||
|
- State your assumptions explicitly. If uncertain, ask.
|
||||||
|
- If multiple interpretations exist, present them - don't pick silently.
|
||||||
|
- If a simpler approach exists, say so. Push back when warranted.
|
||||||
|
- If something is unclear, stop. Name what's confusing. Ask.
|
||||||
|
|
||||||
|
## 2. Simplicity First
|
||||||
|
|
||||||
|
**Minimum code that solves the problem. Nothing speculative.**
|
||||||
|
|
||||||
|
- No features beyond what was asked.
|
||||||
|
- No abstractions for single-use code.
|
||||||
|
- No "flexibility" or "configurability" that wasn't requested.
|
||||||
|
- No error handling for impossible scenarios.
|
||||||
|
- If you write 200 lines and it could be 50, rewrite it.
|
||||||
|
|
||||||
|
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
||||||
|
|
||||||
|
## 3. Surgical Changes
|
||||||
|
|
||||||
|
**Touch only what you must. Clean up only your own mess.**
|
||||||
|
|
||||||
|
When editing existing code:
|
||||||
|
- Don't "improve" adjacent code, comments, or formatting.
|
||||||
|
- Don't refactor things that aren't broken.
|
||||||
|
- Match existing style, even if you'd do it differently.
|
||||||
|
- If you notice unrelated dead code, mention it - don't delete it.
|
||||||
|
|
||||||
|
When your changes create orphans:
|
||||||
|
- Remove imports/variables/functions that YOUR changes made unused.
|
||||||
|
- Don't remove pre-existing dead code unless asked.
|
||||||
|
|
||||||
|
The test: Every changed line should trace directly to the user's request.
|
||||||
|
|
||||||
|
## 4. Goal-Driven Execution
|
||||||
|
|
||||||
|
**Define success criteria. Loop until verified.**
|
||||||
|
|
||||||
|
Transform tasks into verifiable goals:
|
||||||
|
- "Add validation" → "Write tests for invalid inputs, then make them pass"
|
||||||
|
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
|
||||||
|
- "Refactor X" → "Ensure tests pass before and after"
|
||||||
|
|
||||||
|
For multi-step tasks, state a brief plan:
|
||||||
|
```
|
||||||
|
1. [Step] → verify: [check]
|
||||||
|
2. [Step] → verify: [check]
|
||||||
|
3. [Step] → verify: [check]
|
||||||
|
```
|
||||||
|
|
||||||
|
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
|
||||||
79
jeecg-boot/db/mes-xsl-mixing-production-plan-menu.sql
Normal file
79
jeecg-boot/db/mes-xsl-mixing-production-plan-menu.sql
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
-- 密炼生产计划维护 菜单与权限(挂载到 MES密炼工程)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mixer_parent_id = (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE name = 'MES密炼工程' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE url = '/mes' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, '1860000000000000001');
|
||||||
|
|
||||||
|
INSERT INTO sys_permission
|
||||||
|
(`id`,`parent_id`,`name`,`url`,`component`,`component_name`,`menu_type`,`perms`,`perms_type`,`sort_no`,`always_show`,`icon`,`is_route`,`is_leaf`,`keep_alive`,`hidden`,`hide_tab`,`description`,`status`,`del_flag`,`create_by`,`create_time`,`rule_flag`,`internal_or_external`)
|
||||||
|
VALUES
|
||||||
|
('1860000000000000301',@mixer_parent_id,'密炼生产计划维护','/mes/mixingproductionplaninfo','mes/mixingproductionplaninfo/index','MesXslMixingProductionPlanList',1,NULL,'1',90,0,'ant-design:calendar-outlined',1,1,1,0,0,'密炼生产计划维护',1,0,'admin',NOW(),0,0)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id`=VALUES(`parent_id`),
|
||||||
|
`name`=VALUES(`name`),
|
||||||
|
`url`=VALUES(`url`),
|
||||||
|
`component`=VALUES(`component`),
|
||||||
|
`component_name`=VALUES(`component_name`),
|
||||||
|
`menu_type`=VALUES(`menu_type`),
|
||||||
|
`sort_no`=VALUES(`sort_no`),
|
||||||
|
`is_route`=VALUES(`is_route`),
|
||||||
|
`is_leaf`=VALUES(`is_leaf`),
|
||||||
|
`keep_alive`=VALUES(`keep_alive`),
|
||||||
|
`icon`=VALUES(`icon`),
|
||||||
|
`status`=VALUES(`status`),
|
||||||
|
`del_flag`=VALUES(`del_flag`);
|
||||||
|
|
||||||
|
INSERT INTO sys_permission
|
||||||
|
(`id`,`parent_id`,`name`,`menu_type`,`perms`,`perms_type`,`sort_no`,`status`,`del_flag`,`create_by`,`create_time`)
|
||||||
|
VALUES
|
||||||
|
('1860000000000000302','1860000000000000301','查询',2,'xslmes:mes_xsl_mixing_production_plan:list','1',1,'1',0,'admin',NOW()),
|
||||||
|
('1860000000000000303','1860000000000000301','整表保存',2,'xslmes:mes_xsl_mixing_production_plan:saveAll','1',2,'1',0,'admin',NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`name`=VALUES(`name`),
|
||||||
|
`perms`=VALUES(`perms`),
|
||||||
|
`sort_no`=VALUES(`sort_no`),
|
||||||
|
`status`=VALUES(`status`),
|
||||||
|
`del_flag`=VALUES(`del_flag`);
|
||||||
|
|
||||||
|
-- admin 角色授权
|
||||||
|
INSERT INTO sys_role_permission(id, role_id, permission_id, operate_date, operate_ip)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'f6817f48af4fb3af11b9e8bf182f618b', p.id, NOW(), '127.0.0.1'
|
||||||
|
FROM sys_permission p
|
||||||
|
WHERE p.id IN (
|
||||||
|
'1860000000000000301',
|
||||||
|
'1860000000000000302', '1860000000000000303'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys_role_permission rp
|
||||||
|
WHERE rp.role_id = 'f6817f48af4fb3af11b9e8bf182f618b'
|
||||||
|
AND rp.permission_id = p.id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 强制修复:确保菜单路由与组件路径正确
|
||||||
|
UPDATE sys_permission
|
||||||
|
SET
|
||||||
|
parent_id = @mixer_parent_id,
|
||||||
|
url = '/mes/mixingproductionplaninfo',
|
||||||
|
component = 'mes/mixingproductionplaninfo/index',
|
||||||
|
component_name = 'MesXslMixingProductionPlanList',
|
||||||
|
menu_type = 1,
|
||||||
|
is_route = 1,
|
||||||
|
is_leaf = 1,
|
||||||
|
hidden = 0,
|
||||||
|
status = '1',
|
||||||
|
del_flag = 0
|
||||||
|
WHERE id = '1860000000000000301';
|
||||||
78
jeecg-boot/db/mes-xsl-raw-material-demand-plan-menu.sql
Normal file
78
jeecg-boot/db/mes-xsl-raw-material-demand-plan-menu.sql
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
-- 原材料需求计划 菜单与权限(挂载到 MES密炼工程,兼容 MES管理)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @raw_parent_id = (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE name = 'MES密炼工程' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @raw_parent_id = IFNULL(@raw_parent_id, (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE url = '/mes' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
SET @raw_parent_id = IFNULL(@raw_parent_id, '1860000000000000001');
|
||||||
|
|
||||||
|
INSERT INTO sys_permission
|
||||||
|
(`id`,`parent_id`,`name`,`url`,`component`,`component_name`,`menu_type`,`perms`,`perms_type`,`sort_no`,`always_show`,`icon`,`is_route`,`is_leaf`,`keep_alive`,`hidden`,`hide_tab`,`description`,`status`,`del_flag`,`create_by`,`create_time`,`rule_flag`,`internal_or_external`)
|
||||||
|
VALUES
|
||||||
|
('1900000000000000710',@raw_parent_id,'原材料需求计划','/mes/rawmaterialdemandplan','mes/rawmaterialdemandplan/index','MesXslRawMaterialDemandPlanList',1,NULL,'1',95,0,'ant-design:ordered-list-outlined',1,1,1,0,0,'原材料需求计划',1,0,'admin',NOW(),0,0)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id`=VALUES(`parent_id`),
|
||||||
|
`name`=VALUES(`name`),
|
||||||
|
`url`=VALUES(`url`),
|
||||||
|
`component`=VALUES(`component`),
|
||||||
|
`component_name`=VALUES(`component_name`),
|
||||||
|
`menu_type`=VALUES(`menu_type`),
|
||||||
|
`sort_no`=VALUES(`sort_no`),
|
||||||
|
`is_route`=VALUES(`is_route`),
|
||||||
|
`is_leaf`=VALUES(`is_leaf`),
|
||||||
|
`keep_alive`=VALUES(`keep_alive`),
|
||||||
|
`icon`=VALUES(`icon`),
|
||||||
|
`status`=VALUES(`status`),
|
||||||
|
`hidden`=VALUES(`hidden`),
|
||||||
|
`del_flag`=VALUES(`del_flag`);
|
||||||
|
|
||||||
|
INSERT INTO sys_permission
|
||||||
|
(`id`,`parent_id`,`name`,`menu_type`,`perms`,`perms_type`,`sort_no`,`status`,`del_flag`,`create_by`,`create_time`)
|
||||||
|
VALUES
|
||||||
|
('1900000000000000711','1900000000000000710','导出',2,'xslmes:mes_xsl_raw_material_demand_plan:exportXls','1',1,'1',0,'admin',NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`name`=VALUES(`name`),
|
||||||
|
`perms`=VALUES(`perms`),
|
||||||
|
`sort_no`=VALUES(`sort_no`),
|
||||||
|
`status`=VALUES(`status`),
|
||||||
|
`del_flag`=VALUES(`del_flag`);
|
||||||
|
|
||||||
|
INSERT INTO sys_role_permission(id, role_id, permission_id, operate_date, operate_ip)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'f6817f48af4fb3af11b9e8bf182f618b', p.id, NOW(), '127.0.0.1'
|
||||||
|
FROM sys_permission p
|
||||||
|
WHERE p.id IN ('1900000000000000710', '1900000000000000711')
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys_role_permission rp
|
||||||
|
WHERE rp.role_id = 'f6817f48af4fb3af11b9e8bf182f618b'
|
||||||
|
AND rp.permission_id = p.id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 强制修复:按 ID + 名称 + URL 三重兜底,确保能显示
|
||||||
|
UPDATE sys_permission
|
||||||
|
SET
|
||||||
|
parent_id = @raw_parent_id,
|
||||||
|
url = '/mes/rawmaterialdemandplan',
|
||||||
|
component = 'mes/rawmaterialdemandplan/index',
|
||||||
|
component_name = 'MesXslRawMaterialDemandPlanList',
|
||||||
|
menu_type = 1,
|
||||||
|
is_route = 1,
|
||||||
|
is_leaf = 1,
|
||||||
|
hidden = 0,
|
||||||
|
status = '1',
|
||||||
|
del_flag = 0,
|
||||||
|
redirect = NULL
|
||||||
|
WHERE id = '1900000000000000710'
|
||||||
|
OR name = '原材料需求计划'
|
||||||
|
OR url = '/mes/rawmaterialdemandplan';
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package org.jeecg.modules.xslmes.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||||
|
import org.jeecg.common.system.base.controller.JeecgController;
|
||||||
|
import org.jeecg.common.system.query.QueryGenerator;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslMixingProductionPlan;
|
||||||
|
import org.jeecg.modules.xslmes.service.IMesXslMixingProductionPlanService;
|
||||||
|
import org.jeecg.modules.xslmes.vo.MesXslMixingProductionPlanOrderOptionVO;
|
||||||
|
import org.jeecg.modules.xslmes.vo.MesXslMixingProductionPlanSaveAllVO;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@Tag(name = "密炼生产计划维护")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/xslmes/mesXslMixingProductionPlan")
|
||||||
|
public class MesXslMixingProductionPlanController
|
||||||
|
extends JeecgController<MesXslMixingProductionPlan, IMesXslMixingProductionPlanService> {
|
||||||
|
|
||||||
|
private final IMesXslMixingProductionPlanService mixingProductionPlanService;
|
||||||
|
|
||||||
|
public MesXslMixingProductionPlanController(
|
||||||
|
IMesXslMixingProductionPlanService mixingProductionPlanService) {
|
||||||
|
this.mixingProductionPlanService = mixingProductionPlanService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "密炼生产计划维护-分页列表查询")
|
||||||
|
@GetMapping("/list")
|
||||||
|
public Result<IPage<MesXslMixingProductionPlan>> queryPageList(
|
||||||
|
MesXslMixingProductionPlan model,
|
||||||
|
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||||
|
@RequestParam(name = "pageSize", defaultValue = "20") Integer pageSize,
|
||||||
|
HttpServletRequest req) {
|
||||||
|
QueryWrapper<MesXslMixingProductionPlan> queryWrapper =
|
||||||
|
QueryGenerator.initQueryWrapper(model, req.getParameterMap());
|
||||||
|
queryWrapper.orderByAsc("sort_no").orderByAsc("create_time");
|
||||||
|
IPage<MesXslMixingProductionPlan> pageList =
|
||||||
|
mixingProductionPlanService.page(new Page<>(pageNo, pageSize), queryWrapper);
|
||||||
|
return Result.OK(pageList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AutoLog(value = "密炼生产计划维护-整表保存")
|
||||||
|
@Operation(summary = "密炼生产计划维护-整表保存")
|
||||||
|
@RequiresPermissions("xslmes:mes_xsl_mixing_production_plan:saveAll")
|
||||||
|
@PostMapping("/saveAll")
|
||||||
|
public Result<String> saveAll(@RequestBody MesXslMixingProductionPlanSaveAllVO req) {
|
||||||
|
mixingProductionPlanService.saveAllRows(req == null ? null : req.getRows());
|
||||||
|
return Result.OK("保存成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "密炼生产计划维护-班次生产订单候选分页")
|
||||||
|
@GetMapping("/orderOptionPage")
|
||||||
|
public Result<IPage<MesXslMixingProductionPlanOrderOptionVO>> orderOptionPage(
|
||||||
|
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||||
|
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
|
||||||
|
@RequestParam(name = "keyword", required = false) String keyword,
|
||||||
|
@RequestParam(name = "machineId", required = false) String machineId,
|
||||||
|
@RequestParam(name = "machineName", required = false) String machineName) {
|
||||||
|
return Result.OK(
|
||||||
|
mixingProductionPlanService.queryOrderOptions(
|
||||||
|
pageNo, pageSize, keyword, machineId, machineName));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
import org.jeecg.common.api.vo.Result;
|
import org.jeecg.common.api.vo.Result;
|
||||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||||
@@ -97,9 +98,19 @@ public class MesXslProductionOrderController
|
|||||||
@Operation(summary = "生产订单-拆分生成母胶计划")
|
@Operation(summary = "生产订单-拆分生成母胶计划")
|
||||||
@RequiresPermissions("xslmes:mes_xsl_production_order:split")
|
@RequiresPermissions("xslmes:mes_xsl_production_order:split")
|
||||||
@PostMapping("/split")
|
@PostMapping("/split")
|
||||||
public Result<MesXslMasterBatchPlan> split(@RequestParam(name = "id", required = true) String id) {
|
public Result<List<MesXslMasterBatchPlan>> split(@RequestParam(name = "id", required = true) String id) {
|
||||||
MesXslMasterBatchPlan plan = mesXslProductionOrderService.splitToMasterBatchPlan(id);
|
List<MesXslMasterBatchPlan> plans = mesXslProductionOrderService.splitToMasterBatchPlan(id);
|
||||||
return Result.OK("拆分成功", plan);
|
return Result.OK("拆分成功", plans);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AutoLog(value = "生产订单-批量拆分生成计划")
|
||||||
|
@Operation(summary = "生产订单-批量拆分生成计划")
|
||||||
|
@RequiresPermissions("xslmes:mes_xsl_production_order:split")
|
||||||
|
@PostMapping("/splitBatch")
|
||||||
|
public Result<Integer> splitBatch(@RequestParam(name = "ids", required = true) String ids) {
|
||||||
|
List<String> idList = Arrays.asList(ids.split(","));
|
||||||
|
int count = mesXslProductionOrderService.splitToMasterBatchPlanBatch(idList);
|
||||||
|
return Result.OK("批量拆分成功", count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresPermissions("xslmes:mes_xsl_production_order:exportXls")
|
@RequiresPermissions("xslmes:mes_xsl_production_order:exportXls")
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
package org.jeecg.modules.xslmes.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
import org.jeecg.common.system.vo.LoginUser;
|
||||||
|
import org.jeecg.common.util.oConvertUtils;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialDemandPlanSummary;
|
||||||
|
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
|
||||||
|
import org.jeecgframework.poi.excel.entity.ExportParams;
|
||||||
|
import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
|
||||||
|
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原材料需求计划(汇总查询)
|
||||||
|
*/
|
||||||
|
@Tag(name = "原材料需求计划")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/xslmes/mesXslRawMaterialDemandPlan")
|
||||||
|
public class MesXslRawMaterialDemandPlanController {
|
||||||
|
|
||||||
|
@Autowired private JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
@Operation(summary = "原材料需求计划-分页列表查询")
|
||||||
|
@GetMapping("/list")
|
||||||
|
public Result<IPage<MesXslRawMaterialDemandPlanSummary>> queryPageList(
|
||||||
|
MesXslRawMaterialDemandPlanSummary query,
|
||||||
|
@RequestParam(name = "groupByMachine", required = false, defaultValue = "0") Integer groupByMachine,
|
||||||
|
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||||
|
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
|
||||||
|
boolean groupedByMachine = groupByMachine != null && groupByMachine == 1;
|
||||||
|
List<Object> params = new ArrayList<>();
|
||||||
|
String groupedSql = buildGroupedSql(query, groupedByMachine, params);
|
||||||
|
|
||||||
|
String countSql = "SELECT COUNT(1) FROM (" + groupedSql + ") t";
|
||||||
|
Long total = jdbcTemplate.queryForObject(countSql, Long.class, params.toArray());
|
||||||
|
long totalCount = total == null ? 0L : total;
|
||||||
|
|
||||||
|
int offset = Math.max((pageNo - 1) * pageSize, 0);
|
||||||
|
String pageSql = groupedSql + buildOrderBy(groupedByMachine) + " LIMIT ? OFFSET ?";
|
||||||
|
List<Object> pageParams = new ArrayList<>(params);
|
||||||
|
pageParams.add(pageSize);
|
||||||
|
pageParams.add(offset);
|
||||||
|
List<MesXslRawMaterialDemandPlanSummary> rows = queryRows(pageSql, pageParams, groupedByMachine);
|
||||||
|
|
||||||
|
Page<MesXslRawMaterialDemandPlanSummary> page = new Page<>(pageNo, pageSize, totalCount);
|
||||||
|
page.setRecords(rows);
|
||||||
|
return Result.OK(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresPermissions("xslmes:mes_xsl_raw_material_demand_plan:exportXls")
|
||||||
|
@RequestMapping("/exportXls")
|
||||||
|
public ModelAndView exportXls(
|
||||||
|
HttpServletRequest request,
|
||||||
|
MesXslRawMaterialDemandPlanSummary query,
|
||||||
|
@RequestParam(name = "groupByMachine", required = false, defaultValue = "0") Integer groupByMachine) {
|
||||||
|
boolean groupedByMachine = groupByMachine != null && groupByMachine == 1;
|
||||||
|
List<Object> params = new ArrayList<>();
|
||||||
|
String groupedSql = buildGroupedSql(query, groupedByMachine, params) + buildOrderBy(groupedByMachine);
|
||||||
|
List<MesXslRawMaterialDemandPlanSummary> exportList = queryRows(groupedSql, params, groupedByMachine);
|
||||||
|
|
||||||
|
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||||
|
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
|
||||||
|
mv.addObject(NormalExcelConstants.FILE_NAME, "原材料需求计划");
|
||||||
|
mv.addObject(NormalExcelConstants.CLASS, MesXslRawMaterialDemandPlanSummary.class);
|
||||||
|
mv.addObject(
|
||||||
|
NormalExcelConstants.PARAMS,
|
||||||
|
new ExportParams(
|
||||||
|
"原材料需求计划",
|
||||||
|
"导出人:" + (sysUser == null ? "admin" : sysUser.getRealname()),
|
||||||
|
"原材料需求计划",
|
||||||
|
ExcelType.XSSF));
|
||||||
|
mv.addObject(NormalExcelConstants.DATA_LIST, exportList);
|
||||||
|
String exportFields = request.getParameter(NormalExcelConstants.EXPORT_FIELDS);
|
||||||
|
if (oConvertUtils.isNotEmpty(exportFields)) {
|
||||||
|
mv.addObject(NormalExcelConstants.EXPORT_FIELDS, exportFields);
|
||||||
|
}
|
||||||
|
return mv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildGroupedSql(
|
||||||
|
MesXslRawMaterialDemandPlanSummary query, boolean groupedByMachine, List<Object> params) {
|
||||||
|
String machineExpr = "COALESCE(NULLIF(TRIM(t.machine_name),''), '')";
|
||||||
|
String erpCodeExpr = "COALESCE(NULLIF(TRIM(t.erp_code),''), '')";
|
||||||
|
String rawMaterialExpr = "COALESCE(NULLIF(TRIM(t.raw_material_name),''), '')";
|
||||||
|
|
||||||
|
StringBuilder sql = new StringBuilder();
|
||||||
|
sql.append("SELECT ");
|
||||||
|
if (groupedByMachine) {
|
||||||
|
sql.append(machineExpr).append(" AS machineName, ");
|
||||||
|
} else {
|
||||||
|
sql.append("'' AS machineName, ");
|
||||||
|
}
|
||||||
|
sql.append(erpCodeExpr)
|
||||||
|
.append(" AS erpCode, ")
|
||||||
|
.append(rawMaterialExpr)
|
||||||
|
.append(" AS rawMaterialName, ")
|
||||||
|
.append("SUM(COALESCE(t.demand_weight,0)) AS demandWeight, ")
|
||||||
|
.append("SUM(COALESCE(t.standard_weight,0)) AS standardWeight, ")
|
||||||
|
.append("SUM(COALESCE(t.actual_weight,0)) AS actualWeight ")
|
||||||
|
.append("FROM mes_xsl_raw_material_demand_plan t ")
|
||||||
|
.append("WHERE (t.del_flag = 0 OR t.del_flag IS NULL) ");
|
||||||
|
|
||||||
|
if (query != null && StringUtils.isNotBlank(query.getErpCode())) {
|
||||||
|
sql.append("AND ").append(erpCodeExpr).append(" LIKE ? ");
|
||||||
|
params.add("%" + query.getErpCode().trim() + "%");
|
||||||
|
}
|
||||||
|
if (query != null && StringUtils.isNotBlank(query.getRawMaterialName())) {
|
||||||
|
sql.append("AND ").append(rawMaterialExpr).append(" LIKE ? ");
|
||||||
|
params.add("%" + query.getRawMaterialName().trim() + "%");
|
||||||
|
}
|
||||||
|
if (groupedByMachine && query != null && StringUtils.isNotBlank(query.getMachineName())) {
|
||||||
|
sql.append("AND ").append(machineExpr).append(" LIKE ? ");
|
||||||
|
params.add("%" + query.getMachineName().trim() + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.append("GROUP BY ");
|
||||||
|
if (groupedByMachine) {
|
||||||
|
sql.append(machineExpr).append(", ");
|
||||||
|
}
|
||||||
|
sql.append(erpCodeExpr).append(", ").append(rawMaterialExpr);
|
||||||
|
return sql.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MesXslRawMaterialDemandPlanSummary> queryRows(
|
||||||
|
String sql, List<Object> params, boolean groupedByMachine) {
|
||||||
|
return jdbcTemplate.query(
|
||||||
|
sql,
|
||||||
|
rs -> {
|
||||||
|
List<MesXslRawMaterialDemandPlanSummary> list = new ArrayList<>();
|
||||||
|
int seq = 1;
|
||||||
|
while (rs.next()) {
|
||||||
|
MesXslRawMaterialDemandPlanSummary row = new MesXslRawMaterialDemandPlanSummary();
|
||||||
|
row.setMachineName(groupedByMachine ? trim(rs.getString("machineName")) : "");
|
||||||
|
row.setErpCode(trim(rs.getString("erpCode")));
|
||||||
|
row.setRawMaterialName(trim(rs.getString("rawMaterialName")));
|
||||||
|
row.setDemandWeight(rs.getBigDecimal("demandWeight"));
|
||||||
|
row.setStandardWeight(rs.getBigDecimal("standardWeight"));
|
||||||
|
row.setActualWeight(rs.getBigDecimal("actualWeight"));
|
||||||
|
row.setId(buildRowId(row, groupedByMachine, seq++));
|
||||||
|
list.add(row);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
},
|
||||||
|
params.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildOrderBy(boolean groupedByMachine) {
|
||||||
|
if (groupedByMachine) {
|
||||||
|
return " ORDER BY machineName, rawMaterialName, erpCode";
|
||||||
|
}
|
||||||
|
return " ORDER BY rawMaterialName, erpCode";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildRowId(MesXslRawMaterialDemandPlanSummary row, boolean groupedByMachine, int seq) {
|
||||||
|
String machine = groupedByMachine ? StringUtils.defaultString(row.getMachineName()) : "ALL";
|
||||||
|
return machine + "_" + StringUtils.defaultString(row.getErpCode()) + "_" + seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String trim(String value) {
|
||||||
|
return value == null ? "" : value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package org.jeecg.modules.xslmes.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import org.jeecg.common.aspect.annotation.Dict;
|
||||||
|
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密炼生产计划维护
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("mes_xsl_mixing_production_plan")
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Schema(description = "密炼生产计划维护")
|
||||||
|
public class MesXslMixingProductionPlan implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.ASSIGN_ID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Excel(name = "排序号", width = 10)
|
||||||
|
@Schema(description = "排序号")
|
||||||
|
private Integer sortNo;
|
||||||
|
|
||||||
|
@Excel(name = "机台", width = 20, dictTable = "mes_xsl_equipment_ledger", dicText = "equipment_name", dicCode = "id")
|
||||||
|
@Dict(dictTable = "mes_xsl_equipment_ledger", dicText = "equipment_name", dicCode = "id")
|
||||||
|
@Schema(description = "机台ID")
|
||||||
|
private String machineId;
|
||||||
|
|
||||||
|
@Excel(name = "机台", width = 20)
|
||||||
|
@Schema(description = "机台名称")
|
||||||
|
private String machineName;
|
||||||
|
|
||||||
|
@Schema(description = "早班计划ID")
|
||||||
|
private String morningPlanId;
|
||||||
|
@Schema(description = "早班计划类型 M母胶/F终胶")
|
||||||
|
private String morningPlanType;
|
||||||
|
@Excel(name = "早班生产订单", width = 20)
|
||||||
|
private String morningOrderNo;
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
|
@Schema(description = "早班订单日期")
|
||||||
|
private Date morningOrderDate;
|
||||||
|
@Excel(name = "早班配方名称", width = 20)
|
||||||
|
private String morningFormulaName;
|
||||||
|
@Excel(name = "早班计划重量", width = 15)
|
||||||
|
private BigDecimal morningPlanWeight;
|
||||||
|
@Excel(name = "早班计划车数", width = 12)
|
||||||
|
private Integer morningPlannedCarCount;
|
||||||
|
@Excel(name = "早班已排产车数", width = 12)
|
||||||
|
private Integer morningScheduledCarCount;
|
||||||
|
@Excel(name = "早班完成车数", width = 12)
|
||||||
|
private Integer morningFinishedCarCount;
|
||||||
|
@Excel(name = "早班计划", width = 12)
|
||||||
|
private Integer morningPlanCount;
|
||||||
|
@Excel(name = "早班备注", width = 20)
|
||||||
|
private String morningRemark;
|
||||||
|
|
||||||
|
@Schema(description = "中班计划ID")
|
||||||
|
private String noonPlanId;
|
||||||
|
@Schema(description = "中班计划类型 M母胶/F终胶")
|
||||||
|
private String noonPlanType;
|
||||||
|
@Excel(name = "中班生产订单", width = 20)
|
||||||
|
private String noonOrderNo;
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
|
@Schema(description = "中班订单日期")
|
||||||
|
private Date noonOrderDate;
|
||||||
|
@Excel(name = "中班配方名称", width = 20)
|
||||||
|
private String noonFormulaName;
|
||||||
|
@Excel(name = "中班计划重量", width = 15)
|
||||||
|
private BigDecimal noonPlanWeight;
|
||||||
|
@Excel(name = "中班计划车数", width = 12)
|
||||||
|
private Integer noonPlannedCarCount;
|
||||||
|
@Excel(name = "中班已排产车数", width = 12)
|
||||||
|
private Integer noonScheduledCarCount;
|
||||||
|
@Excel(name = "中班完成车数", width = 12)
|
||||||
|
private Integer noonFinishedCarCount;
|
||||||
|
@Excel(name = "中班计划", width = 12)
|
||||||
|
private Integer noonPlanCount;
|
||||||
|
@Excel(name = "中班备注", width = 20)
|
||||||
|
private String noonRemark;
|
||||||
|
|
||||||
|
@Schema(description = "晚班计划ID")
|
||||||
|
private String nightPlanId;
|
||||||
|
@Schema(description = "晚班计划类型 M母胶/F终胶")
|
||||||
|
private String nightPlanType;
|
||||||
|
@Excel(name = "晚班生产订单", width = 20)
|
||||||
|
private String nightOrderNo;
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
|
@Schema(description = "晚班订单日期")
|
||||||
|
private Date nightOrderDate;
|
||||||
|
@Excel(name = "晚班配方名称", width = 20)
|
||||||
|
private String nightFormulaName;
|
||||||
|
@Excel(name = "晚班计划重量", width = 15)
|
||||||
|
private BigDecimal nightPlanWeight;
|
||||||
|
@Excel(name = "晚班计划车数", width = 12)
|
||||||
|
private Integer nightPlannedCarCount;
|
||||||
|
@Excel(name = "晚班已排产车数", width = 12)
|
||||||
|
private Integer nightScheduledCarCount;
|
||||||
|
@Excel(name = "晚班完成车数", width = 12)
|
||||||
|
private Integer nightFinishedCarCount;
|
||||||
|
@Excel(name = "晚班计划", width = 12)
|
||||||
|
private Integer nightPlanCount;
|
||||||
|
@Excel(name = "晚班备注", width = 20)
|
||||||
|
private String nightRemark;
|
||||||
|
|
||||||
|
private Integer tenantId;
|
||||||
|
private String sysOrgCode;
|
||||||
|
private String createBy;
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date createTime;
|
||||||
|
private String updateBy;
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date updateTime;
|
||||||
|
private Integer delFlag;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.jeecg.modules.xslmes.entity;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原材料需求计划汇总(按原材料或按机台+原材料)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "原材料需求计划汇总")
|
||||||
|
public class MesXslRawMaterialDemandPlanSummary implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "主键")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Excel(name = "机台", width = 20)
|
||||||
|
@Schema(description = "机台")
|
||||||
|
private String machineName;
|
||||||
|
|
||||||
|
@Excel(name = "ERP编号", width = 18)
|
||||||
|
@Schema(description = "ERP编号")
|
||||||
|
private String erpCode;
|
||||||
|
|
||||||
|
@Excel(name = "原材料名称", width = 24)
|
||||||
|
@Schema(description = "原材料名称")
|
||||||
|
private String rawMaterialName;
|
||||||
|
|
||||||
|
@Excel(name = "需求重量", width = 15)
|
||||||
|
@Schema(description = "需求重量")
|
||||||
|
private BigDecimal demandWeight;
|
||||||
|
|
||||||
|
@Excel(name = "标准重量", width = 15)
|
||||||
|
@Schema(description = "标准重量")
|
||||||
|
private BigDecimal standardWeight;
|
||||||
|
|
||||||
|
@Excel(name = "实际重量", width = 15)
|
||||||
|
@Schema(description = "实际重量")
|
||||||
|
private BigDecimal actualWeight;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package org.jeecg.modules.xslmes.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslMixingProductionPlan;
|
||||||
|
|
||||||
|
public interface MesXslMixingProductionPlanMapper extends BaseMapper<MesXslMixingProductionPlan> {}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
package org.jeecg.modules.xslmes.service;
|
package org.jeecg.modules.xslmes.service;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import java.util.List;
|
||||||
import org.jeecg.modules.xslmes.entity.MesXslMasterBatchPlan;
|
import org.jeecg.modules.xslmes.entity.MesXslMasterBatchPlan;
|
||||||
import org.jeecg.modules.xslmes.entity.MesXslProductionOrder;
|
import org.jeecg.modules.xslmes.entity.MesXslProductionOrder;
|
||||||
|
|
||||||
public interface IMesXslMasterBatchPlanService extends IService<MesXslMasterBatchPlan> {
|
public interface IMesXslMasterBatchPlanService extends IService<MesXslMasterBatchPlan> {
|
||||||
|
|
||||||
|
List<MesXslMasterBatchPlan> generateBatchFromProductionOrder(MesXslProductionOrder productionOrder);
|
||||||
|
|
||||||
MesXslMasterBatchPlan generateFromProductionOrder(MesXslProductionOrder productionOrder);
|
MesXslMasterBatchPlan generateFromProductionOrder(MesXslProductionOrder productionOrder);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package org.jeecg.modules.xslmes.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import java.util.List;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslMixingProductionPlan;
|
||||||
|
import org.jeecg.modules.xslmes.vo.MesXslMixingProductionPlanOrderOptionVO;
|
||||||
|
|
||||||
|
public interface IMesXslMixingProductionPlanService extends IService<MesXslMixingProductionPlan> {
|
||||||
|
void saveAllRows(List<MesXslMixingProductionPlan> rows);
|
||||||
|
|
||||||
|
IPage<MesXslMixingProductionPlanOrderOptionVO> queryOrderOptions(
|
||||||
|
Integer pageNo, Integer pageSize, String keyword, String machineId, String machineName);
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
package org.jeecg.modules.xslmes.service;
|
package org.jeecg.modules.xslmes.service;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import java.util.List;
|
||||||
import org.jeecg.modules.xslmes.entity.MesXslMasterBatchPlan;
|
import org.jeecg.modules.xslmes.entity.MesXslMasterBatchPlan;
|
||||||
import org.jeecg.modules.xslmes.entity.MesXslProductionOrder;
|
import org.jeecg.modules.xslmes.entity.MesXslProductionOrder;
|
||||||
|
|
||||||
public interface IMesXslProductionOrderService extends IService<MesXslProductionOrder> {
|
public interface IMesXslProductionOrderService extends IService<MesXslProductionOrder> {
|
||||||
|
|
||||||
MesXslMasterBatchPlan splitToMasterBatchPlan(String id);
|
List<MesXslMasterBatchPlan> splitToMasterBatchPlan(String id);
|
||||||
|
|
||||||
|
int splitToMasterBatchPlanBatch(List<String> ids);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jeecg.common.exception.JeecgBootException;
|
import org.jeecg.common.exception.JeecgBootException;
|
||||||
import org.jeecg.modules.mes.material.entity.MesMaterial;
|
import org.jeecg.modules.mes.material.entity.MesMaterial;
|
||||||
@@ -33,7 +37,7 @@ public class MesXslFinalBatchPlanServiceImpl
|
|||||||
return exists;
|
return exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
MesMaterial finalMaterial = resolveFinalMaterial(productionOrder.getMaterialCode());
|
MesMaterial finalMaterial = resolveFinalMaterial(productionOrder);
|
||||||
if (finalMaterial == null) {
|
if (finalMaterial == null) {
|
||||||
throw new JeecgBootException("未找到对应终胶物料,请确认MES物料编码");
|
throw new JeecgBootException("未找到对应终胶物料,请确认MES物料编码");
|
||||||
}
|
}
|
||||||
@@ -60,16 +64,67 @@ public class MesXslFinalBatchPlanServiceImpl
|
|||||||
return plan;
|
return plan;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MesMaterial resolveFinalMaterial(String mesMaterialCode) {
|
private MesMaterial resolveFinalMaterial(MesXslProductionOrder productionOrder) {
|
||||||
if (StringUtils.isBlank(mesMaterialCode)) {
|
for (String seedCode : resolveSplitSeedCodes(productionOrder)) {
|
||||||
|
for (String candidateCode : buildFinalCandidates(seedCode)) {
|
||||||
|
MesMaterial matched = resolveMaterialByBaseCode(candidateCode);
|
||||||
|
if (matched != null) {
|
||||||
|
MesMaterial resolved = new MesMaterial();
|
||||||
|
resolved.setMaterialCode(candidateCode);
|
||||||
|
resolved.setMaterialName(StringUtils.defaultIfBlank(matched.getMaterialName(), candidateCode));
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MesMaterial resolveMaterialByBaseCode(String baseCode) {
|
||||||
|
if (StringUtils.isBlank(baseCode)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
MesMaterial exact =
|
||||||
|
mesMaterialMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<MesMaterial>()
|
||||||
|
.eq(MesMaterial::getMaterialCode, baseCode)
|
||||||
|
.last("LIMIT 1"));
|
||||||
|
if (exact != null) {
|
||||||
|
return exact;
|
||||||
|
}
|
||||||
return mesMaterialMapper.selectOne(
|
return mesMaterialMapper.selectOne(
|
||||||
new LambdaQueryWrapper<MesMaterial>()
|
new LambdaQueryWrapper<MesMaterial>()
|
||||||
.eq(MesMaterial::getMaterialCode, mesMaterialCode.trim())
|
.likeRight(MesMaterial::getMaterialCode, baseCode)
|
||||||
.last("LIMIT 1"));
|
.last("LIMIT 1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> resolveSplitSeedCodes(MesXslProductionOrder productionOrder) {
|
||||||
|
Set<String> seeds = new LinkedHashSet<>();
|
||||||
|
if (productionOrder != null) {
|
||||||
|
if (StringUtils.isNotBlank(productionOrder.getMesMaterialName())) {
|
||||||
|
seeds.add(productionOrder.getMesMaterialName().trim());
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(productionOrder.getMaterialCode())) {
|
||||||
|
seeds.add(productionOrder.getMaterialCode().trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>(seeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> buildFinalCandidates(String code) {
|
||||||
|
List<String> candidates = new ArrayList<>(2);
|
||||||
|
if (StringUtils.isBlank(code)) {
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
String trimmed = code.trim();
|
||||||
|
if (trimmed.length() > 0 && (trimmed.startsWith("F") || trimmed.startsWith("f"))) {
|
||||||
|
candidates.add(trimmed);
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
candidates.add("F" + trimmed);
|
||||||
|
candidates.add(trimmed);
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
|
||||||
private String buildOrderSerialNo(MesXslProductionOrder productionOrder) {
|
private String buildOrderSerialNo(MesXslProductionOrder productionOrder) {
|
||||||
String orderNo = StringUtils.defaultIfBlank(productionOrder.getProductionOrderNo(), productionOrder.getId());
|
String orderNo = StringUtils.defaultIfBlank(productionOrder.getProductionOrderNo(), productionOrder.getId());
|
||||||
return orderNo + "-F-" + System.currentTimeMillis();
|
return orderNo + "-F-" + System.currentTimeMillis();
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jeecg.common.exception.JeecgBootException;
|
import org.jeecg.common.exception.JeecgBootException;
|
||||||
import org.jeecg.modules.mes.material.entity.MesMaterial;
|
import org.jeecg.modules.mes.material.entity.MesMaterial;
|
||||||
@@ -25,28 +27,55 @@ public class MesXslMasterBatchPlanServiceImpl
|
|||||||
@Autowired private MesMaterialMapper mesMaterialMapper;
|
@Autowired private MesMaterialMapper mesMaterialMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MesXslMasterBatchPlan generateFromProductionOrder(MesXslProductionOrder productionOrder) {
|
public List<MesXslMasterBatchPlan> generateBatchFromProductionOrder(MesXslProductionOrder productionOrder) {
|
||||||
if (productionOrder == null || StringUtils.isBlank(productionOrder.getId())) {
|
if (productionOrder == null || StringUtils.isBlank(productionOrder.getId())) {
|
||||||
throw new JeecgBootException("生产订单不存在,无法拆分");
|
throw new JeecgBootException("生产订单不存在,无法拆分");
|
||||||
}
|
}
|
||||||
MesMaterial motherMaterial = resolveMotherMaterial(productionOrder.getMaterialCode());
|
int totalSegments = normalizeSegmentCount(productionOrder.getProcessSegmentCount());
|
||||||
if (motherMaterial == null) {
|
int motherSegments = Math.max(totalSegments - 1, 0);
|
||||||
throw new JeecgBootException("未找到对应母胶物料(优先B1,其次B2)");
|
if (motherSegments <= 0) {
|
||||||
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
List<MesXslMasterBatchPlan> existingPlans =
|
||||||
MesXslMasterBatchPlan exists =
|
this.list(
|
||||||
this.getOne(new LambdaQueryWrapper<MesXslMasterBatchPlan>().eq(MesXslMasterBatchPlan::getSourceOrderId, productionOrder.getId()));
|
new LambdaQueryWrapper<MesXslMasterBatchPlan>()
|
||||||
if (exists != null) {
|
.eq(MesXslMasterBatchPlan::getSourceOrderId, productionOrder.getId())
|
||||||
return exists;
|
.orderByAsc(MesXslMasterBatchPlan::getCreateTime)
|
||||||
|
.orderByAsc(MesXslMasterBatchPlan::getId));
|
||||||
|
List<MesXslMasterBatchPlan> result = new ArrayList<>();
|
||||||
|
for (int stageIndex = 1; stageIndex <= motherSegments; stageIndex++) {
|
||||||
|
MesMaterial motherMaterial = resolveMotherMaterialByStage(productionOrder, stageIndex);
|
||||||
|
if (motherMaterial == null) {
|
||||||
|
throw new JeecgBootException("未找到对应母胶物料(第" + stageIndex + "段,编码前缀B" + stageIndex + ")");
|
||||||
|
}
|
||||||
|
MesXslMasterBatchPlan matched = findExistingByMaterialCode(existingPlans, motherMaterial.getMaterialCode());
|
||||||
|
if (matched != null) {
|
||||||
|
result.add(matched);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MesXslMasterBatchPlan plan = buildMotherPlan(productionOrder, motherMaterial, stageIndex);
|
||||||
|
this.save(plan);
|
||||||
|
result.add(plan);
|
||||||
|
existingPlans.add(plan);
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MesXslMasterBatchPlan generateFromProductionOrder(MesXslProductionOrder productionOrder) {
|
||||||
|
List<MesXslMasterBatchPlan> plans = generateBatchFromProductionOrder(productionOrder);
|
||||||
|
return plans.isEmpty() ? null : plans.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MesXslMasterBatchPlan buildMotherPlan(
|
||||||
|
MesXslProductionOrder productionOrder, MesMaterial motherMaterial, int stageIndex) {
|
||||||
BigDecimal planWeight = productionOrder.getPlanQty() == null ? BigDecimal.ZERO : productionOrder.getPlanQty();
|
BigDecimal planWeight = productionOrder.getPlanQty() == null ? BigDecimal.ZERO : productionOrder.getPlanQty();
|
||||||
BigDecimal perCarWeight = BigDecimal.ZERO;
|
BigDecimal perCarWeight = BigDecimal.ZERO;
|
||||||
int planCarCount = calcPlanCarCount(planWeight, perCarWeight);
|
int planCarCount = calcPlanCarCount(planWeight, perCarWeight);
|
||||||
|
|
||||||
MesXslMasterBatchPlan plan = new MesXslMasterBatchPlan();
|
MesXslMasterBatchPlan plan = new MesXslMasterBatchPlan();
|
||||||
plan.setSourceOrderId(productionOrder.getId());
|
plan.setSourceOrderId(productionOrder.getId());
|
||||||
plan.setOrderSerialNo(buildOrderSerialNo(productionOrder));
|
plan.setOrderSerialNo(buildOrderSerialNo(productionOrder, stageIndex));
|
||||||
plan.setOrderNo(productionOrder.getProductionOrderNo());
|
plan.setOrderNo(productionOrder.getProductionOrderNo());
|
||||||
plan.setProductionSegmentCount(productionOrder.getProcessSegmentCount());
|
plan.setProductionSegmentCount(productionOrder.getProcessSegmentCount());
|
||||||
plan.setOrderDate(productionOrder.getOrderDate());
|
plan.setOrderDate(productionOrder.getOrderDate());
|
||||||
@@ -58,45 +87,93 @@ public class MesXslMasterBatchPlanServiceImpl
|
|||||||
plan.setScheduledCarCount(0);
|
plan.setScheduledCarCount(0);
|
||||||
plan.setFinishedCarCount(0);
|
plan.setFinishedCarCount(0);
|
||||||
plan.setStatus(0);
|
plan.setStatus(0);
|
||||||
this.save(plan);
|
|
||||||
return plan;
|
return plan;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MesMaterial resolveMotherMaterial(String mesMaterialCode) {
|
private MesXslMasterBatchPlan findExistingByMaterialCode(
|
||||||
if (StringUtils.isBlank(mesMaterialCode)) {
|
List<MesXslMasterBatchPlan> existingPlans, String materialCode) {
|
||||||
|
if (StringUtils.isBlank(materialCode) || existingPlans == null || existingPlans.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String code = mesMaterialCode.trim();
|
for (MesXslMasterBatchPlan plan : existingPlans) {
|
||||||
List<String> candidates = buildMotherCandidates(code);
|
if (plan != null && materialCode.equalsIgnoreCase(StringUtils.trimToEmpty(plan.getMaterialCode()))) {
|
||||||
for (String c : candidates) {
|
return plan;
|
||||||
MesMaterial found =
|
|
||||||
mesMaterialMapper.selectOne(
|
|
||||||
new LambdaQueryWrapper<MesMaterial>()
|
|
||||||
.eq(MesMaterial::getMaterialCode, c)
|
|
||||||
.last("LIMIT 1"));
|
|
||||||
if (found != null) {
|
|
||||||
return found;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> buildMotherCandidates(String code) {
|
private MesMaterial resolveMotherMaterialByStage(MesXslProductionOrder productionOrder, int stageIndex) {
|
||||||
List<String> list = new ArrayList<>(2);
|
for (String seedCode : resolveSplitSeedCodes(productionOrder)) {
|
||||||
|
List<String> candidates = buildMotherCandidates(seedCode, stageIndex);
|
||||||
|
for (String candidateCode : candidates) {
|
||||||
|
MesMaterial matched = resolveMaterialByBaseCode(candidateCode);
|
||||||
|
if (matched != null) {
|
||||||
|
MesMaterial resolved = new MesMaterial();
|
||||||
|
resolved.setMaterialCode(candidateCode);
|
||||||
|
resolved.setMaterialName(StringUtils.defaultIfBlank(matched.getMaterialName(), candidateCode));
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MesMaterial resolveMaterialByBaseCode(String baseCode) {
|
||||||
|
if (StringUtils.isBlank(baseCode)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
MesMaterial exact =
|
||||||
|
mesMaterialMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<MesMaterial>()
|
||||||
|
.eq(MesMaterial::getMaterialCode, baseCode)
|
||||||
|
.last("LIMIT 1"));
|
||||||
|
if (exact != null) {
|
||||||
|
return exact;
|
||||||
|
}
|
||||||
|
return mesMaterialMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<MesMaterial>()
|
||||||
|
.likeRight(MesMaterial::getMaterialCode, baseCode)
|
||||||
|
.last("LIMIT 1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> resolveSplitSeedCodes(MesXslProductionOrder productionOrder) {
|
||||||
|
Set<String> seeds = new LinkedHashSet<>();
|
||||||
|
if (productionOrder != null) {
|
||||||
|
if (StringUtils.isNotBlank(productionOrder.getMesMaterialName())) {
|
||||||
|
seeds.add(productionOrder.getMesMaterialName().trim());
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(productionOrder.getMaterialCode())) {
|
||||||
|
seeds.add(productionOrder.getMaterialCode().trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>(seeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> buildMotherCandidates(String code, int stageIndex) {
|
||||||
|
List<String> list = new ArrayList<>(1);
|
||||||
|
if (StringUtils.isBlank(code) || stageIndex <= 0) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
if (code.length() > 1 && (code.startsWith("F") || code.startsWith("f"))) {
|
if (code.length() > 1 && (code.startsWith("F") || code.startsWith("f"))) {
|
||||||
String suffix = code.substring(1);
|
String suffix = code.substring(1);
|
||||||
list.add("B1" + suffix);
|
list.add("B" + stageIndex + suffix);
|
||||||
list.add("B2" + suffix);
|
|
||||||
} else {
|
} else {
|
||||||
list.add("B1" + code);
|
list.add("B" + stageIndex + code);
|
||||||
list.add("B2" + code);
|
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildOrderSerialNo(MesXslProductionOrder productionOrder) {
|
private int normalizeSegmentCount(Integer segmentCount) {
|
||||||
|
if (segmentCount == null || segmentCount <= 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return segmentCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildOrderSerialNo(MesXslProductionOrder productionOrder, int stageIndex) {
|
||||||
String orderNo = StringUtils.defaultIfBlank(productionOrder.getProductionOrderNo(), productionOrder.getId());
|
String orderNo = StringUtils.defaultIfBlank(productionOrder.getProductionOrderNo(), productionOrder.getId());
|
||||||
return orderNo + "-" + System.currentTimeMillis();
|
return orderNo + "-B" + stageIndex + "-" + System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calcPlanCarCount(BigDecimal planWeight, BigDecimal perCarWeight) {
|
private int calcPlanCarCount(BigDecimal planWeight, BigDecimal perCarWeight) {
|
||||||
|
|||||||
@@ -0,0 +1,416 @@
|
|||||||
|
package org.jeecg.modules.xslmes.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jeecg.common.constant.CommonConstant;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslEquipmentLedger;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslFinalBatchPlan;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslMasterBatchPlan;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslMixingSpec;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslMixingProductionPlan;
|
||||||
|
import org.jeecg.modules.xslmes.mapper.MesXslEquipmentLedgerMapper;
|
||||||
|
import org.jeecg.modules.xslmes.mapper.MesXslFinalBatchPlanMapper;
|
||||||
|
import org.jeecg.modules.xslmes.mapper.MesXslMasterBatchPlanMapper;
|
||||||
|
import org.jeecg.modules.xslmes.mapper.MesXslMixingSpecMapper;
|
||||||
|
import org.jeecg.modules.xslmes.mapper.MesXslMixingProductionPlanMapper;
|
||||||
|
import org.jeecg.modules.xslmes.service.IMesXslMixingProductionPlanService;
|
||||||
|
import org.jeecg.modules.xslmes.vo.MesXslMixingProductionPlanOrderOptionVO;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MesXslMixingProductionPlanServiceImpl
|
||||||
|
extends ServiceImpl<MesXslMixingProductionPlanMapper, MesXslMixingProductionPlan>
|
||||||
|
implements IMesXslMixingProductionPlanService {
|
||||||
|
|
||||||
|
private final MesXslEquipmentLedgerMapper equipmentLedgerMapper;
|
||||||
|
private final MesXslMasterBatchPlanMapper masterBatchPlanMapper;
|
||||||
|
private final MesXslFinalBatchPlanMapper finalBatchPlanMapper;
|
||||||
|
private final MesXslMixingSpecMapper mixingSpecMapper;
|
||||||
|
|
||||||
|
public MesXslMixingProductionPlanServiceImpl(
|
||||||
|
MesXslEquipmentLedgerMapper equipmentLedgerMapper,
|
||||||
|
MesXslMasterBatchPlanMapper masterBatchPlanMapper,
|
||||||
|
MesXslFinalBatchPlanMapper finalBatchPlanMapper,
|
||||||
|
MesXslMixingSpecMapper mixingSpecMapper) {
|
||||||
|
this.equipmentLedgerMapper = equipmentLedgerMapper;
|
||||||
|
this.masterBatchPlanMapper = masterBatchPlanMapper;
|
||||||
|
this.finalBatchPlanMapper = finalBatchPlanMapper;
|
||||||
|
this.mixingSpecMapper = mixingSpecMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void saveAllRows(List<MesXslMixingProductionPlan> rows) {
|
||||||
|
this.remove(new LambdaQueryWrapper<>());
|
||||||
|
if (CollectionUtils.isEmpty(rows)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Map<String, String> machineNameCache = new HashMap<>();
|
||||||
|
List<MesXslMixingProductionPlan> saveList = new ArrayList<>();
|
||||||
|
int sort = 1;
|
||||||
|
for (MesXslMixingProductionPlan row : rows) {
|
||||||
|
if (row == null || !hasBusinessData(row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
normalizeRow(row, machineNameCache);
|
||||||
|
row.setSortNo(sort++);
|
||||||
|
saveList.add(row);
|
||||||
|
}
|
||||||
|
if (!saveList.isEmpty()) {
|
||||||
|
this.saveBatch(saveList, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasBusinessData(MesXslMixingProductionPlan row) {
|
||||||
|
return StringUtils.isNotBlank(row.getMachineId())
|
||||||
|
|| StringUtils.isNotBlank(row.getMorningPlanId())
|
||||||
|
|| StringUtils.isNotBlank(row.getNoonPlanId())
|
||||||
|
|| StringUtils.isNotBlank(row.getNightPlanId())
|
||||||
|
|| StringUtils.isNotBlank(row.getMorningRemark())
|
||||||
|
|| StringUtils.isNotBlank(row.getNoonRemark())
|
||||||
|
|| StringUtils.isNotBlank(row.getNightRemark())
|
||||||
|
|| row.getMorningPlanCount() != null
|
||||||
|
|| row.getNoonPlanCount() != null
|
||||||
|
|| row.getNightPlanCount() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void normalizeRow(
|
||||||
|
MesXslMixingProductionPlan row, Map<String, String> machineNameCache) {
|
||||||
|
row.setId(null);
|
||||||
|
row.setMachineId(StringUtils.trimToNull(row.getMachineId()));
|
||||||
|
row.setMachineName(resolveMachineName(row.getMachineId(), machineNameCache));
|
||||||
|
fillShiftFromPlan(row, "morning");
|
||||||
|
fillShiftFromPlan(row, "noon");
|
||||||
|
fillShiftFromPlan(row, "night");
|
||||||
|
Date now = new Date();
|
||||||
|
if (row.getCreateTime() == null) {
|
||||||
|
row.setCreateTime(now);
|
||||||
|
}
|
||||||
|
row.setUpdateTime(now);
|
||||||
|
if (row.getDelFlag() == null) {
|
||||||
|
row.setDelFlag(CommonConstant.DEL_FLAG_0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveMachineName(String machineId, Map<String, String> cache) {
|
||||||
|
if (StringUtils.isBlank(machineId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return cache.computeIfAbsent(
|
||||||
|
machineId,
|
||||||
|
id -> {
|
||||||
|
MesXslEquipmentLedger ledger = equipmentLedgerMapper.selectById(id);
|
||||||
|
return ledger == null ? null : ledger.getEquipmentName();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillShiftFromPlan(MesXslMixingProductionPlan row, String shift) {
|
||||||
|
String planId = getPlanId(row, shift);
|
||||||
|
String planType = StringUtils.upperCase(StringUtils.trimToEmpty(getPlanType(row, shift)));
|
||||||
|
if (StringUtils.isBlank(planId)) {
|
||||||
|
clearShiftPlanFields(row, shift);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("M".equals(planType)) {
|
||||||
|
MesXslMasterBatchPlan plan = masterBatchPlanMapper.selectById(planId);
|
||||||
|
if (plan == null) {
|
||||||
|
clearShiftPlanFields(row, shift);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setShiftFromMasterPlan(row, shift, plan);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MesXslFinalBatchPlan finalPlan = finalBatchPlanMapper.selectById(planId);
|
||||||
|
if (finalPlan == null) {
|
||||||
|
clearShiftPlanFields(row, shift);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setShiftFromFinalPlan(row, shift, finalPlan);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPlanId(MesXslMixingProductionPlan row, String shift) {
|
||||||
|
return switch (shift) {
|
||||||
|
case "morning" -> StringUtils.trimToNull(row.getMorningPlanId());
|
||||||
|
case "noon" -> StringUtils.trimToNull(row.getNoonPlanId());
|
||||||
|
default -> StringUtils.trimToNull(row.getNightPlanId());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPlanType(MesXslMixingProductionPlan row, String shift) {
|
||||||
|
return switch (shift) {
|
||||||
|
case "morning" -> row.getMorningPlanType();
|
||||||
|
case "noon" -> row.getNoonPlanType();
|
||||||
|
default -> row.getNightPlanType();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setShiftFromMasterPlan(
|
||||||
|
MesXslMixingProductionPlan row, String shift, MesXslMasterBatchPlan plan) {
|
||||||
|
setShiftPlanType(row, shift, "M");
|
||||||
|
setShiftOrderNo(row, shift, plan.getOrderNo());
|
||||||
|
setShiftOrderDate(row, shift, plan.getOrderDate());
|
||||||
|
setShiftFormulaName(
|
||||||
|
row,
|
||||||
|
shift,
|
||||||
|
resolveFormulaNameByMachineAndMaterial(
|
||||||
|
row.getMachineId(), row.getMachineName(), plan.getMaterialCode(), plan.getMesMaterialName()));
|
||||||
|
setShiftPlanWeight(row, shift, plan.getPlanWeight());
|
||||||
|
setShiftPlannedCarCount(row, shift, plan.getPlannedCarCount());
|
||||||
|
setShiftScheduledCarCount(row, shift, plan.getScheduledCarCount());
|
||||||
|
setShiftFinishedCarCount(row, shift, plan.getFinishedCarCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setShiftFromFinalPlan(
|
||||||
|
MesXslMixingProductionPlan row, String shift, MesXslFinalBatchPlan plan) {
|
||||||
|
setShiftPlanType(row, shift, "F");
|
||||||
|
setShiftOrderNo(row, shift, plan.getOrderNo());
|
||||||
|
setShiftOrderDate(row, shift, plan.getOrderDate());
|
||||||
|
setShiftFormulaName(
|
||||||
|
row,
|
||||||
|
shift,
|
||||||
|
resolveFormulaNameByMachineAndMaterial(
|
||||||
|
row.getMachineId(), row.getMachineName(), plan.getMaterialCode(), plan.getMesMaterialName()));
|
||||||
|
setShiftPlanWeight(row, shift, plan.getPlanWeight());
|
||||||
|
setShiftPlannedCarCount(row, shift, plan.getPlannedCarCount());
|
||||||
|
setShiftScheduledCarCount(row, shift, plan.getScheduledCarCount());
|
||||||
|
setShiftFinishedCarCount(row, shift, plan.getFinishedCarCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearShiftPlanFields(MesXslMixingProductionPlan row, String shift) {
|
||||||
|
setShiftPlanType(row, shift, null);
|
||||||
|
setShiftOrderNo(row, shift, null);
|
||||||
|
setShiftOrderDate(row, shift, null);
|
||||||
|
setShiftFormulaName(row, shift, null);
|
||||||
|
setShiftPlanWeight(row, shift, null);
|
||||||
|
setShiftPlannedCarCount(row, shift, null);
|
||||||
|
setShiftScheduledCarCount(row, shift, null);
|
||||||
|
setShiftFinishedCarCount(row, shift, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setShiftPlanType(MesXslMixingProductionPlan row, String shift, String value) {
|
||||||
|
switch (shift) {
|
||||||
|
case "morning" -> row.setMorningPlanType(value);
|
||||||
|
case "noon" -> row.setNoonPlanType(value);
|
||||||
|
default -> row.setNightPlanType(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setShiftOrderNo(MesXslMixingProductionPlan row, String shift, String value) {
|
||||||
|
switch (shift) {
|
||||||
|
case "morning" -> row.setMorningOrderNo(value);
|
||||||
|
case "noon" -> row.setNoonOrderNo(value);
|
||||||
|
default -> row.setNightOrderNo(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setShiftOrderDate(MesXslMixingProductionPlan row, String shift, Date value) {
|
||||||
|
switch (shift) {
|
||||||
|
case "morning" -> row.setMorningOrderDate(value);
|
||||||
|
case "noon" -> row.setNoonOrderDate(value);
|
||||||
|
default -> row.setNightOrderDate(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setShiftFormulaName(MesXslMixingProductionPlan row, String shift, String value) {
|
||||||
|
switch (shift) {
|
||||||
|
case "morning" -> row.setMorningFormulaName(value);
|
||||||
|
case "noon" -> row.setNoonFormulaName(value);
|
||||||
|
default -> row.setNightFormulaName(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setShiftPlanWeight(
|
||||||
|
MesXslMixingProductionPlan row, String shift, java.math.BigDecimal value) {
|
||||||
|
switch (shift) {
|
||||||
|
case "morning" -> row.setMorningPlanWeight(value);
|
||||||
|
case "noon" -> row.setNoonPlanWeight(value);
|
||||||
|
default -> row.setNightPlanWeight(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setShiftPlannedCarCount(MesXslMixingProductionPlan row, String shift, Integer value) {
|
||||||
|
switch (shift) {
|
||||||
|
case "morning" -> row.setMorningPlannedCarCount(value);
|
||||||
|
case "noon" -> row.setNoonPlannedCarCount(value);
|
||||||
|
default -> row.setNightPlannedCarCount(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setShiftScheduledCarCount(MesXslMixingProductionPlan row, String shift, Integer value) {
|
||||||
|
switch (shift) {
|
||||||
|
case "morning" -> row.setMorningScheduledCarCount(value);
|
||||||
|
case "noon" -> row.setNoonScheduledCarCount(value);
|
||||||
|
default -> row.setNightScheduledCarCount(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setShiftFinishedCarCount(MesXslMixingProductionPlan row, String shift, Integer value) {
|
||||||
|
switch (shift) {
|
||||||
|
case "morning" -> row.setMorningFinishedCarCount(value);
|
||||||
|
case "noon" -> row.setNoonFinishedCarCount(value);
|
||||||
|
default -> row.setNightFinishedCarCount(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPage<MesXslMixingProductionPlanOrderOptionVO> queryOrderOptions(
|
||||||
|
Integer pageNo, Integer pageSize, String keyword, String machineId, String machineName) {
|
||||||
|
String machineIdTrimmed = StringUtils.trimToNull(machineId);
|
||||||
|
String machineNameTrimmed = StringUtils.trimToNull(machineName);
|
||||||
|
List<MesXslMixingProductionPlanOrderOptionVO> all = new ArrayList<>();
|
||||||
|
LambdaQueryWrapper<MesXslMasterBatchPlan> masterWrapper =
|
||||||
|
new LambdaQueryWrapper<MesXslMasterBatchPlan>()
|
||||||
|
.and(w -> w.eq(MesXslMasterBatchPlan::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesXslMasterBatchPlan::getDelFlag));
|
||||||
|
LambdaQueryWrapper<MesXslFinalBatchPlan> finalWrapper =
|
||||||
|
new LambdaQueryWrapper<MesXslFinalBatchPlan>()
|
||||||
|
.and(w -> w.eq(MesXslFinalBatchPlan::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesXslFinalBatchPlan::getDelFlag));
|
||||||
|
if (StringUtils.isNotBlank(keyword)) {
|
||||||
|
masterWrapper.and(
|
||||||
|
w ->
|
||||||
|
w.like(MesXslMasterBatchPlan::getOrderNo, keyword)
|
||||||
|
.or()
|
||||||
|
.like(MesXslMasterBatchPlan::getMesMaterialName, keyword));
|
||||||
|
finalWrapper.and(
|
||||||
|
w ->
|
||||||
|
w.like(MesXslFinalBatchPlan::getOrderNo, keyword)
|
||||||
|
.or()
|
||||||
|
.like(MesXslFinalBatchPlan::getMesMaterialName, keyword));
|
||||||
|
}
|
||||||
|
List<MesXslMasterBatchPlan> masters = masterBatchPlanMapper.selectList(masterWrapper);
|
||||||
|
for (MesXslMasterBatchPlan m : masters) {
|
||||||
|
MesXslMixingProductionPlanOrderOptionVO vo = new MesXslMixingProductionPlanOrderOptionVO();
|
||||||
|
vo.setPlanId(m.getId());
|
||||||
|
vo.setPlanType("M");
|
||||||
|
vo.setOrderNo(m.getOrderNo());
|
||||||
|
vo.setOrderDate(m.getOrderDate());
|
||||||
|
vo.setFormulaName(
|
||||||
|
resolveFormulaNameByMachineAndMaterial(
|
||||||
|
machineIdTrimmed, machineNameTrimmed, m.getMaterialCode(), m.getMesMaterialName()));
|
||||||
|
vo.setPlanWeight(m.getPlanWeight());
|
||||||
|
vo.setPlannedCarCount(m.getPlannedCarCount());
|
||||||
|
vo.setScheduledCarCount(m.getScheduledCarCount());
|
||||||
|
vo.setFinishedCarCount(m.getFinishedCarCount());
|
||||||
|
all.add(vo);
|
||||||
|
}
|
||||||
|
List<MesXslFinalBatchPlan> finals = finalBatchPlanMapper.selectList(finalWrapper);
|
||||||
|
for (MesXslFinalBatchPlan f : finals) {
|
||||||
|
MesXslMixingProductionPlanOrderOptionVO vo = new MesXslMixingProductionPlanOrderOptionVO();
|
||||||
|
vo.setPlanId(f.getId());
|
||||||
|
vo.setPlanType("F");
|
||||||
|
vo.setOrderNo(f.getOrderNo());
|
||||||
|
vo.setOrderDate(f.getOrderDate());
|
||||||
|
vo.setFormulaName(
|
||||||
|
resolveFormulaNameByMachineAndMaterial(
|
||||||
|
machineIdTrimmed, machineNameTrimmed, f.getMaterialCode(), f.getMesMaterialName()));
|
||||||
|
vo.setPlanWeight(f.getPlanWeight());
|
||||||
|
vo.setPlannedCarCount(f.getPlannedCarCount());
|
||||||
|
vo.setScheduledCarCount(f.getScheduledCarCount());
|
||||||
|
vo.setFinishedCarCount(f.getFinishedCarCount());
|
||||||
|
all.add(vo);
|
||||||
|
}
|
||||||
|
all.sort(
|
||||||
|
Comparator.comparing(
|
||||||
|
MesXslMixingProductionPlanOrderOptionVO::getOrderDate,
|
||||||
|
Comparator.nullsLast(Comparator.reverseOrder()))
|
||||||
|
.thenComparing(
|
||||||
|
MesXslMixingProductionPlanOrderOptionVO::getOrderNo,
|
||||||
|
Comparator.nullsLast(Comparator.reverseOrder()))
|
||||||
|
.thenComparing(
|
||||||
|
MesXslMixingProductionPlanOrderOptionVO::getPlanId,
|
||||||
|
Comparator.nullsLast(Comparator.reverseOrder())));
|
||||||
|
long current = pageNo == null || pageNo < 1 ? 1 : pageNo;
|
||||||
|
long size = pageSize == null || pageSize < 1 ? 20 : pageSize;
|
||||||
|
int from = (int) ((current - 1) * size);
|
||||||
|
int to = (int) Math.min(from + size, all.size());
|
||||||
|
List<MesXslMixingProductionPlanOrderOptionVO> pageRecords =
|
||||||
|
from >= all.size() ? Collections.emptyList() : all.subList(from, to);
|
||||||
|
Page<MesXslMixingProductionPlanOrderOptionVO> page = new Page<>(current, size);
|
||||||
|
page.setTotal(all.size());
|
||||||
|
page.setRecords(
|
||||||
|
pageRecords.stream().filter(Objects::nonNull).collect(Collectors.toList()));
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveFormulaNameByMachineAndMaterial(
|
||||||
|
String machineId, String machineName, String materialCode, String fallbackName) {
|
||||||
|
String code = StringUtils.trimToEmpty(materialCode);
|
||||||
|
if (StringUtils.isBlank(code)) {
|
||||||
|
return StringUtils.defaultString(fallbackName);
|
||||||
|
}
|
||||||
|
String targetSpec = buildTargetSpecName(code);
|
||||||
|
LambdaQueryWrapper<MesXslMixingSpec> exactWrapper =
|
||||||
|
new LambdaQueryWrapper<MesXslMixingSpec>()
|
||||||
|
.eq(MesXslMixingSpec::getSpecName, targetSpec)
|
||||||
|
.and(
|
||||||
|
w ->
|
||||||
|
w.eq(MesXslMixingSpec::getDelFlag, CommonConstant.DEL_FLAG_0)
|
||||||
|
.or()
|
||||||
|
.isNull(MesXslMixingSpec::getDelFlag))
|
||||||
|
.last("LIMIT 1");
|
||||||
|
appendMachineCondition(exactWrapper, machineId, machineName);
|
||||||
|
MesXslMixingSpec exact = mixingSpecMapper.selectOne(exactWrapper);
|
||||||
|
if (exact != null && StringUtils.isNotBlank(exact.getSpecName())) {
|
||||||
|
return exact.getSpecName().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
LambdaQueryWrapper<MesXslMixingSpec> prefixWrapper =
|
||||||
|
new LambdaQueryWrapper<MesXslMixingSpec>()
|
||||||
|
.likeRight(MesXslMixingSpec::getSpecName, code)
|
||||||
|
.and(
|
||||||
|
w ->
|
||||||
|
w.eq(MesXslMixingSpec::getDelFlag, CommonConstant.DEL_FLAG_0)
|
||||||
|
.or()
|
||||||
|
.isNull(MesXslMixingSpec::getDelFlag))
|
||||||
|
.orderByAsc(MesXslMixingSpec::getSpecName)
|
||||||
|
.last("LIMIT 1");
|
||||||
|
appendMachineCondition(prefixWrapper, machineId, machineName);
|
||||||
|
MesXslMixingSpec prefix = mixingSpecMapper.selectOne(prefixWrapper);
|
||||||
|
if (prefix != null && StringUtils.isNotBlank(prefix.getSpecName())) {
|
||||||
|
return prefix.getSpecName().trim();
|
||||||
|
}
|
||||||
|
return StringUtils.defaultIfBlank(fallbackName, targetSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildTargetSpecName(String materialCode) {
|
||||||
|
String code = StringUtils.trimToEmpty(materialCode).toUpperCase();
|
||||||
|
if (code.matches(".*SA\\d{2}$")) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
return code + "SA01";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendMachineCondition(
|
||||||
|
LambdaQueryWrapper<MesXslMixingSpec> wrapper, String machineId, String machineName) {
|
||||||
|
String machineIdTrimmed = StringUtils.trimToNull(machineId);
|
||||||
|
String machineNameTrimmed = StringUtils.trimToNull(machineName);
|
||||||
|
if (machineIdTrimmed != null && machineNameTrimmed != null) {
|
||||||
|
wrapper.and(
|
||||||
|
w ->
|
||||||
|
w.eq(MesXslMixingSpec::getMachineId, machineIdTrimmed)
|
||||||
|
.or()
|
||||||
|
.eq(MesXslMixingSpec::getMachineName, machineNameTrimmed));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (machineIdTrimmed != null) {
|
||||||
|
wrapper.eq(MesXslMixingSpec::getMachineId, machineIdTrimmed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (machineNameTrimmed != null) {
|
||||||
|
wrapper.eq(MesXslMixingSpec::getMachineName, machineNameTrimmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
package org.jeecg.modules.xslmes.service.impl;
|
package org.jeecg.modules.xslmes.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jeecg.common.exception.JeecgBootException;
|
import org.jeecg.common.exception.JeecgBootException;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslFinalBatchPlan;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslMixingSpec;
|
||||||
import org.jeecg.modules.xslmes.service.IMesXslFinalBatchPlanService;
|
import org.jeecg.modules.xslmes.service.IMesXslFinalBatchPlanService;
|
||||||
import org.jeecg.modules.xslmes.entity.MesXslMasterBatchPlan;
|
import org.jeecg.modules.xslmes.entity.MesXslMasterBatchPlan;
|
||||||
import org.jeecg.modules.xslmes.entity.MesXslProductionOrder;
|
import org.jeecg.modules.xslmes.entity.MesXslProductionOrder;
|
||||||
|
import org.jeecg.modules.xslmes.mapper.MesXslMixingSpecMapper;
|
||||||
import org.jeecg.modules.xslmes.mapper.MesXslProductionOrderMapper;
|
import org.jeecg.modules.xslmes.mapper.MesXslProductionOrderMapper;
|
||||||
import org.jeecg.modules.xslmes.service.IMesXslMasterBatchPlanService;
|
import org.jeecg.modules.xslmes.service.IMesXslMasterBatchPlanService;
|
||||||
import org.jeecg.modules.xslmes.service.IMesXslProductionOrderService;
|
import org.jeecg.modules.xslmes.service.IMesXslProductionOrderService;
|
||||||
@@ -20,10 +26,11 @@ public class MesXslProductionOrderServiceImpl
|
|||||||
|
|
||||||
@Autowired private IMesXslMasterBatchPlanService masterBatchPlanService;
|
@Autowired private IMesXslMasterBatchPlanService masterBatchPlanService;
|
||||||
@Autowired private IMesXslFinalBatchPlanService finalBatchPlanService;
|
@Autowired private IMesXslFinalBatchPlanService finalBatchPlanService;
|
||||||
|
@Autowired private MesXslMixingSpecMapper mixingSpecMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public MesXslMasterBatchPlan splitToMasterBatchPlan(String id) {
|
public List<MesXslMasterBatchPlan> splitToMasterBatchPlan(String id) {
|
||||||
if (StringUtils.isBlank(id)) {
|
if (StringUtils.isBlank(id)) {
|
||||||
throw new JeecgBootException("生产订单ID不能为空");
|
throw new JeecgBootException("生产订单ID不能为空");
|
||||||
}
|
}
|
||||||
@@ -34,12 +41,67 @@ public class MesXslProductionOrderServiceImpl
|
|||||||
if (order.getSplitStatus() != null && order.getSplitStatus() == 1) {
|
if (order.getSplitStatus() != null && order.getSplitStatus() == 1) {
|
||||||
throw new JeecgBootException("该生产订单已拆分,无需重复操作");
|
throw new JeecgBootException("该生产订单已拆分,无需重复操作");
|
||||||
}
|
}
|
||||||
MesXslMasterBatchPlan plan = masterBatchPlanService.generateFromProductionOrder(order);
|
List<MesXslMasterBatchPlan> plans = masterBatchPlanService.generateBatchFromProductionOrder(order);
|
||||||
finalBatchPlanService.generateFromProductionOrder(order);
|
MesXslFinalBatchPlan finalPlan = finalBatchPlanService.generateFromProductionOrder(order);
|
||||||
|
validateMixingSpecExists(plans, finalPlan);
|
||||||
MesXslProductionOrder update = new MesXslProductionOrder();
|
MesXslProductionOrder update = new MesXslProductionOrder();
|
||||||
update.setId(order.getId());
|
update.setId(order.getId());
|
||||||
update.setSplitStatus(1);
|
update.setSplitStatus(1);
|
||||||
this.updateById(update);
|
this.updateById(update);
|
||||||
return plan;
|
return plans;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public int splitToMasterBatchPlanBatch(List<String> ids) {
|
||||||
|
if (ids == null || ids.isEmpty()) {
|
||||||
|
throw new JeecgBootException("请至少选择一条生产订单");
|
||||||
|
}
|
||||||
|
int successCount = 0;
|
||||||
|
for (String id : ids) {
|
||||||
|
if (StringUtils.isBlank(id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
splitToMasterBatchPlan(id.trim());
|
||||||
|
successCount++;
|
||||||
|
}
|
||||||
|
return successCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateMixingSpecExists(
|
||||||
|
List<MesXslMasterBatchPlan> masterPlans, MesXslFinalBatchPlan finalPlan) {
|
||||||
|
List<String> requiredSpecs = new ArrayList<>();
|
||||||
|
if (masterPlans != null) {
|
||||||
|
for (MesXslMasterBatchPlan plan : masterPlans) {
|
||||||
|
if (plan != null && StringUtils.isNotBlank(plan.getMaterialCode())) {
|
||||||
|
requiredSpecs.add(plan.getMaterialCode().trim() + "SA01");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (finalPlan != null && StringUtils.isNotBlank(finalPlan.getMaterialCode())) {
|
||||||
|
requiredSpecs.add(finalPlan.getMaterialCode().trim() + "SA01");
|
||||||
|
}
|
||||||
|
if (requiredSpecs.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<String> missingSpecs = new ArrayList<>();
|
||||||
|
for (String specName : requiredSpecs) {
|
||||||
|
MesXslMixingSpec found =
|
||||||
|
mixingSpecMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<MesXslMixingSpec>()
|
||||||
|
.eq(MesXslMixingSpec::getSpecName, specName)
|
||||||
|
.and(
|
||||||
|
w ->
|
||||||
|
w.eq(MesXslMixingSpec::getDelFlag, 0)
|
||||||
|
.or()
|
||||||
|
.isNull(MesXslMixingSpec::getDelFlag))
|
||||||
|
.last("LIMIT 1"));
|
||||||
|
if (found == null) {
|
||||||
|
missingSpecs.add(specName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!missingSpecs.isEmpty()) {
|
||||||
|
throw new JeecgBootException("对应物料的混炼示方不存在,请先生成混炼示方!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package org.jeecg.modules.xslmes.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "密炼生产计划-班次生产订单候选")
|
||||||
|
public class MesXslMixingProductionPlanOrderOptionVO {
|
||||||
|
@Schema(description = "计划ID")
|
||||||
|
private String planId;
|
||||||
|
|
||||||
|
@Schema(description = "计划类型 M母胶/F终胶")
|
||||||
|
private String planType;
|
||||||
|
|
||||||
|
@Schema(description = "订单编号")
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
|
@Schema(description = "订单日期")
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
|
private Date orderDate;
|
||||||
|
|
||||||
|
@Schema(description = "胶料名称")
|
||||||
|
private String formulaName;
|
||||||
|
|
||||||
|
@Schema(description = "计划重量")
|
||||||
|
private BigDecimal planWeight;
|
||||||
|
|
||||||
|
@Schema(description = "计划车数")
|
||||||
|
private Integer plannedCarCount;
|
||||||
|
|
||||||
|
@Schema(description = "已排产车数")
|
||||||
|
private Integer scheduledCarCount;
|
||||||
|
|
||||||
|
@Schema(description = "完成车数")
|
||||||
|
private Integer finishedCarCount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.jeecg.modules.xslmes.vo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslMixingProductionPlan;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class MesXslMixingProductionPlanSaveAllVO {
|
||||||
|
private List<MesXslMixingProductionPlan> rows;
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
-- 密炼生产计划维护(早/中/晚班)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_mixing_production_plan` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`sort_no` int DEFAULT NULL COMMENT '排序号',
|
||||||
|
`machine_id` varchar(32) DEFAULT NULL COMMENT '机台ID(mes_xsl_equipment_ledger.id)',
|
||||||
|
`machine_name` varchar(128) DEFAULT NULL COMMENT '机台名称冗余',
|
||||||
|
|
||||||
|
`morning_plan_id` varchar(32) DEFAULT NULL COMMENT '早班计划ID(母胶/终胶计划)',
|
||||||
|
`morning_plan_type` varchar(2) DEFAULT NULL COMMENT '早班计划类型:M母胶/F终胶',
|
||||||
|
`morning_order_no` varchar(64) DEFAULT NULL COMMENT '早班生产订单',
|
||||||
|
`morning_order_date` date DEFAULT NULL COMMENT '早班订单日期',
|
||||||
|
`morning_formula_name` varchar(128) DEFAULT NULL COMMENT '早班配方名称',
|
||||||
|
`morning_plan_weight` decimal(18,6) DEFAULT NULL COMMENT '早班计划重量',
|
||||||
|
`morning_planned_car_count` int DEFAULT NULL COMMENT '早班计划车数',
|
||||||
|
`morning_scheduled_car_count` int DEFAULT NULL COMMENT '早班已排产车数',
|
||||||
|
`morning_finished_car_count` int DEFAULT NULL COMMENT '早班完成车数',
|
||||||
|
`morning_plan_count` int DEFAULT NULL COMMENT '早班计划',
|
||||||
|
`morning_remark` varchar(500) DEFAULT NULL COMMENT '早班备注',
|
||||||
|
|
||||||
|
`noon_plan_id` varchar(32) DEFAULT NULL COMMENT '中班计划ID(母胶/终胶计划)',
|
||||||
|
`noon_plan_type` varchar(2) DEFAULT NULL COMMENT '中班计划类型:M母胶/F终胶',
|
||||||
|
`noon_order_no` varchar(64) DEFAULT NULL COMMENT '中班生产订单',
|
||||||
|
`noon_order_date` date DEFAULT NULL COMMENT '中班订单日期',
|
||||||
|
`noon_formula_name` varchar(128) DEFAULT NULL COMMENT '中班配方名称',
|
||||||
|
`noon_plan_weight` decimal(18,6) DEFAULT NULL COMMENT '中班计划重量',
|
||||||
|
`noon_planned_car_count` int DEFAULT NULL COMMENT '中班计划车数',
|
||||||
|
`noon_scheduled_car_count` int DEFAULT NULL COMMENT '中班已排产车数',
|
||||||
|
`noon_finished_car_count` int DEFAULT NULL COMMENT '中班完成车数',
|
||||||
|
`noon_plan_count` int DEFAULT NULL COMMENT '中班计划',
|
||||||
|
`noon_remark` varchar(500) DEFAULT NULL COMMENT '中班备注',
|
||||||
|
|
||||||
|
`night_plan_id` varchar(32) DEFAULT NULL COMMENT '晚班计划ID(母胶/终胶计划)',
|
||||||
|
`night_plan_type` varchar(2) DEFAULT NULL COMMENT '晚班计划类型:M母胶/F终胶',
|
||||||
|
`night_order_no` varchar(64) DEFAULT NULL COMMENT '晚班生产订单',
|
||||||
|
`night_order_date` date DEFAULT NULL COMMENT '晚班订单日期',
|
||||||
|
`night_formula_name` varchar(128) DEFAULT NULL COMMENT '晚班配方名称',
|
||||||
|
`night_plan_weight` decimal(18,6) DEFAULT NULL COMMENT '晚班计划重量',
|
||||||
|
`night_planned_car_count` int DEFAULT NULL COMMENT '晚班计划车数',
|
||||||
|
`night_scheduled_car_count` int DEFAULT NULL COMMENT '晚班已排产车数',
|
||||||
|
`night_finished_car_count` int DEFAULT NULL COMMENT '晚班完成车数',
|
||||||
|
`night_plan_count` int DEFAULT NULL COMMENT '晚班计划',
|
||||||
|
`night_remark` varchar(500) DEFAULT NULL COMMENT '晚班备注',
|
||||||
|
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门编码',
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mxmp_machine` (`machine_id`),
|
||||||
|
KEY `idx_mxmp_sort` (`sort_no`),
|
||||||
|
KEY `idx_mxmp_tenant` (`tenant_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES密炼生产计划维护';
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
-- 原材料需求计划明细表
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_raw_material_demand_plan` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`machine_id` varchar(32) DEFAULT NULL COMMENT '机台ID',
|
||||||
|
`machine_name` varchar(64) DEFAULT NULL COMMENT '机台名称',
|
||||||
|
`erp_code` varchar(64) NOT NULL COMMENT 'ERP编号',
|
||||||
|
`raw_material_name` varchar(128) NOT NULL COMMENT '原材料名称',
|
||||||
|
`demand_weight` decimal(18,6) DEFAULT '0.000000' COMMENT '需求重量',
|
||||||
|
`standard_weight` decimal(18,6) DEFAULT '0.000000' COMMENT '标准重量',
|
||||||
|
`actual_weight` decimal(18,6) DEFAULT '0.000000' COMMENT '实际重量',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户ID',
|
||||||
|
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '所属部门',
|
||||||
|
`create_by` varchar(50) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(50) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` tinyint(1) DEFAULT '0' COMMENT '删除标识(0-正常,1-删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_raw_material_demand_machine` (`machine_name`),
|
||||||
|
KEY `idx_raw_material_demand_erp` (`erp_code`),
|
||||||
|
KEY `idx_raw_material_demand_name` (`raw_material_name`),
|
||||||
|
KEY `idx_raw_material_demand_del` (`del_flag`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='原材料需求计划明细';
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package org.jeecg.modules.xslmes.test;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import org.jeecg.JeecgSystemApplication;
|
||||||
|
import org.jeecg.common.exception.JeecgBootException;
|
||||||
|
import org.jeecg.modules.mes.material.entity.MesMaterial;
|
||||||
|
import org.jeecg.modules.mes.material.mapper.MesMaterialMapper;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslFinalBatchPlan;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslMasterBatchPlan;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslMixingSpec;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslProductionOrder;
|
||||||
|
import org.jeecg.modules.xslmes.mapper.MesXslFinalBatchPlanMapper;
|
||||||
|
import org.jeecg.modules.xslmes.mapper.MesXslMasterBatchPlanMapper;
|
||||||
|
import org.jeecg.modules.xslmes.mapper.MesXslMixingSpecMapper;
|
||||||
|
import org.jeecg.modules.xslmes.mapper.MesXslProductionOrderMapper;
|
||||||
|
import org.jeecg.modules.xslmes.service.IMesXslProductionOrderService;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@SpringBootTest(
|
||||||
|
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||||
|
classes = JeecgSystemApplication.class)
|
||||||
|
@Transactional
|
||||||
|
class MesXslProductionOrderSplitIntegrationTest {
|
||||||
|
|
||||||
|
@Resource private IMesXslProductionOrderService productionOrderService;
|
||||||
|
@Resource private MesXslProductionOrderMapper productionOrderMapper;
|
||||||
|
@Resource private MesXslMasterBatchPlanMapper masterBatchPlanMapper;
|
||||||
|
@Resource private MesXslFinalBatchPlanMapper finalBatchPlanMapper;
|
||||||
|
@Resource private MesMaterialMapper materialMapper;
|
||||||
|
@Resource private MesXslMixingSpecMapper mixingSpecMapper;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void splitShouldGenerateB1B2AndFWhenSegmentCountIs3() {
|
||||||
|
String base = "UT" + System.currentTimeMillis();
|
||||||
|
MesXslProductionOrder order = createOrder(base, 3);
|
||||||
|
|
||||||
|
createMaterial("B1" + base + "SA01", "B1-" + base);
|
||||||
|
createMaterial("B2" + base + "SA01", "B2-" + base);
|
||||||
|
createMaterial("F" + base + "SA01", "F-" + base);
|
||||||
|
|
||||||
|
createMixingSpec("B1" + base + "SA01");
|
||||||
|
createMixingSpec("B2" + base + "SA01");
|
||||||
|
createMixingSpec("F" + base + "SA01");
|
||||||
|
|
||||||
|
List<MesXslMasterBatchPlan> masters = productionOrderService.splitToMasterBatchPlan(order.getId());
|
||||||
|
Assertions.assertEquals(2, masters.size(), "段数=3时应生成2条母胶计划");
|
||||||
|
|
||||||
|
List<MesXslMasterBatchPlan> dbMasters =
|
||||||
|
masterBatchPlanMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<MesXslMasterBatchPlan>()
|
||||||
|
.eq(MesXslMasterBatchPlan::getSourceOrderId, order.getId())
|
||||||
|
.orderByAsc(MesXslMasterBatchPlan::getMaterialCode));
|
||||||
|
Assertions.assertEquals(2, dbMasters.size());
|
||||||
|
Assertions.assertEquals("B1" + base, dbMasters.get(0).getMaterialCode());
|
||||||
|
Assertions.assertEquals("B2" + base, dbMasters.get(1).getMaterialCode());
|
||||||
|
|
||||||
|
MesXslFinalBatchPlan finalPlan =
|
||||||
|
finalBatchPlanMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<MesXslFinalBatchPlan>()
|
||||||
|
.eq(MesXslFinalBatchPlan::getSourceOrderId, order.getId())
|
||||||
|
.last("LIMIT 1"));
|
||||||
|
Assertions.assertNotNull(finalPlan, "应生成1条终胶计划");
|
||||||
|
Assertions.assertEquals("F" + base, finalPlan.getMaterialCode());
|
||||||
|
|
||||||
|
MesXslProductionOrder updated = productionOrderMapper.selectById(order.getId());
|
||||||
|
Assertions.assertEquals(1, updated.getSplitStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void splitShouldFailWhenAnyMixingSpecMissing() {
|
||||||
|
String base = "UT" + (System.currentTimeMillis() + 7);
|
||||||
|
MesXslProductionOrder order = createOrder(base, 3);
|
||||||
|
|
||||||
|
createMaterial("B1" + base + "SA01", "B1-" + base);
|
||||||
|
createMaterial("B2" + base + "SA01", "B2-" + base);
|
||||||
|
createMaterial("F" + base + "SA01", "F-" + base);
|
||||||
|
|
||||||
|
// 故意缺少 FxxxSA01
|
||||||
|
createMixingSpec("B1" + base + "SA01");
|
||||||
|
createMixingSpec("B2" + base + "SA01");
|
||||||
|
|
||||||
|
JeecgBootException ex =
|
||||||
|
Assertions.assertThrows(
|
||||||
|
JeecgBootException.class,
|
||||||
|
() -> productionOrderService.splitToMasterBatchPlan(order.getId()));
|
||||||
|
Assertions.assertEquals("对应物料的混炼示方不存在,请先生成混炼示方!", ex.getMessage());
|
||||||
|
|
||||||
|
List<MesXslMasterBatchPlan> dbMasters =
|
||||||
|
masterBatchPlanMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<MesXslMasterBatchPlan>()
|
||||||
|
.eq(MesXslMasterBatchPlan::getSourceOrderId, order.getId()));
|
||||||
|
Assertions.assertTrue(dbMasters.isEmpty(), "失败后母胶计划应回滚");
|
||||||
|
|
||||||
|
MesXslFinalBatchPlan finalPlan =
|
||||||
|
finalBatchPlanMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<MesXslFinalBatchPlan>()
|
||||||
|
.eq(MesXslFinalBatchPlan::getSourceOrderId, order.getId())
|
||||||
|
.last("LIMIT 1"));
|
||||||
|
Assertions.assertNull(finalPlan, "失败后终胶计划应回滚");
|
||||||
|
|
||||||
|
MesXslProductionOrder unchanged = productionOrderMapper.selectById(order.getId());
|
||||||
|
Assertions.assertTrue(
|
||||||
|
unchanged.getSplitStatus() == null || unchanged.getSplitStatus() == 0, "失败后拆分状态不应置为已拆分");
|
||||||
|
}
|
||||||
|
|
||||||
|
private MesXslProductionOrder createOrder(String baseCode, int segmentCount) {
|
||||||
|
MesXslProductionOrder order = new MesXslProductionOrder();
|
||||||
|
order.setProductionOrderNo("PO-" + baseCode);
|
||||||
|
order.setOrderDate(new Date());
|
||||||
|
order.setProcessSegmentCount(segmentCount);
|
||||||
|
order.setMesMaterialName(baseCode);
|
||||||
|
order.setMaterialCode(baseCode);
|
||||||
|
order.setPlanQty(new BigDecimal("100.00"));
|
||||||
|
order.setSplitStatus(0);
|
||||||
|
order.setDelFlag(0);
|
||||||
|
productionOrderMapper.insert(order);
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createMaterial(String code, String name) {
|
||||||
|
MesMaterial material = new MesMaterial();
|
||||||
|
material.setMaterialCode(code);
|
||||||
|
material.setMaterialName(name);
|
||||||
|
material.setDelFlag(0);
|
||||||
|
materialMapper.insert(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createMixingSpec(String specName) {
|
||||||
|
MesXslMixingSpec spec = new MesXslMixingSpec();
|
||||||
|
spec.setSpecName(specName);
|
||||||
|
spec.setDelFlag(0);
|
||||||
|
mixingSpecMapper.insert(spec);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<MesXslMixingProductionPlanList />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import MesXslMixingProductionPlanList from '../../xslmes/mesXslMixingProductionPlan/MesXslMixingProductionPlanList.vue';
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<MesXslRawMaterialDemandPlanList />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import MesXslRawMaterialDemandPlanList from '../../xslmes/mesXslRawMaterialDemandPlan/MesXslRawMaterialDemandPlanList.vue';
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
|
||||||
|
enum Api {
|
||||||
|
list = '/xslmes/mesXslMixingProductionPlan/list',
|
||||||
|
saveAll = '/xslmes/mesXslMixingProductionPlan/saveAll',
|
||||||
|
orderOptionPage = '/xslmes/mesXslMixingProductionPlan/orderOptionPage',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||||
|
|
||||||
|
export const saveAll = (params) => defHttp.post({ url: Api.saveAll, params });
|
||||||
|
|
||||||
|
export const orderOptionPage = (params) => defHttp.get({ url: Api.orderOptionPage, params }, { successMessageMode: 'none' });
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mixing-plan-page">
|
||||||
|
<div class="mixing-plan-toolbar">
|
||||||
|
<a-button type="primary" preIcon="ant-design:save-outlined" :loading="saving" @click="handleSaveAll">保存</a-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-table
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="rows"
|
||||||
|
:pagination="false"
|
||||||
|
:scroll="{ x: 1560 }"
|
||||||
|
row-key="_rowKey"
|
||||||
|
size="small"
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record, index }">
|
||||||
|
<template v-if="column.dataIndex === 'machineName'">
|
||||||
|
<a-input
|
||||||
|
:value="record.machineName"
|
||||||
|
readonly
|
||||||
|
placeholder="点击选择机台"
|
||||||
|
class="picker-input"
|
||||||
|
@click="openMachinePicker(index)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template
|
||||||
|
v-else-if="
|
||||||
|
column.dataIndex === 'morningOrderNo' ||
|
||||||
|
column.dataIndex === 'noonOrderNo' ||
|
||||||
|
column.dataIndex === 'nightOrderNo'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
:value="record[column.dataIndex]"
|
||||||
|
readonly
|
||||||
|
placeholder="点击选择生产订单计划"
|
||||||
|
class="picker-input"
|
||||||
|
@click="openOrderPicker(index, shiftByOrderField(column.dataIndex as string))"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template
|
||||||
|
v-else-if="
|
||||||
|
column.dataIndex === 'morningFormulaName' ||
|
||||||
|
column.dataIndex === 'noonFormulaName' ||
|
||||||
|
column.dataIndex === 'nightFormulaName'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-input :value="record[column.dataIndex]" readonly />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template
|
||||||
|
v-else-if="
|
||||||
|
column.dataIndex === 'morningPlanCount' ||
|
||||||
|
column.dataIndex === 'noonPlanCount' ||
|
||||||
|
column.dataIndex === 'nightPlanCount'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
:value="record[column.dataIndex]"
|
||||||
|
:min="0"
|
||||||
|
:precision="0"
|
||||||
|
:controls="false"
|
||||||
|
style="width: 100%"
|
||||||
|
@change="(v) => updateCell(index, column.dataIndex as string, v)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template
|
||||||
|
v-else-if="
|
||||||
|
column.dataIndex === 'morningRemark' || column.dataIndex === 'noonRemark' || column.dataIndex === 'nightRemark'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-input :value="record[column.dataIndex]" @change="(e) => updateCell(index, column.dataIndex as string, e?.target?.value)" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else-if="column.key === 'action'">
|
||||||
|
<a-button type="link" size="small" danger @click="removeRow(index)">删行</a-button>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.key === 'insert'">
|
||||||
|
<a-button type="text" size="small" class="insert-plus-btn" title="新增" @click="insertBelow(index)">+</a-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
|
||||||
|
<MesXslEquipmentLedgerSelectModal @register="registerEquipmentModal" @select="onEquipmentSelect" />
|
||||||
|
<MesXslMixingPlanOrderSelectModal @register="registerOrderModal" @select="onOrderSelect" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { useModal } from '/@/components/Modal';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import { buildUUID } from '/@/utils/uuid';
|
||||||
|
import MesXslEquipmentLedgerSelectModal from '/@/views/xslmes/mesXslEquipInspectConfig/components/MesXslEquipmentLedgerSelectModal.vue';
|
||||||
|
import MesXslMixingPlanOrderSelectModal from './components/MesXslMixingPlanOrderSelectModal.vue';
|
||||||
|
import { list, saveAll } from './MesXslMixingProductionPlan.api';
|
||||||
|
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
const saving = ref(false);
|
||||||
|
const rows = ref<Recordable[]>([]);
|
||||||
|
const pickerContext = ref<{ rowIndex: number; shift?: 'morning' | 'noon' | 'night' } | null>(null);
|
||||||
|
|
||||||
|
const [registerEquipmentModal, { openModal: openEquipmentModal }] = useModal();
|
||||||
|
const [registerOrderModal, { openModal: openOrderModal }] = useModal();
|
||||||
|
|
||||||
|
const columns = computed(() => [
|
||||||
|
{ title: '新增', key: 'insert', width: 64, fixed: 'left', align: 'center' },
|
||||||
|
{ title: '机台', dataIndex: 'machineName', width: 108, fixed: 'left', align: 'center' },
|
||||||
|
{
|
||||||
|
title: '早班',
|
||||||
|
align: 'center',
|
||||||
|
children: [
|
||||||
|
{ title: '生产订单', dataIndex: 'morningOrderNo', width: 120, align: 'center' },
|
||||||
|
{ title: '配方名称', dataIndex: 'morningFormulaName', width: 110, align: 'center' },
|
||||||
|
{ title: '计划', dataIndex: 'morningPlanCount', width: 72, align: 'center' },
|
||||||
|
{ title: '备注', dataIndex: 'morningRemark', width: 98, align: 'center' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '中班',
|
||||||
|
align: 'center',
|
||||||
|
children: [
|
||||||
|
{ title: '生产订单', dataIndex: 'noonOrderNo', width: 120, align: 'center' },
|
||||||
|
{ title: '配方名称', dataIndex: 'noonFormulaName', width: 110, align: 'center' },
|
||||||
|
{ title: '计划', dataIndex: 'noonPlanCount', width: 72, align: 'center' },
|
||||||
|
{ title: '备注', dataIndex: 'noonRemark', width: 98, align: 'center' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '晚班',
|
||||||
|
align: 'center',
|
||||||
|
children: [
|
||||||
|
{ title: '生产订单', dataIndex: 'nightOrderNo', width: 120, align: 'center' },
|
||||||
|
{ title: '配方名称', dataIndex: 'nightFormulaName', width: 110, align: 'center' },
|
||||||
|
{ title: '计划', dataIndex: 'nightPlanCount', width: 72, align: 'center' },
|
||||||
|
{ title: '备注', dataIndex: 'nightRemark', width: 98, align: 'center' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ title: '删除', key: 'action', width: 64, fixed: 'right', align: 'center' },
|
||||||
|
]);
|
||||||
|
|
||||||
|
function createEmptyRow(): Recordable {
|
||||||
|
return {
|
||||||
|
_rowKey: buildUUID(),
|
||||||
|
id: '',
|
||||||
|
machineId: '',
|
||||||
|
machineName: '',
|
||||||
|
morningPlanId: '',
|
||||||
|
morningPlanType: '',
|
||||||
|
morningOrderNo: '',
|
||||||
|
morningOrderDate: '',
|
||||||
|
morningFormulaName: '',
|
||||||
|
morningPlanWeight: null,
|
||||||
|
morningPlannedCarCount: null,
|
||||||
|
morningScheduledCarCount: null,
|
||||||
|
morningFinishedCarCount: null,
|
||||||
|
morningPlanCount: null,
|
||||||
|
morningRemark: '',
|
||||||
|
noonPlanId: '',
|
||||||
|
noonPlanType: '',
|
||||||
|
noonOrderNo: '',
|
||||||
|
noonOrderDate: '',
|
||||||
|
noonFormulaName: '',
|
||||||
|
noonPlanWeight: null,
|
||||||
|
noonPlannedCarCount: null,
|
||||||
|
noonScheduledCarCount: null,
|
||||||
|
noonFinishedCarCount: null,
|
||||||
|
noonPlanCount: null,
|
||||||
|
noonRemark: '',
|
||||||
|
nightPlanId: '',
|
||||||
|
nightPlanType: '',
|
||||||
|
nightOrderNo: '',
|
||||||
|
nightOrderDate: '',
|
||||||
|
nightFormulaName: '',
|
||||||
|
nightPlanWeight: null,
|
||||||
|
nightPlannedCarCount: null,
|
||||||
|
nightScheduledCarCount: null,
|
||||||
|
nightFinishedCarCount: null,
|
||||||
|
nightPlanCount: null,
|
||||||
|
nightRemark: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function shiftByOrderField(field: string): 'morning' | 'noon' | 'night' {
|
||||||
|
if (field.startsWith('morning')) return 'morning';
|
||||||
|
if (field.startsWith('noon')) return 'noon';
|
||||||
|
return 'night';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCell(index: number, field: string, value: any) {
|
||||||
|
const row = rows.value[index];
|
||||||
|
if (!row) return;
|
||||||
|
row[field] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBlankRows(count = 12) {
|
||||||
|
return Array.from({ length: count }, () => createEmptyRow());
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertBelow(index: number) {
|
||||||
|
rows.value.splice(index + 1, 0, createEmptyRow());
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeRow(index: number) {
|
||||||
|
rows.value.splice(index, 1);
|
||||||
|
if (!rows.value.length) {
|
||||||
|
rows.value = createBlankRows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMachinePicker(rowIndex: number) {
|
||||||
|
pickerContext.value = { rowIndex };
|
||||||
|
const row = rows.value[rowIndex];
|
||||||
|
openEquipmentModal(true, { equipmentLedgerId: row?.machineId || '' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEquipmentSelect(payload: Recordable) {
|
||||||
|
const ctx = pickerContext.value;
|
||||||
|
if (!ctx) return;
|
||||||
|
const row = rows.value[ctx.rowIndex];
|
||||||
|
if (!row) return;
|
||||||
|
row.machineId = payload?.equipmentLedgerId || '';
|
||||||
|
row.machineName = payload?.equipmentName || '';
|
||||||
|
pickerContext.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openOrderPicker(rowIndex: number, shift: 'morning' | 'noon' | 'night') {
|
||||||
|
pickerContext.value = { rowIndex, shift };
|
||||||
|
const row = rows.value[rowIndex];
|
||||||
|
openOrderModal(true, {
|
||||||
|
planId: row?.[`${shift}PlanId`] || '',
|
||||||
|
machineId: row?.machineId || '',
|
||||||
|
machineName: row?.machineName || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOrderSelect(payload: Recordable | null) {
|
||||||
|
const ctx = pickerContext.value;
|
||||||
|
if (!ctx || !ctx.shift) return;
|
||||||
|
const row = rows.value[ctx.rowIndex];
|
||||||
|
if (!row) return;
|
||||||
|
const shift = ctx.shift;
|
||||||
|
row[`${shift}PlanId`] = payload?.planId || '';
|
||||||
|
row[`${shift}PlanType`] = payload?.planType || '';
|
||||||
|
row[`${shift}OrderNo`] = payload?.orderNo || '';
|
||||||
|
row[`${shift}OrderDate`] = payload?.orderDate || '';
|
||||||
|
row[`${shift}FormulaName`] = payload?.formulaName || '';
|
||||||
|
row[`${shift}PlanWeight`] = payload?.planWeight ?? null;
|
||||||
|
row[`${shift}PlannedCarCount`] = payload?.plannedCarCount ?? null;
|
||||||
|
row[`${shift}ScheduledCarCount`] = payload?.scheduledCarCount ?? null;
|
||||||
|
row[`${shift}FinishedCarCount`] = payload?.finishedCarCount ?? null;
|
||||||
|
pickerContext.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadRows() {
|
||||||
|
const res = await list({ pageNo: 1, pageSize: 500 });
|
||||||
|
const records = (res?.records || res?.result?.records || []) as Recordable[];
|
||||||
|
rows.value = (records || []).map((item) => ({ ...createEmptyRow(), ...item, _rowKey: item.id || buildUUID() }));
|
||||||
|
if (!rows.value.length) {
|
||||||
|
rows.value = createBlankRows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSaveAll() {
|
||||||
|
saving.value = true;
|
||||||
|
try {
|
||||||
|
const payload = rows.value.map((r) => {
|
||||||
|
const { _rowKey, ...rest } = r;
|
||||||
|
return rest;
|
||||||
|
});
|
||||||
|
await saveAll({ rows: payload });
|
||||||
|
createMessage.success('保存成功');
|
||||||
|
await loadRows();
|
||||||
|
} finally {
|
||||||
|
saving.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadRows();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.mixing-plan-page {
|
||||||
|
padding: 8px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mixing-plan-toolbar {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-input {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-table-thead > tr > th) {
|
||||||
|
text-align: center !important;
|
||||||
|
padding: 4px 6px !important;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-table-tbody > tr > td) {
|
||||||
|
padding: 2px 3px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-input),
|
||||||
|
:deep(.ant-input-number),
|
||||||
|
:deep(.ant-input-number-input) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-input),
|
||||||
|
:deep(.ant-input-number) {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-input-number-input) {
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-plus-btn {
|
||||||
|
color: #52c41a;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-plus-btn:hover,
|
||||||
|
.insert-plus-btn:focus {
|
||||||
|
color: #73d13d;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<BasicModal v-bind="$attrs" title="选择生产订单计划" :width="1100" @register="registerModal" @ok="handleOk">
|
||||||
|
<BasicTable @register="registerTable" />
|
||||||
|
</BasicModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||||
|
import { BasicTable, useTable } from '/@/components/Table';
|
||||||
|
import { orderOptionPage } from '../MesXslMixingProductionPlan.api';
|
||||||
|
|
||||||
|
const emit = defineEmits(['register', 'select']);
|
||||||
|
|
||||||
|
const selectedRow = ref<Recordable | null>(null);
|
||||||
|
const machineId = ref('');
|
||||||
|
const machineName = ref('');
|
||||||
|
|
||||||
|
const [registerTable, { reload, getSelectRowKeys, getSelectRows, setSelectedRowKeys, clearSelectedRowKeys }] = useTable({
|
||||||
|
api: orderOptionPage,
|
||||||
|
columns: [
|
||||||
|
{ title: '类型', dataIndex: 'planType', width: 70, customRender: ({ text }) => (text === 'M' ? '母胶' : '终胶') },
|
||||||
|
{ title: '订单编号', dataIndex: 'orderNo', width: 150 },
|
||||||
|
{ title: '订单日期', dataIndex: 'orderDate', width: 120 },
|
||||||
|
{ title: '胶料名称', dataIndex: 'formulaName', width: 170 },
|
||||||
|
{ title: '计划重量', dataIndex: 'planWeight', width: 120 },
|
||||||
|
{ title: '计划车数', dataIndex: 'plannedCarCount', width: 100 },
|
||||||
|
{ title: '已排产车数', dataIndex: 'scheduledCarCount', width: 110 },
|
||||||
|
{ title: '完成车数', dataIndex: 'finishedCarCount', width: 100 },
|
||||||
|
],
|
||||||
|
rowKey: 'planId',
|
||||||
|
useSearchForm: true,
|
||||||
|
formConfig: {
|
||||||
|
labelWidth: 80,
|
||||||
|
schemas: [{ label: '关键字', field: 'keyword', component: 'Input', colProps: { span: 10 } }],
|
||||||
|
},
|
||||||
|
pagination: { pageSize: 10 },
|
||||||
|
canResize: false,
|
||||||
|
showIndexColumn: false,
|
||||||
|
immediate: true,
|
||||||
|
beforeFetch: (params) => {
|
||||||
|
return Object.assign(params, {
|
||||||
|
machineId: machineId.value || undefined,
|
||||||
|
machineName: machineName.value || undefined,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
rowSelection: {
|
||||||
|
type: 'radio',
|
||||||
|
columnWidth: 48,
|
||||||
|
onChange: (_keys, rows) => {
|
||||||
|
selectedRow.value = rows?.[0] ?? null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
clickToRowSelect: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||||
|
selectedRow.value = null;
|
||||||
|
clearSelectedRowKeys?.();
|
||||||
|
setModalProps({ confirmLoading: false });
|
||||||
|
machineId.value = data?.machineId || '';
|
||||||
|
machineName.value = data?.machineName || '';
|
||||||
|
if (data?.planId) {
|
||||||
|
setSelectedRowKeys?.([data.planId]);
|
||||||
|
}
|
||||||
|
reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleOk() {
|
||||||
|
const row = selectedRow.value || ((getSelectRows?.() || []) as Recordable[])[0];
|
||||||
|
if (!row?.planId) {
|
||||||
|
emit('select', null);
|
||||||
|
closeModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit('select', {
|
||||||
|
planId: row.planId,
|
||||||
|
planType: row.planType,
|
||||||
|
orderNo: row.orderNo || '',
|
||||||
|
orderDate: row.orderDate || '',
|
||||||
|
formulaName: row.formulaName || '',
|
||||||
|
planWeight: row.planWeight ?? null,
|
||||||
|
plannedCarCount: row.plannedCarCount ?? null,
|
||||||
|
scheduledCarCount: row.scheduledCarCount ?? null,
|
||||||
|
finishedCarCount: row.finishedCarCount ?? null,
|
||||||
|
});
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -8,6 +8,7 @@ enum Api {
|
|||||||
deleteBatch = '/xslmes/mesXslProductionOrder/deleteBatch',
|
deleteBatch = '/xslmes/mesXslProductionOrder/deleteBatch',
|
||||||
queryById = '/xslmes/mesXslProductionOrder/queryById',
|
queryById = '/xslmes/mesXslProductionOrder/queryById',
|
||||||
split = '/xslmes/mesXslProductionOrder/split',
|
split = '/xslmes/mesXslProductionOrder/split',
|
||||||
|
splitBatch = '/xslmes/mesXslProductionOrder/splitBatch',
|
||||||
exportXls = '/xslmes/mesXslProductionOrder/exportXls',
|
exportXls = '/xslmes/mesXslProductionOrder/exportXls',
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,4 +27,7 @@ export const queryById = (params) => defHttp.get({ url: Api.queryById, params })
|
|||||||
export const splitToMasterBatchPlan = (params, handleSuccess) =>
|
export const splitToMasterBatchPlan = (params, handleSuccess) =>
|
||||||
defHttp.post({ url: Api.split, params }, { joinParamsToUrl: true }).then(() => handleSuccess());
|
defHttp.post({ url: Api.split, params }, { joinParamsToUrl: true }).then(() => handleSuccess());
|
||||||
|
|
||||||
|
export const splitToMasterBatchPlanBatch = (params, handleSuccess) =>
|
||||||
|
defHttp.post({ url: Api.splitBatch, params }, { joinParamsToUrl: true }).then(() => handleSuccess());
|
||||||
|
|
||||||
export const getExportUrl = Api.exportXls;
|
export const getExportUrl = Api.exportXls;
|
||||||
|
|||||||
@@ -11,6 +11,15 @@
|
|||||||
>
|
>
|
||||||
导出
|
导出
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
v-auth="'xslmes:mes_xsl_production_order:split'"
|
||||||
|
preIcon="ant-design:split-cells-outlined"
|
||||||
|
:disabled="selectedRowKeys.length === 0"
|
||||||
|
@click="handleBatchSplit"
|
||||||
|
>
|
||||||
|
拆分
|
||||||
|
</a-button>
|
||||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-menu>
|
<a-menu>
|
||||||
@@ -32,11 +41,14 @@
|
|||||||
import { BasicTable, TableAction } from '/@/components/Table';
|
import { BasicTable, TableAction } from '/@/components/Table';
|
||||||
import { useModal } from '/@/components/Modal';
|
import { useModal } from '/@/components/Modal';
|
||||||
import { useListPage } from '/@/hooks/system/useListPage';
|
import { useListPage } from '/@/hooks/system/useListPage';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import { Modal } from 'ant-design-vue';
|
||||||
import MesXslProductionOrderModal from './modules/MesXslProductionOrderModal.vue';
|
import MesXslProductionOrderModal from './modules/MesXslProductionOrderModal.vue';
|
||||||
import { columns, searchFormSchema } from './MesXslProductionOrder.data';
|
import { columns, searchFormSchema } from './MesXslProductionOrder.data';
|
||||||
import { batchDelete, deleteOne, getExportUrl, list, splitToMasterBatchPlan } from './MesXslProductionOrder.api';
|
import { batchDelete, deleteOne, getExportUrl, list, splitToMasterBatchPlanBatch } from './MesXslProductionOrder.api';
|
||||||
|
|
||||||
const [registerModal, { openModal }] = useModal();
|
const [registerModal, { openModal }] = useModal();
|
||||||
|
const { createMessage } = useMessage();
|
||||||
const { tableContext, onExportXls } = useListPage({
|
const { tableContext, onExportXls } = useListPage({
|
||||||
tableProps: {
|
tableProps: {
|
||||||
title: '生产订单',
|
title: '生产订单',
|
||||||
@@ -65,8 +77,25 @@
|
|||||||
async function batchHandleDelete() {
|
async function batchHandleDelete() {
|
||||||
await batchDelete({ ids: selectedRowKeys.value.join(',') }, reload);
|
await batchDelete({ ids: selectedRowKeys.value.join(',') }, reload);
|
||||||
}
|
}
|
||||||
async function handleSplit(record) {
|
async function handleBatchSplit() {
|
||||||
await splitToMasterBatchPlan({ id: record.id }, reload);
|
if (!selectedRowKeys.value.length) {
|
||||||
|
createMessage.warning('请先勾选要拆分的生产订单');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认批量拆分',
|
||||||
|
content: `将拆分 ${selectedRowKeys.value.length} 条生产订单,是否继续?`,
|
||||||
|
okText: '确认',
|
||||||
|
cancelText: '取消',
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
await splitToMasterBatchPlanBatch({ ids: selectedRowKeys.value.join(',') }, reload);
|
||||||
|
createMessage.success('批量拆分成功');
|
||||||
|
} catch (e: any) {
|
||||||
|
createMessage.error(e?.message || '批量拆分失败');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function handleSuccess() {
|
function handleSuccess() {
|
||||||
reload();
|
reload();
|
||||||
@@ -74,12 +103,6 @@
|
|||||||
function getTableAction(record) {
|
function getTableAction(record) {
|
||||||
return [
|
return [
|
||||||
{ label: '编辑', onClick: handleEdit.bind(null, record), auth: 'xslmes:mes_xsl_production_order:edit' },
|
{ label: '编辑', onClick: handleEdit.bind(null, record), auth: 'xslmes:mes_xsl_production_order:edit' },
|
||||||
{
|
|
||||||
label: '拆分',
|
|
||||||
onClick: handleSplit.bind(null, record),
|
|
||||||
auth: 'xslmes:mes_xsl_production_order:split',
|
|
||||||
ifShow: () => record.splitStatus !== 1,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
function getDropDownAction(record) {
|
function getDropDownAction(record) {
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
|
||||||
|
enum Api {
|
||||||
|
list = '/xslmes/mesXslRawMaterialDemandPlan/list',
|
||||||
|
exportXls = '/xslmes/mesXslRawMaterialDemandPlan/exportXls',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getExportUrl = Api.exportXls;
|
||||||
|
|
||||||
|
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||||
|
|
||||||
|
export const baseColumns: BasicColumn[] = [
|
||||||
|
{ title: 'ERP编号', align: 'center', dataIndex: 'erpCode', width: 180 },
|
||||||
|
{ title: '原材料名称', align: 'center', dataIndex: 'rawMaterialName', width: 220, ellipsis: true },
|
||||||
|
{ title: '需求重量', align: 'center', dataIndex: 'demandWeight', width: 140 },
|
||||||
|
{ title: '标准重量', align: 'center', dataIndex: 'standardWeight', width: 140 },
|
||||||
|
{ title: '实际重量', align: 'center', dataIndex: 'actualWeight', width: 140 },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const machineColumns: BasicColumn[] = [
|
||||||
|
{ title: '机台', align: 'center', dataIndex: 'machineName', width: 160 },
|
||||||
|
...baseColumns,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const searchFormSchema: FormSchema[] = [
|
||||||
|
{ label: 'ERP编号', field: 'erpCode', component: 'JInput', colProps: { span: 6 } },
|
||||||
|
{ label: '原材料名称', field: 'rawMaterialName', component: 'JInput', colProps: { span: 6 } },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const superQuerySchema = {
|
||||||
|
machineName: { title: '机台', order: 0, view: 'text' },
|
||||||
|
erpCode: { title: 'ERP编号', order: 1, view: 'text' },
|
||||||
|
rawMaterialName: { title: '原材料名称', order: 2, view: 'text' },
|
||||||
|
demandWeight: { title: '需求重量', order: 3, view: 'number' },
|
||||||
|
standardWeight: { title: '标准重量', order: 4, view: 'number' },
|
||||||
|
actualWeight: { title: '实际重量', order: 5, view: 'number' },
|
||||||
|
};
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<BasicTable @register="registerTable">
|
||||||
|
<template #tableTitle>
|
||||||
|
<a-checkbox v-model:checked="groupByMachine" @change="onGroupByMachineChange">按机台统计</a-checkbox>
|
||||||
|
<a-button
|
||||||
|
style="margin-left: 8px"
|
||||||
|
type="primary"
|
||||||
|
v-auth="'xslmes:mes_xsl_raw_material_demand_plan:exportXls'"
|
||||||
|
preIcon="ant-design:export-outlined"
|
||||||
|
@click="onExportXls"
|
||||||
|
>
|
||||||
|
导出
|
||||||
|
</a-button>
|
||||||
|
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" name="xslmes-mesXslRawMaterialDemandPlan" setup>
|
||||||
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
|
import { BasicTable } from '/@/components/Table';
|
||||||
|
import { useListPage } from '/@/hooks/system/useListPage';
|
||||||
|
import {
|
||||||
|
baseColumns,
|
||||||
|
machineColumns,
|
||||||
|
searchFormSchema,
|
||||||
|
superQuerySchema,
|
||||||
|
} from './MesXslRawMaterialDemandPlan.data';
|
||||||
|
import { list, getExportUrl } from './MesXslRawMaterialDemandPlan.api';
|
||||||
|
|
||||||
|
const queryParam = reactive<any>({ groupByMachine: 0 });
|
||||||
|
const groupByMachine = ref(false);
|
||||||
|
|
||||||
|
const { tableContext, onExportXls } = useListPage({
|
||||||
|
tableProps: {
|
||||||
|
title: '原材料需求计划',
|
||||||
|
api: list,
|
||||||
|
columns: baseColumns,
|
||||||
|
canResize: true,
|
||||||
|
formConfig: {
|
||||||
|
schemas: searchFormSchema,
|
||||||
|
autoSubmitOnEnter: true,
|
||||||
|
showAdvancedButton: true,
|
||||||
|
},
|
||||||
|
beforeFetch: (params) => {
|
||||||
|
return Object.assign(params, queryParam, {
|
||||||
|
groupByMachine: groupByMachine.value ? 1 : 0,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exportConfig: {
|
||||||
|
name: '原材料需求计划',
|
||||||
|
url: getExportUrl,
|
||||||
|
params: queryParam,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [registerTable, { reload, setColumns }] = tableContext;
|
||||||
|
const superQueryConfig = reactive(superQuerySchema);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
applyColumns();
|
||||||
|
});
|
||||||
|
|
||||||
|
function applyColumns() {
|
||||||
|
setColumns(groupByMachine.value ? machineColumns : baseColumns);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onGroupByMachineChange() {
|
||||||
|
queryParam.groupByMachine = groupByMachine.value ? 1 : 0;
|
||||||
|
applyColumns();
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSuperQuery(params) {
|
||||||
|
Object.keys(params).forEach((k) => {
|
||||||
|
queryParam[k] = params[k];
|
||||||
|
});
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
11
skills-lock.json
Normal file
11
skills-lock.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"skills": {
|
||||||
|
"karpathy-guidelines": {
|
||||||
|
"source": "multica-ai/andrej-karpathy-skills",
|
||||||
|
"sourceType": "github",
|
||||||
|
"skillPath": "skills/karpathy-guidelines/SKILL.md",
|
||||||
|
"computedHash": "0ec1641120accbb458780e0bd19a50a4182f2b97863e90851b91307f29802c9f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user