Compare commits

..

2 Commits

Author SHA1 Message Date
86ecbf0e78 功能重构开发0130 2026-01-30 10:20:32 +08:00
e5926e82fd 功能重构开发 2026-01-26 17:01:42 +08:00
12 changed files with 347 additions and 402 deletions

View File

@@ -3,6 +3,7 @@ package com.zg.project.wisdom.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.github.pagehelper.PageHelper;
import com.zg.project.wisdom.domain.dto.BorrowReturnDTO;
import com.zg.project.wisdom.domain.dto.RkBillCreateDTO;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -42,8 +43,11 @@ public class RkBillController extends BaseController
@PreAuthorize("@ss.hasPermi('wisdom:bill:list')")
@PostMapping("/list")
public TableDataInfo list(@RequestBody RkBill rkBill) {
startPage();
PageHelper.startPage(rkBill.getPageNum(), rkBill.getPageSize());
List<RkBill> list = rkBillService.selectRkBillList(rkBill);
return getDataTable(list);
}

View File

@@ -109,7 +109,8 @@ public class RkInfoController extends BaseController
*/
@PostMapping("/statistic")
public AjaxResult statistic(@RequestBody RkInfo query) {
return AjaxResult.success(rkInfoService.getStockStatistic(query));
StockStatisticVO stockStatistic = rkInfoService.getStockStatistic(query);
return AjaxResult.success(stockStatistic);
}
}

View File

