add:增加出库分配支持同单分配到同一个托盘逻辑

fix:出库库存变动时如果库存数0则变成出库状态,不为0的为组盘状态等待后续走回库逻辑
This commit is contained in:
zhangzq
2025-08-19 14:48:44 +08:00
parent 953cc5bae5
commit 48506ecb4a
10 changed files with 146 additions and 55 deletions

View File

@@ -113,6 +113,8 @@ public interface IStructattrService extends IService<Structattr> {
/**
* 生成库存变动记录表,更新载具冻结数量
* 如果是拣选出库则库存数为0的变成出库状态
* 拣选的,扣减冻结数之后变成组盘状态,如果需要回库则通过余料回库回去
* @param changeDto
*/
void changeStruct(StructattrChangeDto changeDto);

View File

@@ -51,6 +51,9 @@
and ivt.lock_type = '0'
and gro.frozen_qty = 0
</if>
<if test="inv_code != null and inv_code != ''">
and gro.frozen_qty = 0 and ( ivt.lock_type = '0' or ivt.inv_code = #{inv_code})
</if>
<if test="order_by != null and order_by != ''">
order by ${order_by}
</if>

View File

@@ -24,6 +24,10 @@ public class StrategyStructParam {
* 同步单号
*/
private String ext_code;
/**
* 分配的单号
*/
private String inv_code;
/**
* 来源单据类型
*/

View File

@@ -313,6 +313,7 @@ public class StructattrServiceImpl extends ServiceImpl<StructattrMapper, Structa
* @param param根据库区需要物料及数量,单据,批次分配具体货位
* @return 返回结果为仓位--对应--货物组盘物料信息及出库冻结数量
* 当前分配不会自动锁定货位及冻结出库数量,分配完成后需要手动锁定货位并冻结出库数量
* 混料模式下,同一个出库单允许分配到同一个托盘上
*/
@Override
public List<StrategyStructMaterialVO> outBoundSectDiv(StrategyStructParam param) {
@@ -403,8 +404,11 @@ public class StructattrServiceImpl extends ServiceImpl<StructattrMapper, Structa
.set("frozen_qty", 0)
.set("qty", subtract)
.set("update_time", now)
.set("status", GROUP_PLATE_STATUS.code("组盘"))
.set("status", GROUP_PLATE_STATUS.code("出库"))
.eq("group_id", vehicleMater.getGroup_id());
if (subtract.intValue()<=0){
update.set("status", GROUP_PLATE_STATUS.code("出库"));
}
iMdPbGroupplateService.update(update);
}
StIvtStructivtflow record = new StIvtStructivtflow();

View File

@@ -71,29 +71,29 @@ public class BigScreenServiceImpl implements BigScreenService {
item.put("pointUse", pointUse(storCode));
// //2.【实时库存分析】数据
item.put("ivtAnalyse", ivtAnalyse(storCode));
//3.【出入库趋势】数据
//3.【出入库趋势】数据
item.put("inAndOutTrend", inAndOutTrend(storCode));
// //4.【今日出入库】数据
item.put("toDayInAndOut", toDayInAndOut(storCode));
// //5.【今日出入库】数据
item.put("realTask", realTask(storCode));
//6.【未完成单据】数据
//6.【未完成单据】数据
item.put("unIos", unIos(storCode));
result.add(item);
}
return result;
}
/**
//
// * 货位使用
// *
// * @return JSONObject {
// * total_qty: 总货位数
// * use_qty: 已用货位
// * emp_qty: 空余货位
// * use_percentage: 百分比(使用货位百分比)
// * }
// */
//
// * 货位使用
// *
// * @return JSONObject {
// * total_qty: 总货位数
// * use_qty: 已用货位
// * emp_qty: 空余货位
// * use_percentage: 百分比(使用货位百分比)
// * }
// */
private JSONObject pointUse(String storCode) {
// 返回数据
JSONObject result = new JSONObject();

View File

@@ -0,0 +1,76 @@
package org.nl.wms.decision_manage.service.strategyConfig.decisioner.impl.diy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.nl.common.exception.BadRequestException;
import org.nl.common.utils.MapOf;
import org.nl.wms.basedata_manage.service.IStructattrService;
import org.nl.wms.basedata_manage.service.dao.StructattrVechielDto;
import org.nl.wms.basedata_manage.service.dto.StrategyMater;
import org.nl.wms.basedata_manage.service.dto.StrategyStructMaterialVO;
import org.nl.wms.basedata_manage.service.dto.StrategyStructParam;
import org.nl.wms.decision_manage.service.strategyConfig.decisioner.Decisioner;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/*
* @author ZZQ
* @Date 2024/4/6 16:18
* 先进先出策略(支持同单分配)
*/
@Service("fifo2")
@Slf4j
public class FIFO2RuleHandler extends Decisioner<StrategyStructMaterialVO, StrategyStructParam> {
/**
* 出入库明细服务
*/
@Autowired
private IStructattrService iStructattrService;
@Override
public List<StrategyStructMaterialVO> handler(List<StrategyStructMaterialVO> list, StrategyStructParam param) {
//分配数量
//当前条件只有id批次
log.info("---------执行fifo出库分配规则相同单据允许分配在同一个货位---------");
List<StrategyMater> maters = param.getStrategyMaters();
StrategyMater strategyMater = maters.get(0);
BigDecimal planQty = strategyMater.getQty();
List<StructattrVechielDto> vechielDtos =
iStructattrService.collectVechicle(MapOf.of("material_id", strategyMater.getMaterial_id()
, "pcsn", strategyMater.getPcsn()
, "inv_code", param.getInv_code()
, "stor_code", param.getStor_code()
, "sect_code", param.getSect_code()
, "plan_qty", planQty
, "order_by", "gro.update_time asc")
);
if (ObjectUtils.isEmpty(vechielDtos)) {
throw new BadRequestException("当前出库策略:先进先出,库存分配失败,失败原因:库存不足!");
}
List<StrategyStructMaterialVO> divStruct = new ArrayList<>();
for (StructattrVechielDto vechielDto : vechielDtos) {
if (planQty.intValue()<=0){
break;
}
BigDecimal qty = vechielDto.getQty();
BigDecimal subQty = planQty.subtract(qty);
if (subQty.doubleValue()>=0){
vechielDto.setFrozen_qty(vechielDto.getQty());
}else {
vechielDto.setFrozen_qty(planQty);
}
planQty=subQty;
StrategyStructMaterialVO materialVO = new StrategyStructMaterialVO();
BeanUtils.copyProperties(vechielDto,materialVO);
divStruct.add(materialVO);
}
return divStruct;
}
}

View File

@@ -17,7 +17,7 @@ import java.util.stream.Collectors;
* @Date 2025/2/1 16:18
* 相同巷道,自下而上分配,左右相邻
*/
@Service("sameBlockNum")
//@Service("sameBlockNum")
@Slf4j
public class SameBlockNumRuleHandler extends Decisioner<Structattr, JSONObject> {

View File

@@ -563,6 +563,7 @@ public class OutBillServiceImpl extends ServiceImpl<IOStorInvMapper,IOStorInv> i
StrategyStructParam.builder()
.stor_code(ioStorInv.getStor_code())
.sect_code(sectCode)
.inv_code(ioStorInv.getBill_code())
.strategyMaters(list)
.build()
);
@@ -921,21 +922,18 @@ public class OutBillServiceImpl extends ServiceImpl<IOStorInvMapper,IOStorInv> i
}
@Override
@Transactional
public void allSetPoint(JSONObject whereJson) {
//出库点
String point_code = whereJson.getString("point_code");
if (StrUtil.isBlank(point_code)){
if (StrUtil.isBlank(whereJson.getString("point_code"))){
throw new BadRequestException("未选择出库点");
}
String iostorinv_id = whereJson.getString("iostorinv_id");
//查询主表信息
IOStorInv ioStorInv = ioStorInvMapper.selectById(iostorinv_id);
if (ObjectUtil.isEmpty(ioStorInv)) {
throw new BadRequestException("未查到相关出库单");
}
List<IOStorInvDis> ioStorInvDisList = ioStorInvDisMapper.selectList(new LambdaQueryWrapper<>(IOStorInvDis.class)
.eq(IOStorInvDis::getIostorinv_id,iostorinv_id)
.eq(IOStorInvDis::getIs_issued,BaseDataEnum.IS_YES_NOT.code(""))
@@ -944,28 +942,30 @@ public class OutBillServiceImpl extends ServiceImpl<IOStorInvMapper,IOStorInv> i
if (ObjectUtil.isEmpty(ioStorInvDisList)){
throw new BadRequestException("当前没有可设置的分配明细");
}
for (IOStorInvDis ioStorInvDis:ioStorInvDisList){
//分配明细中同分配到同载具的进行合并
Map<String, List<IOStorInvDis>> groupDis = ioStorInvDisList.stream().collect(Collectors.groupingBy(dis -> dis.getStoragevehicle_code()));
for (String storageVehicleCode : groupDis.keySet()) {
List<IOStorInvDis> disList = groupDis.get(storageVehicleCode);
IOStorInvDis item = disList.get(0);
//创建任务
JSONObject task_form = new JSONObject();
task_form.put("task_type", "STOutTask");
task_form.put("TaskCode",CodeUtil.getNewCode("TASK_CODE"));
task_form.put("PickingLocation", ioStorInvDis.getStruct_code());
task_form.put("PlacedLocation", point_code);
task_form.put("vehicle_code", ioStorInvDis.getStoragevehicle_code());
task_form.put("PickingLocation", item.getStruct_code());
task_form.put("PlacedLocation", whereJson.getString("point_code"));
task_form.put("vehicle_code", item.getStoragevehicle_code());
StOutTask stOutTask = SpringContextHolder.getBean("STOutTask");
String task_id = stOutTask.create(task_form);
//分配明细表更新任务相关数据
IOStorInvDis dis = new IOStorInvDis();
dis.setIostorinvdis_id(ioStorInvDis.getIostorinvdis_id());
dis.setWork_status(IOSEnum.INBILL_DIS_STATUS.code("生成"));
dis.setTask_id(task_id);
dis.setIs_issued(BaseDataEnum.IS_YES_NOT.code(""));
dis.setPoint_code(point_code);
ioStorInvDisMapper.updateById(dis);
for (IOStorInvDis itemDis : disList) {
//分配明细表更新任务相关数据
IOStorInvDis dis = new IOStorInvDis();
dis.setIostorinvdis_id(itemDis.getIostorinvdis_id());
dis.setWork_status(IOSEnum.INBILL_DIS_STATUS.code("生成"));
dis.setTask_id(task_id);
dis.setIs_issued(BaseDataEnum.IS_YES_NOT.code(""));
dis.setPoint_code(whereJson.getString("point_code"));
ioStorInvDisMapper.updateById(dis);
}
}
}
@@ -1103,20 +1103,22 @@ public class OutBillServiceImpl extends ServiceImpl<IOStorInvMapper,IOStorInv> i
String currentUserId = SecurityUtils.getCurrentUserId();
String nickName = SecurityUtils.getCurrentNickName();
String now = DateUtil.now();
IOStorInvDis ioStorInvDis = ioStorInvDisMapper.selectOne(new LambdaQueryWrapper<>(IOStorInvDis.class)
List<IOStorInvDis> disList = ioStorInvDisMapper.selectList(new LambdaQueryWrapper<>(IOStorInvDis.class)
.eq(IOStorInvDis::getTask_id, task.getTask_id())
);
if (ObjectUtil.isEmpty(ioStorInvDis)) {
if (ObjectUtil.isEmpty(disList)) {
throw new BadRequestException("未找到任务对应的分配明细");
}
// 完成当前分配明细
ioStorInvDisMapper.update(ioStorInvDis,new LambdaUpdateWrapper<>(IOStorInvDis.class)
.set(IOStorInvDis::getWork_status,IOSEnum.INBILL_DIS_STATUS.code("完成"))
.eq(IOStorInvDis::getIostorinvdis_id,ioStorInvDis.getIostorinvdis_id())
);
// 完成当前任务对应的所有分配明细
for (IOStorInvDis ioStorInvDis : disList) {
ioStorInvDisMapper.update(ioStorInvDis,new LambdaUpdateWrapper<>(IOStorInvDis.class)
.set(IOStorInvDis::getWork_status,IOSEnum.INBILL_DIS_STATUS.code("完成"))
.eq(IOStorInvDis::getIostorinvdis_id,ioStorInvDis.getIostorinvdis_id()));
}
IOStorInvDis item = disList.get(0);
//解锁库位
JSONObject finish_map = new JSONObject();
finish_map.put("struct_code",ioStorInvDis.getStruct_code());
finish_map.put("struct_code", item.getStruct_code());
finish_map.put("storagevehicle_code",null);
finish_map.put("inv_type", null);
finish_map.put("inv_id", null);
@@ -1124,17 +1126,17 @@ public class OutBillServiceImpl extends ServiceImpl<IOStorInvMapper,IOStorInv> i
iStructattrService.updateStatusByCode("1",finish_map);
//库存变动:根据冻结数更新物料库存
StructattrChangeDto changeDto = StructattrChangeDto.builder()
.inv(ioStorInvDis.getIostorinv_id())
.storagevehicleCode(ioStorInvDis.getStoragevehicle_code())
.structCode(ioStorInvDis.getStruct_code()).taskType(task.getConfig_code()).inBound(false).build();
.inv(item.getIostorinv_id())
.storagevehicleCode(item.getStoragevehicle_code())
.structCode(item.getStruct_code()).taskType(task.getConfig_code()).inBound(false).build();
iStructattrService.changeStruct(changeDto);
// 查询该明细下是否还有未完成的分配明细
int countDis = ioStorInvDisMapper.selectCount(new LambdaQueryWrapper<>(IOStorInvDis.class)
.eq(IOStorInvDis::getIostorinvdtl_id,ioStorInvDis.getIostorinvdtl_id())
.eq(IOStorInvDis::getIostorinvdtl_id,item.getIostorinvdtl_id())
.ne(IOStorInvDis::getWork_status,IOSEnum.INBILL_DIS_STATUS.code("完成"))
);
// 明细
IOStorInvDtl ioStorInvDtl = ioStorInvDtlMapper.selectById(ioStorInvDis.getIostorinvdtl_id());
IOStorInvDtl ioStorInvDtl = ioStorInvDtlMapper.selectById(item.getIostorinvdtl_id());
if (ObjectUtil.isEmpty(ioStorInvDtl)){
throw new BadRequestException("未找到明细");
}

View File

@@ -261,7 +261,7 @@ export default {
type: Array,
default: () => { return [] }
},
storId: {
storCode: {
type: String,
default: null
}
@@ -298,7 +298,7 @@ export default {
sects: [],
pointList: [],
rules: {
},
}
}
},
watch: {
@@ -321,7 +321,7 @@ export default {
},
methods: {
open() {
crudSectattr.getSect({ 'stor_id': this.storId }).then(res => {
crudSectattr.getSectCode({ 'stor_code': this.storCode }).then(res => {
this.sects = res.content
})
@@ -344,7 +344,7 @@ export default {
},
openStructIvt() {
this.currentRow.remark = ''
this.currentRow.stor_id = this.storId
this.currentRow.stor_code = this.storCode
this.loadingAlldiv = true
checkoutbill.getStructIvt(this.currentRow).then(res => {
this.openParam = res

View File

@@ -203,7 +203,7 @@
</div>
<AddDialog @AddChanged="querytable" />
<ViewDialog :dialog-show.sync="viewShow" :rowmst="mstrow" @AddChanged="querytable" />
<DivDialog :dialog-show.sync="divShow" :open-array="openParam" :stor-id="storId" :rowmst="mstrow" @DivChanged="querytable" />
<DivDialog :dialog-show.sync="divShow" :open-array="openParam" :stor-code="storCode" :rowmst="mstrow" @DivChanged="querytable" />
</div>
</template>
@@ -262,7 +262,7 @@ export default {
checkrows: [],
storlist: [],
billtypelist: [],
storId: null
storCode: null
}
},
mounted: function() {
@@ -382,7 +382,7 @@ export default {
divOpen() {
checkoutbill.getOutBillDtl({ 'iostorinv_id': this.currentRow.iostorinv_id }).then(res => {
this.openParam = res
this.storId = this.currentRow.stor_id
this.storCode = this.currentRow.stor_code
this.divShow = true
this.mstrow = this.currentRow
})