完成简单入出库功能

This commit is contained in:
AlanScipio
2024-03-06 16:26:21 +08:00
parent 24f1f4c3e7
commit 1c38d91567
26 changed files with 948 additions and 182 deletions

View File

@@ -134,16 +134,17 @@ public class VelocityUtils {
if ("mybatis-dynamic".equals(tplBackendType)) {
//MyBatis Dynamic SQL
templates.add("vm/java/controller-dynamic.java.vm");
templates.add("vm/java/service-dynamic.java.vm");
templates.add("vm/java/serviceImpl-dynamic.java.vm");
} else {
//常规
templates.add("vm/java/domain.java.vm");
templates.add("vm/java/controller.java.vm");
templates.add("vm/java/service.java.vm");
templates.add("vm/java/serviceImpl.java.vm");
templates.add("vm/java/mapper.java.vm");
templates.add("vm/xml/mapper.xml.vm");
}
templates.add("vm/java/service.java.vm");
templates.add("vm/sql/sql.vm");
//前端
templates.add("vm/js/api.js.vm");
@@ -190,6 +191,8 @@ public class VelocityUtils {
fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);
} else if (template.contains("service.java.vm")) {
fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className);
} else if (template.contains("service-dynamic.java.vm")) {
fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className);
} else if (template.contains("serviceImpl.java.vm")) {
fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className);
} else if (template.contains("serviceImpl-dynamic.java.vm")) {

View File

@@ -0,0 +1,70 @@
package ${packageName}.service;
import java.util.List;
import ${packageName}.domain .${ClassName};
/**
* ${functionName}Service接口
*
* @author ${author}
* created on ${datetime}
*/
public interface I${ClassName}Service {
/**
* 查询${functionName}
*
* @param ${pkColumn.javaField} ${functionName}主键
* @return ${functionName}
*/
${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField});
/**
* 查询${functionName}列表
*
* @param ${className} ${functionName}
* @return ${functionName}集合
*/
List<${ClassName}> select${ClassName}List(${ClassName} ${className});
/**
* 新增${functionName}
*
* @param ${className} ${functionName}
* @return 结果
*/
int insert${ClassName}(${ClassName} ${className});
/**
* 修改${functionName}
*
* @param ${className} ${functionName}
* @return 结果
*/
int update${ClassName}(${ClassName} ${className});
/**
* 批量删除${functionName}
*
* @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合
* @return 结果
*/
int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s);
/**
* 删除${functionName}信息
*
* @param ${pkColumn.javaField} ${functionName}主键
* @return 结果
*/
int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField});
/**
* 检查${functionName}是否已存在
*
* @param ${pkColumn.javaField} 主键
* @param deleteIfLogicDeleted 如果是已经逻辑删除的数据则是否顺带物理删除掉如果为true则逻辑删除的也会返回null
* @return null:不存在; not null:存在
*/
${ClassName} check${pkColumn.capJavaField}Exists(${pkColumn.javaType} ${pkColumn.javaField}, boolean deleteIfLogicDeleted);
}

View File

@@ -220,4 +220,29 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
}
}
#end
/**
* 检查${functionName}是否已存在
*
* @param ${pkColumn.javaField} 主键
* @param deleteIfLogicDeleted 如果是已经逻辑删除的数据则是否顺带物理删除掉如果为true则逻辑删除的也会返回null
* @return null:不存在; not null:存在
*/
@Transactional
@Override
public ${ClassName} check${pkColumn.capJavaField}Exists(${pkColumn.javaType} ${pkColumn.javaField}, boolean deleteIfLogicDeleted) {
Optional<${ClassName}> existsRecordOpt = ${className}Mapper.selectOne(dsl -> dsl.where(${ClassName}DynamicSqlSupport.${pkColumn.javaField}, SqlBuilder.isEqualTo(${pkColumn.javaField})));
if (existsRecordOpt.isEmpty()) {
return null;
} else {
${ClassName} existsRecord = existsRecordOpt.get();
if (deleteIfLogicDeleted && existsRecord.isLogicDeleted()) {
//顺带物理删除掉
${className}Mapper.deleteByPrimaryKey(existsRecord.get${pkColumn.capJavaField}());
return null;
}
return existsRecord;
}
}
}

View File