@@ -22,6 +22,12 @@ public class RkBill extends BaseEntity {
/** 主键ID */
private Long id;
/** 当前页 */
private Integer pageNum;
/** 每页条数 */
private Integer pageSize;
/** 物资类型 */
@Excel(name = "物资类型")
private String wlType;
@@ -46,7 +52,7 @@ public class RkBill extends BaseEntity {
@Excel(name = "单据号")
private String billNo;
/** 原字段:出入库类型 */
/** 出入库类型 */
@Excel(name = "出入库类型")
private String operationType;
@@ -103,29 +109,14 @@ public class RkBill extends BaseEntity {
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date returnTime;
/** 借用方项目号(借用方项目) */
/** 借用方项目号 */
@Excel(name = "借用方项目号")
private String xmNoCk;
/** 借用方项目描述(借用方项目描述) */
/** 借用方项目描述 */
@Excel(name = "借用方项目描述")
private String xmMsCk;
/** 创建人 */
private String createBy;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 更新人 */
private String updateBy;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/** 是否删除0正常1删除 */
private String isDelete;
@@ -143,256 +134,96 @@ public class RkBill extends BaseEntity {
// ================= getter / setter =================
public Long getId() {
return id;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public void setId(Long id) {
this.id = id;
}
public Integer getPageNum() { return pageNum; }
public void setPageNum(Integer pageNum) { this.pageNum = pageNum; }
public String getWlType() {
return wlType;
}
public Integer getPageSize() { return pageSize; }
public void setPageSize(Integer pageSize) { this.pageSize = pageSize; }
public void setWlType(String wlType) {
this.wlType = wlType;
}
public String getWlType() { return wlType; }
public void setWlType(String wlType) { this.wlType = wlType; }
public String getWlTypeName() {
return wlTypeName;
}
public String getWlTypeName() { return wlTypeName; }
public void setWlTypeName(String wlTypeName) { this.wlTypeName = wlTypeName; }
public void setWlTypeName(String wlTypeName) {
this.wlTypeName = wlTypeName;
}
public String getCangku() { return cangku; }
public void setCangku(String cangku) { this.cangku = cangku; }
public String getCangku() {
return cangku;
}
public String getWarehouseName() { return warehouseName; }
public void setWarehouseName(String warehouseName) { this.warehouseName = warehouseName; }
public void setCangku(String cangku) {
this.cangku = cangku;
}
public String getParentWarehouseName() { return parentWarehouseName; }
public void setParentWarehouseName(String parentWarehouseName) { this.parentWarehouseName = parentWarehouseName; }
public String getWarehouseName() {
return warehouseName;
}
public String getBillNo() { return billNo; }
public void setBillNo(String billNo) { this.billNo = billNo; }
public void setWarehouseName(String warehouseName) {
this.warehouseName = warehouseName;
}
public String getOperationType() { return operationType; }
public void setOperationType(String operationType) { this.operationType = operationType; }
public String getParentWarehouseName() {
return parentWarehouseName;
}
public String getOperationTypeName() { return operationTypeName; }
public void setOperationTypeName(String operationTypeName) { this.operationTypeName = operationTypeName; }
public void setParentWarehouseName(String parentWarehouseName) {
this.parentWarehouseName = parentWarehouseName;
}
public String getBizType() { return bizType; }
public void setBizType(String bizType) { this.bizType = bizType; }
public String getBillNo() {
return billNo;
}
public List<String> getBizTypeList() { return bizTypeList; }
public void setBizTypeList(List<String> bizTypeList) { this.bizTypeList = bizTypeList; }
public void setBillNo(String billNo) {
this.billNo = billNo;
}
public Date getOperationTime() { return operationTime; }
public void setOperationTime(Date operationTime) { this.operationTime = operationTime; }
public String getOperationType() {
return operationType;
}
public String getExecStatus() { return execStatus; }
public void setExecStatus(String execStatus) { this.execStatus = execStatus; }
public void setOperationType(String operationType) {
this.operationType = operationType;
}
public Integer getOperator() { return operator; }
public void setOperator(Integer operator) { this.operator = operator; }
public String getOperationTypeName() {
return operationTypeName;
}
public String getOperatorName() { return operatorName; }
public void setOperatorName(String operatorName) { this.operatorName = operatorName; }
public void setOperationTypeName(String operationTypeName) {
this.operationTypeName = operationTypeName;
}
public String getTeamCode() { return teamCode; }
public void setTeamCode(String teamCode) { this.teamCode = teamCode; }
public String getBizType() {
return bizType;
}
public String getTeamName() { return teamName; }
public void setTeamName(String teamName) { this.teamName = teamName; }
public void setBizType(String bizType) {
this.bizType = bizType;
}
public String getIsDelivery() { return isDelivery; }
public void setIsDelivery(String isDelivery) { this.isDelivery = isDelivery; }
public List<String> getBizTypeList() {
return bizTypeList;
}
public Date getBorrowTime() { return borrowTime; }
public void setBorrowTime(Date borrowTime) { this.borrowTime = borrowTime; }
public void setBizTypeList(List<String> bizTypeList) {
this.bizTypeList = bizTypeList;
}
public Date getReturnTime() { return returnTime; }
public void setReturnTime(Date returnTime) { this.returnTime = returnTime; }
public Date getOperationTime() {
return operationTime;
}
public String getXmNoCk() { return xmNoCk; }
public void setXmNoCk(String xmNoCk) { this.xmNoCk = xmNoCk; }
public void setOperationTime(Date operationTime) {
this.operationTime = operationTime;
}
public String getXmMsCk() { return xmMsCk; }
public void setXmMsCk(String xmMsCk) { this.xmMsCk = xmMsCk; }
public String getExecStatus() {
return execStatus;
}
public String getIsDelete() { return isDelete; }
public void setIsDelete(String isDelete) { this.isDelete = isDelete; }
public void setExecStatus(String execStatus) {
this.execStatus = execStatus;
}
public Date getStartDate() { return startDate; }
public void setStartDate(Date startDate) { this.startDate = startDate; }
public Integer getOperator() {
return operator;
}
public Date getEndDate() { return endDate; }
public void setEndDate(Date endDate) { this.endDate = endDate; }
public void setOperator(Integer operator) {
this.operator = operator;
}
public String getOperatorName() {
return operatorName;
}
public void setOperatorName(String operatorName) {
this.operatorName = operatorName;
}
public String getTeamCode() {
return teamCode;
}
public void setTeamCode(String teamCode) {
this.teamCode = teamCode;
}
public String getTeamName() {
return teamName;
}
public void setTeamName(String teamName) {
this.teamName = teamName;
}
public String getIsDelivery() {
return isDelivery;
}
public void setIsDelivery(String isDelivery) {
this.isDelivery = isDelivery;
}
public Date getBorrowTime() {
return borrowTime;
}
public void setBorrowTime(Date borrowTime) {
this.borrowTime = borrowTime;
}
public Date getReturnTime() {
return returnTime;
}
public void setReturnTime(Date returnTime) {
this.returnTime = returnTime;
}
public void setXmNoCk(String xmNoCk)
{
this.xmNoCk = xmNoCk;
}
public String getXmNoCk()
{
return xmNoCk;
}
public void setXmMsCk(String xmMsCk)
{
this.xmMsCk = xmMsCk;
}
public String getXmMsCk()
{
return xmMsCk;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getIsDelete() {
return isDelete;
}
public void setIsDelete(String isDelete) {
this.isDelete = isDelete;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
// ================= toString =================
public String getRemark() { return remark; }
public void setRemark(String remark) { this.remark = remark; }
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", id)
.append("pageNum", pageNum)
.append("pageSize", pageSize)
.append("wlType", wlType)
.append("wlTypeName", wlTypeName)
.append("cangku", cangku)
@@ -411,16 +242,11 @@ public class RkBill extends BaseEntity {
.append("isDelivery", isDelivery)
.append("borrowTime", borrowTime)
.append("returnTime", returnTime)
.append("xmNoCk", getXmNoCk())
.append("xmMsCk", getXmMsCk())
.append("createBy", createBy)
.append("createTime", createTime)
.append("updateBy", updateBy)
.append("updateTime", updateTime)
.append("isDelete", isDelete)
.append("remark", remark)
.append("xmNoCk", xmNoCk)
.append("xmMsCk", xmMsCk)
.append("startDate", startDate)
.append("endDate", endDate)
.append("remark", remark)
.toString();
}
}

View File

@@ -213,6 +213,7 @@ public class RkInfo extends BaseEntity
private Long sid;
/** 是否需要配送(0否,1是,2配送中,3配送完成) */
@Excel(name = "是否需要配送", readConverterExp = "0否,1是,2配送中,3配送完成")
private String isDelivery;
/** 封样编号1 */

View File

@@ -188,12 +188,12 @@ public class RkRecord extends BaseEntity
private String entityId;
/** 借用时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
// @Excel(name = "借用时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date borrowTime;
/** 归还时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
// @Excel(name = "归还时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date returnTime;

View File

@@ -169,4 +169,14 @@ public interface GysJhMapper
*/
int updateRealQtyById(@Param("gysJhId") Long gysJhId,
@Param("realQty") BigDecimal realQty);
/**
* 回退实际入库数量(累减)
*
* @param rollbackQty 回退数量
* @param gysJhId 供应计划ID
* @return 影响行数
*/
int decreaseRealQtyById(@Param("rollbackQty") BigDecimal rollbackQty,
@Param("gysJhId") Long gysJhId);
}

View File

@@ -146,4 +146,7 @@ public interface RkRecordMapper
* 查询某个单据下仍为预入库状态的记录数量
*/
int countPreInRecordByBillNo(@Param("billNo") String billNo);
int countPreOutRecordByBillNo(@Param("billNo") String billNo);
}

View File

@@ -106,7 +106,7 @@ public class RkBillServiceImpl implements IRkBillService
List<RkInfo> rkInfoList = dto.getRkInfoList();
/* ================== 0⃣ 入库前:供应计划【批量】校验 ================== */
/* ================== 0⃣ 入库前:供应计划【批量】校验(预入库也要校验) ================== */
checkGysJhQtyBeforeInStockBatch(rkInfoList);
/* ================== 1⃣ 主单 rk_bill ================== */
@@ -117,8 +117,6 @@ public class RkBillServiceImpl implements IRkBillService
bill.setBillNo(billNo);
bill.setBizType("0"); // 入库
bill.setExecStatus(bill.getExecStatus());
bill.setOperationTime(dto.getRkBill().getOperationTime());
bill.setCreateTime(now);
bill.setCreateBy(userId);
bill.setIsDelete("0");
@@ -129,12 +127,12 @@ public class RkBillServiceImpl implements IRkBillService
rkBillMapper.insertRkBill(bill);
/* ================== 2⃣ 明细 & 事件 ================== */
/* ================== 2⃣ 明细 & 入库记录 ================== */
for (RkInfo info : rkInfoList) {
info.setExecStatus(bill.getExecStatus());
// 库位编码
// 库位校验
if (StringUtils.isNotBlank(info.getPcode())) {
PcdeDetail pcde = pcdeDetailMapper.selectByPcode(info.getPcode());
if (pcde == null) {
@@ -170,8 +168,10 @@ public class RkBillServiceImpl implements IRkBillService
rkRecordMapper.insertRkRecord(record);
}
/* ================== 3⃣ 入库后:供应计划【批量】更新 ================== */
handleGysJhAfterInStockBatch(rkInfoList);
/* ================== 3⃣ 入库完成后:更新供应计划(❗仅完成入库) ================== */
if ("1".equals(bill.getExecStatus())) {
handleGysJhAfterInStockBatch(rkInfoList);
}
return 1;
}
@@ -335,7 +335,7 @@ public class RkBillServiceImpl implements IRkBillService
execStatus = bill.getExecStatus();
}
// 🚩 标记:本次是否包含预入库
// 标记:本次是否包含预入库
boolean hasPreIn = false;
List<RkInfo> rkInfoList = dto.getRkInfoList();
@@ -346,7 +346,7 @@ public class RkBillServiceImpl implements IRkBillService
/* ================== 4⃣ 插入明细 & 事件 ================== */
for (RkInfo info : rkInfoList) {
/* ===== 4.1 库位校验 ===== */
/* 4.1 库位校验 */
if (StringUtils.isNotBlank(info.getPcode())) {
PcdeDetail pcde = pcdeDetailMapper.selectByPcode(info.getPcode());
if (pcde == null) {
@@ -355,7 +355,7 @@ public class RkBillServiceImpl implements IRkBillService
info.setPcodeId(pcde.getEncodedId());
}
/* ===== 4.2 继承主单字段 ===== */
/* 4.2 继承主单字段 */
info.setBillNo(bill.getBillNo());
info.setOperationType(bill.getOperationType());
info.setBizType(bill.getBizType());
@@ -374,21 +374,21 @@ public class RkBillServiceImpl implements IRkBillService
info.setHasMoved("0");
info.setIsBorrowed("0");
/* ===== 4.3 备注兜底 ===== */
/* 4.3 备注兜底 */
String finalRemark = StringUtils.isNotBlank(info.getRemark())
? info.getRemark()
: bill.getRemark();
info.setRemark(finalRemark);
/* ===== 4.4 审计字段 ===== */
/* 4.4 审计字段 */
info.setCreateTime(now);
info.setCreateBy(bill.getCreateBy());
info.setIsDelete("0");
/* ===== 4.5 插入 rk_info ===== */
/* 4.5 插入 rk_info */
rkInfoMapper.insertRkInfo(info);
/* ===== 4.6 插入 rk_record ===== */
/* 4.6 插入 rk_record */
RkRecord record = buildInRkRecord(bill, info, now);
record.setExecStatus(execStatus);
record.setRkInfoId(info.getId());
@@ -398,8 +398,10 @@ public class RkBillServiceImpl implements IRkBillService
rkRecordMapper.insertRkRecord(record);
}
/* ================== 5⃣ 追加后:供应计划【批量】更新 ================== */
handleGysJhAfterInStockBatch(rkInfoList);
/* ================== 5⃣ 追加后:供应计划【仅完成入库】 ================== */
if ("1".equals(execStatus)) {
handleGysJhAfterInStockBatch(rkInfoList);
}
/* ================== 6⃣ 同步回退主单状态 ================== */
if (hasPreIn && !"0".equals(bill.getExecStatus())) {

View File

@@ -263,13 +263,6 @@ public class RkRecordServiceImpl implements IRkRecordService
return rows;
}
/**
* 批量删除出入库记录
*
* @param ids 需要删除的出入库记录主键
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteRkRecordByIds(Long[] ids) {
@@ -278,55 +271,27 @@ public class RkRecordServiceImpl implements IRkRecordService
return 0;
}
// ================== 1. 查询 record ==================
/* ================== 1️⃣ 查询 record ================== */
List<RkRecord> records = rkRecordMapper.selectRkRecordByIds(Arrays.asList(ids));
if (records == null || records.isEmpty()) {
return 0;
}
// ================== 2. 校验 record 必须全部是预入库 ==================
/* ================== 2️⃣ 校验必须全部是预入库 / 预出库】 ================== */
boolean hasFinishedRecord = records.stream()
.anyMatch(r -> r.getExecStatus() == null || !"0".equals(r.getExecStatus()));
if (hasFinishedRecord) {
throw new ServiceException("仅允许删除预入库/预出库的出入库记录");
}
// ================== 3. 回滚供应计划已入库数量 + 状态 ==================
Map<Long, BigDecimal> rollbackMap = records.stream()
.filter(r -> r.getGysJhId() != null && r.getRealQty() != null)
.collect(Collectors.groupingBy(
RkRecord::getGysJhId,
Collectors.mapping(
RkRecord::getRealQty,
Collectors.reducing(BigDecimal.ZERO, BigDecimal::add)
)
));
for (Map.Entry<Long, BigDecimal> entry : rollbackMap.entrySet()) {
Long jhId = entry.getKey();
BigDecimal rollbackQty = entry.getValue();
// 1⃣ 回滚数量
gysJhMapper.rollbackInQty(jhId, rollbackQty);
// 2⃣ 判断回滚后数量是否为 0
BigDecimal remainQty = gysJhMapper.selectRealQtyById(jhId);
if (remainQty == null || remainQty.compareTo(BigDecimal.ZERO) == 0) {
// 3⃣ 已入库数量为 0回退状态
gysJhMapper.updateStatus(jhId, "0"); // 0=未入库
}
}
// ================== 4. 收集 rkInfoId ==================
/* ================== 3️⃣ 收集 rkInfoId ================== */
List<Long> rkInfoIds = records.stream()
.map(RkRecord::getRkInfoId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
// ================== 5. 校验 info 也必须是预入库 ==================
/* ================== 4 校验 info 也必须是预入库 ================== */
if (!rkInfoIds.isEmpty()) {
int invalidCount = rkInfoMapper.countNotPreByIds(rkInfoIds);
if (invalidCount > 0) {
@@ -337,10 +302,10 @@ public class RkRecordServiceImpl implements IRkRecordService
rkInfoMapper.deleteRkInfoByIds(rkInfoIds.toArray(new Long[0]));
}
// ================== 6. 删除 record ==================
/* ================== 5 删除 record ================== */
int deleteCount = rkRecordMapper.deleteRkRecordByIds(ids);
// ================== 7. 判断是否需要更新 bill 状态 ==================
/* ================== 6 判断是否需要更新 bill 状态 ================== */
Set<String> billNos = records.stream()
.map(RkRecord::getBillNo)
.filter(StringUtils::isNotBlank)
@@ -383,8 +348,17 @@ public class RkRecordServiceImpl implements IRkRecordService
throw new ServiceException("入库记录不存在");
}
/* ====================== 2校验是否发生过移库 ====================== */
List<Long> rkInfoIds = recordList.stream()
/* ====================== 2仅处理【已完成入库】记录 ====================== */
List<RkRecord> finishedList = recordList.stream()
.filter(r -> "1".equals(r.getExecStatus()))
.collect(Collectors.toList());
if (finishedList.isEmpty()) {
throw new ServiceException("所选记录均为预入库状态,无需撤销");
}
/* ====================== 3⃣ 校验是否发生过移库 ====================== */
List<Long> rkInfoIds = finishedList.stream()
.map(RkRecord::getRkInfoId)
.filter(Objects::nonNull)
.distinct()
@@ -392,10 +366,8 @@ public class RkRecordServiceImpl implements IRkRecordService
if (!rkInfoIds.isEmpty()) {
// 查询对应库存
List<RkInfo> infoList = rkInfoMapper.selectRkInfoByIds(rkInfoIds);
// 只要有一条 has_moved = 1直接禁止撤销
boolean hasMoved = infoList.stream()
.anyMatch(info -> "1".equals(info.getHasMoved()));
@@ -404,26 +376,74 @@ public class RkRecordServiceImpl implements IRkRecordService
}
}
/* ====================== 3️⃣ rk_record 回退为预入库 ====================== */
rkRecordMapper.updateExecStatusByIds(recordIds, "0");
/* ====================== 4️⃣ 汇总需回退的供应计划数量 ====================== */
Map<Long, BigDecimal> rollbackQtyMap = finishedList.stream()
.filter(r -> r.getGysJhId() != null && r.getRealQty() != null)
.collect(Collectors.groupingBy(
RkRecord::getGysJhId,
Collectors.mapping(
RkRecord::getRealQty,
Collectors.reducing(BigDecimal.ZERO, BigDecimal::add)
)
));
/* ====================== 4️⃣ rk_info 回退为预入库 ====================== */
/* ====================== 5️⃣ 状态回退 ====================== */
// ① rk_record.exec_status = 0
List<Long> finishedRecordIds = finishedList.stream()
.map(RkRecord::getId)
.collect(Collectors.toList());
rkRecordMapper.updateExecStatusByIds(finishedRecordIds, "0");
// ② rk_info.exec_status = 0
if (!rkInfoIds.isEmpty()) {
rkInfoMapper.updateExecStatusByIds(rkInfoIds, "0");
}
/* ====================== 5⃣ rk_bill 回退为预入库 ====================== */
List<String> billNos = recordList.stream()
// ③ rk_bill.exec_status = 0
Set<String> billNoSet = finishedList.stream()
.map(RkRecord::getBillNo)
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList());
.collect(Collectors.toSet());
for (String billNo : billNos) {
for (String billNo : billNoSet) {
rkBillMapper.updateExecStatusByBillNo(billNo, "0");
}
return recordIds.size();
/* ====================== 6⃣ 回退供应计划(关键补充) ====================== */
for (Map.Entry<Long, BigDecimal> entry : rollbackQtyMap.entrySet()) {
Long gysJhId = entry.getKey();
BigDecimal rollbackQty = entry.getValue();
// ① 实际入库数量回退(累减)
gysJhMapper.decreaseRealQtyById(rollbackQty, gysJhId);
// ② 重算状态
GysJh gysJh = gysJhMapper.selectGysJhById(gysJhId);
if (gysJh == null) {
continue;
}
BigDecimal planQty = gysJh.getJhQty();
BigDecimal realQty = gysJh.getRealQty() == null
? BigDecimal.ZERO
: gysJh.getRealQty();
String status;
if (realQty.compareTo(BigDecimal.ZERO) == 0) {
status = "0"; // 未到货
} else if (planQty != null && realQty.compareTo(planQty) >= 0) {
status = "1"; // 已入库
} else {
status = "2"; // 部分入库
}
gysJhMapper.updateStatusById(gysJhId, status);
}
return finishedRecordIds.size();
}
@@ -435,22 +455,22 @@ public class RkRecordServiceImpl implements IRkRecordService
throw new ServiceException("入库记录ID不能为空");
}
/* ================== 1. 查询 rk_record ================== */
/* ================== 1️⃣ 查询 rk_record ================== */
List<RkRecord> recordList = rkRecordMapper.selectRkRecordByIds(recordIds);
if (recordList == null || recordList.isEmpty()) {
throw new ServiceException("入库记录不存在");
}
/* ================== 2. 仅处理预入库记录 ================== */
/* ================== 2️⃣ 仅处理预入库记录 ================== */
List<RkRecord> preRecordList = recordList.stream()
.filter(r -> "0".equals(r.getExecStatus())) // 预入库
.filter(r -> "0".equals(r.getExecStatus())) // 0 = 预入库
.collect(Collectors.toList());
if (preRecordList.isEmpty()) {
throw new ServiceException("所选入库记录均已完成,无需重复入库");
}
/* ================== 3. 校验:完成入库数量 ≤ 计划交货数量 ================== */
/* ================== 3️⃣ 校验:已入库 + 本次完成 ≤ 计划数量 ================== */
Map<Long, BigDecimal> finishQtyMap = new HashMap<>();
for (RkRecord record : preRecordList) {
@@ -475,16 +495,21 @@ public class RkRecordServiceImpl implements IRkRecordService
}
BigDecimal planQty = gysJh.getJhQty();
if (planQty != null && finishQty.compareTo(planQty) > 0) {
BigDecimal alreadyQty = gysJh.getRealQty() == null
? BigDecimal.ZERO
: gysJh.getRealQty();
if (planQty != null && alreadyQty.add(finishQty).compareTo(planQty) > 0) {
throw new ServiceException(
"一键入库数量超出供应计划数量,物料号:" + gysJh.getWlNo()
+ ",计划数量:" + planQty
+ "本次完成入库:" + finishQty
+ "入库:" + alreadyQty
+ ",本次完成:" + finishQty
);
}
}
/* ================== 4. 状态推进 ================== */
/* ================== 4️⃣ 状态推进 ================== */
// ① rk_record.exec_status = 1
List<Long> preRecordIds = preRecordList.stream()
@@ -493,7 +518,7 @@ public class RkRecordServiceImpl implements IRkRecordService
rkRecordMapper.updateExecStatusByIds(preRecordIds, "1");
// ② rk_info.exec_status = 1只推进本次涉及的库存)
// ② rk_info.exec_status = 1本次涉及的库存)
List<Long> rkInfoIds = preRecordList.stream()
.map(RkRecord::getRkInfoId)
.filter(Objects::nonNull)
@@ -504,54 +529,56 @@ public class RkRecordServiceImpl implements IRkRecordService
rkInfoMapper.updateExecStatusByIds(rkInfoIds, "1");
}
// ③ rk_bill.exec_status = 1只有 bill 下所有 record 均完成)
// ③ rk_bill.exec_status = 1只有 bill 下所有 record 均完成)
Set<String> billNoSet = preRecordList.stream()
.map(RkRecord::getBillNo)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
for (String billNo : billNoSet) {
// 查询该 bill 下是否仍存在 预入库 record
int unFinishedCount = rkRecordMapper.countPreInRecordByBillNo(billNo);
// 只有当不存在任何预入库 record 时,才推进 bill 状态
if (unFinishedCount == 0) {
rkBillMapper.updateExecStatusByBillNo(billNo, "1");
}
}
/* ================== 5. 同步修正供应计划 ================== */
/* ================== 5️⃣ 累加更新供应计划(关键修复点) ================== */
for (Map.Entry<Long, BigDecimal> entry : finishQtyMap.entrySet()) {
Long gysJhId = entry.getKey();
BigDecimal finishQty = entry.getValue();
// ① 修正实收数量
gysJhMapper.updateRealQtyById(gysJhId, finishQty);
// ✅ 改为【累加】而不是覆盖
gysJhMapper.increaseRealQtyById(finishQty, gysJhId);
// ② 修正状态
// 重新计算状态
GysJh gysJh = gysJhMapper.selectGysJhById(gysJhId);
if (gysJh == null) {
continue;
}
BigDecimal planQty = gysJh.getJhQty();
BigDecimal realQty = gysJh.getRealQty();
BigDecimal realQty = gysJh.getRealQty() == null
? BigDecimal.ZERO
: gysJh.getRealQty();
if (realQty == null || realQty.compareTo(BigDecimal.ZERO) == 0) {
gysJhMapper.updateStatusById(gysJhId, "0"); // 未到货
String status;
if (realQty.compareTo(BigDecimal.ZERO) == 0) {
status = "0"; // 未到货
} else if (planQty != null && realQty.compareTo(planQty) >= 0) {
gysJhMapper.updateStatusById(gysJhId, "1"); // 已入库
status = "1"; // 已入库
} else {
gysJhMapper.updateStatusById(gysJhId, "2"); // 部分入库
status = "2"; // 部分入库
}
gysJhMapper.updateStatusById(gysJhId, status);
}
return preRecordIds.size();
}
@Override
@Transactional(rollbackFor = Exception.class)
public int rollbackOut(List<Long> recordIds) {
@@ -628,23 +655,23 @@ public class RkRecordServiceImpl implements IRkRecordService
throw new ServiceException("出库记录ID不能为空");
}
// ================== 1. 查询 rk_record ==================
/* ================== 1️⃣ 查询 rk_record ================== */
List<RkRecord> recordList = rkRecordMapper.selectRkRecordByIds(recordIds);
if (recordList == null || recordList.isEmpty()) {
throw new ServiceException("出库记录不存在");
}
// ================== 2. 只保留预出库记录 ==================
/* ================== 2️⃣ 仅保留预出库记录 ================== */
List<RkRecord> preOutList = recordList.stream()
.filter(r -> "1".equals(r.getBizType())) // 出库
.filter(r -> "0".equals(r.getExecStatus())) // 预出库
.filter(r -> "1".equals(r.getBizType())) // 出库
.filter(r -> "0".equals(r.getExecStatus())) // 预出库
.collect(Collectors.toList());
if (preOutList.isEmpty()) {
throw new ServiceException("所选出库记录均已完成出库");
}
// ================== 3. 库存校验 + 扣减 ==================
/* ================== 3️⃣ 库存校验 + 扣减 ================== */
for (RkRecord record : preOutList) {
Long rkInfoId = record.getRkInfoId();
@@ -670,24 +697,24 @@ public class RkRecordServiceImpl implements IRkRecordService
BigDecimal newQty = remainQty.subtract(outQty);
// 扣库存
// 扣库存数量
rkInfoMapper.updateRealQtyById(rkInfoId, newQty);
// 同步出库状态
// ② 若扣完,同步库存出库状态
if (newQty.compareTo(BigDecimal.ZERO) == 0) {
rkInfoMapper.updateIsChukuById(rkInfoId, "1");
}
}
// ================== 4. 推进状态 ==================
/* ================== 4️⃣ 状态推进 ================== */
// ① rk_record.exec_status = 1
List<Long> preOutIds = preOutList.stream()
// ① rk_record.exec_status = 1(仅本次完成的)
List<Long> finishedRecordIds = preOutList.stream()
.map(RkRecord::getId)
.collect(Collectors.toList());
rkRecordMapper.updateExecStatusByIds(preOutIds, "1");
rkRecordMapper.updateExecStatusByIds(finishedRecordIds, "1");
// ② rk_info.exec_status = 1
// ② rk_info.exec_status = 1(涉及到的库存)
List<Long> rkInfoIds = preOutList.stream()
.map(RkRecord::getRkInfoId)
.filter(Objects::nonNull)
@@ -697,17 +724,26 @@ public class RkRecordServiceImpl implements IRkRecordService
rkInfoMapper.updateExecStatusByIds(rkInfoIds, "1");
}
// ③ rk_bill.exec_status = 1
// 只有当【该单据下不存在任何预出库记录】才允许完成
List<String> billNos = preOutList.stream()
.map(RkRecord::getBillNo)
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList());
for (String billNo : billNos) {
// 判断该出库单下是否还存在预出库记录
int preOutCount = rkRecordMapper.countPreOutRecordByBillNo(billNo);
if (preOutCount > 0) {
// 只要还有一条预出库,整单不能完成
continue;
}
rkBillMapper.updateExecStatusByBillNo(billNo, "1");
}
return preOutIds.size();
return finishedRecordIds.size();
}
@Override

