feat(stock): 添加退库功能- 在 IWmsStockIoService接口中添加 scanReturnStockByBo 方法
- 在 WmsStockIo 模型中添加 parentId 字段用于关联原出库单 - 在 WmsStockIoBo 中添加 parentId 字段 - 在 WmsStockIoController 中添加 scanReturnStock 控制器方法 - 在 WmsStockIoServiceImpl 中实现 scanReturnStockByBo 方法的业务逻辑
This commit is contained in:
@@ -173,4 +173,14 @@ public class WmsStockIoController extends BaseController {
|
|||||||
return R.fail(e.getMessage());
|
return R.fail(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//退库操作
|
||||||
|
@PostMapping("/scanReturnStock")
|
||||||
|
public R<Void> scanReturnStock(@RequestBody WmsStockIoBo bo) {
|
||||||
|
try {
|
||||||
|
boolean result = iWmsStockIoService.scanReturnStockByBo(bo);
|
||||||
|
return toAjax(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return R.fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ public class WmsStockIo extends BaseEntity {
|
|||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
private String remark;
|
private String remark;
|
||||||
|
//
|
||||||
|
private Long parentId;
|
||||||
/**
|
/**
|
||||||
* 删除标志(0=正常,1=已删除)
|
* 删除标志(0=正常,1=已删除)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -53,5 +53,9 @@ public class WmsStockIoBo extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父级ID(用于退库时关联原出库单)
|
||||||
|
*/
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,4 +79,6 @@ public interface IWmsStockIoService {
|
|||||||
boolean scanInStockByBo(WmsStockIoDetailBo bo);
|
boolean scanInStockByBo(WmsStockIoDetailBo bo);
|
||||||
|
|
||||||
boolean scanOutStockByBo(WmsStockIoDetailBo bo);
|
boolean scanOutStockByBo(WmsStockIoDetailBo bo);
|
||||||
|
|
||||||
|
boolean scanReturnStockByBo(WmsStockIoBo bo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ public class WmsStockIoServiceImpl implements IWmsStockIoService {
|
|||||||
private final WmsStockMapper stockMapper;
|
private final WmsStockMapper stockMapper;
|
||||||
private final WmsProductMapper productMapper;
|
private final WmsProductMapper productMapper;
|
||||||
private final WmsRawMaterialMapper rawMaterialMapper;
|
private final WmsRawMaterialMapper rawMaterialMapper;
|
||||||
|
private final WmsWarehouseMapper warehouseMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private WmsStockLogMapper stockLogMapper;
|
private WmsStockLogMapper stockLogMapper;
|
||||||
|
|
||||||
@@ -132,6 +133,7 @@ public class WmsStockIoServiceImpl implements IWmsStockIoService {
|
|||||||
public void addWithDetail(WmsStockIoWithDetailBo bo) {
|
public void addWithDetail(WmsStockIoWithDetailBo bo) {
|
||||||
// 插入主表
|
// 插入主表
|
||||||
WmsStockIo stockIo = BeanUtil.toBean(bo, WmsStockIo.class);
|
WmsStockIo stockIo = BeanUtil.toBean(bo, WmsStockIo.class);
|
||||||
|
stockIo.setParentId(bo.getStockIoId());
|
||||||
baseMapper.insert(stockIo);
|
baseMapper.insert(stockIo);
|
||||||
// 插入明细
|
// 插入明细
|
||||||
if (bo.getDetails() != null && !bo.getDetails().isEmpty()) {
|
if (bo.getDetails() != null && !bo.getDetails().isEmpty()) {
|
||||||
@@ -186,6 +188,191 @@ public class WmsStockIoServiceImpl implements IWmsStockIoService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public boolean scanReturnStockByBo(WmsStockIoBo bo) {
|
||||||
|
// 退库操作:根据id查询明细表,根据parent_id获取原出库单明细,验证退库数量
|
||||||
|
if (bo.getStockIoId() == null) {
|
||||||
|
throw new ServiceException("退库单ID不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 根据id查询退库单明细
|
||||||
|
WmsStockIoDetail returnDetail = stockIoDetailMapper.selectById(bo.getStockIoId());
|
||||||
|
if (returnDetail == null) {
|
||||||
|
throw new ServiceException("退库明细不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证退库数量必须大于0
|
||||||
|
if (returnDetail.getQuantity() == null || returnDetail.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
throw new ServiceException("退库数量必须大于0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证物品类型和物品ID的有效性
|
||||||
|
if (returnDetail.getItemType() == null || returnDetail.getItemId() == null) {
|
||||||
|
throw new ServiceException("物品类型和物品ID不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!"product".equals(returnDetail.getItemType()) && !"raw_material".equals(returnDetail.getItemType())) {
|
||||||
|
throw new ServiceException("物品类型必须是product或raw_material");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证库位ID的有效性
|
||||||
|
if (returnDetail.getWarehouseId() == null) {
|
||||||
|
throw new ServiceException("库位ID不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证物品是否存在
|
||||||
|
if ("product".equals(returnDetail.getItemType())) {
|
||||||
|
WmsProduct product = productMapper.selectById(returnDetail.getItemId());
|
||||||
|
if (product == null) {
|
||||||
|
throw new ServiceException("产品不存在");
|
||||||
|
}
|
||||||
|
} else if ("raw_material".equals(returnDetail.getItemType())) {
|
||||||
|
WmsRawMaterial rawMaterial = rawMaterialMapper.selectById(returnDetail.getItemId());
|
||||||
|
if (rawMaterial == null) {
|
||||||
|
throw new ServiceException("原材料不存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证库位是否存在
|
||||||
|
WmsWarehouse warehouse = warehouseMapper.selectById(returnDetail.getWarehouseId());
|
||||||
|
if (warehouse == null) {
|
||||||
|
throw new ServiceException("库位不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证库位是否启用
|
||||||
|
if (warehouse.getIsEnabled() == null || warehouse.getIsEnabled() != 1) {
|
||||||
|
throw new ServiceException("库位未启用,无法进行退库操作");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取退库单主表信息
|
||||||
|
WmsStockIo returnStockIo = baseMapper.selectById(returnDetail.getStockIoId());
|
||||||
|
if (returnStockIo == null) {
|
||||||
|
throw new ServiceException("退库单不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证退库单类型必须是入库类型
|
||||||
|
if (!"in".equals(returnStockIo.getIoType())) {
|
||||||
|
throw new ServiceException("退库单类型必须是入库类型");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证退库单状态必须是已提交或已审核状态
|
||||||
|
if (returnStockIo.getStatus() == null || (returnStockIo.getStatus() != 1 && returnStockIo.getStatus() != 2)) {
|
||||||
|
throw new ServiceException("退库单必须是已提交或已审核状态才能执行退库操作");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 根据parent_id获取原出库单的所有明细
|
||||||
|
if (returnStockIo.getParentId() == null) {
|
||||||
|
throw new ServiceException("退库单未关联原出库单");
|
||||||
|
}
|
||||||
|
|
||||||
|
WmsStockIo originalOutStockIo = baseMapper.selectById(returnStockIo.getParentId());
|
||||||
|
if (originalOutStockIo == null) {
|
||||||
|
throw new ServiceException("原出库单不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!"out".equals(originalOutStockIo.getIoType())) {
|
||||||
|
throw new ServiceException("原单据不是出库单");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证原出库单状态必须是已审核状态
|
||||||
|
if (originalOutStockIo.getStatus() == null || originalOutStockIo.getStatus() != 2) {
|
||||||
|
throw new ServiceException("原出库单必须是已审核状态才能进行退库操作");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 获取原出库单的所有明细
|
||||||
|
List<WmsStockIoDetail> originalOutDetails = stockIoDetailMapper.selectList(
|
||||||
|
Wrappers.<WmsStockIoDetail>lambdaQuery()
|
||||||
|
.eq(WmsStockIoDetail::getStockIoId, returnStockIo.getParentId())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (originalOutDetails == null || originalOutDetails.isEmpty()) {
|
||||||
|
throw new ServiceException("原出库单明细不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 查找对应的原出库明细(根据物品类型、物品ID、库位ID匹配)
|
||||||
|
WmsStockIoDetail originalOutDetail = null;
|
||||||
|
for (WmsStockIoDetail detail : originalOutDetails) {
|
||||||
|
if (detail.getItemType().equals(returnDetail.getItemType()) &&
|
||||||
|
detail.getItemId().equals(returnDetail.getItemId()) &&
|
||||||
|
detail.getWarehouseId().equals(returnDetail.getWarehouseId())) {
|
||||||
|
originalOutDetail = detail;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (originalOutDetail == null) {
|
||||||
|
throw new ServiceException("未找到对应的原出库明细");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 查询该物品已退库的总数量
|
||||||
|
BigDecimal totalReturnedQty = BigDecimal.ZERO;
|
||||||
|
List<WmsStockIo> returnStockIos = baseMapper.selectList(
|
||||||
|
Wrappers.<WmsStockIo>lambdaQuery()
|
||||||
|
.eq(WmsStockIo::getParentId, returnStockIo.getParentId())
|
||||||
|
.eq(WmsStockIo::getIoType, "in") // 退库单类型为入库
|
||||||
|
);
|
||||||
|
|
||||||
|
for (WmsStockIo returnStockIoItem : returnStockIos) {
|
||||||
|
// 排除当前正在处理的退库单
|
||||||
|
if (returnStockIoItem.getStockIoId().equals(returnStockIo.getStockIoId())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<WmsStockIoDetail> returnDetails = stockIoDetailMapper.selectList(
|
||||||
|
Wrappers.<WmsStockIoDetail>lambdaQuery()
|
||||||
|
.eq(WmsStockIoDetail::getStockIoId, returnStockIoItem.getStockIoId())
|
||||||
|
.eq(WmsStockIoDetail::getItemType, returnDetail.getItemType())
|
||||||
|
.eq(WmsStockIoDetail::getItemId, returnDetail.getItemId())
|
||||||
|
.eq(WmsStockIoDetail::getWarehouseId, returnDetail.getWarehouseId())
|
||||||
|
);
|
||||||
|
|
||||||
|
for (WmsStockIoDetail detail : returnDetails) {
|
||||||
|
totalReturnedQty = totalReturnedQty.add(detail.getQuantity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 验证退库数量不能超过原出库数量
|
||||||
|
BigDecimal remainingQty = originalOutDetail.getQuantity().subtract(totalReturnedQty);
|
||||||
|
if (returnDetail.getQuantity().compareTo(remainingQty) > 0) {
|
||||||
|
throw new ServiceException("退库数量超过原出库数量,原出库数量:" + originalOutDetail.getQuantity() +
|
||||||
|
",已退库数量:" + totalReturnedQty + ",剩余可退数量:" + remainingQty +
|
||||||
|
",本次退库数量:" + returnDetail.getQuantity());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 执行退库操作(增加库存)
|
||||||
|
String unit = returnDetail.getUnit();
|
||||||
|
// 如果unit为空,自动查item表补全
|
||||||
|
if (unit == null || unit.trim().isEmpty()) {
|
||||||
|
if ("product".equals(returnDetail.getItemType())) {
|
||||||
|
WmsProduct p = productMapper.selectById(returnDetail.getItemId());
|
||||||
|
unit = p != null ? p.getUnit() : null;
|
||||||
|
} else if ("raw_material".equals(returnDetail.getItemType())) {
|
||||||
|
WmsRawMaterial r = rawMaterialMapper.selectById(returnDetail.getItemId());
|
||||||
|
unit = r != null ? r.getUnit() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unit == null || unit.trim().isEmpty()) {
|
||||||
|
throw new ServiceException("未能获取到单位");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行入库操作(退库就是入库)
|
||||||
|
changeStock(returnDetail.getWarehouseId(), returnDetail.getItemType(),
|
||||||
|
returnDetail.getItemId(), returnDetail.getQuantity(), true, unit);
|
||||||
|
|
||||||
|
// 记录退库操作日志
|
||||||
|
WmsStockLog returnLog = new WmsStockLog();
|
||||||
|
returnLog.setWarehouseId(returnDetail.getWarehouseId());
|
||||||
|
returnLog.setItemType(returnDetail.getItemType());
|
||||||
|
returnLog.setItemId(returnDetail.getItemId());
|
||||||
|
returnLog.setChangeQty(returnDetail.getQuantity());
|
||||||
|
returnLog.setChangeType("退库");
|
||||||
|
returnLog.setChangeTime(new Date());
|
||||||
|
stockLogMapper.insert(returnLog);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审核出入库/移库单,变更库存,含库存校验
|
* 审核出入库/移库单,变更库存,含库存校验
|
||||||
|
|||||||
Reference in New Issue
Block a user