rsf-admin/.env
@@ -1,3 +1,3 @@ VITE_BASE_IP=192.168.4.16 VITE_BASE_IP=192.168.4.41 # VITE_BASE_IP=47.76.147.249 VITE_BASE_PORT=8080 rsf-admin/src/page/orders/outStock/OutOrderItemList.jsx
@@ -143,6 +143,7 @@ <NumberField source="workQty" label="table.field.outStockItem.workQty" /> <NumberField source="qty" label="table.field.outStockItem.qty" /> <TextField source="stockUnit" label="table.field.outStockItem.stockUnit" /> <TextField source="splrBatch" label="table.field.outStockItem.splrBatch" /> <TextField source="purUnit" label="table.field.outStockItem.purUnit" /> <TextField source="splrCode" label="table.field.outStockItem.splrCode" /> <TextField source="splrName" label="table.field.outStockItem.splrName" /> rsf-server/src/main/java/com/vincent/rsf/server/common/constant/Constants.java
@@ -108,6 +108,12 @@ public static final String TASK_TYPE_ORDER_OUT_STOCK = "OrderOutStock"; /** * 波次出库 */ public static final String TASK_TYPE_WAVE_OUT_STOCK = "WaveOutStock"; /** * 拣料出库 */ public static final String TASK_TYPE_OUT_PICK = "pick"; rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/dto/OrderOutItemDto.java
@@ -21,6 +21,10 @@ private String siteNo; private String sourceId; private String source; @Data public static class staListDto{ private String staNo; rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/WaveToLocParams.java
New file @@ -0,0 +1,47 @@ package com.vincent.rsf.server.manager.controller.params; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; /** * @author Ryan * @description * @param * @return * @time 2025/6/25 11:20 */ @Data @Accessors(chain = true) public class WaveToLocParams implements Serializable { @ApiModelProperty("数量") private Double anfme; @ApiModelProperty("物料编码") private String matnrCode; @ApiModelProperty("批次") private String batch; @ApiModelProperty("动态字段索引") private String fieldsIndex; @ApiModelProperty("物料名称") private String maktx; @ApiModelProperty("执行数量") private Double workQty; @ApiModelProperty("单位") private String unit; @ApiModelProperty("波次明细ID") private Long itemId; @ApiModelProperty("波次单ID") private Long waveId; } rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/TaskResouceType.java
@@ -6,11 +6,11 @@ public enum TaskResouceType { //订单类型 TASK_RESOUCE_WAVE_TYPE("1", "波次任务"), TASK_RESOUCE_WAVE_TYPE("1", "波次出库任务"), TASK_RESOUCE_STOCK_TYPE("2", "库存出库任务"), TASK_RESOUCE_PAKIN_TYPE("3", "组拖任务"), TASK_RESOUCE_PAKIN_TYPE("3", "组拖入库任务"), TASK_RESOUCE_ORDER_TYPE("4", "单据出库任务"), rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/TaskStsType.java
@@ -39,10 +39,10 @@ GENERATE_WAVE_SEED("197", "等待容器到达"), WAVE_SEED("199", "播种中/盘点中"), COMPLETE_OUT("198", "出库完成"), WAVE_SEED("199", "播种中/盘点中/待确认"), UPDATED_OUT("200", "库存更新完成"), ; rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
@@ -46,6 +46,8 @@ private WcsService wcsService; @Autowired private OutStockService outStockService; @Autowired private WaveService waveService; /** @@ -72,9 +74,17 @@ List<LocItem> items = map.getItems(); Map<Long, List<LocItem>> listMap = items.stream().collect(Collectors.groupingBy(LocItem::getLocId)); AsnOrder order; Wave wave; if (!Objects.isNull(map.getSourceId())) { order = outStockService.getById(map.getSourceId()); if (map.getType().equals(Constants.TASK_TYPE_WAVE_OUT_STOCK)) { order = new AsnOrder(); wave = waveService.getById(map.getSourceId()); } else { wave = new Wave(); order = outStockService.getById(map.getSourceId()); } } else { wave = new Wave(); order = new AsnOrder(); } @@ -117,9 +127,11 @@ List<LocItem> locItemList = listMap.get(key); Double outQty = locItemList.stream().mapToDouble(LocItem::getOutQty).sum(); if (map.getType().equals(Constants.TASK_TYPE_OUT_STOCK) || map.getType().equals(Constants.TASK_TYPE_ORDER_OUT_STOCK)) { if (map.getType().equals(Constants.TASK_TYPE_OUT_STOCK) || map.getType().equals(Constants.TASK_TYPE_ORDER_OUT_STOCK) || map.getType().equals(Constants.TASK_TYPE_WAVE_OUT_STOCK)) { if (orgQty.compareTo(outQty) > 0) { //拣料出库 //拣料出库 -- 盘点出库 DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>() .eq(DeviceSite::getSite, siteNo) .eq(DeviceSite::getChannel, loc.getChannel()) @@ -199,14 +211,16 @@ .setCreateBy(loginUserId) .setCreateTime(new Date()) .setUpdateTime(new Date()) .setOrderType(OrderType.ORDER_OUT.type) .setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type)); .setOrderType(OrderType.ORDER_OUT.type); if (map.getType().equals(Constants.TASK_TYPE_ORDER_OUT_STOCK)) { taskItem.setWkType(Short.parseShort(order.getWkType())) .setSourceCode(order.getCode()) .setSourceId(order.getId()); } else if (map.getType().equals(Constants.TASK_TYPE_WAVE_OUT_STOCK)) { taskItem.setSourceId(wave.getId()).setSourceCode(wave.getCode()); } else if (map.getType().equals(Constants.TASK_TYPE_OUT_CHECK) || map.getType().equals(Constants.TASK_TYPE_OUT_STOCK)) { taskItem.setSource(item.getId()) taskItem.setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type)) .setSource(item.getId()) .setSourceId(item.getLocId()) .setSourceCode(item.getLocCode()); } rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -414,7 +414,8 @@ if (Cools.isEmpty(param.getOrderId())) { throw new CoolException("单据ID为空"); } WaveRule waveRule = waveRuleService.getOne(new LambdaQueryWrapper<WaveRule>().eq(WaveRule::getId, param.getWaveId())); WaveRule waveRule = waveRuleService.getOne(new LambdaQueryWrapper<WaveRule>() .eq(WaveRule::getId, param.getWaveId())); if (Cools.isEmpty(waveRule)) { throw new CoolException("未找到当前策略"); } @@ -573,7 +574,6 @@ .eq(AsnOrderItem::getAsnId, orderId)); List<OrderOutItemDto> list = new ArrayList<>(); Set<ExistDto> existDtos = new HashSet<>(); for (AsnOrderItem asnOrderItem : asnOrderItems) { BigDecimal issued = new BigDecimal(asnOrderItem.getAnfme().toString()) .subtract(new BigDecimal(asnOrderItem.getWorkQty().toString()) rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java
@@ -5,9 +5,13 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.vincent.rsf.framework.common.Cools; import com.vincent.rsf.framework.common.R; import com.vincent.rsf.framework.exception.CoolException; import com.vincent.rsf.server.common.constant.Constants; import com.vincent.rsf.server.manager.controller.dto.OrderOutItemDto; import com.vincent.rsf.server.manager.controller.params.LocToTaskParams; import com.vincent.rsf.server.manager.controller.params.WaveToLocParams; import com.vincent.rsf.server.manager.enums.*; import com.vincent.rsf.server.manager.entity.*; import com.vincent.rsf.server.manager.mapper.WaveMapper; @@ -48,6 +52,10 @@ private LocService locService; @Autowired private OutStockService outStockService; @Autowired private WaveService waveService; @Autowired private WaveRuleServiceImpl waveRuleService; /** * @param @@ -77,7 +85,17 @@ } /**生成出库任务*/ try { generateOutTask(waveItems, loginUserId, waves); List<WaveToLocParams> params = new ArrayList<>(); for (WaveItem item : waveItems) { WaveToLocParams locParams = new WaveToLocParams(); BeanUtils.copyProperties(item, locParams); locParams.setBatch(item.getSplrBatch()) .setItemId(item.getId()) .setWaveId(item.getWaveId()); params.add(locParams); } List<OrderOutItemDto> results = LocManageUtil.getOutOrderList(params, null); generateOutTask(results, loginUserId, waves); } catch (Exception e) { log.error(e.getMessage()); throw new CoolException("出库任务生成失败!!!"); @@ -119,13 +137,26 @@ throw new CoolException("波次明细不存在!!"); } for (int i = 0; i < items.size(); i++) { List<LocItem> locItems = LocManageUtil.getEfficiencyFirstItemList(items.get(i).getMatnrCode(), items.get(i).getSplrBatch(), items.get(i).getAnfme()); items.get(i).setStockLocs(JSONArray.toJSONString(locItems)).setStockQty(items.get(i).getAnfme()); WaveRule waveRule = waveRuleService.getOne(new LambdaQueryWrapper<WaveRule>() .eq(WaveRule::getType, WaveRuleType.First_In_First_Out.type)); if (Cools.isEmpty(waveRule)) { throw new CoolException("未找到当前策略"); } List<WaveToLocParams> params = new ArrayList<>(); for (WaveItem item : items) { WaveToLocParams locParams = new WaveToLocParams(); BeanUtils.copyProperties(item, locParams); locParams.setBatch(item.getSplrBatch()) .setItemId(item.getId()) .setWaveId(item.getWaveId()); params.add(locParams); } List<OrderOutItemDto> results = LocManageUtil.getOutOrderList(params, waveRule); /**生成出库任务*/ try { generateOutTask(items, loginUserId, waves); generateOutTask(results, loginUserId, waves); } catch (Exception e) { log.error("UNK", e); throw new CoolException(e.getMessage()); @@ -151,102 +182,42 @@ */ @Synchronized @Transactional(rollbackFor = Exception.class) public void generateOutTask(List<WaveItem> itemParams, Long loginUserId, Wave wave) throws Exception { List<LocItem> locItemList = new ArrayList<>(); for (WaveItem param : itemParams) { String locs = param.getStockLocs(); List<LocItem> locItems = JSONArray.parseArray(locs, LocItem.class); /***将有货有的明细信息存放到库位信息中*/ for (int i = 0; i < locItems.size(); i++) { locItems.get(i) .setSourceId(param.getWaveId()) .setSourceCode(param.getWaveCode()) .setSource(param.getId()); } locItemList.addAll(locItems); } if (locItemList.isEmpty()) { throw new CoolException("没有合适库位!!"); } public void generateOutTask(List<OrderOutItemDto> itemParams, Long loginUserId, Wave wave) throws Exception { /**拆分波次明细库位集,合并相同库位,分解任务明细*/ Map<Long, List<LocItem>> listMap = locItemList.stream().collect(Collectors.groupingBy(LocItem::getLocId)); /**根据库位汇总信息,生成任务明细**/ listMap.keySet().forEach(key -> { List<LocItem> locItems = listMap.get(key); LocItem item1 = locItems.stream().findFirst().get(); WaveItem waveItem = waveItemService.getById(item1.getSource()); if (Objects.isNull(waveItem)) { throw new CoolException("数据错误:波次明细不存在!!"); } //TODO 当前任务完成后,通过定时事件判断是全盘出库,还是拣料再入库 Loc loc = locService.getById(key); Task task = new Task(); String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TASK_CODE, null); if (StringUtils.isBlank(ruleCode)) { throw new CoolException("编码规则错误:请检查「SYS_TASK_CODE」是否设置完成!!"); } for (OrderOutItemDto itemDto : itemParams) { LocToTaskParams taskParams = new LocToTaskParams(); Loc loc = locService.getById(itemDto.getLocId()); if (Objects.isNull(loc)) { throw new CoolException("库位不存在!!"); continue; } // List<TaskItem> items = taskItemService.list(new LambdaQueryWrapper<TaskItem>().in(TaskItem::getSourceId, wave.getId())); // if (!items.isEmpty()) { // throw new CoolException("波次任务已生成,不能重复生成!!"); // } task.setTaskCode(ruleCode) .setTaskType(TaskType.TASK_TYPE_OUT.type) .setTaskStatus(TaskStsType.GENERATE_OUT.id) .setResource(TaskResouceType.TASK_RESOUCE_WAVE_TYPE.val) .setBarcode(loc.getBarcode()) .setOrgLoc(loc.getCode()) .setSort(Constants.TASK_SORT_DEFAULT_VALUE) .setCreateBy(loginUserId) .setUpdateBy(loginUserId) .setTargSite(wave.getTargSite()); if (!taskService.save(task)) { throw new CoolException("任务生成失败!!"); } List<TaskItem> taskItems = new ArrayList<>(); /**生成任务明细信息*/ for (LocItem item : locItems) { TaskItem taskItem = new TaskItem(); BeanUtils.copyProperties(item, taskItem); taskItem.setTaskId(task.getId()) .setAnfme(item.getAnfme()) .setId(null) .setSourceCode(wave.getCode()) taskParams.setItems(Arrays.asList(itemDto.getLocItem())) .setSiteNo(itemDto.getSiteNo()) .setType(Constants.TASK_TYPE_WAVE_OUT_STOCK) .setSourceId(wave.getId()) .setSource(item.getSource()); taskItems.add(taskItem); } if (!taskItemService.saveBatch(taskItems)) { throw new CoolException("任务明细保存失败!!"); .setTarLoc(loc.getCode()); locItemService.generateTask(TaskResouceType.TASK_RESOUCE_WAVE_TYPE.val, taskParams, loginUserId); } /**修改波次执行数量*/ taskItems.forEach(item -> { boolean update = waveItemService.update(new LambdaUpdateWrapper<WaveItem>() .eq(WaveItem::getId, item.getSource()) .set(WaveItem::getExceStatus, WaveItemExceStatus.WAVE_EXCE_STATUS_SEED.val) .set(WaveItem::getWorkQty, item.getAnfme())); if (!update) { throw new CoolException("波次执行数量修改失败!!"); } }); List<WaveItem> waveItems = waveItemService.list(new LambdaQueryWrapper<WaveItem>().eq(WaveItem::getWaveId, wave.getId())); double sum = waveItems.stream().mapToDouble(WaveItem::getWorkQty).sum(); /**波次主单信息修改*/ if (!this.update(new LambdaUpdateWrapper<Wave>() .eq(Wave::getId, wave.getId()) .set(Wave::getWorkQty, sum) .set(Wave::getExceStatus, WaveExceStatus.WAVE_EXCE_STATUS_TASK.val))) { throw new CoolException("波次主单信息修改失败!!"); } }); // /**修改波次执行数量*/ // taskItems.forEach(item -> { // boolean update = waveItemService.update(new LambdaUpdateWrapper<WaveItem>() // .eq(WaveItem::getId, item.getSource()) // .set(WaveItem::getExceStatus, WaveItemExceStatus.WAVE_EXCE_STATUS_SEED.val) // .set(WaveItem::getWorkQty, item.getAnfme())); // if (!update) { // throw new CoolException("波次执行数量修改失败!!"); // } // }); // // List<WaveItem> waveItems = waveItemService.list(new LambdaQueryWrapper<WaveItem>().eq(WaveItem::getWaveId, wave.getId())); // double sum = waveItems.stream().mapToDouble(WaveItem::getWorkQty).sum(); // /**波次主单信息修改*/ // if (!this.update(new LambdaUpdateWrapper<Wave>() // .eq(Wave::getId, wave.getId()) // .set(Wave::getWorkQty, sum) // .set(Wave::getExceStatus, WaveExceStatus.WAVE_EXCE_STATUS_TASK.val))) { // throw new CoolException("波次主单信息修改失败!!"); // } } /** rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/LocManageUtil.java
@@ -3,17 +3,18 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.vincent.rsf.framework.common.SpringUtils; import com.vincent.rsf.server.api.utils.LocUtils; import com.vincent.rsf.server.manager.entity.DeviceSite; import com.vincent.rsf.server.manager.entity.Loc; import com.vincent.rsf.server.manager.entity.LocItem; import com.vincent.rsf.server.manager.service.DeviceSiteService; import com.vincent.rsf.server.manager.service.LocItemService; import com.vincent.rsf.server.manager.service.LocService; import com.vincent.rsf.server.manager.controller.dto.ExistDto; import com.vincent.rsf.server.manager.controller.dto.OrderOutItemDto; import com.vincent.rsf.server.manager.controller.params.WaveToLocParams; import com.vincent.rsf.server.manager.entity.*; import com.vincent.rsf.server.manager.enums.TaskType; import com.vincent.rsf.server.manager.enums.WaveRuleType; import com.vincent.rsf.server.manager.service.*; import com.vincent.rsf.server.manager.enums.LocStsType; import java.util.Comparator; import java.util.List; import java.util.Objects; import javax.swing.*; import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; public class LocManageUtil { @@ -110,6 +111,95 @@ return locItems; } /** * 获取出库库位信息 * @param params * @param waveRule * @return */ public static List<OrderOutItemDto> getOutOrderList(List<WaveToLocParams> params, WaveRule waveRule) { LocService locService = SpringUtils.getBean(LocService.class); LocItemService locItemService = SpringUtils.getBean(LocItemService.class); DeviceSiteService deviceSiteService = SpringUtils.getBean(DeviceSiteService.class); List<OrderOutItemDto> list = new ArrayList<>(); Set<ExistDto> existDtos = new HashSet<>(); for (WaveToLocParams item : params) { BigDecimal issued = new BigDecimal(item.getAnfme().toString()) .subtract(new BigDecimal(item.getWorkQty().toString())); if (issued.doubleValue() <= 0) { continue; } List<LocItem> locItems; if (Objects.isNull(waveRule)) { locItems = LocManageUtil.getFirstInFirstOutItemList(item.getMatnrCode(), item.getBatch(), item.getAnfme()); } else { if (WaveRuleType.Efficiency_First.type.equals(waveRule.getType())) { locItems = LocManageUtil.getEfficiencyFirstItemList(item.getMatnrCode(), item.getBatch(), item.getAnfme()); } else if (WaveRuleType.First_In_First_Out.type.equals(waveRule.getType())) { locItems = LocManageUtil.getFirstInFirstOutItemList(item.getMatnrCode(), item.getBatch(), item.getAnfme()); } else { locItems = LocManageUtil.getFirstInFirstOutItemList(item.getMatnrCode(), item.getBatch(), item.getAnfme()); } } for (LocItem locItem : locItems) { Loc loc = locService.getById(locItem.getLocId()); List<LocItem> itemList = locItemService.list(new LambdaQueryWrapper<LocItem>().eq(LocItem::getLocCode, locItem.getLocCode())); if (issued.doubleValue() > 0) { ExistDto existDto = new ExistDto().setBatch(locItem.getBatch()).setMatnr(locItem.getMatnrCode()).setLocNo(locItem.getLocCode()); if (existDtos.add(existDto)) { locItem.setOutQty(issued.doubleValue() >= locItem.getAnfme() ? locItem.getAnfme() : issued.doubleValue()); locItem.setBarcode(loc.getBarcode()) .setSourceId(item.getWaveId()) .setSource(item.getItemId()); OrderOutItemDto orderOutItemDto = new OrderOutItemDto(); orderOutItemDto.setLocItem(locItem); List<DeviceSite> deviceSites = deviceSiteService.list(new LambdaQueryWrapper<DeviceSite>() .eq(DeviceSite::getChannel, loc.getChannel()) .eq(DeviceSite::getType, issued.doubleValue() >= locItem.getAnfme() && itemList.size() == 1 ? TaskType.TASK_TYPE_OUT.type : TaskType.TASK_TYPE_PICK_AGAIN_OUT.type) ); if (!deviceSites.isEmpty()) { List<OrderOutItemDto.staListDto> maps = new ArrayList<>(); for (DeviceSite sta : deviceSites) { OrderOutItemDto.staListDto staListDto = new OrderOutItemDto.staListDto(); staListDto.setStaNo(sta.getSite()); staListDto.setStaName(sta.getSite()); maps.add(staListDto); } orderOutItemDto.setStaNos(maps); //默认获取第一站点 DeviceSite deviceSite = deviceSites.stream().findFirst().get(); orderOutItemDto.setSiteNo(deviceSite.getSite()); } orderOutItemDto.setSource(item.getItemId()).setSource(item.getWaveId()); list.add(orderOutItemDto); issued = issued.subtract(new BigDecimal(locItem.getAnfme().toString())); } } } // if (issued.doubleValue() > 0) { // LocItem locItem = new LocItem() // .setId(new Random().nextLong()) // .setMatnrCode(item.getMatnrCode()) // .setMaktx(item.getMaktx()) // .setAnfme(0.00) // .setWorkQty(issued.doubleValue()) // .setOutQty(issued.doubleValue()) // .setUnit(item.getUnit()) // .setBatch(item.getBatch()); // OrderOutItemDto orderOutItemDto = new OrderOutItemDto(); // orderOutItemDto.setLocItem(locItem); // list.add(orderOutItemDto); // } } return list; } }