View File

@@ -311,4 +311,11 @@
WHERE id = #{gysJhId}
</update>
<update id="decreaseRealQtyById">
UPDATE gys_jh
SET real_qty = IFNULL(real_qty, 0) - #{rollbackQty}
WHERE id = #{gysJhId}
AND IFNULL(real_qty, 0) >= #{rollbackQty}
</update>
</mapper>

View File

@@ -194,9 +194,9 @@
resultType="com.zg.project.wisdom.domain.vo.StockStatisticVO">
SELECT
IFNULL(SUM(ri.real_qty * ri.ht_dj), 0) AS total_amount,
COUNT(DISTINCT ri.pcode) AS location_count,
IFNULL(SUM(ri.real_qty), 0) AS total_quantity
IFNULL(SUM(ri.real_qty * ri.ht_dj), 0) AS totalAmount,
COUNT(DISTINCT ri.pcode) AS locationCount,
IFNULL(SUM(ri.real_qty), 0) AS totalQuantity
FROM rk_info ri
<where>
ri.exec_status = 1

View File

@@ -5,20 +5,18 @@
<mapper namespace="com.zg.project.wisdom.mapper.RkRecordMapper">
<!-- ===================== resultMap ===================== -->
<resultMap type="RkRecord" id="RkRecordResult">
<resultMap id="RkRecordResult" type="com.zg.project.wisdom.domain.RkRecord">
<result property="id" column="id"/>
<!-- ✅ 新增:关联 rk_info 主键 -->
<result property="rkInfoId" column="rk_info_id"/>
<result property="bizType" column="biz_type"/>
<result property="operationType" column="operation_type"/>
<result property="operationTypeName" column="operation_type_name"/>
<result property="bizType" column="biz_type"/>
<result property="wlType" column="wl_type"/>
<result property="wlTypeName" column="wl_type_name"/>
<!-- 仓库层级 -->
<result property="cangku" column="cangku"/>
<result property="warehouseName" column="warehouse_name"/>
<result property="parentWarehouseCode" column="parent_warehouse_code"/>
@@ -60,6 +58,7 @@
<result property="pcodeId" column="pcode_id"/>
<result property="trayCode" column="tray_code"/>
<result property="entityId" column="entity_id"/>
<result property="teamCode" column="team_code"/>
<result property="teamName" column="team_name"/>
@@ -85,52 +84,54 @@
<result property="isDelete" column="is_delete"/>
</resultMap>
<!-- ===================== 查询字段(连表补齐 ===================== -->
<!-- ===================== 查询字段(统一视图 ===================== -->
<sql id="selectRkRecordVo">
SELECT
rr.*,
/* 理货员姓名 */
su.nick_name AS operator_name,
/* 物资类型名称 */
mt.type_name AS wl_type_name,
/* 出入库类型名称:入库 / 出库 二选一 */
COALESCE(sit.type_name, sot.type_name) AS operation_type_name,
/* 仓库信息:小仓/大仓 */
wh.warehouse_name,
wh.parent_warehouse_code,
wh.parent_warehouse_name,
-- 小仓
wh.warehouse_name AS warehouse_name,
-- ✅ 大仓(直接取小仓行里的父仓字段)
wh.parent_warehouse_code AS parent_warehouse_code,
wh.parent_warehouse_name AS parent_warehouse_name,
ct.team_name AS team_name
FROM rk_record rr
LEFT JOIN sys_user su
ON rr.operator = su.user_id
LEFT JOIN material_type mt
ON rr.wl_type = mt.type_code
LEFT JOIN stock_in_type sit
ON rr.operation_type = sit.type_code
LEFT JOIN stock_out_type sot
ON rr.operation_type = sot.type_code
LEFT JOIN warehouse_info wh
ON rr.cangku = wh.warehouse_code
LEFT JOIN sys_user su ON rr.operator = su.user_id
LEFT JOIN material_type mt ON rr.wl_type = mt.type_code
LEFT JOIN stock_in_type sit ON rr.operation_type = sit.type_code
LEFT JOIN stock_out_type sot ON rr.operation_type = sot.type_code
LEFT JOIN warehouse_info wh ON rr.cangku = wh.warehouse_code
LEFT JOIN construction_team ct
ON rr.team_code = ct.team_code
AND ct.is_delete = '0'
</sql>
<!-- ===================== 查询列表 ===================== -->
<select id="selectRkRecordList" parameterType="RkRecord" resultMap="RkRecordResult">
<select id="selectRkRecordList"
parameterType="RkRecord"
resultMap="RkRecordResult">
<include refid="selectRkRecordVo"/>
<where>
AND rr.exec_status = '1'
<if test="operationType != null and operationType != ''">
AND rr.operation_type = #{operationType}
</if>
<!-- 多 bizType -->
<if test="bizTypeList != null and bizTypeList.size > 0">
AND rb.biz_type IN
AND rr.biz_type IN
<foreach collection="bizTypeList"
item="bt"
open="("
@@ -139,37 +140,48 @@
#{bt}
</foreach>
</if>
<if test="bizType != null and bizType != ''">
AND rr.biz_type = #{bizType}
</if>
<if test="pcode != null and pcode != ''">
AND rr.pcode = #{pcode}
</if>
<if test="wlType != null and wlType != ''">
AND rr.wl_type = #{wlType}
</if>
<if test="cangku != null and cangku != ''">
AND rr.cangku = #{cangku}
</if>
<if test="operator != null and operator != ''">
AND rr.operator = #{operator}
</if>
<if test="isChuku != null and isChuku != ''">
AND rr.is_chuku = #{isChuku}
</if>
<if test="status != null and status != ''">
AND rr.status = #{status}
</if>
<if test="execStatus != null and execStatus != ''">
AND rr.exec_status = #{execStatus}
</if>
<if test="billNo != null and billNo != ''">
AND rr.bill_no = #{billNo}
</if>
<if test="isDelivery != null and isDelivery != ''">
AND rr.is_delivery = #{isDelivery}
</if>
<!-- 项目号 -->
<!-- 项目 -->
<if test="xmNo != null and xmNo != ''">
AND rr.xm_no LIKE concat('%', #{xmNo}, '%')
</if>
@@ -177,50 +189,81 @@
AND rr.xm_ms LIKE concat('%', #{xmMs}, '%')
</if>
<!-- 订单编号 -->
<!-- 订单 -->
<if test="sapNo != null and sapNo != ''">
AND rr.sap_no LIKE concat('%', #{sapNo}, '%')
</if>
<!-- 供应商名称 -->
<!-- 供应商 -->
<if test="gysMc != null and gysMc != ''">
AND rr.gys_mc LIKE concat('%', #{gysMc}, '%')
</if>
<!-- 所属小仓warehouseCode 实际就是 cangku -->
<if test="cangku != null and cangku != ''">
AND rr.cangku = #{cangku}
</if>
<if test="wlNo != null and wlNo != ''">
AND rr.wl_no LIKE concat('%', #{wlNo}, '%')
</if>
<!-- 物料描述 -->
<if test="wlMs != null and wlMs != ''">
AND rr.wl_ms LIKE concat('%', #{wlMs}, '%')
</if>
<!-- 是否借料 -->
<if test="isBorrowed != null and isBorrowed != ''">
AND rr.is_borrowed = #{isBorrowed}
</if>
<!-- 默认不查删除 -->
<!-- 删除标识 -->
<if test="isDelete == null">
AND (rr.is_delete = '0' OR rr.is_delete = 0 OR rr.is_delete IS NULL)
</if>
<!-- 如果前端明确传 isDelete则按传入的精确过滤 -->
<if test="isDelete != null and isDelete != ''">
AND rr.is_delete = #{isDelete}
</if>
<!-- 出入库时间范围 -->
<if test="startDate != null">
AND rr.operation_time &gt;= #{startDate}
</if>
<if test="endDate != null">
AND rr.operation_time &lt;= #{endDate}
<!-- ================= 时间条件(最终正确版) ================= -->
<if test="startDate != null or endDate != null">
AND (
<!-- 普通入库 / 出库:精确到时分秒 -->
(
rr.biz_type IN ('0','1')
<if test="startDate != null">
AND rr.operation_time &gt;= #{startDate}
</if>
<if test="endDate != null">
AND rr.operation_time &lt;= #{endDate}
</if>
)
OR
<!-- 借料出库:按 borrow_time按天 -->
(
rr.biz_type = '2'
<if test="startDate != null">
AND rr.borrow_time &gt;= DATE(#{startDate})
</if>
<if test="endDate != null">
AND rr.borrow_time &lt; DATE_ADD(DATE(#{endDate}), INTERVAL 1 DAY)
</if>
)
OR
<!-- 还料入库:按 return_time按天 -->
(
rr.biz_type = '3'
<if test="startDate != null">
AND rr.return_time &gt;= DATE(#{startDate})
</if>
<if test="endDate != null">
AND rr.return_time &lt; DATE_ADD(DATE(#{endDate}), INTERVAL 1 DAY)
</if>
)
)
</if>
</where>
ORDER BY rr.exec_status = '0' DESC, rr.operation_time DESC
ORDER BY rr.exec_status = '0' DESC,
rr.operation_time DESC
</select>
<!-- ===================== 按 ID 查询 ===================== -->
<select id="selectRkRecordById" parameterType="Long" resultMap="RkRecordResult">
<include refid="selectRkRecordVo"/>
@@ -623,12 +666,24 @@
AND is_delete = '0'
</select>
<select id="countPreOutRecordByBillNo"
parameterType="java.lang.String"
resultType="java.lang.Integer">
SELECT COUNT(1)
FROM rk_record
WHERE bill_no = #{billNo}
AND biz_type = '1' <!-- 仅统计出库记录 -->
AND exec_status = '0' <!-- 预出库 -->
AND is_delete = '0'
</select>
<update id="updateBorrowReturnRecordById">
UPDATE rk_record
SET
biz_type = '3',
is_borrowed = #{isBorrowed},
return_time = #{returnTime},
update_by = #{updateBy},
update_by = #{updateBy},
update_time = #{updateTime}
WHERE id = #{id}
AND is_delete = '0'