Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
zccbbg 2024-08-04 18:08:39 +08:00
commit c59b16fe4d
28 changed files with 494 additions and 31 deletions

View file

@ -78,7 +78,6 @@ public class ShipmentOrderController extends BaseController {
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody ShipmentOrderBo bo) {
bo.setShipmentOrderStatus(ServiceConstants.ShipmentOrderStatus.PENDING);
shipmentOrderService.insertByBo(bo);
return R.ok();
}
@ -91,11 +90,23 @@ public class ShipmentOrderController extends BaseController {
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ShipmentOrderBo bo) {
bo.setShipmentOrderStatus(ServiceConstants.ShipmentOrderStatus.PENDING);
shipmentOrderService.updateByBo(bo);
return R.ok();
}
/**
* 出库
*/
@SaCheckPermission("wms:shipmentOrder:shipment")
@Log(title = "出库单", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/shipment")
public R<Void> shipment(@Validated(AddGroup.class) @RequestBody ShipmentOrderBo bo) {
bo.setShipmentOrderStatus(ServiceConstants.ShipmentOrderStatus.FINISH);
shipmentOrderService.shipment(bo);
return R.ok();
}
/**
* 删除出库单
*

View file

@ -105,4 +105,14 @@ public class ShipmentOrderDetailController extends BaseController {
shipmentOrderDetailService.deleteByIds(List.of(ids));
return R.ok();
}
/**
* 获取出库单详情详细信息
*
*/
@SaCheckPermission("wms:shipmentOrderDetail:query")
@GetMapping("/list/{shipmentOrderId}")
public R<List<ShipmentOrderDetailVo>> listByShipmentOrderId(@NotNull @PathVariable Long shipmentOrderId) {
return R.ok(shipmentOrderDetailService.queryByShipmentOrderId(shipmentOrderId));
}
}

View file

