feat:入库、移库功能页面完善
This commit is contained in:
@@ -21,6 +21,11 @@ public interface ErrorCodeConstants {
|
|||||||
ErrorCode IOSTOR_INV_UPDATE_STATUS_INVALID = new ErrorCode(6_000_8, "只有生成状态的出库单允许修改");
|
ErrorCode IOSTOR_INV_UPDATE_STATUS_INVALID = new ErrorCode(6_000_8, "只有生成状态的出库单允许修改");
|
||||||
ErrorCode IOSTOR_INV_NO_PENDING_ALLOCATION = new ErrorCode(6_000_9, "当前明细没有生成状态的分配记录");
|
ErrorCode IOSTOR_INV_NO_PENDING_ALLOCATION = new ErrorCode(6_000_9, "当前明细没有生成状态的分配记录");
|
||||||
ErrorCode IOSTOR_INV_NO_MOVE_LOCATION = new ErrorCode(6_001_0, "没有可用于移库的空闲仓位");
|
ErrorCode IOSTOR_INV_NO_MOVE_LOCATION = new ErrorCode(6_001_0, "没有可用于移库的空闲仓位");
|
||||||
|
ErrorCode INBOUND_GROUP_PLATE_INVALID = new ErrorCode(6_001_1, "组盘数据不存在或不是生成状态");
|
||||||
|
ErrorCode INBOUND_STATUS_INVALID = new ErrorCode(6_001_2, "只有生成状态的入库单允许修改或分配");
|
||||||
|
ErrorCode INBOUND_TARGET_INVALID = new ErrorCode(6_001_3, "目标货位不可用或与所选库区、仓库不一致");
|
||||||
|
ErrorCode INBOUND_NOT_ALLOCATED = new ErrorCode(6_001_4, "入库单尚未完成货位分配");
|
||||||
|
ErrorCode INBOUND_ALLOCATION_TASK_CREATED = new ErrorCode(6_001_5, "入库任务已创建,不允许取消货位分配");
|
||||||
|
|
||||||
// ================ 移库单相关错误码 =================
|
// ================ 移库单相关错误码 =================
|
||||||
ErrorCode MOVE_INV_NOT_EXISTS = new ErrorCode(6_002_0, "移库单不存在");
|
ErrorCode MOVE_INV_NOT_EXISTS = new ErrorCode(6_002_0, "移库单不存在");
|
||||||
|
|||||||
@@ -46,6 +46,80 @@ public class IostorInvController {
|
|||||||
return success(iostorInvService.createOutbound(reqVO));
|
return success(iostorInvService.createOutbound(reqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/create-inbound")
|
||||||
|
@Operation(summary = "创建入库单及明细")
|
||||||
|
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:create')")
|
||||||
|
public CommonResult<Long> createInbound(@Valid @RequestBody InboundSaveReqVO reqVO) {
|
||||||
|
return success(iostorInvService.createInbound(reqVO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update-inbound")
|
||||||
|
@Operation(summary = "修改入库单及明细")
|
||||||
|
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:update')")
|
||||||
|
public CommonResult<Boolean> updateInbound(@Valid @RequestBody InboundSaveReqVO reqVO) {
|
||||||
|
iostorInvService.updateInbound(reqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete-inbound")
|
||||||
|
@Operation(summary = "删除生成状态的入库单")
|
||||||
|
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:delete')")
|
||||||
|
public CommonResult<Boolean> deleteInbound(@RequestParam("iostorinvId") Long iostorinvId) {
|
||||||
|
iostorInvService.deleteInbound(iostorinvId);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/inbound-group-plates")
|
||||||
|
@Operation(summary = "查询生成状态的待入库组盘数据")
|
||||||
|
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:query')")
|
||||||
|
public CommonResult<List<cn.code.nl.module.wms.dal.dataobject.groupplate.GroupPlateDO>>
|
||||||
|
getInboundGroupPlates(@RequestParam("storId") String storId) {
|
||||||
|
return success(iostorInvService.getInboundGroupPlates(storId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/allocate-inbound")
|
||||||
|
@Operation(summary = "分配入库货位并按库区类型创建任务")
|
||||||
|
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:update')")
|
||||||
|
public CommonResult<Boolean> allocateInbound(@Valid @RequestBody InboundAllocationReqVO reqVO) {
|
||||||
|
iostorInvService.allocateInbound(reqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/cancel-inbound-allocation")
|
||||||
|
@Operation(summary = "取消单条入库货位分配并解锁货位")
|
||||||
|
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:update')")
|
||||||
|
public CommonResult<Boolean> cancelInboundAllocation(
|
||||||
|
@RequestParam("iostorinvId") Long iostorinvId,
|
||||||
|
@RequestParam("iostorinvdtlId") Long iostorinvdtlId) {
|
||||||
|
iostorInvService.cancelInboundAllocation(iostorinvId, iostorinvdtlId);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/inbound-available-structs")
|
||||||
|
@Operation(summary = "查询入库可用货位")
|
||||||
|
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:query')")
|
||||||
|
public CommonResult<List<cn.code.nl.module.wms.dal.dataobject.structAttr.StrucAttrDO>>
|
||||||
|
getInboundAvailableStructs(@RequestParam("storId") String storId,
|
||||||
|
@RequestParam("sectId") String sectId) {
|
||||||
|
return success(iostorInvService.getInboundAvailableStructs(storId, sectId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/set-inbound-point")
|
||||||
|
@Operation(summary = "设置入库起点并创建入库任务")
|
||||||
|
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:update')")
|
||||||
|
public CommonResult<Boolean> setInboundPoint(@Valid @RequestBody InboundSetPointReqVO reqVO) {
|
||||||
|
iostorInvService.setInboundPoint(reqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/complete-inbound")
|
||||||
|
@Operation(summary = "完成入库并绑定木箱与仓位")
|
||||||
|
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:update')")
|
||||||
|
public CommonResult<Boolean> completeInbound(@RequestParam("iostorinvId") Long iostorinvId) {
|
||||||
|
iostorInvService.completeInbound(iostorinvId);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/availableInventoryPage")
|
@GetMapping("/availableInventoryPage")
|
||||||
@Operation(summary = "获得可用库存分页")
|
@Operation(summary = "获得可用库存分页")
|
||||||
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:query')")
|
@PreAuthorize("@ss.hasPermission('wms:iostor-inv:query')")
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ import static cn.code.nl.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_
|
|||||||
@Data
|
@Data
|
||||||
public class IostorInvPageReqVO extends PageParam {
|
public class IostorInvPageReqVO extends PageParam {
|
||||||
|
|
||||||
|
@Schema(description = "出入类型:0入库,1出库")
|
||||||
|
private String ioType;
|
||||||
|
|
||||||
@Schema(description = "单据编号")
|
@Schema(description = "单据编号")
|
||||||
private String billCode;
|
private String billCode;
|
||||||
|
|
||||||
@@ -22,7 +25,7 @@ public class IostorInvPageReqVO extends PageParam {
|
|||||||
private String billType;
|
private String billType;
|
||||||
|
|
||||||
@Schema(description = "业务日期")
|
@Schema(description = "业务日期")
|
||||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
private LocalDateTime[] bizDate;
|
private LocalDateTime[] bizDate;
|
||||||
|
|
||||||
@Schema(description = "仓库标识", example = "12635")
|
@Schema(description = "仓库标识", example = "12635")
|
||||||
@@ -86,4 +89,4 @@ public class IostorInvPageReqVO extends PageParam {
|
|||||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
private String[] uploadTime;
|
private String[] uploadTime;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ public class OutboundAllocationRespVO {
|
|||||||
private String qtyUnitName;
|
private String qtyUnitName;
|
||||||
private String sectId;
|
private String sectId;
|
||||||
private String sectName;
|
private String sectName;
|
||||||
|
private String structId;
|
||||||
private String structCode;
|
private String structCode;
|
||||||
private String structName;
|
private String structName;
|
||||||
|
private String pointCode;
|
||||||
|
private String taskId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cn.code.nl.module.wms.controller.admin.moveinv.vo;
|
package cn.code.nl.module.wms.controller.admin.moveinv.vo;
|
||||||
|
|
||||||
import cn.code.nl.module.wms.dal.dataobject.moveinvdtl.MoveInvDtlDO;
|
import cn.code.nl.module.wms.dal.dataobject.moveinvdtl.MoveInvDtlDO;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import cn.code.nl.module.system.api.user.AdminUserApi;
|
import cn.code.nl.module.system.api.user.AdminUserApi;
|
||||||
import org.dromara.core.trans.anno.Trans;
|
import org.dromara.core.trans.anno.Trans;
|
||||||
@@ -17,6 +18,10 @@ import java.util.List;
|
|||||||
@Data
|
@Data
|
||||||
public class MoveInvRespVO implements VO {
|
public class MoveInvRespVO implements VO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EasyTrans 根据主键识别并缓存当前 VO,缺少该注解会导致分页结果翻译失败。
|
||||||
|
*/
|
||||||
|
@TableId
|
||||||
private Long moveinvId;
|
private Long moveinvId;
|
||||||
private String billCode;
|
private String billCode;
|
||||||
private String billType;
|
private String billType;
|
||||||
|
|||||||
@@ -60,6 +60,11 @@ public class IostorInvDO extends BaseDO {
|
|||||||
* 仓库名称
|
* 仓库名称
|
||||||
*/
|
*/
|
||||||
private String storName;
|
private String storName;
|
||||||
|
/**
|
||||||
|
* 创建人姓名,仅用于列表返回,不对应数据库字段。
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String creatorName;
|
||||||
/**
|
/**
|
||||||
* 来源方标识
|
* 来源方标识
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public interface IostorInvMapper extends BaseMapperX<IostorInvDO> {
|
|||||||
|
|
||||||
default PageResult<IostorInvDO> selectPage(IostorInvPageReqVO reqVO) {
|
default PageResult<IostorInvDO> selectPage(IostorInvPageReqVO reqVO) {
|
||||||
return selectPage(reqVO, new LambdaQueryWrapperX<IostorInvDO>()
|
return selectPage(reqVO, new LambdaQueryWrapperX<IostorInvDO>()
|
||||||
|
.eqIfPresent(IostorInvDO::getIoType, reqVO.getIoType())
|
||||||
.eqIfPresent(IostorInvDO::getBillCode, reqVO.getBillCode())
|
.eqIfPresent(IostorInvDO::getBillCode, reqVO.getBillCode())
|
||||||
.eqIfPresent(IostorInvDO::getBillType, reqVO.getBillType())
|
.eqIfPresent(IostorInvDO::getBillType, reqVO.getBillType())
|
||||||
.betweenIfPresent(IostorInvDO::getBizDate, reqVO.getBizDate())
|
.betweenIfPresent(IostorInvDO::getBizDate, reqVO.getBizDate())
|
||||||
@@ -44,4 +45,4 @@ public interface IostorInvMapper extends BaseMapperX<IostorInvDO> {
|
|||||||
.orderByDesc(IostorInvDO::getIostorinvId));
|
.orderByDesc(IostorInvDO::getIostorinvId));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import cn.code.nl.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||||||
import cn.code.nl.framework.mybatis.core.mapper.BaseMapperX;
|
import cn.code.nl.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
import cn.code.nl.module.wms.dal.dataobject.iostorinvdis.IostorinvDisDO;
|
import cn.code.nl.module.wms.dal.dataobject.iostorinvdis.IostorinvDisDO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Delete;
|
||||||
import cn.code.nl.module.wms.controller.admin.iostorinvdis.vo.*;
|
import cn.code.nl.module.wms.controller.admin.iostorinvdis.vo.*;
|
||||||
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.OutboundAllocationRespVO;
|
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.OutboundAllocationRespVO;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
@@ -24,6 +25,18 @@ public interface IostorinvDisMapper extends BaseMapperX<IostorinvDisDO> {
|
|||||||
@Param("iostorinvdtlId") Long iostorinvdtlId,
|
@Param("iostorinvdtlId") Long iostorinvdtlId,
|
||||||
@Param("sectId") String sectId);
|
@Param("sectId") String sectId);
|
||||||
|
|
||||||
|
@Delete("""
|
||||||
|
DELETE FROM wms_iostorinvdis
|
||||||
|
WHERE iostorinvdis_id = #{allocationId}
|
||||||
|
""")
|
||||||
|
int physicallyDeleteInboundAllocation(@Param("allocationId") String allocationId);
|
||||||
|
|
||||||
|
@Delete("""
|
||||||
|
DELETE FROM wms_iostorinvdis
|
||||||
|
WHERE iostorinv_id = #{iostorinvId}
|
||||||
|
""")
|
||||||
|
int physicallyDeleteByIostorinvId(@Param("iostorinvId") Long iostorinvId);
|
||||||
|
|
||||||
default PageResult<IostorinvDisDO> selectPage(IostorinvDisPageReqVO reqVO) {
|
default PageResult<IostorinvDisDO> selectPage(IostorinvDisPageReqVO reqVO) {
|
||||||
return selectPage(reqVO, new LambdaQueryWrapperX<IostorinvDisDO>()
|
return selectPage(reqVO, new LambdaQueryWrapperX<IostorinvDisDO>()
|
||||||
.eqIfPresent(IostorinvDisDO::getIostorinvId, reqVO.getIostorinvId())
|
.eqIfPresent(IostorinvDisDO::getIostorinvId, reqVO.getIostorinvId())
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import cn.code.nl.framework.mybatis.core.mapper.BaseMapperX;
|
|||||||
import cn.code.nl.module.wms.dal.dataobject.iostorinvdtl.IostorinvDtlDO;
|
import cn.code.nl.module.wms.dal.dataobject.iostorinvdtl.IostorinvDtlDO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Delete;
|
||||||
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.AvailableInventoryPageReqVO;
|
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.AvailableInventoryPageReqVO;
|
||||||
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.AvailableInventoryRespVO;
|
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.AvailableInventoryRespVO;
|
||||||
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.IostorInvDetailRespVO;
|
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.IostorInvDetailRespVO;
|
||||||
@@ -24,6 +25,12 @@ import cn.code.nl.module.wms.controller.admin.iostorinvdtl.vo.*;
|
|||||||
@Mapper
|
@Mapper
|
||||||
public interface IostorinvDtlMapper extends BaseMapperX<IostorinvDtlDO> {
|
public interface IostorinvDtlMapper extends BaseMapperX<IostorinvDtlDO> {
|
||||||
|
|
||||||
|
@Delete("""
|
||||||
|
DELETE FROM wms_iostorinvdtl
|
||||||
|
WHERE iostorinv_id = #{iostorinvId}
|
||||||
|
""")
|
||||||
|
int physicallyDeleteByIostorinvId(@Param("iostorinvId") Long iostorinvId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询指定仓库的可用库存。
|
* 分页查询指定仓库的可用库存。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -83,6 +83,68 @@ public interface StrucAttrMapper extends BaseMapperX<StrucAttrDO> {
|
|||||||
int lockForTask(@Param("structId") String structId,
|
int lockForTask(@Param("structId") String structId,
|
||||||
@Param("taskCode") String taskCode);
|
@Param("taskCode") String taskCode);
|
||||||
|
|
||||||
|
@Update("""
|
||||||
|
UPDATE wms_structattr
|
||||||
|
SET lock_type = '2',
|
||||||
|
inv_type = '0',
|
||||||
|
inv_id = #{invId},
|
||||||
|
inv_code = #{invCode}
|
||||||
|
WHERE struct_id = #{structId}
|
||||||
|
AND deleted = '0'
|
||||||
|
AND is_used = 1
|
||||||
|
AND lock_type = '1'
|
||||||
|
AND (storagevehicle_code IS NULL OR storagevehicle_code = '')
|
||||||
|
""")
|
||||||
|
int lockForInbound(@Param("structId") String structId,
|
||||||
|
@Param("invId") String invId,
|
||||||
|
@Param("invCode") String invCode);
|
||||||
|
|
||||||
|
@Update("""
|
||||||
|
UPDATE wms_structattr
|
||||||
|
SET storagevehicle_code = #{vehicleCode},
|
||||||
|
storagevehicle_qty = 1,
|
||||||
|
lock_type = '1',
|
||||||
|
inv_type = NULL,
|
||||||
|
inv_id = NULL,
|
||||||
|
inv_code = NULL,
|
||||||
|
task_code = NULL
|
||||||
|
WHERE struct_id = #{structId}
|
||||||
|
AND deleted = '0'
|
||||||
|
AND lock_type = '2'
|
||||||
|
AND inv_id = #{invId}
|
||||||
|
""")
|
||||||
|
int completeInbound(@Param("structId") String structId,
|
||||||
|
@Param("invId") String invId,
|
||||||
|
@Param("vehicleCode") String vehicleCode);
|
||||||
|
|
||||||
|
@Update("""
|
||||||
|
UPDATE wms_structattr
|
||||||
|
SET lock_type = '1',
|
||||||
|
inv_type = NULL,
|
||||||
|
inv_id = NULL,
|
||||||
|
inv_code = NULL,
|
||||||
|
task_code = NULL
|
||||||
|
WHERE deleted = '0'
|
||||||
|
AND inv_id = #{invId}
|
||||||
|
AND lock_type = '2'
|
||||||
|
""")
|
||||||
|
int unlockInbound(@Param("invId") String invId);
|
||||||
|
|
||||||
|
@Update("""
|
||||||
|
UPDATE wms_structattr
|
||||||
|
SET lock_type = '1',
|
||||||
|
inv_type = NULL,
|
||||||
|
inv_id = NULL,
|
||||||
|
inv_code = NULL,
|
||||||
|
task_code = NULL
|
||||||
|
WHERE struct_id = #{structId}
|
||||||
|
AND deleted = '0'
|
||||||
|
AND inv_id = #{invId}
|
||||||
|
AND lock_type = '2'
|
||||||
|
""")
|
||||||
|
int unlockInboundStruct(@Param("structId") String structId,
|
||||||
|
@Param("invId") String invId);
|
||||||
|
|
||||||
default PageResult<StrucAttrDO> selectPage(StrucAttrPageReqVO reqVO) {
|
default PageResult<StrucAttrDO> selectPage(StrucAttrPageReqVO reqVO) {
|
||||||
return selectPage(reqVO, new LambdaQueryWrapperX<StrucAttrDO>()
|
return selectPage(reqVO, new LambdaQueryWrapperX<StrucAttrDO>()
|
||||||
.eqIfPresent(StrucAttrDO::getStructCode, reqVO.getStructCode())
|
.eqIfPresent(StrucAttrDO::getStructCode, reqVO.getStructCode())
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cn.code.nl.module.wms.framework.rpc.config;
|
|||||||
|
|
||||||
import cn.code.nl.module.base.api.codegen.CodeGenApi;
|
import cn.code.nl.module.base.api.codegen.CodeGenApi;
|
||||||
import cn.code.nl.module.infra.api.config.ConfigApi;
|
import cn.code.nl.module.infra.api.config.ConfigApi;
|
||||||
|
import cn.code.nl.module.system.api.user.AdminUserApi;
|
||||||
import cn.code.nl.module.task.api.TransportTaskApi;
|
import cn.code.nl.module.task.api.TransportTaskApi;
|
||||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -12,6 +13,6 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
* @Date: 2026/7/22 13:50
|
* @Date: 2026/7/22 13:50
|
||||||
*/
|
*/
|
||||||
@Configuration(value = "wmsRpcConfiguration", proxyBeanMethods = false)
|
@Configuration(value = "wmsRpcConfiguration", proxyBeanMethods = false)
|
||||||
@EnableFeignClients(clients = {CodeGenApi.class, TransportTaskApi.class, ConfigApi.class})
|
@EnableFeignClients(clients = {CodeGenApi.class, TransportTaskApi.class, ConfigApi.class, AdminUserApi.class})
|
||||||
public class RpcConfiguration {
|
public class RpcConfiguration {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,25 @@ public interface IostorInvService {
|
|||||||
*/
|
*/
|
||||||
Long createOutbound(@Valid IostorInvCreateReqVO reqVO);
|
Long createOutbound(@Valid IostorInvCreateReqVO reqVO);
|
||||||
|
|
||||||
|
Long createInbound(@Valid InboundSaveReqVO reqVO);
|
||||||
|
|
||||||
|
void updateInbound(@Valid InboundSaveReqVO reqVO);
|
||||||
|
|
||||||
|
void deleteInbound(Long iostorinvId);
|
||||||
|
|
||||||
|
List<cn.code.nl.module.wms.dal.dataobject.groupplate.GroupPlateDO> getInboundGroupPlates(String storId);
|
||||||
|
|
||||||
|
void allocateInbound(@Valid InboundAllocationReqVO reqVO);
|
||||||
|
|
||||||
|
void cancelInboundAllocation(Long iostorinvId, Long iostorinvdtlId);
|
||||||
|
|
||||||
|
List<cn.code.nl.module.wms.dal.dataobject.structAttr.StrucAttrDO>
|
||||||
|
getInboundAvailableStructs(String storId, String sectId);
|
||||||
|
|
||||||
|
void setInboundPoint(@Valid InboundSetPointReqVO reqVO);
|
||||||
|
|
||||||
|
void completeInbound(Long iostorinvId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得指定仓库的可用库存分页。
|
* 获得指定仓库的可用库存分页。
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -8,8 +8,14 @@ import cn.code.nl.module.base.api.codegen.CodeGenApi;
|
|||||||
import cn.code.nl.module.base.api.codegen.dto.CodeGenerateReqDTO;
|
import cn.code.nl.module.base.api.codegen.dto.CodeGenerateReqDTO;
|
||||||
import cn.code.nl.module.task.api.TransportTaskApi;
|
import cn.code.nl.module.task.api.TransportTaskApi;
|
||||||
import cn.code.nl.module.task.dto.TransportTaskCreateReqDTO;
|
import cn.code.nl.module.task.dto.TransportTaskCreateReqDTO;
|
||||||
|
import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateReqDTO;
|
||||||
|
import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateRespDTO;
|
||||||
|
import cn.code.nl.module.system.api.user.AdminUserApi;
|
||||||
|
import cn.code.nl.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
import cn.code.nl.module.wms.dal.dataobject.bsrealstorattr.BsrealStorAttrDO;
|
import cn.code.nl.module.wms.dal.dataobject.bsrealstorattr.BsrealStorAttrDO;
|
||||||
import cn.code.nl.module.wms.dal.dataobject.iostorinvdtl.IostorinvDtlDO;
|
import cn.code.nl.module.wms.dal.dataobject.iostorinvdtl.IostorinvDtlDO;
|
||||||
|
import cn.code.nl.module.wms.dal.dataobject.moveinvdtl.MoveInvDtlDO;
|
||||||
|
import cn.code.nl.module.wms.service.moveinv.MoveInvService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -18,6 +24,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.*;
|
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.*;
|
||||||
import cn.code.nl.module.wms.dal.dataobject.iostorinv.IostorInvDO;
|
import cn.code.nl.module.wms.dal.dataobject.iostorinv.IostorInvDO;
|
||||||
import cn.code.nl.framework.common.pojo.PageResult;
|
import cn.code.nl.framework.common.pojo.PageResult;
|
||||||
@@ -30,9 +37,11 @@ import cn.code.nl.module.wms.dal.mysql.bsrealstorattr.BsrealStorAttrMapper;
|
|||||||
import cn.code.nl.module.wms.dal.mysql.groupplate.GroupPlateMapper;
|
import cn.code.nl.module.wms.dal.mysql.groupplate.GroupPlateMapper;
|
||||||
import cn.code.nl.module.wms.dal.mysql.iostorinvdis.IostorinvDisMapper;
|
import cn.code.nl.module.wms.dal.mysql.iostorinvdis.IostorinvDisMapper;
|
||||||
import cn.code.nl.module.wms.dal.mysql.structAttr.StrucAttrMapper;
|
import cn.code.nl.module.wms.dal.mysql.structAttr.StrucAttrMapper;
|
||||||
|
import cn.code.nl.module.wms.dal.mysql.sectattr.SectAttrMapper;
|
||||||
import cn.code.nl.module.wms.dal.dataobject.groupplate.GroupPlateDO;
|
import cn.code.nl.module.wms.dal.dataobject.groupplate.GroupPlateDO;
|
||||||
import cn.code.nl.module.wms.dal.dataobject.iostorinvdis.IostorinvDisDO;
|
import cn.code.nl.module.wms.dal.dataobject.iostorinvdis.IostorinvDisDO;
|
||||||
import cn.code.nl.module.wms.dal.dataobject.structAttr.StrucAttrDO;
|
import cn.code.nl.module.wms.dal.dataobject.structAttr.StrucAttrDO;
|
||||||
|
import cn.code.nl.module.wms.dal.dataobject.sectattr.SectAttrDO;
|
||||||
import cn.code.nl.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import cn.code.nl.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
|
|
||||||
import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
@@ -74,10 +83,453 @@ public class IostorInvServiceImpl implements IostorInvService {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private StrucAttrMapper strucAttrMapper;
|
private StrucAttrMapper strucAttrMapper;
|
||||||
|
@Resource
|
||||||
|
private SectAttrMapper sectAttrMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private TransportTaskApi transportTaskApi;
|
private TransportTaskApi transportTaskApi;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MoveInvService moveInvService;
|
||||||
|
@Resource
|
||||||
|
private AdminUserApi adminUserApi;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Long createInbound(InboundSaveReqVO reqVO) {
|
||||||
|
List<GroupPlateDO> groups = validateInboundGroups(reqVO.getGroupIds(), null);
|
||||||
|
IostorInvDO header = new IostorInvDO();
|
||||||
|
header.setBillCode(generateIoBillCode("createInbound"));
|
||||||
|
header.setIoType("0");
|
||||||
|
header.setBillType(reqVO.getBillType());
|
||||||
|
header.setBizDate(reqVO.getBizDate());
|
||||||
|
header.setStorId(reqVO.getStorId());
|
||||||
|
header.setBillStatus("10");
|
||||||
|
header.setCreateMode("01");
|
||||||
|
header.setRemark(reqVO.getRemark());
|
||||||
|
header.setDetailCount(groups.size());
|
||||||
|
header.setTotalQty(groups.stream().map(GroupPlateDO::getQty)
|
||||||
|
.filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
|
||||||
|
header.setTotalWeight(header.getTotalQty());
|
||||||
|
iostorInvMapper.insert(header);
|
||||||
|
insertInboundDetails(header.getIostorinvId(), groups);
|
||||||
|
insertInboundAllocationPlaceholders(header, groups);
|
||||||
|
groups.forEach(group -> updateGroupStatus(group.getGroupId(), "01"));
|
||||||
|
return header.getIostorinvId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void updateInbound(InboundSaveReqVO reqVO) {
|
||||||
|
IostorInvDO header = requireInbound(reqVO.getIostorinvId(), "10");
|
||||||
|
List<IostorinvDtlDO> oldDetails = iostorinvDtlMapper.selectList(
|
||||||
|
new LambdaQueryWrapperX<IostorinvDtlDO>()
|
||||||
|
.eq(IostorinvDtlDO::getIostorinvId, header.getIostorinvId()));
|
||||||
|
Set<Long> oldGroupIds = oldDetails.stream().map(IostorinvDtlDO::getSourceBilldtlId)
|
||||||
|
.filter(StrUtil::isNotBlank).map(Long::valueOf).collect(Collectors.toSet());
|
||||||
|
List<GroupPlateDO> groups = validateInboundGroups(reqVO.getGroupIds(), oldGroupIds);
|
||||||
|
oldGroupIds.forEach(id -> updateGroupStatus(id, "00"));
|
||||||
|
strucAttrMapper.unlockInbound(String.valueOf(header.getIostorinvId()));
|
||||||
|
iostorinvDisMapper.delete(new LambdaQueryWrapperX<IostorinvDisDO>()
|
||||||
|
.eq(IostorinvDisDO::getIostorinvId, String.valueOf(header.getIostorinvId())));
|
||||||
|
iostorinvDtlMapper.delete(new LambdaQueryWrapperX<IostorinvDtlDO>()
|
||||||
|
.eq(IostorinvDtlDO::getIostorinvId, header.getIostorinvId()));
|
||||||
|
IostorInvDO update = new IostorInvDO();
|
||||||
|
update.setIostorinvId(header.getIostorinvId());
|
||||||
|
update.setBillType(reqVO.getBillType());
|
||||||
|
update.setBizDate(reqVO.getBizDate());
|
||||||
|
update.setStorId(reqVO.getStorId());
|
||||||
|
update.setRemark(reqVO.getRemark());
|
||||||
|
update.setDetailCount(groups.size());
|
||||||
|
update.setTotalQty(groups.stream().map(GroupPlateDO::getQty)
|
||||||
|
.filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
|
||||||
|
update.setTotalWeight(update.getTotalQty());
|
||||||
|
iostorInvMapper.updateById(update);
|
||||||
|
insertInboundDetails(header.getIostorinvId(), groups);
|
||||||
|
insertInboundAllocationPlaceholders(header, groups);
|
||||||
|
groups.forEach(group -> updateGroupStatus(group.getGroupId(), "01"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void deleteInbound(Long iostorinvId) {
|
||||||
|
IostorInvDO header = requireInbound(iostorinvId, "10");
|
||||||
|
List<IostorinvDtlDO> details = iostorinvDtlMapper.selectList(
|
||||||
|
new LambdaQueryWrapperX<IostorinvDtlDO>()
|
||||||
|
.eq(IostorinvDtlDO::getIostorinvId, iostorinvId));
|
||||||
|
// 入库单删除后,原组盘数据恢复为生成状态,允许再次选择建单。
|
||||||
|
details.stream()
|
||||||
|
.map(IostorinvDtlDO::getSourceBilldtlId)
|
||||||
|
.filter(StrUtil::isNotBlank)
|
||||||
|
.map(Long::valueOf)
|
||||||
|
.forEach(groupId -> updateGroupStatus(groupId, "00"));
|
||||||
|
// 分配表、明细表按业务要求物理删除,避免留下孤立记录。
|
||||||
|
iostorinvDisMapper.physicallyDeleteByIostorinvId(iostorinvId);
|
||||||
|
iostorinvDtlMapper.physicallyDeleteByIostorinvId(iostorinvId);
|
||||||
|
// 主表沿用 MyBatis-Plus 逻辑删除,仅更新 deleted 字段。
|
||||||
|
iostorInvMapper.deleteById(header.getIostorinvId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GroupPlateDO> getInboundGroupPlates(String storId) {
|
||||||
|
// 生成状态的组盘数据尚未进入仓库,因此此处不按 storId 过滤。
|
||||||
|
return groupPlateMapper.selectList(new LambdaQueryWrapperX<GroupPlateDO>()
|
||||||
|
.eq(GroupPlateDO::getStatus, "00")
|
||||||
|
.orderByDesc(GroupPlateDO::getGroupId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void allocateInbound(InboundAllocationReqVO reqVO) {
|
||||||
|
IostorInvDO header = iostorInvMapper.selectById(reqVO.getIostorinvId());
|
||||||
|
if (header == null || !"0".equals(header.getIoType())
|
||||||
|
|| !Set.of("10", "20").contains(header.getBillStatus())) {
|
||||||
|
throw exception(INBOUND_STATUS_INVALID);
|
||||||
|
}
|
||||||
|
for (InboundAllocationReqVO.Item item : reqVO.getItems()) {
|
||||||
|
IostorinvDtlDO detail = iostorinvDtlMapper.selectById(item.getIostorinvdtlId());
|
||||||
|
SectAttrDO sect = sectAttrMapper.selectById(item.getSectId());
|
||||||
|
if (detail == null || !Objects.equals(detail.getIostorinvId(), header.getIostorinvId())
|
||||||
|
|| sect == null || !Objects.equals(header.getStorId(), sect.getStorId())) {
|
||||||
|
throw exception(INBOUND_TARGET_INVALID);
|
||||||
|
}
|
||||||
|
StrucAttrDO target;
|
||||||
|
if (Boolean.TRUE.equals(reqVO.getAutoAllocate())) {
|
||||||
|
target = getInboundAvailableStructs(header.getStorId(), item.getSectId()).stream()
|
||||||
|
.findFirst().orElseThrow(() -> exception(INBOUND_TARGET_INVALID));
|
||||||
|
} else {
|
||||||
|
if (StrUtil.isBlank(item.getStructId())) {
|
||||||
|
throw exception(INBOUND_TARGET_INVALID);
|
||||||
|
}
|
||||||
|
target = strucAttrMapper.selectById(item.getStructId());
|
||||||
|
}
|
||||||
|
if (target == null
|
||||||
|
|| !Objects.equals(header.getStorId(), target.getStorId())
|
||||||
|
|| !Objects.equals(item.getSectId(), target.getSectId())
|
||||||
|
|| !"1".equals(target.getLockType())
|
||||||
|
|| StrUtil.isNotBlank(target.getStoragevehicleCode())) {
|
||||||
|
throw exception(INBOUND_TARGET_INVALID);
|
||||||
|
}
|
||||||
|
if (strucAttrMapper.lockForInbound(target.getStructId(),
|
||||||
|
String.valueOf(header.getIostorinvId()), header.getBillCode()) != 1) {
|
||||||
|
throw exception(INBOUND_TARGET_INVALID);
|
||||||
|
}
|
||||||
|
IostorinvDisDO allocation = iostorinvDisMapper.selectOne(
|
||||||
|
new LambdaQueryWrapperX<IostorinvDisDO>()
|
||||||
|
.eq(IostorinvDisDO::getIostorinvId, String.valueOf(header.getIostorinvId()))
|
||||||
|
.eq(IostorinvDisDO::getIostorinvdtlId,
|
||||||
|
String.valueOf(detail.getIostorinvdtlId())));
|
||||||
|
if (allocation == null || StrUtil.isNotBlank(allocation.getStructId())) {
|
||||||
|
throw exception(INBOUND_TARGET_INVALID);
|
||||||
|
}
|
||||||
|
allocation.setSectId(sect.getSectId());
|
||||||
|
allocation.setSectCode(sect.getSectCode());
|
||||||
|
allocation.setSectName(sect.getSectName());
|
||||||
|
allocation.setStructId(target.getStructId());
|
||||||
|
allocation.setStructCode(target.getStructCode());
|
||||||
|
allocation.setStructName(target.getStructName());
|
||||||
|
iostorinvDisMapper.updateById(allocation);
|
||||||
|
detail.setAssignQty(detail.getPlanQty());
|
||||||
|
detail.setUnassignQty(BigDecimal.ZERO);
|
||||||
|
detail.setBillStatus("30");
|
||||||
|
iostorinvDtlMapper.updateById(detail);
|
||||||
|
}
|
||||||
|
refreshInboundAllocationStatus(header.getIostorinvId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void cancelInboundAllocation(Long iostorinvId, Long iostorinvdtlId) {
|
||||||
|
IostorInvDO header = iostorInvMapper.selectById(iostorinvId);
|
||||||
|
if (header == null || !"0".equals(header.getIoType())
|
||||||
|
|| !Set.of("20", "30").contains(header.getBillStatus())) {
|
||||||
|
throw exception(INBOUND_STATUS_INVALID);
|
||||||
|
}
|
||||||
|
IostorinvDtlDO detail = iostorinvDtlMapper.selectById(iostorinvdtlId);
|
||||||
|
IostorinvDisDO allocation = iostorinvDisMapper.selectOne(
|
||||||
|
new LambdaQueryWrapperX<IostorinvDisDO>()
|
||||||
|
.eq(IostorinvDisDO::getIostorinvId, String.valueOf(iostorinvId))
|
||||||
|
.eq(IostorinvDisDO::getIostorinvdtlId, String.valueOf(iostorinvdtlId)));
|
||||||
|
if (detail == null || !Objects.equals(detail.getIostorinvId(), iostorinvId)
|
||||||
|
|| allocation == null || StrUtil.isBlank(allocation.getStructId())) {
|
||||||
|
throw exception(INBOUND_NOT_ALLOCATED);
|
||||||
|
}
|
||||||
|
// 任务一旦创建并下发,不能只释放货位;需走后续的任务撤销流程。
|
||||||
|
if (StrUtil.isNotBlank(allocation.getTaskId())) {
|
||||||
|
throw exception(INBOUND_ALLOCATION_TASK_CREATED);
|
||||||
|
}
|
||||||
|
if (strucAttrMapper.unlockInboundStruct(allocation.getStructId(),
|
||||||
|
String.valueOf(iostorinvId)) != 1) {
|
||||||
|
throw exception(INBOUND_TARGET_INVALID);
|
||||||
|
}
|
||||||
|
// 取消分配要求直接物理删除分配记录,不保留逻辑删除数据。
|
||||||
|
iostorinvDisMapper.physicallyDeleteInboundAllocation(allocation.getIostorinvdisId());
|
||||||
|
detail.setAssignQty(BigDecimal.ZERO);
|
||||||
|
detail.setUnassignQty(detail.getPlanQty());
|
||||||
|
detail.setBillStatus("10");
|
||||||
|
iostorinvDtlMapper.updateById(detail);
|
||||||
|
refreshInboundAllocationStatus(iostorinvId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<StrucAttrDO> getInboundAvailableStructs(String storId, String sectId) {
|
||||||
|
return strucAttrMapper.selectList(new LambdaQueryWrapperX<StrucAttrDO>()
|
||||||
|
.eq(StrucAttrDO::getStorId, storId)
|
||||||
|
.eq(StrucAttrDO::getSectId, sectId)
|
||||||
|
.eq(StrucAttrDO::getIsUsed, true)
|
||||||
|
.eq(StrucAttrDO::getLockType, "1")
|
||||||
|
.and(wrapper -> wrapper.isNull(StrucAttrDO::getStoragevehicleCode)
|
||||||
|
.or().eq(StrucAttrDO::getStoragevehicleCode, ""))
|
||||||
|
.orderByAsc(StrucAttrDO::getRowNum, StrucAttrDO::getColNum,
|
||||||
|
StrucAttrDO::getLayerNum, StrucAttrDO::getStructId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void setInboundPoint(InboundSetPointReqVO reqVO) {
|
||||||
|
IostorInvDO header = iostorInvMapper.selectById(reqVO.getIostorinvId());
|
||||||
|
if (header == null || !"0".equals(header.getIoType())
|
||||||
|
|| !Set.of("10", "20", "30").contains(header.getBillStatus())) {
|
||||||
|
throw exception(INBOUND_STATUS_INVALID);
|
||||||
|
}
|
||||||
|
IostorinvDisDO allocation = iostorinvDisMapper.selectOne(
|
||||||
|
new LambdaQueryWrapperX<IostorinvDisDO>()
|
||||||
|
.eq(IostorinvDisDO::getIostorinvId, String.valueOf(header.getIostorinvId()))
|
||||||
|
.eq(IostorinvDisDO::getIostorinvdtlId,
|
||||||
|
String.valueOf(reqVO.getIostorinvdtlId())));
|
||||||
|
if (allocation == null || StrUtil.isBlank(allocation.getStructId())) {
|
||||||
|
throw exception(INBOUND_NOT_ALLOCATED);
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(allocation.getTaskId())) {
|
||||||
|
throw exception(INBOUND_ALLOCATION_TASK_CREATED);
|
||||||
|
}
|
||||||
|
allocation.setPointCode(reqVO.getPointCode());
|
||||||
|
SectAttrDO sect = sectAttrMapper.selectById(allocation.getSectId());
|
||||||
|
if (sect != null && "主存区".equals(sect.getSectTypeAttr())) {
|
||||||
|
IostorinvDtlDO detail = iostorinvDtlMapper.selectById(reqVO.getIostorinvdtlId());
|
||||||
|
GroupPlateDO group = groupPlateMapper.selectById(Long.valueOf(detail.getSourceBilldtlId()));
|
||||||
|
Long taskId = createAndIssueInboundTask(reqVO.getPointCode(), allocation.getStructCode(),
|
||||||
|
group, header.getIostorinvId(), detail.getSeqNo(), allocation.getIostorinvdisId());
|
||||||
|
allocation.setTaskId(String.valueOf(taskId));
|
||||||
|
allocation.setIsIssued("1");
|
||||||
|
allocation.setWorkStatus("20");
|
||||||
|
}
|
||||||
|
iostorinvDisMapper.updateById(allocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void completeInbound(Long iostorinvId) {
|
||||||
|
IostorInvDO header = requireInbound(iostorinvId, "30");
|
||||||
|
List<IostorinvDisDO> allocations = iostorinvDisMapper.selectList(
|
||||||
|
new LambdaQueryWrapperX<IostorinvDisDO>()
|
||||||
|
.eq(IostorinvDisDO::getIostorinvId, String.valueOf(iostorinvId)));
|
||||||
|
if (allocations.isEmpty()) {
|
||||||
|
throw exception(INBOUND_NOT_ALLOCATED);
|
||||||
|
}
|
||||||
|
for (IostorinvDisDO allocation : allocations) {
|
||||||
|
if (strucAttrMapper.completeInbound(allocation.getStructId(),
|
||||||
|
String.valueOf(iostorinvId), allocation.getStoragevehicleCode()) != 1) {
|
||||||
|
throw exception(INBOUND_TARGET_INVALID);
|
||||||
|
}
|
||||||
|
IostorinvDtlDO detail = iostorinvDtlMapper.selectById(
|
||||||
|
Long.valueOf(allocation.getIostorinvdtlId()));
|
||||||
|
updateGroupStatus(Long.valueOf(detail.getSourceBilldtlId()), "02");
|
||||||
|
allocation.setWorkStatus("99");
|
||||||
|
allocation.setRealQty(allocation.getPlanQty());
|
||||||
|
iostorinvDisMapper.updateById(allocation);
|
||||||
|
detail.setBillStatus("99");
|
||||||
|
detail.setRealQty(detail.getPlanQty());
|
||||||
|
iostorinvDtlMapper.updateById(detail);
|
||||||
|
}
|
||||||
|
IostorInvDO update = new IostorInvDO();
|
||||||
|
update.setIostorinvId(iostorinvId);
|
||||||
|
update.setBillStatus("99");
|
||||||
|
update.setConfirmTime(java.time.LocalDateTime.now());
|
||||||
|
iostorInvMapper.updateById(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<GroupPlateDO> validateInboundGroups(List<Long> groupIds, Set<Long> editableIds) {
|
||||||
|
Set<Long> ids = new LinkedHashSet<>(groupIds);
|
||||||
|
if (ids.size() != groupIds.size()) {
|
||||||
|
throw exception(INBOUND_GROUP_PLATE_INVALID);
|
||||||
|
}
|
||||||
|
List<GroupPlateDO> groups = groupPlateMapper.selectBatchIds(ids);
|
||||||
|
if (groups.size() != ids.size() || groups.stream().anyMatch(group ->
|
||||||
|
!("00".equals(group.getStatus())
|
||||||
|
|| editableIds != null && editableIds.contains(group.getGroupId())))) {
|
||||||
|
throw exception(INBOUND_GROUP_PLATE_INVALID);
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertInboundDetails(Long headerId, List<GroupPlateDO> groups) {
|
||||||
|
int seq = 1;
|
||||||
|
for (GroupPlateDO group : groups) {
|
||||||
|
IostorinvDtlDO detail = new IostorinvDtlDO();
|
||||||
|
detail.setIostorinvId(headerId);
|
||||||
|
detail.setSeqNo(seq++);
|
||||||
|
detail.setMaterialId(group.getMaterialId());
|
||||||
|
detail.setMaterialCode(group.getMaterialCode());
|
||||||
|
detail.setPcsn(group.getPcsn());
|
||||||
|
detail.setQtyUnitId(StrUtil.blankToDefault(group.getQtyUnitId(), "0"));
|
||||||
|
detail.setPlanQty(group.getQty());
|
||||||
|
detail.setAssignQty(BigDecimal.ZERO);
|
||||||
|
detail.setUnassignQty(group.getQty());
|
||||||
|
detail.setBillStatus("10");
|
||||||
|
detail.setSourceBillCode(group.getExtCode());
|
||||||
|
detail.setSourceBillType(group.getExtType());
|
||||||
|
detail.setSourceBilldtlId(String.valueOf(group.getGroupId()));
|
||||||
|
// 入库明细表没有独立载具字段,使用来源装载点保存木箱号。
|
||||||
|
detail.setSourceLoadPort(group.getVehicleCode());
|
||||||
|
iostorinvDtlMapper.insert(detail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入库单创建时即生成分配占位记录。此时没有库区、货位和起点,状态统一为生成。
|
||||||
|
*/
|
||||||
|
private void insertInboundAllocationPlaceholders(IostorInvDO header, List<GroupPlateDO> groups) {
|
||||||
|
List<IostorinvDtlDO> details = iostorinvDtlMapper.selectList(
|
||||||
|
new LambdaQueryWrapperX<IostorinvDtlDO>()
|
||||||
|
.eq(IostorinvDtlDO::getIostorinvId, header.getIostorinvId())
|
||||||
|
.orderByAsc(IostorinvDtlDO::getSeqNo));
|
||||||
|
Map<String, GroupPlateDO> groupMap = groups.stream().collect(
|
||||||
|
Collectors.toMap(group -> String.valueOf(group.getGroupId()), group -> group));
|
||||||
|
for (IostorinvDtlDO detail : details) {
|
||||||
|
insertInboundAllocationPlaceholder(header, detail, groupMap.get(detail.getSourceBilldtlId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertInboundAllocationPlaceholder(IostorInvDO header, IostorinvDtlDO detail) {
|
||||||
|
GroupPlateDO group = StrUtil.isBlank(detail.getSourceBilldtlId())
|
||||||
|
? null : groupPlateMapper.selectById(Long.valueOf(detail.getSourceBilldtlId()));
|
||||||
|
insertInboundAllocationPlaceholder(header, detail, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertInboundAllocationPlaceholder(IostorInvDO header, IostorinvDtlDO detail,
|
||||||
|
GroupPlateDO group) {
|
||||||
|
IostorinvDisDO row = new IostorinvDisDO();
|
||||||
|
row.setIostorinvdisId(IdUtil.getSnowflakeNextIdStr());
|
||||||
|
row.setIostorinvId(String.valueOf(header.getIostorinvId()));
|
||||||
|
row.setIostorinvdtlId(String.valueOf(detail.getIostorinvdtlId()));
|
||||||
|
row.setSeqNo(String.valueOf(detail.getSeqNo()));
|
||||||
|
row.setMaterialId(detail.getMaterialId());
|
||||||
|
row.setMaterialCode(detail.getMaterialCode());
|
||||||
|
row.setPcsn(StrUtil.blankToDefault(detail.getPcsn(), ""));
|
||||||
|
row.setWorkStatus("10");
|
||||||
|
row.setStoragevehicleCode(group == null ? detail.getSourceLoadPort() : group.getVehicleCode());
|
||||||
|
row.setIsIssued("0");
|
||||||
|
row.setQtyUnitId(detail.getQtyUnitId());
|
||||||
|
row.setQtyUnitName(group == null ? "" : StrUtil.blankToDefault(group.getQtyUnitName(), ""));
|
||||||
|
row.setPlanQty(detail.getPlanQty());
|
||||||
|
row.setRealQty(BigDecimal.ZERO);
|
||||||
|
row.setHandType(false);
|
||||||
|
iostorinvDisMapper.insert(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshInboundAllocationStatus(Long iostorinvId) {
|
||||||
|
List<IostorinvDisDO> allocations = iostorinvDisMapper.selectList(
|
||||||
|
new LambdaQueryWrapperX<IostorinvDisDO>()
|
||||||
|
.eq(IostorinvDisDO::getIostorinvId, String.valueOf(iostorinvId)));
|
||||||
|
boolean allAllocated = !allocations.isEmpty()
|
||||||
|
&& allocations.stream().allMatch(item -> StrUtil.isNotBlank(item.getStructId()));
|
||||||
|
IostorInvDO update = new IostorInvDO();
|
||||||
|
update.setIostorinvId(iostorinvId);
|
||||||
|
update.setBillStatus(allAllocated ? "30" : "20");
|
||||||
|
update.setDisTime(java.time.LocalDateTime.now());
|
||||||
|
iostorInvMapper.updateById(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IostorInvDO requireInbound(Long id, String status) {
|
||||||
|
IostorInvDO header = iostorInvMapper.selectById(id);
|
||||||
|
if (header == null || !"0".equals(header.getIoType())) {
|
||||||
|
throw exception(IOSTOR_INV_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
if (!status.equals(header.getBillStatus())) {
|
||||||
|
throw exception(INBOUND_STATUS_INVALID);
|
||||||
|
}
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateGroupStatus(Long groupId, String status) {
|
||||||
|
GroupPlateDO update = new GroupPlateDO();
|
||||||
|
update.setGroupId(groupId);
|
||||||
|
update.setStatus(status);
|
||||||
|
groupPlateMapper.updateById(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateIoBillCode(String scene) {
|
||||||
|
try {
|
||||||
|
CommonResult<String> result = codeGenApi.generate(
|
||||||
|
new CodeGenerateReqDTO().setRuleCode("IO_CODE"));
|
||||||
|
if (result != null && result.isSuccess() && StrUtil.isNotBlank(result.getData())) {
|
||||||
|
return result.getData();
|
||||||
|
}
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
log.error("[{}][生成入库单号异常]", scene, ex);
|
||||||
|
}
|
||||||
|
throw exception(IOSTOR_INV_CODE_GENERATE_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IostorinvDisDO buildInboundAllocation(IostorInvDO header, IostorinvDtlDO detail,
|
||||||
|
GroupPlateDO group, SectAttrDO sect,
|
||||||
|
StrucAttrDO target, String pointCode) {
|
||||||
|
IostorinvDisDO row = new IostorinvDisDO();
|
||||||
|
row.setIostorinvdisId(IdUtil.getSnowflakeNextIdStr());
|
||||||
|
row.setIostorinvId(String.valueOf(header.getIostorinvId()));
|
||||||
|
row.setIostorinvdtlId(String.valueOf(detail.getIostorinvdtlId()));
|
||||||
|
row.setSeqNo(String.valueOf(detail.getSeqNo()));
|
||||||
|
row.setSectId(sect.getSectId());
|
||||||
|
row.setSectCode(sect.getSectCode());
|
||||||
|
row.setSectName(sect.getSectName());
|
||||||
|
row.setStructId(target.getStructId());
|
||||||
|
row.setStructCode(target.getStructCode());
|
||||||
|
row.setStructName(target.getStructName());
|
||||||
|
row.setMaterialId(detail.getMaterialId());
|
||||||
|
row.setMaterialCode(detail.getMaterialCode());
|
||||||
|
row.setPcsn(StrUtil.blankToDefault(detail.getPcsn(), ""));
|
||||||
|
row.setWorkStatus("10");
|
||||||
|
row.setStoragevehicleCode(group.getVehicleCode());
|
||||||
|
row.setIsIssued("0");
|
||||||
|
row.setQtyUnitId(detail.getQtyUnitId());
|
||||||
|
row.setQtyUnitName(StrUtil.blankToDefault(group.getQtyUnitName(), ""));
|
||||||
|
row.setPlanQty(detail.getPlanQty());
|
||||||
|
row.setRealQty(BigDecimal.ZERO);
|
||||||
|
row.setPointCode(pointCode);
|
||||||
|
row.setHandType(false);
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long createAndIssueInboundTask(String sourcePoint, String targetPoint,
|
||||||
|
GroupPlateDO group, long taskGroupId,
|
||||||
|
long sortSeq, String bizId) {
|
||||||
|
TransportTaskCreateReqDTO task = new TransportTaskCreateReqDTO();
|
||||||
|
task.setTaskName("入库任务");
|
||||||
|
task.setOwnerService("WMS");
|
||||||
|
task.setBizType("INBOUND");
|
||||||
|
task.setBizId(bizId);
|
||||||
|
task.setHandleCode("INTASK");
|
||||||
|
task.setTaskType("010501");
|
||||||
|
task.setAcsTaskType("7");
|
||||||
|
task.setAgvSystemType("1");
|
||||||
|
task.setPointCode1(sourcePoint);
|
||||||
|
task.setPointCode2(targetPoint);
|
||||||
|
task.setVehicleCode(group.getVehicleCode());
|
||||||
|
if (StrUtil.isNotBlank(group.getMaterialId())) {
|
||||||
|
task.setMaterialId(Long.valueOf(group.getMaterialId()));
|
||||||
|
}
|
||||||
|
task.setMaterialCode(group.getMaterialCode());
|
||||||
|
task.setTaskGroupId(taskGroupId);
|
||||||
|
task.setSortSeq(sortSeq);
|
||||||
|
task.setIsAutoIssue("0");
|
||||||
|
task.setProductArea("LK");
|
||||||
|
task.setIsCreateFinish(true);
|
||||||
|
Long taskId = transportTaskApi.createTransportTask(task).getCheckedData();
|
||||||
|
transportTaskApi.issueTransportTaskRollBack(taskId).getCheckedData();
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建出库单及其明细。
|
* 创建出库单及其明细。
|
||||||
*/
|
*/
|
||||||
@@ -300,9 +752,24 @@ public class IostorInvServiceImpl implements IostorInvService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public List<OutboundAllocationRespVO> getOutboundAllocations(
|
public List<OutboundAllocationRespVO> getOutboundAllocations(
|
||||||
Long iostorinvId, Long iostorinvdtlId, String sectId) {
|
Long iostorinvId, Long iostorinvdtlId, String sectId) {
|
||||||
return iostorinvDisMapper.selectAllocationList(iostorinvId, iostorinvdtlId, sectId);
|
List<OutboundAllocationRespVO> result =
|
||||||
|
iostorinvDisMapper.selectAllocationList(iostorinvId, iostorinvdtlId, sectId);
|
||||||
|
// 兼容入库功能上线前已经创建的单据:旧数据没有同步生成分配占位记录,
|
||||||
|
// 首次点击明细查询时自动补建,避免页面无法继续分配货位。
|
||||||
|
if (result.isEmpty() && iostorinvdtlId != null) {
|
||||||
|
IostorInvDO header = iostorInvMapper.selectById(iostorinvId);
|
||||||
|
IostorinvDtlDO detail = iostorinvDtlMapper.selectById(iostorinvdtlId);
|
||||||
|
if (header != null && "0".equals(header.getIoType()) && detail != null
|
||||||
|
&& Objects.equals(detail.getIostorinvId(), iostorinvId)) {
|
||||||
|
insertInboundAllocationPlaceholder(header, detail);
|
||||||
|
result = iostorinvDisMapper.selectAllocationList(
|
||||||
|
iostorinvId, iostorinvdtlId, sectId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -413,6 +880,7 @@ public class IostorInvServiceImpl implements IostorInvService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void oneClickSet(OutboundOneClickSetReqVO reqVO) {
|
public void oneClickSet(OutboundOneClickSetReqVO reqVO) {
|
||||||
// 1. 校验出库单和当前选中明细,避免为其他单据的明细生成任务。
|
// 1. 校验出库单和当前选中明细,避免为其他单据的明细生成任务。
|
||||||
IostorInvDO header = iostorInvMapper.selectById(reqVO.getIostorinvId());
|
IostorInvDO header = iostorInvMapper.selectById(reqVO.getIostorinvId());
|
||||||
@@ -449,9 +917,7 @@ public class IostorInvServiceImpl implements IostorInvService {
|
|||||||
Map<String, List<StrucAttrDO>> targetGroups = targetStructs.stream()
|
Map<String, List<StrucAttrDO>> targetGroups = targetStructs.stream()
|
||||||
.collect(java.util.stream.Collectors.groupingBy(this::placementGroupKey,
|
.collect(java.util.stream.Collectors.groupingBy(this::placementGroupKey,
|
||||||
LinkedHashMap::new, java.util.stream.Collectors.toList()));
|
LinkedHashMap::new, java.util.stream.Collectors.toList()));
|
||||||
// 6. 预先准备移库空位、两个出库点当前负载,以及本批任务共用的任务组和执行序号。
|
// 6. 预先查询两个出库点当前负载,并生成本批任务共用的任务组和执行序号。
|
||||||
List<StrucAttrDO> freeMoveLocations = selectFreeMoveLocations(header.getStorId(), targetStructIds);
|
|
||||||
Iterator<StrucAttrDO> freeLocationIterator = freeMoveLocations.iterator();
|
|
||||||
long[] destinationLoads = loadDestinationCounts();
|
long[] destinationLoads = loadDestinationCounts();
|
||||||
long taskGroupId = IdUtil.getSnowflakeNextId();
|
long taskGroupId = IdUtil.getSnowflakeNextId();
|
||||||
long sortSeq = 0;
|
long sortSeq = 0;
|
||||||
@@ -484,31 +950,36 @@ public class IostorInvServiceImpl implements IostorInvService {
|
|||||||
.findFirst().orElseThrow();
|
.findFirst().orElseThrow();
|
||||||
}
|
}
|
||||||
if ("MOVE".equals(action.type())) {
|
if ("MOVE".equals(action.type())) {
|
||||||
// 9. 挡路箱创建移库任务,并锁定起点、目的仓位,防止规划后被其他任务占用。
|
// 9. 先按挡路载具自动创建移库单,由移库单统一选择同库区同层目标仓位并加移出、移入锁。
|
||||||
if (!freeLocationIterator.hasNext()) {
|
MoveInvAutoCreateReqDTO moveReq = new MoveInvAutoCreateReqDTO();
|
||||||
throw exception(IOSTOR_INV_NO_MOVE_LOCATION);
|
moveReq.setVehicleCodes(List.of(action.vehicleCode()));
|
||||||
}
|
MoveInvAutoCreateRespDTO moveResult = moveInvService.autoCreate(moveReq);
|
||||||
StrucAttrDO destination = freeLocationIterator.next();
|
MoveInvDtlDO moveDetail = moveInvService.get(moveResult.getMoveinvId()).getDetails().stream()
|
||||||
Long taskId = createAndIssueTask("010505", "移库任务", source.getStructCode(),
|
.filter(item -> action.vehicleCode().equals(item.getStoragevehicleCode()))
|
||||||
destination.getStructCode(), action.vehicleCode(), null, null,
|
.findFirst()
|
||||||
taskGroupId, ++sortSeq, "MOVE", action.structId());
|
.orElseThrow();
|
||||||
strucAttrMapper.lockForTask(source.getStructId(), String.valueOf(taskId));
|
// 10. 任务起点、目的点严格使用移库单明细,保证移库任务与移库单据完全一致。
|
||||||
strucAttrMapper.lockForTask(destination.getStructId(), String.valueOf(taskId));
|
Long taskId = createAndIssueTask("010505", "移库任务",
|
||||||
|
moveDetail.getTurnoutStructCode(), moveDetail.getTurninStructCode(),
|
||||||
|
action.vehicleCode(), null, null, taskGroupId, ++sortSeq,
|
||||||
|
"MOVE", String.valueOf(moveDetail.getMoveinvdtlId()));
|
||||||
|
// 11. 将任务标识和下发状态回写移库明细,便于后续任务回调按明细处理移库完成。
|
||||||
|
moveInvService.bindTask(moveDetail.getMoveinvdtlId(), taskId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 10. 非本次分配的载具只参与通道规划,不创建出库任务。
|
// 12. 非本次分配的载具只参与通道规划,不创建出库任务。
|
||||||
List<IostorinvDisDO> vehicleAllocations = allocationsByVehicle.get(action.vehicleCode());
|
List<IostorinvDisDO> vehicleAllocations = allocationsByVehicle.get(action.vehicleCode());
|
||||||
if (CollUtil.isEmpty(vehicleAllocations)) {
|
if (CollUtil.isEmpty(vehicleAllocations)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 11. 比较两个出库点未完成任务数,选择当前负载较小的目的点。
|
// 13. 比较两个出库点未完成任务数,选择当前负载较小的目的点。
|
||||||
String destinationPoint = chooseDestination(destinationLoads);
|
String destinationPoint = chooseDestination(destinationLoads);
|
||||||
Long taskId = createAndIssueTask("010503", "出库任务", source.getStructCode(),
|
Long taskId = createAndIssueTask("010503", "出库任务", source.getStructCode(),
|
||||||
destinationPoint, action.vehicleCode(), detail.getMaterialId(),
|
destinationPoint, action.vehicleCode(), detail.getMaterialId(),
|
||||||
detail.getMaterialCode(), taskGroupId, ++sortSeq, "OUT",
|
detail.getMaterialCode(), taskGroupId, ++sortSeq, "OUT",
|
||||||
vehicleAllocations.get(0).getIostorinvdisId());
|
vehicleAllocations.get(0).getIostorinvdisId());
|
||||||
destinationLoads[destinationPoint.equals(OUTBOUND_POINT_1.getPointCode()) ? 0 : 1]++;
|
destinationLoads[destinationPoint.equals(OUTBOUND_POINT_1.getPointCode()) ? 0 : 1]++;
|
||||||
// 12. 同载具的全部分配记录关联同一个任务,并推进到“执行中/已下发”状态。
|
// 14. 同载具的全部分配记录关联同一个任务,并推进到“执行中/已下发”状态。
|
||||||
vehicleAllocations.forEach(allocation -> {
|
vehicleAllocations.forEach(allocation -> {
|
||||||
IostorinvDisDO update = new IostorinvDisDO();
|
IostorinvDisDO update = new IostorinvDisDO();
|
||||||
update.setIostorinvdisId(allocation.getIostorinvdisId());
|
update.setIostorinvdisId(allocation.getIostorinvdisId());
|
||||||
@@ -518,7 +989,7 @@ public class IostorInvServiceImpl implements IostorInvService {
|
|||||||
update.setIsIssued("1");
|
update.setIsIssued("1");
|
||||||
iostorinvDisMapper.updateById(update);
|
iostorinvDisMapper.updateById(update);
|
||||||
});
|
});
|
||||||
// 13. 锁定出库起点仓位,直到任务回调完成后再由后续流程释放。
|
// 15. 锁定出库起点仓位,直到任务回调完成后再由后续流程释放。
|
||||||
strucAttrMapper.lockForTask(source.getStructId(), String.valueOf(taskId));
|
strucAttrMapper.lockForTask(source.getStructId(), String.valueOf(taskId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -798,6 +1269,20 @@ public class IostorInvServiceImpl implements IostorInvService {
|
|||||||
.in(BsrealStorAttrDO::getStorId, storIds)),
|
.in(BsrealStorAttrDO::getStorId, storIds)),
|
||||||
BsrealStorAttrDO::getStorId, BsrealStorAttrDO::getStorName);
|
BsrealStorAttrDO::getStorId, BsrealStorAttrDO::getStorName);
|
||||||
pageResult.getList().forEach(item -> item.setStorName(storNameMap.get(item.getStorId())));
|
pageResult.getList().forEach(item -> item.setStorName(storNameMap.get(item.getStorId())));
|
||||||
|
Set<Long> creatorIds = pageResult.getList().stream()
|
||||||
|
.map(IostorInvDO::getCreator)
|
||||||
|
.filter(StrUtil::isNotBlank)
|
||||||
|
.map(Long::valueOf)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if (CollUtil.isNotEmpty(creatorIds)) {
|
||||||
|
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(creatorIds);
|
||||||
|
pageResult.getList().forEach(item -> {
|
||||||
|
if (StrUtil.isNotBlank(item.getCreator())) {
|
||||||
|
AdminUserRespDTO user = userMap.get(Long.valueOf(item.getCreator()));
|
||||||
|
item.setCreatorName(user == null ? null : user.getNickname());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
return pageResult;
|
return pageResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,4 +31,12 @@ public interface MoveInvService {
|
|||||||
List<StrucAttrDO> getAvailableTargetStructs(String sourceStructCode);
|
List<StrucAttrDO> getAvailableTargetStructs(String sourceStructCode);
|
||||||
|
|
||||||
MoveInvAutoCreateRespDTO autoCreate(MoveInvAutoCreateReqDTO reqDTO);
|
MoveInvAutoCreateRespDTO autoCreate(MoveInvAutoCreateReqDTO reqDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将已创建并下发的搬运任务绑定到移库明细。
|
||||||
|
*
|
||||||
|
* @param moveinvdtlId 移库明细标识
|
||||||
|
* @param taskId 搬运任务标识
|
||||||
|
*/
|
||||||
|
void bindTask(Long moveinvdtlId, Long taskId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package cn.code.nl.module.wms.service.moveinv;
|
package cn.code.nl.module.wms.service.moveinv;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.code.nl.framework.common.pojo.CommonResult;
|
import cn.code.nl.framework.common.pojo.CommonResult;
|
||||||
import cn.code.nl.framework.common.pojo.PageResult;
|
import cn.code.nl.framework.common.pojo.PageResult;
|
||||||
import cn.code.nl.module.base.api.codegen.CodeGenApi;
|
import cn.code.nl.module.base.api.codegen.CodeGenApi;
|
||||||
import cn.code.nl.module.base.api.codegen.dto.CodeGenerateReqDTO;
|
import cn.code.nl.module.base.api.codegen.dto.CodeGenerateReqDTO;
|
||||||
import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateReqDTO;
|
import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateReqDTO;
|
||||||
import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateRespDTO;
|
import cn.code.nl.module.wms.api.moveinv.dto.MoveInvAutoCreateRespDTO;
|
||||||
|
import cn.code.nl.module.system.api.user.AdminUserApi;
|
||||||
|
import cn.code.nl.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvDetailSaveReqVO;
|
import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvDetailSaveReqVO;
|
||||||
import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvInventoryRespVO;
|
import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvInventoryRespVO;
|
||||||
import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvPageReqVO;
|
import cn.code.nl.module.wms.controller.admin.moveinv.vo.MoveInvPageReqVO;
|
||||||
@@ -56,6 +59,8 @@ public class MoveInvServiceImpl implements MoveInvService {
|
|||||||
@Resource
|
@Resource
|
||||||
private MoveInvDtlMapper moveInvDtlMapper;
|
private MoveInvDtlMapper moveInvDtlMapper;
|
||||||
@Resource
|
@Resource
|
||||||
|
private AdminUserApi adminUserApi;
|
||||||
|
@Resource
|
||||||
private StrucAttrMapper strucAttrMapper;
|
private StrucAttrMapper strucAttrMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private CodeGenApi codeGenApi;
|
private CodeGenApi codeGenApi;
|
||||||
@@ -129,6 +134,7 @@ public class MoveInvServiceImpl implements MoveInvService {
|
|||||||
throw exception(MOVE_INV_NOT_EXISTS);
|
throw exception(MOVE_INV_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
MoveInvRespVO respVO = convert(moveInv);
|
MoveInvRespVO respVO = convert(moveInv);
|
||||||
|
fillCreatorNames(List.of(respVO));
|
||||||
respVO.setDetails(moveInvDtlMapper.selectByMoveinvId(id));
|
respVO.setDetails(moveInvDtlMapper.selectByMoveinvId(id));
|
||||||
return respVO;
|
return respVO;
|
||||||
}
|
}
|
||||||
@@ -140,9 +146,31 @@ public class MoveInvServiceImpl implements MoveInvService {
|
|||||||
public PageResult<MoveInvRespVO> getPage(MoveInvPageReqVO reqVO) {
|
public PageResult<MoveInvRespVO> getPage(MoveInvPageReqVO reqVO) {
|
||||||
PageResult<MoveInvDO> page = moveInvMapper.selectPage(reqVO);
|
PageResult<MoveInvDO> page = moveInvMapper.selectPage(reqVO);
|
||||||
List<MoveInvRespVO> list = page.getList().stream().map(this::convert).toList();
|
List<MoveInvRespVO> list = page.getList().stream().map(this::convert).toList();
|
||||||
|
fillCreatorNames(list);
|
||||||
return new PageResult<>(list, page.getTotal());
|
return new PageResult<>(list, page.getTotal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人字段由框架自动填充为字符串,这里显式批量查询姓名,避免自动翻译类型不匹配。
|
||||||
|
*/
|
||||||
|
private void fillCreatorNames(List<MoveInvRespVO> list) {
|
||||||
|
Set<Long> creatorIds = list.stream()
|
||||||
|
.map(MoveInvRespVO::getCreator)
|
||||||
|
.filter(StrUtil::isNotBlank)
|
||||||
|
.map(Long::valueOf)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if (creatorIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(creatorIds);
|
||||||
|
list.forEach(item -> {
|
||||||
|
if (StrUtil.isNotBlank(item.getCreator())) {
|
||||||
|
AdminUserRespDTO user = userMap.get(Long.valueOf(item.getCreator()));
|
||||||
|
item.setCreatorName(user == null ? null : user.getNickname());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询移库可用库存。
|
* 查询移库可用库存。
|
||||||
*/
|
*/
|
||||||
@@ -204,6 +232,19 @@ public class MoveInvServiceImpl implements MoveInvService {
|
|||||||
return new MoveInvAutoCreateRespDTO(moveInv.getMoveinvId(), moveInv.getBillCode());
|
return new MoveInvAutoCreateRespDTO(moveInv.getMoveinvId(), moveInv.getBillCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将任务标识回写到移库明细,并标记为已下发。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void bindTask(Long moveinvdtlId, Long taskId) {
|
||||||
|
MoveInvDtlDO update = new MoveInvDtlDO();
|
||||||
|
update.setMoveinvdtlId(moveinvdtlId);
|
||||||
|
update.setTaskId(taskId);
|
||||||
|
update.setWorkStatus("20");
|
||||||
|
update.setIsIssued("1");
|
||||||
|
moveInvDtlMapper.updateById(update);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建主表、明细并锁定来源和目标仓位。
|
* 创建主表、明细并锁定来源和目标仓位。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,8 +15,11 @@
|
|||||||
dis.qty_unit_name,
|
dis.qty_unit_name,
|
||||||
dis.sect_id,
|
dis.sect_id,
|
||||||
dis.sect_name,
|
dis.sect_name,
|
||||||
|
dis.struct_id,
|
||||||
dis.struct_code,
|
dis.struct_code,
|
||||||
dis.struct_name
|
dis.struct_name,
|
||||||
|
dis.point_code,
|
||||||
|
dis.task_id
|
||||||
FROM wms_iostorinvdis dis
|
FROM wms_iostorinvdis dis
|
||||||
LEFT JOIN base_materialbase mb
|
LEFT JOIN base_materialbase mb
|
||||||
ON mb.material_id = dis.material_id
|
ON mb.material_id = dis.material_id
|
||||||
|
|||||||
@@ -108,6 +108,7 @@
|
|||||||
d.source_bill_code,
|
d.source_bill_code,
|
||||||
d.source_bill_type,
|
d.source_bill_type,
|
||||||
d.source_billdtl_id,
|
d.source_billdtl_id,
|
||||||
|
d.source_load_port AS vehicle_code,
|
||||||
d.remark
|
d.remark
|
||||||
FROM wms_iostorinvdtl d
|
FROM wms_iostorinvdtl d
|
||||||
LEFT JOIN base_materialbase mb
|
LEFT JOIN base_materialbase mb
|
||||||
|
|||||||
@@ -1,10 +1,24 @@
|
|||||||
import type { Dayjs } from 'dayjs';
|
import type { Dayjs } from 'dayjs';
|
||||||
|
|
||||||
import type { PageParam, PageResult } from '@vben/request';
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
import type { WmsStrucAttrApi } from '#/api/wms/structAttr';
|
||||||
|
|
||||||
import { requestClient } from '#/api/request';
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
export namespace WmsIostorInvApi {
|
export namespace WmsIostorInvApi {
|
||||||
|
export interface InboundGroupPlate {
|
||||||
|
groupId: string;
|
||||||
|
vehicleCode?: string;
|
||||||
|
status: string;
|
||||||
|
materialId?: string;
|
||||||
|
materialCode: string;
|
||||||
|
pcsn?: string;
|
||||||
|
qty: number;
|
||||||
|
qtyUnitId?: string;
|
||||||
|
qtyUnitName?: string;
|
||||||
|
extCode?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/** 出入库单主表信息 */
|
/** 出入库单主表信息 */
|
||||||
export interface IostorInv {
|
export interface IostorInv {
|
||||||
iostorinvId: string; // 出入单标识
|
iostorinvId: string; // 出入单标识
|
||||||
@@ -127,8 +141,11 @@ export namespace WmsIostorInvApi {
|
|||||||
qtyUnitName?: string;
|
qtyUnitName?: string;
|
||||||
sectId?: string;
|
sectId?: string;
|
||||||
sectName?: string;
|
sectName?: string;
|
||||||
|
structId?: string;
|
||||||
structCode?: string;
|
structCode?: string;
|
||||||
structName?: string;
|
structName?: string;
|
||||||
|
pointCode?: string;
|
||||||
|
taskId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ManualAllocationInventory {
|
export interface ManualAllocationInventory {
|
||||||
@@ -157,6 +174,75 @@ export function getIostorInvPage(params: PageParam) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getInboundGroupPlates(storId: string) {
|
||||||
|
return requestClient.get<WmsIostorInvApi.InboundGroupPlate[]>(
|
||||||
|
'/wms/iostor-inv/inbound-group-plates',
|
||||||
|
{ params: { storId } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createInbound(data: {
|
||||||
|
billType: string;
|
||||||
|
bizDate: string;
|
||||||
|
groupIds: string[];
|
||||||
|
remark?: string;
|
||||||
|
storId: string;
|
||||||
|
}) {
|
||||||
|
return requestClient.post('/wms/iostor-inv/create-inbound', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateInbound(data: {
|
||||||
|
billType: string;
|
||||||
|
bizDate: string;
|
||||||
|
groupIds: string[];
|
||||||
|
iostorinvId: string;
|
||||||
|
remark?: string;
|
||||||
|
storId: string;
|
||||||
|
}) {
|
||||||
|
return requestClient.put('/wms/iostor-inv/update-inbound', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteInbound(iostorinvId: string) {
|
||||||
|
return requestClient.delete('/wms/iostor-inv/delete-inbound', {
|
||||||
|
params: { iostorinvId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function allocateInbound(data: {
|
||||||
|
autoAllocate?: boolean;
|
||||||
|
iostorinvId: string;
|
||||||
|
items: { iostorinvdtlId: string; sectId: string; structId: string }[];
|
||||||
|
}) {
|
||||||
|
return requestClient.post('/wms/iostor-inv/allocate-inbound', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cancelInboundAllocation(iostorinvId: string, iostorinvdtlId: string) {
|
||||||
|
return requestClient.delete('/wms/iostor-inv/cancel-inbound-allocation', {
|
||||||
|
params: { iostorinvId, iostorinvdtlId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getInboundAvailableStructs(storId: string, sectId: string) {
|
||||||
|
return requestClient.get<WmsStrucAttrApi.StrucAttr[]>(
|
||||||
|
'/wms/iostor-inv/inbound-available-structs',
|
||||||
|
{ params: { storId, sectId } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setInboundPoint(data: {
|
||||||
|
iostorinvId: string;
|
||||||
|
iostorinvdtlId: string;
|
||||||
|
pointCode: string;
|
||||||
|
}) {
|
||||||
|
return requestClient.post('/wms/iostor-inv/set-inbound-point', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function completeInbound(iostorinvId: string) {
|
||||||
|
return requestClient.post('/wms/iostor-inv/complete-inbound', undefined, {
|
||||||
|
params: { iostorinvId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 查询出入库单主表详情 */
|
/** 查询出入库单主表详情 */
|
||||||
export function getIostorInv(id: string) {
|
export function getIostorInv(id: string) {
|
||||||
return requestClient.get<WmsIostorInvApi.IostorInv>(
|
return requestClient.get<WmsIostorInvApi.IostorInv>(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { WmsIostorInvApi } from '#/api/wms/iostorinv';
|
import type { WmsIostorInvApi } from '#/api/wms/iostorinv';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
@@ -17,6 +17,8 @@ const header = ref<WmsIostorInvApi.IostorInv>();
|
|||||||
const details = ref<WmsIostorInvApi.OutboundDisplayDetail[]>([]);
|
const details = ref<WmsIostorInvApi.OutboundDisplayDetail[]>([]);
|
||||||
const allocations = ref<WmsIostorInvApi.OutboundAllocation[]>([]);
|
const allocations = ref<WmsIostorInvApi.OutboundAllocation[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
const isInbound = computed(() => header.value?.ioType === '0');
|
||||||
|
const modalTitle = computed(() => isInbound.value ? '入库单详情' : '出库单详情');
|
||||||
|
|
||||||
const detailColumns = [
|
const detailColumns = [
|
||||||
{ key: 'index', title: '序号', width: 60 },
|
{ key: 'index', title: '序号', width: 60 },
|
||||||
@@ -38,7 +40,7 @@ const allocationColumns = [
|
|||||||
{ dataIndex: 'materialName', title: '物料名称', width: 170 },
|
{ dataIndex: 'materialName', title: '物料名称', width: 170 },
|
||||||
{ dataIndex: 'storagevehicleCode', title: '箱号', width: 150 },
|
{ dataIndex: 'storagevehicleCode', title: '箱号', width: 150 },
|
||||||
{ dataIndex: 'pcsn', title: '批次', width: 140 },
|
{ dataIndex: 'pcsn', title: '批次', width: 140 },
|
||||||
{ dataIndex: 'planQty', title: '出库重量', width: 120 },
|
{ dataIndex: 'planQty', key: 'allocationQty', title: '分配重量', width: 120 },
|
||||||
{ dataIndex: 'sectName', title: '库区', width: 130 },
|
{ dataIndex: 'sectName', title: '库区', width: 130 },
|
||||||
{ dataIndex: 'structCode', title: '仓位编码', width: 130 },
|
{ dataIndex: 'structCode', title: '仓位编码', width: 130 },
|
||||||
{ dataIndex: 'structName', title: '仓位名称', width: 160 },
|
{ dataIndex: 'structName', title: '仓位名称', width: 160 },
|
||||||
@@ -80,7 +82,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal class="w-[1500px] max-w-[98vw]" title="出入库单详情">
|
<Modal class="w-[1500px] max-w-[98vw]" :title="modalTitle">
|
||||||
<div class="px-3 pb-3">
|
<div class="px-3 pb-3">
|
||||||
<Descriptions bordered class="mb-4" size="small" :column="4">
|
<Descriptions bordered class="mb-4" size="small" :column="4">
|
||||||
<DescriptionsItem label="单据编号">{{ header?.billCode }}</DescriptionsItem>
|
<DescriptionsItem label="单据编号">{{ header?.billCode }}</DescriptionsItem>
|
||||||
@@ -130,6 +132,9 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
<template v-else-if="column.dataIndex === 'workStatus'">
|
<template v-else-if="column.dataIndex === 'workStatus'">
|
||||||
{{ statusLabel(record.workStatus) }}
|
{{ statusLabel(record.workStatus) }}
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="column.key === 'allocationQty'">
|
||||||
|
{{ record.planQty }}
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -393,6 +393,13 @@ export function useGridColumns(): VxeTableGridOptions<WmsStrucAttrApi.StrucAttr>
|
|||||||
{ field: 'blockNum', title: '块', minWidth: 120 },
|
{ field: 'blockNum', title: '块', minWidth: 120 },
|
||||||
{ field: 'isTempstruct', title: '是否临时仓位', minWidth: 120, formatter: boolFormatter },
|
{ field: 'isTempstruct', title: '是否临时仓位', minWidth: 120, formatter: boolFormatter },
|
||||||
{ field: 'isUsed', title: '是否启用', minWidth: 120, formatter: boolFormatter },
|
{ field: 'isUsed', title: '是否启用', minWidth: 120, formatter: boolFormatter },
|
||||||
|
{ field: 'storagevehicleCode', title: '存储载具号', minWidth: 150 },
|
||||||
|
{
|
||||||
|
field: 'lockType',
|
||||||
|
title: '锁定类型',
|
||||||
|
minWidth: 120,
|
||||||
|
cellRender: { name: 'CellDict', props: { type: DICT_TYPE.WMS_LOCK_TYPE } },
|
||||||
|
},
|
||||||
{ field: 'isZdepth', title: '是否判断高度', minWidth: 120, formatter: boolFormatter },
|
{ field: 'isZdepth', title: '是否判断高度', minWidth: 120, formatter: boolFormatter },
|
||||||
{ field: 'createTime', title: '创建时间', minWidth: 120, formatter: 'formatDateTime' },
|
{ field: 'createTime', title: '创建时间', minWidth: 120, formatter: 'formatDateTime' },
|
||||||
{ field: 'remark', title: '备注', minWidth: 120 },
|
{ field: 'remark', title: '备注', minWidth: 120 },
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ const WMS_DICT = {
|
|||||||
WMS_VEHICLE_TYPE: 'wms_vehicle_type', // 载具类型
|
WMS_VEHICLE_TYPE: 'wms_vehicle_type', // 载具类型
|
||||||
WMS_OVER_STRUCT_TYPE: 'wms_over_struct_type', // 是否超限
|
WMS_OVER_STRUCT_TYPE: 'wms_over_struct_type', // 是否超限
|
||||||
WMS_OUT_BILL_TYPE: 'wms_out_bill_type', // 出库单据类型
|
WMS_OUT_BILL_TYPE: 'wms_out_bill_type', // 出库单据类型
|
||||||
|
WMS_IN_BILL_TYPE: 'wms_in_bill_type', // 入库单据类型
|
||||||
WMS_IO_BILL_STATUS: 'wms_io_bill_status', // 出入库单据状态
|
WMS_IO_BILL_STATUS: 'wms_io_bill_status', // 出入库单据状态
|
||||||
WMS_CREATE_TYPE: 'wms_create_type', // 生成方式
|
WMS_CREATE_TYPE: 'wms_create_type', // 生成方式
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
Reference in New Issue
Block a user