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 * @param changeDto
*/ */
void changeStruct(StructattrChangeDto changeDto); void changeStruct(StructattrChangeDto changeDto);

View File

@@ -51,6 +51,9 @@
and ivt.lock_type = '0' and ivt.lock_type = '0'
and gro.frozen_qty = 0 and gro.frozen_qty = 0
</if> </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 != ''"> <if test="order_by != null and order_by != ''">
order by ${order_by} order by ${order_by}
</if> </if>

View File

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

View File

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

View File

@@ -71,29 +71,29 @@ public class BigScreenServiceImpl implements BigScreenService {
item.put("pointUse", pointUse(storCode)); item.put("pointUse", pointUse(storCode));
// //2.【实时库存分析】数据 // //2.【实时库存分析】数据
item.put("ivtAnalyse", ivtAnalyse(storCode)); item.put("ivtAnalyse", ivtAnalyse(storCode));
//3.【出入库趋势】数据 //3.【出入库趋势】数据
item.put("inAndOutTrend", inAndOutTrend(storCode)); item.put("inAndOutTrend", inAndOutTrend(storCode));
// //4.【今日出入库】数据 // //4.【今日出入库】数据
item.put("toDayInAndOut", toDayInAndOut(storCode)); item.put("toDayInAndOut", toDayInAndOut(storCode));
// //5.【今日出入库】数据 // //5.【今日出入库】数据
item.put("realTask", realTask(storCode)); item.put("realTask", realTask(storCode));
//6.【未完成单据】数据 //6.【未完成单据】数据
item.put("unIos", unIos(storCode)); item.put("unIos", unIos(storCode));
result.add(item); result.add(item);
} }
return result; return result;
} }
/** /**
// //
// * 货位使用 // * 货位使用
// * // *
// * @return JSONObject { // * @return JSONObject {
// * total_qty: 总货位数 // * total_qty: 总货位数
// * use_qty: 已用货位 // * use_qty: 已用货位
// * emp_qty: 空余货位 // * emp_qty: 空余货位
// * use_percentage: 百分比(使用货位百分比) // * use_percentage: 百分比(使用货位百分比)
// * } // * }
// */ // */
private JSONObject pointUse(String storCode) { private JSONObject pointUse(String storCode) {
// 返回数据 // 返回数据
JSONObject result = new JSONObject(); 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 * @Date 2025/2/1 16:18
* 相同巷道,自下而上分配,左右相邻 * 相同巷道,自下而上分配,左右相邻
*/ */
@Service("sameBlockNum") //@Service("sameBlockNum")
@Slf4j @Slf4j
public class SameBlockNumRuleHandler extends Decisioner<Structattr, JSONObject> { public class SameBlockNumRuleHandler extends Decisioner<Structattr, JSONObject> {

View File

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

View File

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

View File

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