@ -3,6 +3,7 @@ package com.ruoyi.wms.domain.bo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
import com.ruoyi.common.mybatis.core.domain.PlaceAndItem;
import com.ruoyi.wms.domain.entity.Inventory;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
@ -22,7 +23,7 @@ import java.math.BigDecimal;
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = Inventory.class, reverseConvertGenerate = false)
public class InventoryBo extends BaseEntity {
public class InventoryBo extends BaseEntity implements PlaceAndItem {
/**
*
@ -59,5 +60,15 @@ public class InventoryBo extends BaseEntity {
*/
private String remark;
/**
* 最小数量
*/
private BigDecimal minQuantity;
private String itemName;
private String itemCode;
private String skuName;
private String skuCode;
private Long itemCategory;
}

View file

@ -113,5 +113,7 @@ public class InventoryDetailBo extends BaseEntity {
@NotNull(message = "剩余数量不能为空", groups = { AddGroup.class, EditGroup.class })
private BigDecimal remainQuantity;
/** 出库数量 */
private BigDecimal shipmentQuantity;
}

View file

@ -11,6 +11,7 @@ import lombok.EqualsAndHashCode;
import com.ruoyi.wms.domain.entity.InventoryHistory;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 库存记录业务对象 wms_inventory_history
@ -48,6 +49,26 @@ public class InventoryHistoryBo extends BaseEntity {
@NotNull(message = "物料ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long skuId;
/**
* 批号
*/
private String batchNumber;
/**
* 生产日期
*/
private LocalDateTime productionDate;
/**
* 过期时间
*/
private LocalDateTime expirationTime;
/**
* 金额
*/
private BigDecimal amount;
/**
* 库存变化
*/

View file

@ -3,7 +3,9 @@ package com.ruoyi.wms.domain.bo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
import com.ruoyi.wms.domain.entity.InventoryDetail;
import io.github.linpeilie.annotations.AutoMapper;
import io.github.linpeilie.annotations.AutoMappers;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@ -22,7 +24,10 @@ import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = ReceiptOrderDetail.class, reverseConvertGenerate = false)
@AutoMappers({
@AutoMapper(target = ReceiptOrderDetail.class, reverseConvertGenerate = false),
@AutoMapper(target = InventoryDetail.class, reverseConvertGenerate = false)
})
public class ReceiptOrderDetailBo extends BaseEntity {
/**

View file

@ -0,0 +1,15 @@
package com.ruoyi.wms.domain.bo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ShipmentDataBo {
private List<InventoryDetailBo> shipmentInventoryDetailList;
private List<InventoryBo> shipmentInventoryList;
}

View file

@ -67,6 +67,16 @@ public class ShipmentOrderBo extends BaseEntity {
*/
private Integer shipmentOrderStatus;
/**
* 仓库id
*/
private Long warehouseId;
/**
* 库区id
*/
private Long areaId;
/**
* 备注
*/

View file

@ -4,6 +4,7 @@ import com.ruoyi.wms.domain.entity.ShipmentOrderDetail;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
import io.github.linpeilie.annotations.AutoMappers;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
@ -20,7 +21,10 @@ import java.math.BigDecimal;
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = ShipmentOrderDetail.class, reverseConvertGenerate = false)
@AutoMappers({
@AutoMapper(target = ShipmentOrderDetail.class, reverseConvertGenerate = false),
@AutoMapper(target = InventoryBo.class, reverseConvertGenerate = false)
})
public class ShipmentOrderDetailBo extends BaseEntity {
/**
@ -68,7 +72,6 @@ public class ShipmentOrderDetailBo extends BaseEntity {
/**
* 备注
*/
@NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
private String remark;

View file

@ -3,6 +3,7 @@ package com.ruoyi.wms.domain.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
import com.ruoyi.common.mybatis.core.domain.PlaceAndItem;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -18,7 +19,7 @@ import java.math.BigDecimal;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("wms_inventory")
public class Inventory extends BaseEntity {
public class Inventory extends BaseEntity implements PlaceAndItem {
@Serial
private static final long serialVersionUID=1L;

View file

@ -3,6 +3,9 @@ package com.ruoyi.wms.domain.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
import com.ruoyi.common.mybatis.core.domain.PlaceAndItem;
import com.ruoyi.wms.domain.bo.InventoryDetailBo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -19,7 +22,8 @@ import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("wms_inventory_detail")
public class InventoryDetail extends BaseEntity {
@AutoMapper(target = InventoryDetailBo.class, reverseConvertGenerate = false)
public class InventoryDetail extends BaseEntity implements PlaceAndItem {
@Serial
private static final long serialVersionUID=1L;

View file

@ -8,6 +8,7 @@ import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 库存记录对象 wms_inventory_history
@ -40,6 +41,22 @@ public class InventoryHistory extends BaseEntity {
* 物料ID
*/
private Long skuId;
/**
* 批号
*/
private String batchNumber;
/**
* 生产日期
*/
private LocalDateTime productionDate;
/**
* 过期时间
*/
private LocalDateTime expirationTime;
/**
* 金额
*/
private BigDecimal amount;
/**
* 库存变化
*/

View file

@ -56,6 +56,14 @@ public class ShipmentOrder extends BaseEntity {
* 出库单状态
*/
private Integer shipmentOrderStatus;
/**
* 仓库id
*/
private Long warehouseId;
/**
* 库区id
*/
private Long areaId;
/**
* 备注
*/

View file

@ -11,6 +11,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 库存记录视图对象 wms_inventory_history
@ -51,6 +52,30 @@ public class InventoryHistoryVo implements Serializable {
@ExcelProperty(value = "物料ID")
private Long skuId;
/**
* 批号
*/
@ExcelProperty(value = "批号")
private String batchNumber;
/**
* 生产日期
*/
@ExcelProperty(value = "生产日期")
private LocalDateTime productionDate;
/**
* 过期时间
*/
@ExcelProperty(value = "过期时间")
private LocalDateTime expirationTime;
/**
* 金额
*/
@ExcelProperty(value = "金额")
private BigDecimal amount;
/**
* 库存变化
*/

View file

@ -60,5 +60,8 @@ public class InventoryVo implements Serializable {
@ExcelProperty(value = "备注")
private String remark;
private ItemSkuVo itemSku;
private ItemVo item;
}

View file

@ -1,6 +1,8 @@
package com.ruoyi.wms.domain.vo;
import java.math.BigDecimal;
import com.ruoyi.common.mybatis.core.domain.PlaceAndItem;
import com.ruoyi.wms.domain.entity.ShipmentOrderDetail;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
@ -21,7 +23,7 @@ import java.io.Serial;
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = ShipmentOrderDetail.class)
public class ShipmentOrderDetailVo implements Serializable {
public class ShipmentOrderDetailVo implements Serializable, PlaceAndItem {
@Serial
private static final long serialVersionUID = 1L;
@ -74,5 +76,7 @@ public class ShipmentOrderDetailVo implements Serializable {
@ExcelProperty(value = "备注")
private String remark;
private ItemSkuVo itemSku;
private BigDecimal maxQuantity;
}

View file

@ -1,6 +1,8 @@
package com.ruoyi.wms.domain.vo;
import java.math.BigDecimal;
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
import com.ruoyi.wms.domain.entity.ShipmentOrder;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
@ -11,6 +13,7 @@ import io.github.linpeilie.annotations.AutoMapper;
import java.io.Serializable;
import java.io.Serial;
import java.util.List;
/**
* 出库单视图对象 wms_shipment_order
@ -21,7 +24,7 @@ import java.io.Serial;
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = ShipmentOrder.class)
public class ShipmentOrderVo implements Serializable {
public class ShipmentOrderVo extends BaseEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ -74,11 +77,23 @@ public class ShipmentOrderVo implements Serializable {
@ExcelProperty(value = "出库单状态")
private Integer shipmentOrderStatus;
/**
* 仓库id
*/
@ExcelProperty(value = "仓库id")
private Long warehouseId;
/**
* 库区id
*/
@ExcelProperty(value = "库区id")
private Long areaId;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
List<ShipmentOrderDetailVo> details;
}

View file

@ -1,8 +1,13 @@
package com.ruoyi.wms.mapper;
import com.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
import com.ruoyi.wms.domain.bo.InventoryDetailBo;
import com.ruoyi.wms.domain.entity.InventoryDetail;
import com.ruoyi.wms.domain.vo.InventoryDetailVo;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
/**
* 库存详情Mapper接口
@ -11,5 +16,5 @@ import com.ruoyi.wms.domain.vo.InventoryDetailVo;
* @date 2024-07-22
*/
public interface InventoryDetailMapper extends BaseMapperPlus<InventoryDetail, InventoryDetailVo> {
void updateRemainQuantity(@Param("list")List<InventoryDetailBo> list, @Param("updateBy") String updateBy, @Param("updateTime") LocalDateTime updateTime);
}

View file

@ -1,6 +1,8 @@
package com.ruoyi.wms.mapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
import com.ruoyi.wms.domain.bo.InventoryBo;
import com.ruoyi.wms.domain.entity.Inventory;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.wms.domain.vo.InventoryVo;
@ -16,4 +18,5 @@ import java.util.List;
*/
public interface InventoryMapper extends BaseMapperPlus<Inventory, InventoryVo> {
Page<InventoryVo> selectVoPageByBo(Page<Object> page, @Param("bo") InventoryBo bo);
}

View file

@ -1,13 +1,20 @@
package com.ruoyi.wms.service;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.exception.base.BaseException;
import com.ruoyi.common.core.utils.MapstructUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
import com.ruoyi.common.mybatis.core.domain.PlaceAndItem;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.wms.domain.bo.InventoryBo;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.ruoyi.wms.domain.bo.InventoryDetailBo;
@ -15,9 +22,9 @@ import com.ruoyi.wms.domain.entity.InventoryDetail;
import com.ruoyi.wms.domain.vo.InventoryDetailVo;
import com.ruoyi.wms.mapper.InventoryDetailMapper;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
* 库存详情Service业务层处理
@ -94,4 +101,68 @@ public class InventoryDetailService extends ServiceImpl<InventoryDetailMapper, I
public void deleteByIds(Collection<Long> ids) {
inventoryDetailMapper.deleteBatchIds(ids);
}
/**
* 计算出库数据
* @param warehouseId
* @param inventoryBoList
*/
public List<InventoryDetailBo> calcShipmentInventoryDetailData(@NotNull Long warehouseId, @NotEmpty List<InventoryBo> inventoryBoList) {
LambdaQueryWrapper<InventoryDetail> inventoryDetailLambdaQueryWrapper = Wrappers.lambdaQuery();
inventoryDetailLambdaQueryWrapper.eq(InventoryDetail::getWarehouseId, warehouseId);
inventoryDetailLambdaQueryWrapper.in(InventoryDetail::getAreaId, inventoryBoList.stream().map(InventoryBo::getAreaId).toList());
inventoryDetailLambdaQueryWrapper.gt(InventoryDetail::getRemainQuantity, 0);
inventoryDetailLambdaQueryWrapper.orderByAsc(InventoryDetail::getSkuId, InventoryDetail::getWarehouseId, InventoryDetail::getAreaId, BaseEntity::getCreateTime);
List<InventoryDetail> inventoryDetailList = inventoryDetailMapper.selectList(inventoryDetailLambdaQueryWrapper);
if (CollUtil.isEmpty(inventoryDetailList)) {
throw new BaseException("库存不足");
}
Set<String> inventoryDetailSizeGroupByKey = inventoryDetailList.stream().map(PlaceAndItem::getKey).collect(Collectors.toSet());
if (inventoryDetailSizeGroupByKey.size() < inventoryBoList.size()) {
throw new BaseException("库存不足");
}
// 计算得到的出库数据集合
List<InventoryDetailBo> shipmentList = new LinkedList<>();
// 本次需出库数map
Map<String, BigDecimal> needMap = inventoryBoList.stream().collect(Collectors.toMap(PlaceAndItem::getKey, InventoryBo::getQuantity));
String lastKey = "";
for (InventoryDetail detail : inventoryDetailList) {
String currentKey = detail.getKey();
if (!needMap.containsKey(currentKey)) {
continue;
}
BigDecimal needQuantity = needMap.get(currentKey);
if (lastKey.equals(currentKey) && needQuantity.compareTo(BigDecimal.ZERO) > 0) {
// 该位置下商品还有入库明细看下需要出库的数量是否为0大于0继续出小于等于0直接跳过
allocateShipmentData(needMap, needQuantity, currentKey, MapstructUtils.convert(detail, InventoryDetailBo.class), shipmentList);
} else if (!lastKey.equals(currentKey)) {
// 如果不一样看下上一个位置下的商品剩余数是否为0不为0则库存不足了
if (!"".equals(lastKey) && needMap.get(lastKey).compareTo(BigDecimal.ZERO) > 0) {
throw new BaseException("库存不足");
}
// 上一个足够继续下一个
allocateShipmentData(needMap, needQuantity, currentKey, MapstructUtils.convert(detail, InventoryDetailBo.class), shipmentList);
}
lastKey = currentKey;
}
return shipmentList;
}
/**
* 分配出库的inventoryDetail
* @param needMap 所需出库数据map
* @param needQuantity 所需出库数
* @param currentKey 当前key
* @param detail 当前入库记录
* @param shipmentInventoryDetailDataBoList 最终分配数据集
*/
private void allocateShipmentData(Map<String, BigDecimal> needMap, BigDecimal needQuantity, String currentKey, InventoryDetailBo detail, List<InventoryDetailBo> shipmentInventoryDetailDataBoList) {
// 计算这条入库明细可出多少数量
BigDecimal shipmentQuantity = needQuantity.compareTo(detail.getRemainQuantity()) > 0 ? detail.getRemainQuantity() : needQuantity;
// 更新所需出库数据map
needMap.put(currentKey, needQuantity.subtract(shipmentQuantity));
// 放入最终出库数据集
detail.setShipmentQuantity(shipmentQuantity);
shipmentInventoryDetailDataBoList.add(detail);
}
}

View file

@ -1,26 +1,34 @@
package com.ruoyi.wms.service;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.exception.base.BaseException;
import com.ruoyi.common.core.utils.MapstructUtils;
import com.ruoyi.common.core.utils.ValidatorUtils;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.mybatis.core.domain.PlaceAndItem;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.wms.domain.bo.InventoryBo;
import com.ruoyi.wms.domain.bo.*;
import com.ruoyi.wms.domain.entity.Inventory;
import com.ruoyi.wms.domain.vo.InventoryVo;
import com.ruoyi.wms.mapper.InventoryMapper;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 库存Service业务层处理
@ -33,6 +41,7 @@ import java.util.List;
public class InventoryService extends ServiceImpl<InventoryMapper, Inventory> {
private final InventoryMapper inventoryMapper;
private final InventoryDetailService inventoryDetailService;
/**
* 查询库存
@ -45,8 +54,7 @@ public class InventoryService extends ServiceImpl<InventoryMapper, Inventory> {
* 查询库存列表
*/
public TableDataInfo<InventoryVo> queryPageList(InventoryBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<Inventory> lqw = buildQueryWrapper(bo);
Page<InventoryVo> result = inventoryMapper.selectVoPage(pageQuery.build(), lqw);
Page<InventoryVo> result = inventoryMapper.selectVoPageByBo(pageQuery.build(), bo);
return TableDataInfo.build(result);
}
@ -145,4 +153,40 @@ public class InventoryService extends ServiceImpl<InventoryMapper, Inventory> {
lqw.in(Inventory::getAreaId, areaIds);
return inventoryMapper.exists(lqw);
}
/**
* 校验库存并计算出库数据
* @param warehouseId
* @param inventoryBoList
*/
public ShipmentDataBo validateInventoryAndCalcShipmentData(@NotNull Long warehouseId, @NotEmpty List<InventoryBo> inventoryBoList) {
List<InventoryBo> shipmentInventoryList = validateInventoryAndCalcShipmentInventoryData(warehouseId, inventoryBoList);
List<InventoryDetailBo> shipmentInventoryDetailList = inventoryDetailService.calcShipmentInventoryDetailData(warehouseId, inventoryBoList);
ShipmentDataBo shipmentDataBo = new ShipmentDataBo(shipmentInventoryDetailList, shipmentInventoryList);
return shipmentDataBo;
}
public List<InventoryBo> validateInventoryAndCalcShipmentInventoryData(@NotNull Long warehouseId, @NotEmpty List<InventoryBo> inventoryBoList) {
LambdaQueryWrapper<Inventory> inventoryLambdaQueryWrapper = Wrappers.lambdaQuery();
inventoryLambdaQueryWrapper.eq(Inventory::getWarehouseId, warehouseId);
inventoryLambdaQueryWrapper.in(Inventory::getAreaId, inventoryBoList.stream().map(InventoryBo::getAreaId).toList());
inventoryLambdaQueryWrapper.gt(Inventory::getQuantity, 0);
List<Inventory> inventoryList = inventoryMapper.selectList(inventoryLambdaQueryWrapper);
if (CollUtil.isEmpty(inventoryList)) {
throw new BaseException("库存不足");
}
Map<String, Inventory> inventoryMap = inventoryList.stream().collect(Collectors.toMap(PlaceAndItem::getKey, Function.identity()));
List<InventoryBo> shipmentData = new LinkedList<>();
for (InventoryBo bo : inventoryBoList) {
Inventory inventory = inventoryMap.get(bo.getKey());
if (inventory == null || inventory.getQuantity().compareTo(bo.getQuantity()) < 0) {
throw new BaseException("库存不足");
}
InventoryBo addData = new InventoryBo();
BeanUtils.copyProperties(bo, addData);
addData.setQuantity(bo.getQuantity().negate());
shipmentData.add(addData);
}
return shipmentData;
}
}

View file

@ -171,6 +171,7 @@ public class ReceiptOrderService {
inventoryDetail.setReceiptOrderId(bo.getId());
inventoryDetail.setOrderNo(bo.getOrderNo());
inventoryDetail.setType(ServiceConstants.InventoryDetailType.RECEIPT);
inventoryDetail.setRemainQuantity(inventoryDetail.getQuantity());
});
inventoryDetailService.saveBatch(inventoryDetailList);
}

View file

@ -3,12 +3,17 @@ package com.ruoyi.wms.service;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.utils.MapstructUtils;
import com.ruoyi.common.mybatis.core.domain.PlaceAndItem;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.utils.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.wms.domain.entity.Inventory;
import com.ruoyi.wms.domain.vo.ItemSkuVo;
import com.ruoyi.wms.domain.vo.ReceiptOrderDetailVo;
import com.ruoyi.wms.mapper.InventoryMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.ruoyi.wms.domain.bo.ShipmentOrderDetailBo;
@ -17,9 +22,10 @@ import com.ruoyi.wms.domain.entity.ShipmentOrderDetail;
import com.ruoyi.wms.mapper.ShipmentOrderDetailMapper;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 出库单详情Service业务层处理
@ -32,6 +38,8 @@ import java.util.Collection;
public class ShipmentOrderDetailService extends ServiceImpl<ShipmentOrderDetailMapper, ShipmentOrderDetail> {
private final ShipmentOrderDetailMapper shipmentOrderDetailMapper;
private final ItemSkuService itemSkuService;
private final InventoryMapper inventoryMapper;
/**
* 查询出库单详情
@ -99,4 +107,34 @@ public class ShipmentOrderDetailService extends ServiceImpl<ShipmentOrderDetailM
}
saveOrUpdateBatch(list);
}
public List<ShipmentOrderDetailVo> queryByShipmentOrderId(Long shipmentOrderId) {
ShipmentOrderDetailBo bo = new ShipmentOrderDetailBo();
bo.setShipmentOrderId(shipmentOrderId);
List<ShipmentOrderDetailVo> details = queryList(bo);
if (CollUtil.isEmpty(details)) {
return Collections.EMPTY_LIST;
}
// 查规格
Set<Long> skuIds = details
.stream()
.map(ShipmentOrderDetailVo::getSkuId)
.collect(Collectors.toSet());
Map<Long, ItemSkuVo> itemSkuMap = itemSkuService.queryVosByIds(skuIds)
.stream()
.collect(Collectors.toMap(ItemSkuVo::getId, Function.identity()));
// 查剩余库存
LambdaQueryWrapper<Inventory> inventoryLambdaQueryWrapper = Wrappers.lambdaQuery();
inventoryLambdaQueryWrapper.eq(Inventory::getWarehouseId, details.get(0).getWarehouseId());
inventoryLambdaQueryWrapper.in(Inventory::getAreaId, details.stream().map(ShipmentOrderDetailVo::getAreaId).toList());
inventoryLambdaQueryWrapper.in(Inventory::getSkuId, details.stream().map(ShipmentOrderDetailVo::getSkuId).toList());
Map<String, BigDecimal> maxQuantityMap = inventoryMapper.selectList(inventoryLambdaQueryWrapper)
.stream()
.collect(Collectors.toMap(PlaceAndItem::getKey, Inventory::getQuantity));
details.forEach(detail -> {
detail.setItemSku(itemSkuMap.get(detail.getSkuId()));
detail.setMaxQuantity(maxQuantityMap.get(detail.getKey()));
});
return details;
}
}

View file

@ -1,26 +1,31 @@
package com.ruoyi.wms.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.constant.ServiceConstants;
import com.ruoyi.common.core.exception.base.BaseException;
import com.ruoyi.common.core.utils.MapstructUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.wms.domain.bo.ShipmentOrderBo;
import com.ruoyi.wms.domain.bo.ShipmentOrderDetailBo;
import com.ruoyi.common.satoken.utils.LoginHelper;
import com.ruoyi.wms.domain.bo.*;
import com.ruoyi.wms.domain.entity.InventoryHistory;
import com.ruoyi.wms.domain.entity.ShipmentOrder;
import com.ruoyi.wms.domain.entity.ShipmentOrderDetail;
import com.ruoyi.wms.domain.vo.ShipmentOrderVo;
import com.ruoyi.wms.mapper.InventoryDetailMapper;
import com.ruoyi.wms.mapper.ShipmentOrderMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.time.LocalDateTime;
import java.util.*;
/**
* 出库单Service业务层处理
@ -34,12 +39,20 @@ public class ShipmentOrderService {
private final ShipmentOrderMapper shipmentOrderMapper;
private final ShipmentOrderDetailService shipmentOrderDetailService;
private final InventoryService inventoryService;
private final InventoryDetailMapper inventoryDetailMapper;
private final InventoryHistoryService inventoryHistoryService;
/**
* 查询出库单
*/
public ShipmentOrderVo queryById(Long id){
return shipmentOrderMapper.selectVoById(id);
ShipmentOrderVo shipmentOrderVo = shipmentOrderMapper.selectVoById(id);
if (shipmentOrderVo == null) {
throw new BaseException("出库单不存在");
}
shipmentOrderVo.setDetails(shipmentOrderDetailService.queryByShipmentOrderId(shipmentOrderVo.getId()));
return shipmentOrderVo;
}
/**
@ -69,6 +82,7 @@ public class ShipmentOrderService {
lqw.eq(bo.getReceivableAmount() != null, ShipmentOrder::getReceivableAmount, bo.getReceivableAmount());
lqw.eq(bo.getTotalQuantity() != null, ShipmentOrder::getTotalQuantity, bo.getTotalQuantity());
lqw.eq(bo.getShipmentOrderStatus() != null, ShipmentOrder::getShipmentOrderStatus, bo.getShipmentOrderStatus());
lqw.orderByDesc(BaseEntity::getCreateTime);
return lqw;
}
@ -117,4 +131,53 @@ public class ShipmentOrderService {
public void deleteByIds(Collection<Long> ids) {
shipmentOrderMapper.deleteBatchIds(ids);
}
/**
* 出库
* @param bo
*/
@Transactional
public void shipment(ShipmentOrderBo bo) {
// 1.校验
validateBeforeShipment(bo);
// 2. 保存入库单和入库单明细
if (Objects.isNull(bo.getId())) {
insertByBo(bo);
} else {
updateByBo(bo);
}
// 3.计算出库数据
ShipmentDataBo shipmentDataBo = inventoryService.validateInventoryAndCalcShipmentData(bo.getWarehouseId(), MapstructUtils.convert(bo.getDetails(), InventoryBo.class));
// 4.更新库存
inventoryService.updateInventoryQuantity(shipmentDataBo.getShipmentInventoryList());
// 5.更新入库记录剩余数
inventoryDetailMapper.updateRemainQuantity(shipmentDataBo.getShipmentInventoryDetailList(), LoginHelper.getUsername(), LocalDateTime.now());
// 6.创建出库记录
saveInventoryHistory(shipmentDataBo.getShipmentInventoryDetailList(), bo);
}
private void saveInventoryHistory(List<InventoryDetailBo> list, ShipmentOrderBo bo){
List<InventoryHistory> inventoryHistoryList = new LinkedList<>();
list.forEach(detail -> {
InventoryHistory inventoryHistory = new InventoryHistory();
inventoryHistory.setFormId(bo.getId());
inventoryHistory.setFormType(bo.getShipmentOrderType());
inventoryHistory.setSkuId(detail.getSkuId());
inventoryHistory.setQuantity(detail.getQuantity());
inventoryHistory.setWarehouseId(detail.getWarehouseId());
inventoryHistory.setAreaId(detail.getAreaId());
inventoryHistory.setBatchNumber(detail.getBatchNumber());
inventoryHistory.setProductionDate(detail.getProductionDate());
inventoryHistory.setExpirationTime(detail.getExpirationTime());
inventoryHistory.setAmount(detail.getAmount());
inventoryHistoryList.add(inventoryHistory);
});
inventoryHistoryService.saveBatch(inventoryHistoryList);
}
private void validateBeforeShipment(ShipmentOrderBo bo) {
if (CollUtil.isEmpty(bo.getDetails())) {
throw new BaseException("商品明细不能为空!");
}
}
}

View file

@ -3,5 +3,22 @@
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.wms.mapper.InventoryDetailMapper">
<update id="updateRemainQuantity">
update wms_inventory_detail
set remain_quantity = remain_quantity -
(
case
<foreach collection="list" item="it">
when id = #{it.id} then #{it.shipmentQuantity}
</foreach>
end
)
,
update_time = #{updateTime},
update_by = #{updateBy}
where id in
<foreach collection="list" open="(" separator="," close=")" item="it">
#{it.id}
</foreach>
</update>
</mapper>

View file

@ -4,4 +4,45 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.wms.mapper.InventoryMapper">
<resultMap id="inventoryVoMap" type="com.ruoyi.wms.domain.vo.InventoryVo">
<association property="itemSku" javaType="com.ruoyi.wms.domain.vo.ItemSkuVo" />
<association property="item" javaType="com.ruoyi.wms.domain.vo.ItemVo" />
</resultMap>
<select id="selectVoPageByBo" resultMap="inventoryVoMap">
select
inventory.*,
sku.*,
item.*
from wms_inventory inventory
inner join wms_item_sku sku on inventory.sku_id=sku.id
inner join wms_item item on sku.item_id=item.id
<where>
<if test="bo.itemName != null and bo.itemName != ''">
item.item_name like concat('%', #{bo.itemName}, '%')
</if>
<if test="bo.itemCode != null and bo.itemCode != ''">
and item.item_code like concat('%', #{bo.itemCode}, '%')
</if>
<if test="bo.skuName != null and bo.skuName != ''">
and sku.sku_name like concat('%', #{bo.skuName}, '%')
</if>
<if test="bo.skuCode != null and bo.skuCode != ''">
and sku.sku_code like concat('%', #{bo.skuCode}, '%')
</if>
<if test="bo.itemCategory != null">
and item.item_category=#{bo.itemCategory}
</if>
<if test="bo.warehouseId != null">
and inventory.warehouse_id=#{bo.warehouseId}
</if>
<if test="bo.areaId != null">
and inventory.area_id=#{bo.areaId}
</if>
<if test="bo.minQuantity != null">
and inventory.quantity>=#{bo.minQuantity}
</if>
</where>
order by inventory.warehouse_id,inventory.area_id,sku.item_id
</select>
</mapper>

View file

@ -0,0 +1,14 @@
package com.ruoyi.common.mybatis.core.domain;
public interface PlaceAndItem {
Long getWarehouseId();
Long getAreaId();
Long getSkuId();
default String getKey() {
return getWarehouseId() + "_" + getAreaId() + "_" + getSkuId();
}
}

View file

@ -3,6 +3,7 @@ package ${packageName}.domain.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
#foreach ($import in $importList)
import ${import};