@@ -1,102 +1,101 @@
package com.ruoyi.job.service;
import java.util.List;
import org.quartz.SchedulerException;
import com.ruoyi.common.core.exception.job.TaskException;
import com.ruoyi.job.domain.SysJob;
import org.quartz.SchedulerException;
import java.util.List;
/**
* 定时任务调度信息信息 服务层
*
*
* @author ruoyi
*/
public interface ISysJobService
{
public interface ISysJobService {
/**
* 获取quartz调度器的计划任务
*
*
* @param job 调度信息
* @return 调度任务集合
*/
public List<SysJob> selectJobList(SysJob job);
List<SysJob> selectJobList(SysJob job);
/**
* 通过调度任务ID查询调度信息
*
*
* @param jobId 调度任务ID
* @return 调度任务对象信息
*/
public SysJob selectJobById(Long jobId);
SysJob selectJobById(Long jobId);
/**
* 暂停任务
*
*
* @param job 调度信息
* @return 结果
*/
public int pauseJob(SysJob job) throws SchedulerException;
int pauseJob(SysJob job) throws SchedulerException;
/**
* 恢复任务
*
*
* @param job 调度信息
* @return 结果
*/
public int resumeJob(SysJob job) throws SchedulerException;
int resumeJob(SysJob job) throws SchedulerException;
/**
* 删除任务后所对应的trigger也将被删除
*
*
* @param job 调度信息
* @return 结果
*/
public int deleteJob(SysJob job) throws SchedulerException;
int deleteJob(SysJob job) throws SchedulerException;
/**
* 批量删除调度信息
*
*
* @param jobIds 需要删除的任务ID
* @return 结果
*/
public void deleteJobByIds(Long[] jobIds) throws SchedulerException;
void deleteJobByIds(Long[] jobIds) throws SchedulerException;
/**
* 任务调度状态修改
*
*
* @param job 调度信息
* @return 结果
*/
public int changeStatus(SysJob job) throws SchedulerException;
int changeStatus(SysJob job) throws SchedulerException;
/**
* 立即运行任务
*
*
* @param job 调度信息
* @return 结果
*/
public boolean run(SysJob job) throws SchedulerException;
boolean run(SysJob job) throws SchedulerException;
/**
* 新增任务
*
*
* @param job 调度信息
* @return 结果
*/
public int insertJob(SysJob job) throws SchedulerException, TaskException;
int insertJob(SysJob job) throws SchedulerException, TaskException;
/**
* 更新任务
*
*
* @param job 调度信息
* @return 结果
*/
public int updateJob(SysJob job) throws SchedulerException, TaskException;
int updateJob(SysJob job) throws SchedulerException, TaskException;
/**
* 校验cron表达式是否有效
*
*
* @param cronExpression 表达式
* @return 结果
*/
public boolean checkCronExpressionIsValid(String cronExpression);
boolean checkCronExpressionIsValid(String cronExpression);
}

View File

@@ -8,6 +8,7 @@ import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.security.annotation.RequiresPermissions;
import com.ruoyi.wms.domain.BaseStock;
import com.ruoyi.wms.domain.vo.StockVo;
import com.ruoyi.wms.service.stock.IBaseStockService;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
@@ -64,4 +65,22 @@ public class BaseStockController extends BaseController {
return success(baseStockService.selectBaseStockByPK(form.getWhsCd(), form.getStgBinCd(), form.getItemCd(), form.getLotNo(), form.getSubLotNo()));
}
/**
* 入库
*/
@RequiresPermissions("wms:BaseStock:instock")
@PostMapping(value = "/instock")
public AjaxResult instock(@RequestBody StockVo stockVo) throws Exception {
return baseStockService.instock(stockVo);
}
/**
* 出库
*/
@RequiresPermissions("wms:BaseStock:outstock")
@PostMapping(value = "/outstock")
public AjaxResult outstock(@RequestBody StockVo stockVo) throws Exception {
return baseStockService.outstock(stockVo);
}
}

View File

@@ -59,113 +59,113 @@ public class InvTransHis extends ExtBaseEntity implements Serializable {
//==================== ↓↓↓↓↓↓ 表字段 ↓↓↓↓↓↓ ====================
/**
* 入出库履历号
* 入出库履历号
*/
@Excel(name = "入出库履历号", sort = 1)
private String invTransNo;
/**
* 从属部门ID
* 从属部门ID
*/
private Integer deptId;
/**
* 入出库类型(1:入库,2:出库)
* 入出库类型(1:入库,2:出库)
*/
private Integer invTransType;
/**
* 仓库代码
* 仓库代码
*/
private String whsCd;
/**
* 货架号
* 货架号
*/
private String stgBinCd;
/**
* 托盘ID
* 托盘ID
*/
private String palletId;
/**
* 标准单位数量
* 标准单位数量
*/
private BigDecimal stdUnitQty;
/**
* 包装单位数量
* 包装单位数量
*/
private BigDecimal pkgUnitQty;
/**
* 交易单号
* 交易单号
*/
private String transOrderNo;
/**
* 交易单明细号
* 交易单明细号
*/
private String transOrderDetlNo;
/**
* 操作员
* 操作员
*/
private String operator;
/**
* 业务区分
* 业务区分
*/
private String businessCls;
/**
* 物品代码
* 物品代码
*/
private String itemCd;
/**
* 批号
* 批号
*/
private String lotNo;
/**
* 子批号
* 子批号
*/
private String subLotNo;
/**
* 序列号
* 序列号
*/
private String serialNo;
/**
* 入出库理由
* 入出库理由
*/
private String reason;
/**
* 备注1
* 备注1
*/
private String remark1;
/**
* 备注2
* 备注2
*/
private String remark2;
/**
* 备注3
* 备注3
*/
private String remark3;
/**
* 备注4
* 备注4
*/
private String remark4;
/**
* 备注5
* 备注5
*/
private String remark5;

View File

@@ -0,0 +1,147 @@
package com.ruoyi.wms.domain.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author Alan Scipio
* created on 2024/3/6
*/
@Data
public class StockVo {
/**
* 入出库类型: 1.入库
*/
public static final int INSTOCK = 1;
/**
* 入出库类型: 2.出库
*/
public static final int OUTSTOCK = 2;
/**
* 入出库类型
*/
private Integer stockType;
/**
* 仓库代码
*/
private String whsCd;
/**
* 货架号
*/
private String stgBinCd;
/**
* 批号
*/
private String lotNo;
/**
* 子批号
*/
private String subLotNo;
/**
* 物品代码
*/
private String itemCd;
/**
* 从属部门ID
*/
private Integer deptId;
/**
* 标准单位数量
*/
private BigDecimal stdUnitQty;
/**
* 包装单位数量
*/
private BigDecimal pkgUnitQty;
/**
* 托盘ID
*/
private String palletId;
/**
* 操作员
*/
private String operator;
/**
* 业务区分
*/
private String businessCls;
/**
* 序列号
*/
private String serialNo;
/**
* 入出库理由
*/
private String reason;
/**
* 交易单号
*/
private String transOrderNo;
/**
* 交易单明细号
*/
private String transOrderDetlNo;
@JsonIgnore
public boolean isOutstock() {
return stockType != null && stockType == OUTSTOCK;
}
@JsonIgnore
public boolean isInstock() {
return stockType != null && stockType == INSTOCK;
}
@JsonIgnore
public boolean equalsKey(StockVo stockVo) {
if (stockVo == null) {
return false;
}
if (stockType == null || !stockType.equals(stockVo.stockType)) {
return false;
}
if (whsCd == null || !whsCd.equals(stockVo.whsCd)) {
return false;
}
if (stgBinCd == null || !stgBinCd.equals(stockVo.stgBinCd)) {
return false;
}
if (itemCd == null || !itemCd.equals(stockVo.itemCd)) {
return false;
}
if (lotNo != null && !lotNo.equals(stockVo.lotNo)) {
return false;
}
if (subLotNo != null && !subLotNo.equals(stockVo.subLotNo)) {
return false;
}
if (stdUnitQty != null && stdUnitQty.compareTo(stockVo.stdUnitQty) != 0) {
return false;
}
if (pkgUnitQty != null && pkgUnitQty.compareTo(stockVo.pkgUnitQty) != 0) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,23 @@
package com.ruoyi.wms.exception;
import com.ruoyi.common.core.exception.base.BaseException;
/**
* 库存异常
*
* @author Alan Scipio
* created on 2024/3/6
*/
public class StockException extends BaseException {
public StockException(String code, Object[] args, String defaultMessage) {
super("wms.stock", code, args, defaultMessage);
}
public StockException(String code, Object[] args) {
super("wms.stock", code, args);
}
public StockException(String defaultMessage) {
super("wms.stock", defaultMessage);
}
}

View File

@@ -59,4 +59,13 @@ public interface IItemInfoService {
* @return 结果
*/
int deleteItemInfoByItemCd(String itemCd);
/**
* 检查物品代码是否已存在
*
* @param itemCd 物品代码
* @param deleteIfLogicDeleted 如果是已经逻辑删除的数据则是否顺带物理删除掉如果为true则逻辑删除的也会返回null
* @return null:不存在; not null:存在
*/
ItemInfo checkItemCdExists(String itemCd, boolean deleteIfLogicDeleted);
}

View File

@@ -58,4 +58,17 @@ public interface IUnitInfoService {
* @return 结果
*/
int deleteUnitInfoByUnitCode(String unitCode);
/**
* 如果不存在就新增
*
* @param unitName 单位名称
* @param remark 备注
*/
void addIfNotExist(String unitName, String remark);
default void addIfNotExist(String unitName) {
addIfNotExist(unitName, null);
}
}

View File

@@ -1,8 +1,8 @@
package com.ruoyi.wms.service.master;
import java.util.List;
import com.ruoyi.wms.domain.WarehouseInfo;
import com.ruoyi.wms.domain .WarehouseInfo;
import java.util.List;
/**
* 仓库基础信息Service接口
@@ -58,4 +58,13 @@ public interface IWarehouseInfoService {
* @return 结果
*/
int deleteWarehouseInfoByWhsCd(String whsCd);
/**
* 检查仓库代码是否存在
*
* @param whsCd 仓库代码
* @param deleteIfLogicDeleted 如果是已经逻辑删除的数据则是否顺带物理删除掉如果为true则逻辑删除的也会返回null
* @return null:不存在; not null:存在
*/
WarehouseInfo checkWhsCdExists(String whsCd, boolean deleteIfLogicDeleted);
}

View File

@@ -46,8 +46,10 @@ public class ItemInfoServiceImpl implements IItemInfoService {
*/
@Override
public ItemInfo selectItemInfoByItemCd(String itemCd) {
Optional<ItemInfo> result = itemInfoMapper.selectOne(dsl -> dsl.where(ItemInfoDynamicSqlSupport.itemCd, SqlBuilder.isEqualTo(itemCd)));
return result.orElse(null);
ItemInfo queryForm = new ItemInfo();
queryForm.setItemCd(itemCd);
List<ItemInfo> result = itemInfoExtMapper.selectPageList(queryForm);
return result.isEmpty() ? null : result.getFirst();
}
/**
@@ -70,6 +72,12 @@ public class ItemInfoServiceImpl implements IItemInfoService {
@Transactional
@Override
public AjaxResult insertItemInfo(ItemInfo item) {
//检查物品代码是否已存在
ItemInfo oldRecord = checkItemCdExists(item.getItemCd(), true);
if (oldRecord != null) {
//存在未删除的记录
return AjaxResult.error("物品代码[" + item.getItemCd() + "]已存在");
}
//上传图片文件
String uploadErrMsg = uploadItemImage(item);
if (StringUtils.isNotBlank(uploadErrMsg)) {
@@ -135,6 +143,30 @@ public class ItemInfoServiceImpl implements IItemInfoService {
return itemInfoMapper.updateByPrimaryKey(record);
}
/**
* 检查物品代码是否已存在
*
* @param itemCd 物品代码
* @param deleteIfLogicDeleted 如果是已经逻辑删除的数据则是否顺带物理删除掉如果为true则逻辑删除的也会返回null
* @return null:不存在; not null:存在
*/
@Transactional
@Override
public ItemInfo checkItemCdExists(String itemCd, boolean deleteIfLogicDeleted) {
Optional<ItemInfo> itemInfo = itemInfoMapper.selectOne(dsl -> dsl.where(ItemInfoDynamicSqlSupport.itemCd, SqlBuilder.isEqualTo(itemCd)));
if (itemInfo.isEmpty()) {
return null;
} else {
ItemInfo result = itemInfo.get();
if (deleteIfLogicDeleted && result.isLogicDeleted()) {
//顺带物理删除掉
itemInfoMapper.deleteByPrimaryKey(result.getItemCd());
return null;
}
return result;
}
}
/**
* 上传图片文件
*

View File

@@ -15,6 +15,7 @@ import org.mybatis.dynamic.sql.render.RenderingStrategies;
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
@@ -70,8 +71,20 @@ public class UnitInfoServiceImpl implements IUnitInfoService {
* @param unitInfo 单位信息管理
* @return 结果
*/
@Transactional
@Override
public int insertUnitInfo(UnitInfo unitInfo) {
//检查是否已存在单位名称
UnitInfo existsRecord = selectByUnitName(unitInfo.getUnitName());
if (existsRecord != null) {
if (existsRecord.isLogicDeleted()) {
//物理删除掉旧数据
unitInfoMapper.deleteByPrimaryKey(existsRecord.getUnitCode());
} else {
throw new IllegalArgumentException("单位名称已存在");
}
}
//单位代码为空时生成
if (StringUtils.isBlank(unitInfo.getUnitCode())) {
String unitCode = sequenceService.getNextSequence(SeqType.UNIT_CD);
unitInfo.setUnitCode(unitCode);
@@ -85,6 +98,7 @@ public class UnitInfoServiceImpl implements IUnitInfoService {
* @param unitInfo 单位信息管理
* @return 结果
*/
@Transactional
@Override
public int updateUnitInfo(UnitInfo unitInfo) {
return unitInfoMapper.updateByPrimaryKeySelective(unitInfo);
@@ -96,6 +110,7 @@ public class UnitInfoServiceImpl implements IUnitInfoService {
* @param unitCodes 需要删除的单位信息管理主键
* @return 结果
*/
@Transactional
@Override
public int deleteUnitInfoByUnitCodes(String[] unitCodes) {
String userId = SecurityUtilsExt.getUserIdStr();
@@ -115,6 +130,7 @@ public class UnitInfoServiceImpl implements IUnitInfoService {
* @param unitCode 单位信息管理主键
* @return 结果
*/
@Transactional
@Override
public int deleteUnitInfoByUnitCode(String unitCode) {
UnitInfo record = new UnitInfo();
@@ -122,4 +138,38 @@ public class UnitInfoServiceImpl implements IUnitInfoService {
record.setDeleteFlag(ExtBaseEntity.DELETED);
return unitInfoMapper.updateByPrimaryKey(record);
}
/**
* 如果不存在就新增
*
* @param unitName 单位名称
* @param remark 备注
*/
@Transactional
@Override
public void addIfNotExist(String unitName, String remark) {
UnitInfo existsRecord = selectByUnitName(unitName);
if (existsRecord != null) {
if (!existsRecord.isLogicDeleted()) {
String unitCode = sequenceService.getNextSequence(SeqType.UNIT_CD);
existsRecord.setUnitCode(unitCode);
existsRecord.setDeleteFlag(ExtBaseEntity.NOT_DELETE);
existsRecord.setRemark1(remark == null ? "" : remark);
unitInfoMapper.updateByPrimaryKeySelective(existsRecord);
}
} else {
UnitInfo newRecord = new UnitInfo();
String unitCode = sequenceService.getNextSequence(SeqType.UNIT_CD);
newRecord.setUnitCode(unitCode);
newRecord.setUnitName(unitName);
newRecord.setRemark1(remark);
unitInfoMapper.insertSelective(newRecord);
}
}
private UnitInfo selectByUnitName(String unitName) {
Optional<UnitInfo> result = unitInfoMapper.selectOne(dsl -> dsl.where(UnitInfoDynamicSqlSupport.unitName, SqlBuilder.isEqualTo(unitName)));
return result.orElse(null);
}
}

View File

@@ -15,6 +15,7 @@ import org.mybatis.dynamic.sql.render.RenderingStrategies;
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
@@ -70,8 +71,16 @@ public class WarehouseInfoServiceImpl implements IWarehouseInfoService {
* @param warehouseInfo 仓库基础信息
* @return 结果
*/
@Transactional
@Override
public int insertWarehouseInfo(WarehouseInfo warehouseInfo) {
// 检查是否存在未删除的记录
// WarehouseInfo existsRecord = checkWhsCdExists(warehouseInfo.getWhsCd(), true);
// if (existsRecord != null) {
// //存在未删除的记录
// throw new IllegalArgumentException("仓库代码[" + warehouseInfo.getWhsCd() + "]已存在");
// }
// 仓库代码为空时,自动生成
if (StringUtils.isBlank(warehouseInfo.getWhsCd())) {
String whsCd = sequenceService.getNextSequence(SeqType.WHS_CD);
warehouseInfo.setWhsCd(whsCd);
@@ -85,6 +94,7 @@ public class WarehouseInfoServiceImpl implements IWarehouseInfoService {
* @param warehouseInfo 仓库基础信息
* @return 结果
*/
@Transactional
@Override
public int updateWarehouseInfo(WarehouseInfo warehouseInfo) {
return warehouseInfoMapper.updateByPrimaryKeySelective(warehouseInfo);
@@ -96,6 +106,7 @@ public class WarehouseInfoServiceImpl implements IWarehouseInfoService {
* @param whsCds 需要删除的仓库基础信息主键
* @return 结果
*/
@Transactional
@Override
public int deleteWarehouseInfoByWhsCds(String[] whsCds) {
String userId = SecurityUtilsExt.getUserIdStr();
@@ -115,6 +126,7 @@ public class WarehouseInfoServiceImpl implements IWarehouseInfoService {
* @param whsCd 仓库基础信息主键
* @return 结果
*/
@Transactional
@Override
public int deleteWarehouseInfoByWhsCd(String whsCd) {
WarehouseInfo record = new WarehouseInfo();
@@ -123,4 +135,26 @@ public class WarehouseInfoServiceImpl implements IWarehouseInfoService {
record.setUpdateTime(DateUtils.getNowDate());
return warehouseInfoMapper.updateByPrimaryKey(record);
}
/**
* 检查仓库代码是否存在
*
* @param whsCd 仓库代码
* @param deleteIfLogicDeleted 如果是已经逻辑删除的数据则是否顺带物理删除掉如果为true则逻辑删除的也会返回null
* @return null:不存在; not null:存在
*/
@Transactional
@Override
public WarehouseInfo checkWhsCdExists(String whsCd, boolean deleteIfLogicDeleted) {
Optional<WarehouseInfo> result = warehouseInfoMapper.selectOne(dsl -> dsl.where(WarehouseInfoDynamicSqlSupport.whsCd, SqlBuilder.isEqualTo(whsCd)));
if (result.isEmpty()) {
return null;
}
WarehouseInfo warehouseInfo = result.get();
if (deleteIfLogicDeleted && warehouseInfo.isLogicDeleted()) {
warehouseInfoMapper.deleteByPrimaryKey(whsCd);
return null;
}
return warehouseInfo;
}
}

View File

@@ -1,11 +1,18 @@
package com.ruoyi.wms.service.stock;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.domain.ExtBaseEntity;
import com.ruoyi.wms.domain.BaseStock;
import com.ruoyi.wms.domain.ItemInfo;
import com.ruoyi.wms.domain.WarehouseInfo;
import com.ruoyi.wms.domain.vo.StockVo;
import com.ruoyi.wms.exception.StockException;
import com.ruoyi.wms.mapper.stock.BaseStockDynamicSqlSupport;
import com.ruoyi.wms.mapper.stock.BaseStockExtMapper;
import com.ruoyi.wms.mapper.stock.BaseStockMapper;
import com.ruoyi.wms.service.master.IItemInfoService;
import com.ruoyi.wms.service.master.IWarehouseInfoService;
import jakarta.annotation.Resource;
import org.mybatis.dynamic.sql.SqlBuilder;
import org.mybatis.dynamic.sql.render.RenderingStrategies;
@@ -15,7 +22,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -34,8 +40,12 @@ public class BaseStockServiceImpl implements IBaseStockService {
private BaseStockExtMapper baseStockExtMapper;
@Resource
private IInvTransHisService invTransHisService;
@Resource
private IWarehouseInfoService warehouseInfoService;
@Resource
private IItemInfoService itemInfoService;
private final Queue<BaseStock> dataQueue = new ConcurrentLinkedQueue<>();
private final Queue<StockVo> dataQueue = new ConcurrentLinkedQueue<>();
/**
* 查询基本库存
@@ -45,14 +55,14 @@ public class BaseStockServiceImpl implements IBaseStockService {
*/
@Override
public BaseStock selectBaseStockByPK(String whsCd, String stgBinCd, String itemCd, String lotNo, String subLotNo) {
Optional<BaseStock> result = baseStockMapper.selectOne(dsl ->
dsl.where(BaseStockDynamicSqlSupport.whsCd, SqlBuilder.isEqualTo(whsCd))
.and(BaseStockDynamicSqlSupport.stgBinCd, SqlBuilder.isEqualTo(stgBinCd))
.and(BaseStockDynamicSqlSupport.itemCd, SqlBuilder.isEqualTo(itemCd))
.and(BaseStockDynamicSqlSupport.lotNo, SqlBuilder.isEqualTo(lotNo))
.and(BaseStockDynamicSqlSupport.subLotNo, SqlBuilder.isEqualTo(subLotNo))
);
return result.orElse(null);
BaseStock queryForm = new BaseStock();
queryForm.setWhsCd(whsCd);
queryForm.setStgBinCd(stgBinCd);
queryForm.setItemCd(itemCd);
queryForm.setLotNo(lotNo);
queryForm.setSubLotNo(subLotNo);
List<BaseStock> result = baseStockExtMapper.selectPageList(queryForm);
return result.isEmpty() ? null : result.getFirst();
}
/**
@@ -69,78 +79,142 @@ public class BaseStockServiceImpl implements IBaseStockService {
/**
* 入库
*
* @param baseStock 库存数据
* @param stockVo 库存数据
* @return 结果
*/
@Transactional
@Override
public AjaxResult instock(BaseStock baseStock) {
//TODO 参数检查
//队列维持数据一致性
dataQueue.offer(baseStock);
BaseStock data = dataQueue.remove();
doInoutStock(data);
public AjaxResult instock(StockVo stockVo) {
// 参数检查
AjaxResult checkResult = checkInoutStock(stockVo);
if (!checkResult.isSuccess()) {
return checkResult;
}
// 队列维持数据一致性
stockVo.setStockType(StockVo.INSTOCK);
dataQueue.offer(stockVo);
StockVo data = null;
while (data == null || !data.equalsKey(stockVo)) {
data = dataQueue.remove();
doInoutStock(data);
}
return AjaxResult.success();
}
/**
* 出库
*
* @param baseStock 库存数据
* @param stockVo 库存数据
* @return 结果
*/
@Transactional
@Override
public AjaxResult outstock(BaseStock baseStock) {
//TODO 参数检查
//队列维持数据一致性
dataQueue.offer(baseStock);
BaseStock data = dataQueue.remove();
doInoutStock(data);
public AjaxResult outstock(StockVo stockVo) {
// 参数检查
AjaxResult checkResult = checkInoutStock(stockVo);
if (!checkResult.isSuccess()) {
return checkResult;
}
// 队列维持数据一致性
stockVo.setStockType(StockVo.OUTSTOCK);
dataQueue.offer(stockVo);
StockVo data = null;
while (data == null || !data.equalsKey(stockVo)) {
data = dataQueue.remove();
doInoutStock(data);
}
return AjaxResult.success();
}
/**
* 入出库操作
*/
private void doInoutStock(BaseStock form) {
private void doInoutStock(StockVo stockVo) {
//查询库存
List<BaseStock> stockList = queryWhenInoutStock(form);
List<BaseStock> stockList = queryWhenInoutStock(stockVo);
//更新库存
if (stockList.isEmpty()) {
if (stockVo.isOutstock()) {
throw new StockException("库存不足");
}
//insert
baseStockMapper.insert(form);
//TODO 写入出库履历
BaseStock newRecord = buildNewRecord(stockVo);
baseStockMapper.insertSelective(newRecord);
//写入出库履历
invTransHisService.addInvTransHis(stockVo);
} else {
//update
BaseStock oldRecord = stockList.getFirst();
BigDecimal newStdUnitQty = oldRecord.getStdUnitQty().add(form.getStdUnitQty());
BigDecimal newPkgUnitQty = oldRecord.getPkgUnitQty().add(form.getPkgUnitQty());
oldRecord.setStdUnitQty(newStdUnitQty);
oldRecord.setPkgUnitQty(newPkgUnitQty);
baseStockMapper.updateByPrimaryKey(oldRecord);
//TODO 写入出库履历
if (stockVo.isOutstock()) {
//出库时的检查
if (oldRecord.getStdUnitQty().compareTo(stockVo.getStdUnitQty()) < 0) {
throw new StockException("库存不足");
}
}
BaseStock updateRecord = buildUpdateRecord(stockVo, oldRecord);
baseStockMapper.updateByPrimaryKeySelective(updateRecord);
//写入出库履历
invTransHisService.addInvTransHis(stockVo);
}
}
/**
* 参数检查
*/
private AjaxResult checkInoutStock(StockVo stockVo) {
//非空检查
if (stockVo == null) {
return AjaxResult.error("stockVo is null");
}
if (stockVo.getStockType() == null || stockVo.getStockType() < 1 || stockVo.getStockType() > 2) {
return AjaxResult.error("入出库类型为空或不合法");
}
if (StringUtils.isBlank(stockVo.getWhsCd())) {
return AjaxResult.error("仓库代码为空");
}
if (StringUtils.isBlank(stockVo.getStgBinCd())) {
return AjaxResult.error("货架号为空");
}
if (StringUtils.isBlank(stockVo.getItemCd())) {
return AjaxResult.error("物品代码为空");
}
if (stockVo.getStdUnitQty() == null) {
return AjaxResult.error("标准单位数量为空");
}
if (stockVo.getStdUnitQty().compareTo(BigDecimal.ZERO) < 0) {
return AjaxResult.error("标准单位数量不能小于0");
}
if (stockVo.getPkgUnitQty() != null && stockVo.getPkgUnitQty().compareTo(BigDecimal.ZERO) < 0) {
return AjaxResult.error("包装单位数量不能小于0");
}
//检查仓库代码是否存在
WarehouseInfo warehouseInfo = warehouseInfoService.checkWhsCdExists(stockVo.getWhsCd(), false);
if (warehouseInfo == null || warehouseInfo.isLogicDeleted()) {
return AjaxResult.error("仓库不存在");
}
//检查物品代码是否存在
ItemInfo itemInfo = itemInfoService.checkItemCdExists(stockVo.getItemCd(), false);
if (itemInfo == null || itemInfo.isLogicDeleted()) {
return AjaxResult.error("物品不存在");
}
return AjaxResult.success();
}
/**
* 入出库时查询库存
*
* @param form 查询条件
* @param stockVo 查询条件
* @return 库存数据
*/
private List<BaseStock> queryWhenInoutStock(BaseStock form) {
private List<BaseStock> queryWhenInoutStock(StockVo stockVo) {
SelectStatementProvider query = SqlBuilder.select(BaseStockMapper.selectList)
.from(BaseStockDynamicSqlSupport.baseStock)
.where(BaseStockDynamicSqlSupport.deleteFlag, SqlBuilder.isEqualTo(ExtBaseEntity.NOT_DELETE))
.and(BaseStockDynamicSqlSupport.whsCd, SqlBuilder.isEqualTo(form.getWhsCd()))
.and(BaseStockDynamicSqlSupport.stgBinCd, SqlBuilder.isEqualTo(form.getStgBinCd()))
.and(BaseStockDynamicSqlSupport.itemCd, SqlBuilder.isEqualTo(form.getItemCd()))
.and(BaseStockDynamicSqlSupport.lotNo, SqlBuilder.isEqualTo(form.getLotNo()))
.and(BaseStockDynamicSqlSupport.subLotNo, SqlBuilder.isEqualTo(form.getSubLotNo()))
.and(BaseStockDynamicSqlSupport.whsCd, SqlBuilder.isEqualTo(stockVo.getWhsCd()))
.and(BaseStockDynamicSqlSupport.stgBinCd, SqlBuilder.isEqualTo(stockVo.getStgBinCd()))
.and(BaseStockDynamicSqlSupport.itemCd, SqlBuilder.isEqualTo(stockVo.getItemCd()))
.and(BaseStockDynamicSqlSupport.lotNo, SqlBuilder.isEqualTo(stockVo.getLotNo()))
.and(BaseStockDynamicSqlSupport.subLotNo, SqlBuilder.isEqualTo(stockVo.getSubLotNo()))
.orderBy(BaseStockDynamicSqlSupport.createTime.descending())
.limit(1)
.build()
@@ -148,4 +222,38 @@ public class BaseStockServiceImpl implements IBaseStockService {
return baseStockMapper.selectMany(query);
}
private BaseStock buildNewRecord(StockVo stockVo) {
BaseStock record = new BaseStock();
record.setWhsCd(stockVo.getWhsCd());
record.setStgBinCd(stockVo.getStgBinCd());
record.setItemCd(stockVo.getItemCd());
record.setLotNo(stockVo.getLotNo());
record.setSubLotNo(stockVo.getSubLotNo());
record.setStdUnitQty(stockVo.getStdUnitQty());
record.setPkgUnitQty(stockVo.getPkgUnitQty());
return record;
}
private BaseStock buildUpdateRecord(StockVo stockVo, BaseStock oldRecord) {
// 标准单位数量
if (stockVo.isOutstock()) {
//出库数量为负数
stockVo.setStdUnitQty(stockVo.getStdUnitQty().negate());
}
BigDecimal newStdUnitQty = oldRecord.getStdUnitQty().add(stockVo.getStdUnitQty());
oldRecord.setStdUnitQty(newStdUnitQty);
// 包装单位数量
if (stockVo.getPkgUnitQty() != null) {
if (stockVo.isOutstock()) {
//出库数量为负数
stockVo.setPkgUnitQty(stockVo.getPkgUnitQty().negate());
}
BigDecimal newPkgUnitQty = oldRecord.getPkgUnitQty().add(stockVo.getPkgUnitQty());
oldRecord.setPkgUnitQty(newPkgUnitQty);
}
return oldRecord;
}
}

View File

@@ -2,6 +2,7 @@ package com.ruoyi.wms.service.stock;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.wms.domain.BaseStock;
import com.ruoyi.wms.domain.vo.StockVo;
import java.util.List;
@@ -32,17 +33,17 @@ public interface IBaseStockService {
/**
* 入库
*
* @param baseStock 库存数据
* @param form 库存数据
* @return 结果
*/
AjaxResult instock(BaseStock baseStock) throws Exception;
AjaxResult instock(StockVo form) throws Exception;
/**
* 出库
*
* @param baseStock 库存数据
* @param form 库存数据
* @return 结果
*/
AjaxResult outstock(BaseStock baseStock) throws Exception;
AjaxResult outstock(StockVo form) throws Exception;
}

View File

@@ -1,6 +1,7 @@
package com.ruoyi.wms.service.stock;
import com.ruoyi.wms.domain.InvTransHis;
import com.ruoyi.wms.domain.vo.StockVo;
import java.util.List;
@@ -42,4 +43,11 @@ public interface IInvTransHisService {
* @return 结果
*/
int deleteInvTransHisByInvTransNo(String invTransNo);
/**
* 新增入出库履历
*
* @param stockVo 库存数据
*/
void addInvTransHis(StockVo stockVo);
}

View File

@@ -1,9 +1,12 @@
package com.ruoyi.wms.service.stock;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.uuid.snowflake.SnowFlakeIdGenerator;
import com.ruoyi.common.core.web.domain.ExtBaseEntity;
import com.ruoyi.common.security.utils.SecurityUtilsExt;
import com.ruoyi.wms.domain.InvTransHis;
import com.ruoyi.wms.domain.vo.StockVo;
import com.ruoyi.wms.mapper.stock.InvTransHisDynamicSqlSupport;
import com.ruoyi.wms.mapper.stock.InvTransHisExtMapper;
import com.ruoyi.wms.mapper.stock.InvTransHisMapper;
@@ -89,4 +92,51 @@ public class InvTransHisServiceImpl implements IInvTransHisService {
record.setUpdateTime(DateUtils.getNowDate());
return invTransHisMapper.updateByPrimaryKey(record);
}
/**
* 新增入出库履历
*
* @param stockVo 库存数据
*/
@Transactional
@Override
public void addInvTransHis(StockVo stockVo) {
if (stockVo == null) {
throw new IllegalArgumentException("stockVo is null");
}
if (stockVo.getStockType() == null || stockVo.getStockType() < 1 || stockVo.getStockType() > 2) {
throw new IllegalArgumentException("stockType is null or invalid");
}
if (StringUtils.isBlank(stockVo.getWhsCd())) {
throw new IllegalArgumentException("whsCd is blank");
}
if (StringUtils.isBlank(stockVo.getStgBinCd())) {
throw new IllegalArgumentException("stgBinCd is blank");
}
if (StringUtils.isBlank(stockVo.getItemCd())) {
throw new IllegalArgumentException("itemCd is blank");
}
if (stockVo.getStdUnitQty() == null) {
throw new IllegalArgumentException("stdUnitQty is null");
}
InvTransHis record = new InvTransHis();
record.setInvTransType(stockVo.getStockType()); // 入出库类型(1:入库,2:出库)
record.setWhsCd(stockVo.getWhsCd()); // 仓库代码
record.setStgBinCd(stockVo.getStgBinCd()); // 货架号
record.setLotNo(stockVo.getLotNo()); // 批号
record.setSubLotNo(stockVo.getSubLotNo()); // 子批号
record.setItemCd(stockVo.getItemCd()); // 物品代码
record.setStdUnitQty(stockVo.getStdUnitQty()); // 标准单位数量
record.setPkgUnitQty(stockVo.getPkgUnitQty()); // 包装单位数量
record.setPalletId(stockVo.getPalletId()); // 托盘ID
record.setSerialNo(stockVo.getSerialNo()); // 序列号
record.setBusinessCls(stockVo.getBusinessCls()); // 业务分类
record.setReason(stockVo.getReason()); // 入出库理由
record.setTransOrderNo(stockVo.getTransOrderNo()); // 交易单号
record.setTransOrderDetlNo(stockVo.getTransOrderDetlNo()); // 交易单明细号
record.setDeptId(stockVo.getDeptId()); // 从属部门ID
record.setOperator(SecurityUtilsExt.getUsernameFromRedis()); // 操作人
record.setInvTransNo(SnowFlakeIdGenerator.nextId()); // 入出库履历号
invTransHisMapper.insertSelective(record);
}
}

View File

@@ -74,6 +74,9 @@
<if test="lotNo != null and lotNo != ''">
and t.LOT_NO like concat('%', #{lotNo}, '%')
</if>
<if test="subLotNo != null and subLotNo != ''">
and t.SUB_LOT_NO like concat('%', #{subLotNo}, '%')
</if>
</where>
</select>