库存模块功能开发

This commit is contained in:
2026-01-22 19:59:49 +08:00
parent a1be0e4abd
commit 9b2b142347
24 changed files with 1848 additions and 501 deletions

View File

@@ -42,6 +42,17 @@ public class MoveRecordController extends BaseController
return AjaxResult.success("移库成功");
}
/**
* 撤销移库
*/
@PreAuthorize("@ss.hasPermi('wisdom:stock:move:rollback')")
@Log(title = "撤销移库", businessType = BusinessType.UPDATE)
@PostMapping("/rollback/{id}")
public AjaxResult rollbackMove(@PathVariable("id") Long moveRecordId) {
moveRecordService.rollbackMove(moveRecordId);
return AjaxResult.success("撤销移库成功");
}
/**
* 查询移库记录列表
*/

View File

@@ -8,7 +8,6 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
@@ -90,7 +89,6 @@ public class RkBillController extends BaseController
return toAjax(rkBillService.appendRkBillDetail(dto));
}
/**
* 修改库存单据
*/
@@ -107,9 +105,30 @@ public class RkBillController extends BaseController
*/
@PreAuthorize("@ss.hasPermi('wisdom:bill:remove')")
@Log(title = "库存单据", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
@DeleteMapping("/delete")
public AjaxResult remove(@RequestBody Long[] ids)
{
return toAjax(rkBillService.deleteRkBillByIds(ids));
}
/**
* 新增出库单据
*/
@PreAuthorize("@ss.hasPermi('wisdom:bill:out:add')")
@Log(title = "出库单据", businessType = BusinessType.INSERT)
@PostMapping("/out/add")
public AjaxResult addOut(@RequestBody RkBillCreateDTO dto) {
return toAjax(rkBillService.insertOutBillAndDetail(dto));
}
/**
* 还料入库
*/
@PreAuthorize("@ss.hasPermi('wisdom:bill:return:add')")
@Log(title = "还料入库", businessType = BusinessType.INSERT)
@PostMapping("/return/add")
public AjaxResult returnIn(@RequestBody RkBillCreateDTO dto) {
return toAjax(rkBillService.insertReturnBillAndDetail(dto));
}
}

View File

@@ -102,12 +102,12 @@ public class RkRecordController extends BaseController
}
/**
* 删除入库记录
* 删除入库记录
*/
@PreAuthorize("@ss.hasPermi('wisdom:record:remove')")
@Log(title = "入库记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
@Log(title = "入库记录", businessType = BusinessType.DELETE)
@DeleteMapping("/delete")
public AjaxResult remove(@RequestBody Long[] ids)
{
return toAjax(rkRecordService.deleteRkRecordByIds(ids));
}
@@ -132,4 +132,34 @@ public class RkRecordController extends BaseController
return toAjax(rkRecordService.finishIn(record.getIds()));
}
/**
* 撤销出库
*/
@PreAuthorize("@ss.hasPermi('wisdom:bill:out:rollback')")
@Log(title = "撤销出库", businessType = BusinessType.UPDATE)
@PostMapping("/out/rollback")
public AjaxResult rollbackOut(@RequestBody RkRecord record) {
return toAjax(rkRecordService.rollbackOut(record.getIds()));
}
/**
* 一键出库(批量完成出库)
*/
@PreAuthorize("@ss.hasPermi('wisdom:record:out:finish')")
@Log(title = "一键出库", businessType = BusinessType.UPDATE)
@PostMapping("/out/finish")
public AjaxResult finishOut(@RequestBody RkRecord record) {
return toAjax(rkRecordService.finishOut(record.getIds()));
}
/**
* 删除预出库记录
*/
@PreAuthorize("@ss.hasPermi('wisdom:record:out:remove')")
@Log(title = "预出库记录删除", businessType = BusinessType.DELETE)
@DeleteMapping("/out/delete")
public AjaxResult deletePreOut(@RequestBody Long[] ids) {
return toAjax(rkRecordService.deletePreOutRecords(ids));
}
}

View File

@@ -11,7 +11,12 @@ import com.zg.framework.web.domain.BaseEntity;
/**
* 供应计划对象 gys_jh
*
*
* 说明:
* - jhQty :计划交货数量
* - realQty :累计已入库数量(落库)
* - waitQty :待入库数量 = jhQty - realQty仅用于反显不落库
*
* @author zg
* @date 2025-05-28
*/
@@ -78,12 +83,28 @@ public class GysJh extends BaseEntity
@Excel(name = "计划交货数量")
private BigDecimal jhQty;
/** 已入库数量(累计) */
@Excel(name = "已入库数量")
private BigDecimal realQty;
/**
* 待入库数量(反显字段,不落库)
*
* 计算规则:
* waitQty = jhQty - realQty
*/
private BigDecimal waitQty;
/** 计量单位 */
@Excel(name = "计量单位")
private String dw;
/** 0未到货1已入库2部分入库 */
// @Excel(name = "0未到货,1已入库,2部分入库")
/**
* 状态
* 0未到货
* 1部分入库
* 2已入库
*/
private String status;
/** 身份码 */
@@ -95,7 +116,6 @@ public class GysJh extends BaseEntity
private String remark;
/** 是否删除0正常 1删除 */
// @Excel(name = "是否删除", readConverterExp = "0=正常,1=删除")
private String isDelete;
/** 查询开始时间yyyy-MM-dd HH:mm:ss */
@@ -106,236 +126,120 @@ public class GysJh extends BaseEntity
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
public void setId(Long id)
{
this.id = id;
}
// ======================= getter / setter =======================
public Long getId()
{
return id;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public void setIndexNo(Long indexNo)
{
this.indexNo = indexNo;
}
public String getXh() { return xh; }
public void setXh(String xh) { this.xh = xh; }
public Long getIndexNo()
{
return indexNo;
}
public Long getIndexNo() { return indexNo; }
public void setIndexNo(Long indexNo) { this.indexNo = indexNo; }
public void setXj(String xj)
{
this.xj = xj;
}
public String getXj() { return xj; }
public void setXj(String xj) { this.xj = xj; }
public String getXj()
{
return xj;
}
public String getXmNo() { return xmNo; }
public void setXmNo(String xmNo) { this.xmNo = xmNo; }
public void setXmNo(String xmNo)
{
this.xmNo = xmNo;
}
public String getXmMs() { return xmMs; }
public void setXmMs(String xmMs) { this.xmMs = xmMs; }
public String getXmNo()
{
return xmNo;
}
public String getWlNo() { return wlNo; }
public void setWlNo(String wlNo) { this.wlNo = wlNo; }
public void setXmMs(String xmMs)
{
this.xmMs = xmMs;
}
public String getWlMs() { return wlMs; }
public void setWlMs(String wlMs) { this.wlMs = wlMs; }
public String getXmMs()
{
return xmMs;
}
public String getGysNo() { return gysNo; }
public void setGysNo(String gysNo) { this.gysNo = gysNo; }
public void setWlNo(String wlNo)
{
this.wlNo = wlNo;
}
public String getGysMc() { return gysMc; }
public void setGysMc(String gysMc) { this.gysMc = gysMc; }
public String getWlNo()
{
return wlNo;
}
public BigDecimal getJhAmt() { return jhAmt; }
public void setJhAmt(BigDecimal jhAmt) { this.jhAmt = jhAmt; }
public void setWlMs(String wlMs)
{
this.wlMs = wlMs;
}
public BigDecimal getHtDj() { return htDj; }
public void setHtDj(BigDecimal htDj) { this.htDj = htDj; }
public String getWlMs()
{
return wlMs;
}
public void setGysNo(String gysNo)
{
this.gysNo = gysNo;
}
public String getGysNo()
{
return gysNo;
}
public void setGysMc(String gysMc)
{
this.gysMc = gysMc;
}
public String getGysMc()
{
return gysMc;
}
public void setJhAmt(BigDecimal jhAmt)
{
this.jhAmt = jhAmt;
}
public BigDecimal getJhAmt()
{
return jhAmt;
}
public void setHtDj(BigDecimal htDj)
{
this.htDj = htDj;
}
public BigDecimal getHtDj()
{
return htDj;
}
public void setSapNo(String sapNo)
{
this.sapNo = sapNo;
}
public String getSapNo()
{
return sapNo;
}
public void setXh(String xh)
{
this.xh = xh;
}
public String getXh()
{
return xh;
}
public void setJhQty(BigDecimal jhQty) { this.jhQty = jhQty; }
public BigDecimal getJhQty() { return jhQty; }
public void setHtQty(BigDecimal htQty) { this.htQty = htQty; }
public BigDecimal getHtQty() { return htQty; }
public void setHtQty(BigDecimal htQty) { this.htQty = htQty; }
public void setDw(String dw)
{
this.dw = dw;
public String getSapNo() { return sapNo; }
public void setSapNo(String sapNo) { this.sapNo = sapNo; }
public BigDecimal getJhQty() { return jhQty; }
public void setJhQty(BigDecimal jhQty) { this.jhQty = jhQty; }
public BigDecimal getRealQty() { return realQty; }
public void setRealQty(BigDecimal realQty) { this.realQty = realQty; }
/**
* 待入库数量(不落库,动态计算)
*/
public BigDecimal getWaitQty() {
if (jhQty == null) {
return BigDecimal.ZERO;
}
BigDecimal inQty = realQty == null ? BigDecimal.ZERO : realQty;
return jhQty.subtract(inQty);
}
public void setWaitQty(BigDecimal waitQty) {
this.waitQty = waitQty;
}
public String getDw()
{
return dw;
}
public String getDw() { return dw; }
public void setDw(String dw) { this.dw = dw; }
public void setStatus(String status)
{
this.status = status;
}
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getStatus()
{
return status;
}
public String getEntityId() { return entityId; }
public void setEntityId(String entityId) { this.entityId = entityId; }
public void setEntityId(String entityId)
{
this.entityId = entityId;
}
public String getRemark() { return remark; }
public void setRemark(String remark) { this.remark = remark; }
public String getEntityId()
{
return entityId;
}
public String getIsDelete() { return isDelete; }
public void setIsDelete(String isDelete) { this.isDelete = isDelete; }
public void setRemark(String remark)
{
this.remark = remark;
}
public Date getBeginTime() { return beginTime; }
public void setBeginTime(Date beginTime) { this.beginTime = beginTime; }
public String getRemark()
{
return remark;
}
public void setIsDelete(String isDelete)
{
this.isDelete = isDelete;
}
public String getIsDelete()
{
return isDelete;
}
public Date getBeginTime() {
return beginTime;
}
public void setBeginTime(Date beginTime) {
this.beginTime = beginTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public Date getEndTime() { return endTime; }
public void setEndTime(Date endTime) { this.endTime = endTime; }
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("indexNo", getIndexNo())
.append("xj", getXj())
.append("xmNo", getXmNo())
.append("xmMs", getXmMs())
.append("wlNo", getWlNo())
.append("wlMs", getWlMs())
.append("gysNo", getGysNo())
.append("gysMc", getGysMc())
.append("jhAmt", getJhAmt())
.append("htDj", getHtDj())
.append("sapNo", getSapNo())
.append("xh", getXh())
.append("jhQty", getJhQty())
.append("htQty", getHtQty())
.append("dw", getDw())
.append("status", getStatus())
.append("entityId", getEntityId())
.append("remark", getRemark())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("isDelete", getIsDelete())
.toString();
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("indexNo", getIndexNo())
.append("xj", getXj())
.append("xmNo", getXmNo())
.append("xmMs", getXmMs())
.append("wlNo", getWlNo())
.append("wlMs", getWlMs())
.append("gysNo", getGysNo())
.append("gysMc", getGysMc())
.append("jhAmt", getJhAmt())
.append("htDj", getHtDj())
.append("sapNo", getSapNo())
.append("xh", getXh())
.append("jhQty", getJhQty())
.append("realQty", getRealQty())
.append("waitQty", getWaitQty())
.append("htQty", getHtQty())
.append("dw", getDw())
.append("status", getStatus())
.append("entityId", getEntityId())
.append("remark", getRemark())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("isDelete", getIsDelete())
.toString();
}
}

View File

@@ -5,6 +5,7 @@ import com.zg.framework.aspectj.lang.annotation.Excel;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.math.BigDecimal;
import java.util.Date;
/**
@@ -20,13 +21,20 @@ public class MoveRecord extends BaseEntity {
/** 主键ID */
private Long id;
/** 关联库存IDrk_info.id */
/** 库存IDrk_info.id */
private Long rkInfoId;
/** 移库生成的新库存IDrk_info.id用于撤销移库 */
private Long newRkInfoId;
/** 实物ID */
@Excel(name = "实物ID")
private String entityId;
/** 实际移库数量 */
@Excel(name = "移库数量")
private BigDecimal realQty;
/** 原仓库 */
@Excel(name = "原仓库")
private String fromCangku;
@@ -112,6 +120,14 @@ public class MoveRecord extends BaseEntity {
this.rkInfoId = rkInfoId;
}
public Long getNewRkInfoId() {
return newRkInfoId;
}
public void setNewRkInfoId(Long newRkInfoId) {
this.newRkInfoId = newRkInfoId;
}
public String getEntityId() {
return entityId;
}
@@ -120,6 +136,14 @@ public class MoveRecord extends BaseEntity {
this.entityId = entityId;
}
public BigDecimal getRealQty() {
return realQty;
}
public void setRealQty(BigDecimal realQty) {
this.realQty = realQty;
}
public String getFromCangku() {
return fromCangku;
}
@@ -261,7 +285,9 @@ public class MoveRecord extends BaseEntity {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("rkInfoId", getRkInfoId())
.append("newRkInfoId", getNewRkInfoId())
.append("entityId", getEntityId())
.append("realQty", getRealQty())
.append("fromCangku", getFromCangku())
.append("fromPcode", getFromPcode())
.append("fromTrayCode", getFromTrayCode())

View File

@@ -77,7 +77,6 @@ public class RkBill extends BaseEntity {
private String execStatus;
/** 理货员用户ID */
// @Excel(name = "理货员")
private Integer operator;
/** 理货员名称(联表) */
@@ -88,10 +87,31 @@ public class RkBill extends BaseEntity {
@Excel(name = "施工队编码")
private String teamCode;
/** 施工队名称(联表) */
@Excel(name = "施工队名称")
private String teamName;
/** 是否需要配送(0否,1是,2配送中,3配送完成) */
@Excel(name = "是否需要配送", readConverterExp = "0=否,1=是,2=配送中,3=配送完成")
private String isDelivery;
/** 借用时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date borrowTime;
/** 归还时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date returnTime;
/** 借用方项目号(借用方项目) */
@Excel(name = "借用方项目号")
private String xmNoCk;
/** 借用方项目描述(借用方项目描述) */
@Excel(name = "借用方项目描述")
private String xmMsCk;
/** 创建人 */
private String createBy;
@@ -251,6 +271,14 @@ public class RkBill extends BaseEntity {
this.teamCode = teamCode;
}
public String getTeamName() {
return teamName;
}
public void setTeamName(String teamName) {
this.teamName = teamName;
}
public String getIsDelivery() {
return isDelivery;
}
@@ -259,6 +287,42 @@ public class RkBill extends BaseEntity {
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;
}
@@ -343,13 +407,18 @@ public class RkBill extends BaseEntity {
.append("operator", operator)
.append("operatorName", operatorName)
.append("teamCode", teamCode)
.append("teamName", teamName)
.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", getRemark())
.append("remark", remark)
.append("startDate", startDate)
.append("endDate", endDate)
.toString();

View File

@@ -102,11 +102,11 @@ public class RkInfo extends BaseEntity
private String xmMs;
/** 出库项目号(借用方项目) */
@Excel(name = "出库项目号")
@Excel(name = "借用方项目号")
private String xmNoCk;
/** 出库项目描述(借用方项目描述) */
@Excel(name = "出库项目描述")
@Excel(name = "借用方项目描述")
private String xmMsCk;
/** 物料号 */
@@ -178,13 +178,13 @@ public class RkInfo extends BaseEntity
private String teamCode;
/** 借用时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "借用时间", width = 20, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
// @Excel(name = "借用时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date borrowTime;
/** 归还时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "归还时间", width = 20, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
// @Excel(name = "归还时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date returnTime;
/** 是否移库过0否 1是 */

View File

@@ -29,6 +29,8 @@ public class RkRecord extends BaseEntity
/** 业务类型 */
@Excel(name = "业务类型", readConverterExp = "0=入库,1=出库,2=借料出库,3=还料入库")
private String bizType;
/** 业务类型列表(查询用) */
private List<String> bizTypeList;
/** 出入库类型 */
// @Excel(name = "出入库类型")
private String operationType;
@@ -275,7 +277,13 @@ public class RkRecord extends BaseEntity
public String getBizType() {
return bizType;
}
public List<String> getBizTypeList() {
return bizTypeList;
}
public void setBizTypeList(List<String> bizTypeList) {
this.bizTypeList = bizTypeList;
}
public void setOperationType(String operationType)
{
this.operationType = operationType;

View File

@@ -132,4 +132,41 @@ public interface GysJhMapper
@Param("dw") String dw);
/**
* 根据供应计划ID累加实际入库数量
*/
void increaseRealQtyById(
@Param("realQty") BigDecimal realQty,
@Param("gysJhId") Long gysJhId
);
void rollbackInQty(
@Param("jhId") Long jhId,
@Param("rollbackQty") BigDecimal rollbackQty
);
/**
* 根据id查询实收数量
*/
BigDecimal selectRealQtyById(@Param("jhId") Long jhId);
/**
* 根据id修改状态
*/
void updateStatus(
@Param("jhId") Long jhId,
@Param("status") String status
);
/**
* 根据供应计划ID直接更新已入库数量real_qty
*
* @param gysJhId 供应计划ID
* @param realQty 已入库数量(以一键入库结果为准)
* @return 影响行数
*/
int updateRealQtyById(@Param("gysJhId") Long gysJhId,
@Param("realQty") BigDecimal realQty);
}

View File

@@ -78,4 +78,12 @@ public interface RkBillMapper
@Param("execStatus") String execStatus
);
/**
* 修改执行状态
* @param billNo
* @param execStatus
* @return
*/
int updateExecStatus(@Param("billNo") String billNo,
@Param("execStatus") String execStatus);
}

View File

@@ -1,5 +1,7 @@
package com.zg.project.wisdom.mapper;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import com.zg.project.wisdom.domain.RkInfo;
import io.lettuce.core.dynamic.annotation.Param;
@@ -58,11 +60,46 @@ public interface RkInfoMapper
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteRkInfoByIds(Long[] ids);
int deleteRkInfoByIds(@Param("ids") Long[] ids);
/**
* 修改库存明细执行状态
*/
int updateExecStatusByIds(@Param("ids") List<Long> ids,
@Param("execStatus") String execStatus);
/**
* 根据单据编号查询库存明细数量
*/
int countByBillNo(String billNo);
/**
* 根据入库明细ID查询非预入库数量
*/
int countNotPreByIds(@Param("rkInfoIds") List<Long> rkInfoIds);
/**
* 根据入库明细ID更新实收数量
*/
void updateRealQtyById(
@Param("id") Long id,
@Param("realQty") BigDecimal realQty
);
/**
* 根据入库明细ID更新出库状态
*/
void updateIsChukuById(
@Param("id") Long id,
@Param("isChuku") String isChuku
);
/**
* 标记借料已归还
*/
int updateBorrowReturn(@Param("id") Long id,
@Param("isBorrowed") String isBorrowed,
@Param("returnTime") Date returnTime);
List<RkInfo> selectRkInfoByIds(@Param("rkInfoIds") List<Long> rkInfoIds);
}

View File

@@ -69,7 +69,7 @@ public interface RkRecordMapper
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteRkRecordByIds(Long[] ids);
int deleteRkRecordByIds(@Param("ids") Long[] ids);
/**
* 修改执行状态
@@ -95,4 +95,30 @@ public interface RkRecordMapper
* @param rkInfo 单据信息
*/
void updateByRkInfo(RkInfo rkInfo);
/**
* 根据单据号查询记录数量
*
* @param billNo 单据号
* @return 记录数量
*/
int countByBillNo(String billNo);
/**
* 根据单据号查询预入库记录数量
*
* @param billNo 单据号
* @return 记录数量
*/
int countPreByBillNo(@Param("billNo") String billNo);
int countPreOutByBillNo(@Param("billNo") String billNo);
/**
* 根据单据ID查询记录
*
* @param rkInfoId 单据ID
* @return 记录
*/
RkRecord selectRkRecordByRkInfoId(@Param("rkInfoId") Long rkInfoId);
}

View File

@@ -60,4 +60,11 @@ public interface IMoveRecordService
* @param dto
*/
void processMove(MoveRequestDTO dto);
/**
* 撤销移库
*
* @param moveRecordId 移库记录ID
*/
void rollbackMove(Long moveRecordId);
}

View File

@@ -66,4 +66,14 @@ public interface IRkBillService
* @return 结果
*/
public int deleteRkBillById(Long id);
/**
* 新增出库
*/
int insertOutBillAndDetail(RkBillCreateDTO dto);
/**
* 还料入库
*/
int insertReturnBillAndDetail(RkBillCreateDTO dto);
}

View File

@@ -76,4 +76,18 @@ public interface IRkRecordService
* 批量将指定入库记录完成入库
*/
int finishIn(List<Long> ids);
/**
* 撤销出库(已出库 → 预出库)
*/
int rollbackOut(List<Long> recordIds);
/**
* 批量完成预出库 → 已出库
*/
int finishOut(List<Long> recordIds);
/**
* 删除预出库记录(仅限 exec_status = 0
*/
int deletePreOutRecords(Long[] ids);
}

View File

@@ -167,13 +167,12 @@ public class GysJhServiceImpl implements IGysJhService
List<GysJh> list = gysJhMapper.getBySapNo(sapNo);
AjaxResult result = AjaxResult.success(list);
if (list == null || list.isEmpty()) {
return result;
return AjaxResult.success(list);
}
// 找出已入库(status=1)的物料号,去重、过滤空值
AjaxResult result = AjaxResult.success(list);
List<String> inStockWlNos = list.stream()
.filter(x -> x != null && "1".equals(String.valueOf(x.getStatus()).trim()))
.map(GysJh::getWlNo)
@@ -185,18 +184,14 @@ public class GysJhServiceImpl implements IGysJhService
if (!inStockWlNos.isEmpty()) {
result.put("warn", true);
result.put("inStockWlNos", inStockWlNos);
// 组装提示文案(可根据前端展示需要调整长度)
String msg = "该 SAP 订单号下,以下物料号已入库过,请注意:"
+ String.join("", inStockWlNos);
result.put("msg", msg);
result.put("msg", "该 SAP 订单号下,以下物料号已全部入库,请注意:"
+ String.join("", inStockWlNos));
}
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int importByMapping(MultipartFile file, List<ExcelFieldMapping> mapping) throws Exception {

View File

@@ -114,18 +114,13 @@ public class MoveRecordServiceImpl implements IMoveRecordService
/**
* 处理库存移库操作
*
* @param dto 移库请求参数包含原库存ID、移库目标列表等
*/
/**
* 处理库存移库操作
*
* @param dto 移库请求参数包含原库存ID、移库目标列表等
* @param dto 移库请求参数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void processMove(MoveRequestDTO dto) {
// ========= 0. 基校验 =========
/* ====================== 0. 基校验 ====================== */
if (dto == null || dto.getFromRkId() == null) {
throw new ServiceException("原库存ID不能为空");
}
@@ -133,18 +128,18 @@ public class MoveRecordServiceImpl implements IMoveRecordService
throw new ServiceException("目标位置列表不能为空");
}
// ========= 1. 查询原库存 =========
/* ====================== 1. 查询原库存 ====================== */
RkInfo original = rkInfoMapper.selectRkInfoById(dto.getFromRkId());
if (original == null || "1".equals(original.getIsDelete())) {
throw new ServiceException("原库存不存在或已删除");
}
BigDecimal originQty = original.getRealQty();
if (originQty == null) {
throw new ServiceException("原库存数量为空");
if (originQty == null || originQty.compareTo(BigDecimal.ZERO) <= 0) {
throw new ServiceException("原库存数量异常");
}
// ========= 2. 计算移库总量 =========
/* ====================== 2. 计算移库总量 ====================== */
BigDecimal moveTotalQty = dto.getTargets().stream()
.map(MoveTargetItem::getRealQty)
.reduce(BigDecimal.ZERO, BigDecimal::add);
@@ -153,146 +148,189 @@ public class MoveRecordServiceImpl implements IMoveRecordService
throw new ServiceException("移库数量不能大于原库存数量");
}
Long userId = SecurityUtils.getUserId();
String username = userId.toString();
String userId = String.valueOf(SecurityUtils.getUserId());
Date now = DateUtils.getNowDate();
// ========= 3. 快照(只用于拷贝字段) =========
/* ====================== 3. 原库存快照 ====================== */
RkInfo snapshot = new RkInfo();
BeanUtils.copyProperties(original, snapshot);
// ========= 4. 查询原 rk_record用于同步更新 =========
RkRecord originRecord = rkRecordMapper.selectRkRecordById(original.getId());
if (originRecord == null) {
throw new ServiceException("未找到对应的出入库记录");
}
// ================== 一、全量移库 ==================
/* ====================== 4. 全量移库(不拆分) ====================== */
if (dto.getTargets().size() == 1 && moveTotalQty.compareTo(originQty) == 0) {
MoveTargetItem target = dto.getTargets().get(0);
// 1⃣ rk_info更新库位
// 4.1 更新原库存(仅换库位
original.setCangku(target.getToCangku());
original.setPcode(target.getToPcode());
original.setTrayCode(target.getToTrayCode());
original.setHasMoved("1");
original.setUpdateBy(username);
original.setUpdateBy(userId);
original.setUpdateTime(now);
rkInfoMapper.updateRkInfo(original);
// 2⃣ rk_record同步更新库位
originRecord.setCangku(target.getToCangku());
originRecord.setPcode(target.getToPcode());
originRecord.setTrayCode(target.getToTrayCode());
originRecord.setHasMoved("1");
originRecord.setUpdateBy(username);
originRecord.setUpdateTime(now);
rkRecordMapper.updateRkRecord(originRecord);
// 4.2 移库流水(全量移库,无新库存)
MoveRecord record = new MoveRecord();
record.setRkInfoId(snapshot.getId());
record.setNewRkInfoId(null); // ✅ 关键:全量移库没有新库存
record.setEntityId(snapshot.getEntityId());
record.setRealQty(originQty);
// 3⃣ move_record记录移库流水绑定原 rk_info.id
moveRecordMapper.insertMoveRecord(
createMoveRecord(original, target, dto)
);
record.setFromCangku(snapshot.getCangku());
record.setFromPcode(snapshot.getPcode());
record.setFromTrayCode(snapshot.getTrayCode());
record.setToCangku(target.getToCangku());
record.setToPcode(target.getToPcode());
record.setToTrayCode(target.getToTrayCode());
record.setMoveReason(dto.getMoveReason());
record.setMovedBy(userId);
record.setMovedAt(now);
record.setCreateBy(userId);
record.setCreateTime(now);
record.setUpdateBy(userId);
record.setUpdateTime(now);
record.setIsDelete("0");
moveRecordMapper.insertMoveRecord(record);
return;
}
// ================== 二、部分移库 ==================
/* ====================== 5. 部分移库(拆分库存) ====================== */
// 1⃣ 原 rk_info 扣减数量
// 5.1 原库存扣减数量
original.setRealQty(originQty.subtract(moveTotalQty));
original.setHasMoved("1");
original.setUpdateBy(username);
original.setUpdateBy(userId);
original.setUpdateTime(now);
rkInfoMapper.updateRkInfo(original);
// 2⃣ 原 rk_record 扣减数量
originRecord.setRealQty(
originRecord.getRealQty().subtract(moveTotalQty)
);
originRecord.setHasMoved("1");
originRecord.setUpdateBy(username);
originRecord.setUpdateTime(now);
rkRecordMapper.updateRkRecord(originRecord);
// 3⃣ 新增目标库存 + 新增目标记录
// 5.2 生成新库存 + 移库流水
for (MoveTargetItem target : dto.getTargets()) {
// —— 新 rk_info
/* ---------- 新库存 ---------- */
RkInfo newInfo = new RkInfo();
BeanUtils.copyProperties(snapshot, newInfo, "id");
newInfo.setCangku(target.getToCangku());
newInfo.setPcode(target.getToPcode());
newInfo.setTrayCode(target.getToTrayCode());
newInfo.setRealQty(target.getRealQty());
newInfo.setHasMoved("1");
newInfo.setCreateBy(username);
newInfo.setCreateBy(userId);
newInfo.setCreateTime(now);
newInfo.setUpdateBy(username);
newInfo.setUpdateBy(userId);
newInfo.setUpdateTime(now);
rkInfoMapper.insertRkInfo(newInfo);
// —— 新 rk_record
RkRecord newRecord = new RkRecord();
BeanUtils.copyProperties(originRecord, newRecord, "id");
newRecord.setCangku(target.getToCangku());
newRecord.setPcode(target.getToPcode());
newRecord.setTrayCode(target.getToTrayCode());
newRecord.setRealQty(target.getRealQty());
newRecord.setHasMoved("1");
newRecord.setCreateBy(username);
newRecord.setCreateTime(now);
newRecord.setUpdateBy(username);
newRecord.setUpdateTime(now);
rkRecordMapper.insertRkRecord(newRecord);
/* ---------- 移库流水(重点) ---------- */
MoveRecord record = new MoveRecord();
record.setRkInfoId(snapshot.getId()); // 原库存
record.setNewRkInfoId(newInfo.getId()); // ✅ 关键新库存ID
record.setEntityId(snapshot.getEntityId());
record.setRealQty(target.getRealQty());
// —— move_record绑定新生成的 rk_info.id
moveRecordMapper.insertMoveRecord(
createMoveRecord(newInfo, target, dto)
);
record.setFromCangku(snapshot.getCangku());
record.setFromPcode(snapshot.getPcode());
record.setFromTrayCode(snapshot.getTrayCode());
record.setToCangku(target.getToCangku());
record.setToPcode(target.getToPcode());
record.setToTrayCode(target.getToTrayCode());
record.setMoveReason(dto.getMoveReason());
record.setMovedBy(userId);
record.setMovedAt(now);
record.setCreateBy(userId);
record.setCreateTime(now);
record.setUpdateBy(userId);
record.setUpdateTime(now);
record.setIsDelete("0");
moveRecordMapper.insertMoveRecord(record);
}
}
/**
* 构建移库记录对象
*
* @param info 原库存记录
* @param target 目标位置数据
* @param dto 请求参数
* @return 构建后的移库记录
* 撤销移库
*/
private MoveRecord createMoveRecord(RkInfo info,
MoveTargetItem target,
MoveRequestDTO dto) {
@Override
@Transactional(rollbackFor = Exception.class)
public void rollbackMove(Long moveRecordId) {
Long userId = SecurityUtils.getUserId();
if (moveRecordId == null) {
throw new ServiceException("移库记录ID不能为空");
}
/* ====================== 1. 查询移库记录 ====================== */
MoveRecord record = moveRecordMapper.selectMoveRecordById(moveRecordId);
if (record == null || "1".equals(record.getIsDelete())) {
throw new ServiceException("移库记录不存在或已撤销");
}
Long originRkInfoId = record.getRkInfoId();
Long newRkInfoId = record.getNewRkInfoId();
/* ====================== 2. 查询原库存 ====================== */
RkInfo origin = rkInfoMapper.selectRkInfoById(originRkInfoId);
if (origin == null || "1".equals(origin.getIsDelete())) {
throw new ServiceException("原库存不存在或已删除,无法撤销");
}
String userId = String.valueOf(SecurityUtils.getUserId());
Date now = DateUtils.getNowDate();
MoveRecord record = new MoveRecord();
/* ====================== 3. 分情况处理 ====================== */
// ★ 关键:绑定 rk_info.id
record.setRkInfoId(info.getId());
// ---------- 情况一:全量移库 ----------
if (newRkInfoId == null) {
record.setEntityId(info.getEntityId());
record.setFromCangku(info.getCangku());
record.setFromPcode(info.getPcode());
record.setFromTrayCode(info.getTrayCode());
record.setToCangku(target.getToCangku());
record.setToPcode(target.getToPcode());
record.setToTrayCode(target.getToTrayCode());
record.setMoveReason(dto.getMoveReason());
record.setMovedBy(userId.toString());
record.setMovedAt(now);
// 回滚库位
origin.setCangku(record.getFromCangku());
origin.setPcode(record.getFromPcode());
origin.setTrayCode(record.getFromTrayCode());
origin.setHasMoved("0");
origin.setUpdateBy(userId);
origin.setUpdateTime(now);
record.setIsDelete("0");
record.setCreateBy(userId.toString());
record.setCreateTime(now);
record.setUpdateBy(userId.toString());
rkInfoMapper.updateRkInfo(origin);
}
// ---------- 情况二:部分移库 ----------
else {
/* 3.1 原库存回补数量 */
BigDecimal qty = record.getRealQty();
if (qty == null || qty.compareTo(BigDecimal.ZERO) <= 0) {
throw new ServiceException("移库数量异常,无法撤销");
}
origin.setRealQty(origin.getRealQty().add(qty));
origin.setHasMoved("0");
origin.setUpdateBy(userId);
origin.setUpdateTime(now);
origin.setHasMoved("0");
rkInfoMapper.updateRkInfo(origin);
/* 3.2 删除移库生成的新库存 */
RkInfo newInfo = rkInfoMapper.selectRkInfoById(newRkInfoId);
if (newInfo != null && !"1".equals(newInfo.getIsDelete())) {
newInfo.setIsDelete("1");
newInfo.setUpdateBy(userId);
newInfo.setUpdateTime(now);
rkInfoMapper.updateRkInfo(newInfo);
}
}
/* ====================== 4. 标记移库记录已撤销 ====================== */
record.setIsDelete("1");
record.setUpdateBy(userId);
record.setUpdateTime(now);
return record;
moveRecordMapper.updateMoveRecord(record);
}
}

View File

@@ -5,6 +5,7 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
import com.zg.common.exception.ServiceException;
import com.zg.common.utils.DateUtils;
import com.zg.common.utils.SecurityUtils;
import com.zg.common.utils.StringUtils;
@@ -100,7 +101,7 @@ public class RkBillServiceImpl implements IRkBillService
Date now = DateUtils.getNowDate();
String userId = String.valueOf(SecurityUtils.getUserId());
// ================== 1. 主单 rk_bill ==================
/* ================== 1. 主单 rk_bill ================== */
RkBill bill = new RkBill();
if (dto.getRkBill() != null) {
BeanUtils.copyProperties(dto.getRkBill(), bill);
@@ -108,7 +109,7 @@ public class RkBillServiceImpl implements IRkBillService
bill.setBillNo(billNo);
bill.setBizType("0"); // 入库
bill.setOperationTime(now);
bill.setOperationTime(dto.getRkBill().getOperationTime());
bill.setCreateTime(now);
bill.setCreateBy(userId);
bill.setIsDelete("0");
@@ -118,17 +119,20 @@ public class RkBillServiceImpl implements IRkBillService
bill.setOperator(Integer.valueOf(userId));
}
// execStatus默认已完成
// execStatus默认已完成(预入库 / 直接入库统一处理)
if (StringUtils.isBlank(bill.getExecStatus())) {
bill.setExecStatus("1");
}
rkBillMapper.insertRkBill(bill);
// ================== 2. 明细 + 事件 + 供应计划 ==================
/* ================== 2. 明细 + 事件 + 供应计划 ================== */
for (RkInfo info : dto.getRkInfoList()) {
// ===== ① 根据 pcode 查询 pcde_detail获取 encoded_id =====
/* ====== ★ 0. 入库前:供应计划数量校验(核心新增) ====== */
checkGysJhQtyBeforeInStock(info);
/* ====== ① 根据 pcode 查询 pcde_detail获取 encoded_id ====== */
if (StringUtils.isNotBlank(info.getPcode())) {
PcdeDetail pcde = pcdeDetailMapper.selectByPcode(info.getPcode());
@@ -139,7 +143,7 @@ public class RkBillServiceImpl implements IRkBillService
info.setPcodeId(pcde.getEncodedId());
}
// ---------- rk_info ----------
/* ---------- rk_info ---------- */
info.setBillNo(billNo);
info.setBizType(bill.getBizType());
info.setOperationType(bill.getOperationType());
@@ -159,20 +163,56 @@ public class RkBillServiceImpl implements IRkBillService
rkInfoMapper.insertRkInfo(info);
// ---------- rk_record ----------
/* ---------- rk_record ---------- */
RkRecord record = buildInRkRecord(bill, info, now);
record.setExecStatus(bill.getExecStatus());
record.setPcodeId(info.getPcodeId());
rkRecordMapper.insertRkRecord(record);
// ---------- 供应计划 ----------
/* ---------- 供应计划 ---------- */
handleGysJhAfterInStock(info);
}
return 1;
}
/**
* 入库前校验:入库数量不能超过供应计划数量
*/
private void checkGysJhQtyBeforeInStock(RkInfo info) {
if (info.getGysJhId() == null || info.getRealQty() == null) {
return;
}
GysJh gysJh = gysJhMapper.selectGysJhById(info.getGysJhId());
if (gysJh == null) {
throw new RuntimeException("供应计划不存在ID" + info.getGysJhId());
}
BigDecimal planQty = gysJh.getJhQty();
if (planQty == null) {
return;
}
BigDecimal alreadyInQty = gysJh.getRealQty();
if (alreadyInQty == null) {
alreadyInQty = BigDecimal.ZERO;
}
BigDecimal afterQty = alreadyInQty.add(info.getRealQty());
if (afterQty.compareTo(planQty) > 0) {
throw new RuntimeException(
"入库数量超出供应计划数量,物料号:" + info.getWlNo()
+ ",计划数量:" + planQty
+ ",已入库:" + alreadyInQty
+ ",本次入库:" + info.getRealQty()
);
}
}
private void handleGysJhAfterInStock(RkInfo info) {
// 1. 未关联供应计划或无实际入库数量,直接跳过
@@ -180,29 +220,36 @@ public class RkBillServiceImpl implements IRkBillService
return;
}
// 2. 扣减供应计划数量jh_qty = jh_qty - realQty
gysJhMapper.decreaseJhQtyById(
info.getGysJhId(),
info.getRealQty()
// 2. 累加 实际入库数量real_qty = real_qty + 本次入库数量
gysJhMapper.increaseRealQtyById(
info.getRealQty(),
info.getGysJhId()
);
// 3. 查询最新供应计划数据(用于判断剩余数量)
// 3. 查询最新供应计划数据
GysJh gysJh = gysJhMapper.selectGysJhById(info.getGysJhId());
if (gysJh == null) {
throw new RuntimeException("供应计划不存在ID" + info.getGysJhId());
}
// 4. 根据剩余数量判断状态
BigDecimal remainQty = gysJh.getJhQty();
BigDecimal planQty = gysJh.getJhQty(); // 计划交货数量
BigDecimal realQty = gysJh.getRealQty(); // 实际入库数量
if (realQty == null) {
realQty = BigDecimal.ZERO;
}
// 4. 状态判定
String status;
if (remainQty == null || remainQty.compareTo(BigDecimal.ZERO) <= 0) {
status = "1"; // 全部入库
if (realQty.compareTo(BigDecimal.ZERO) == 0) {
status = "0"; // 未到货
} else if (planQty != null && realQty.compareTo(planQty) >= 0) {
status = "1"; // 已入库
} else {
status = "2"; // 部分入库
}
// 5. 更新供应计划状态(⚠ 不使用 Map直接参数
// 5. 更新供应计划状态
gysJhMapper.updateStatusById(gysJh.getId(), status);
}
@@ -294,8 +341,8 @@ public class RkBillServiceImpl implements IRkBillService
info.setCangku(bill.getCangku());
// ---------- 操作信息 ----------
info.setOperationTime(now);
info.setOperator(bill.getOperator());
info.setOperationTime(info.getOperationTime());
info.setOperator(info.getOperator());
// ---------- 执行状态 ----------
info.setExecStatus(execStatus);
@@ -359,11 +406,50 @@ public class RkBillServiceImpl implements IRkBillService
* @return 结果
*/
@Override
public int deleteRkBillByIds(Long[] ids)
{
return rkBillMapper.deleteRkBillByIds(ids);
}
@Transactional(rollbackFor = Exception.class)
public int deleteRkBillByIds(Long[] ids) {
if (ids == null || ids.length == 0) {
return 0;
}
int rows = 0;
for (Long id : ids) {
// 1. 查询单据
RkBill bill = rkBillMapper.selectRkBillById(id);
if (bill == null) {
continue;
}
String billNo = bill.getBillNo();
if (billNo == null || billNo.isEmpty()) {
continue;
}
// 2. 校验 rk_record 是否存在
int recordCount = rkRecordMapper.countByBillNo(billNo);
if (recordCount > 0) {
throw new ServiceException(
"单据号【" + billNo + "】仍存在出入库记录,不允许删除"
);
}
// 3. 校验 rk_info 是否存在
int infoCount = rkInfoMapper.countByBillNo(billNo);
if (infoCount > 0) {
throw new ServiceException(
"单据号【" + billNo + "】仍存在库存明细,不允许删除"
);
}
// 4. 真正删除 rk_bill
rows += rkBillMapper.deleteRkBillById(id);
}
return rows;
}
/**
* 删除库存单据信息
*
@@ -375,4 +461,286 @@ public class RkBillServiceImpl implements IRkBillService
{
return rkBillMapper.deleteRkBillById(id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int insertOutBillAndDetail(RkBillCreateDTO dto) {
if (dto == null || dto.getRkInfoList() == null || dto.getRkInfoList().isEmpty()) {
throw new RuntimeException("出库明细不能为空");
}
String billNo = BillNoUtil.generateTodayBillNo("CK", null);
Date now = DateUtils.getNowDate();
String userId = String.valueOf(SecurityUtils.getUserId());
// ================== 1. 主单 rk_bill ==================
RkBill bill = new RkBill();
if (dto.getRkBill() != null) {
BeanUtils.copyProperties(dto.getRkBill(), bill);
}
if (StringUtils.isBlank(bill.getBizType())) {
throw new RuntimeException("bizType 不能为空");
}
bill.setBillNo(billNo);
bill.setOperationTime(now);
bill.setCreateTime(now);
bill.setCreateBy(userId);
bill.setIsDelete("0");
if (bill.getOperator() == null) {
bill.setOperator(Integer.valueOf(userId));
}
// exec_status0=预出库1=已出库
if (StringUtils.isBlank(bill.getExecStatus())) {
bill.setExecStatus("1");
}
boolean isPreOut = "0".equals(bill.getExecStatus());
// ===== 借料出库字段 =====
if ("2".equals(bill.getBizType())) {
bill.setBorrowTime(bill.getBorrowTime() != null ? bill.getBorrowTime() : now);
bill.setXmNoCk(bill.getXmNoCk());
bill.setXmMsCk(bill.getXmMsCk());
}
rkBillMapper.insertRkBill(bill);
// ================== 2. 出库处理 ==================
for (RkInfo outInfo : dto.getRkInfoList()) {
if (outInfo.getId() == null) {
throw new RuntimeException("出库必须指定库存明细ID");
}
if (outInfo.getRealQty() == null) {
throw new RuntimeException("出库数量不能为空");
}
RkInfo dbInfo = rkInfoMapper.selectRkInfoById(outInfo.getId());
if (dbInfo == null) {
throw new RuntimeException("库存不存在ID" + outInfo.getId());
}
BigDecimal remainQty = dbInfo.getRealQty();
BigDecimal outQty = outInfo.getRealQty();
if (!isPreOut && remainQty.compareTo(outQty) < 0) {
throw new RuntimeException("出库数量不能大于库存数量库存ID" + dbInfo.getId());
}
// ===== ① 非预出库才扣库存 =====
if (!isPreOut) {
BigDecimal newQty = remainQty.subtract(outQty);
rkInfoMapper.updateRealQtyById(dbInfo.getId(), newQty);
rkInfoMapper.updateIsChukuById(
dbInfo.getId(),
newQty.compareTo(BigDecimal.ZERO) == 0 ? "1" : "0"
);
}
// ===== ② 出库记录(重点:传 outInfo=====
RkRecord record = buildOutRkRecord(bill, dbInfo, outInfo, outQty, now);
record.setExecStatus(isPreOut ? "0" : "1");
rkRecordMapper.insertRkRecord(record);
}
return 1;
}
private RkRecord buildOutRkRecord(
RkBill bill,
RkInfo dbInfo,
RkInfo outInfo,
BigDecimal outQty,
Date now) {
RkRecord record = new RkRecord();
// ① 库存快照
BeanUtils.copyProperties(dbInfo, record);
record.setId(null);
// ② 本次出库数量
record.setRealQty(outQty);
// ③ 主单上下文
record.setBillNo(bill.getBillNo());
record.setBizType(bill.getBizType());
record.setOperationType(bill.getOperationType());
record.setOperationTime(bill.getOperationTime());
record.setOperator(bill.getOperator());
record.setCangku(dbInfo.getCangku());
record.setTeamCode(dbInfo.getTeamCode());
// ===== ★ 关键:备注来自【出库明细 outInfo】=====
record.setRemark(outInfo.getRemark());
// ④ 借料专属字段
if ("2".equals(bill.getBizType())) {
record.setIsBorrowed("1");
record.setBorrowTime(bill.getBorrowTime());
record.setReturnTime(bill.getReturnTime());
record.setXmNoCk(bill.getXmNoCk());
record.setXmMsCk(bill.getXmMsCk());
} else {
record.setIsBorrowed("0");
}
// ⑤ 状态字段
record.setIsChuku("1");
record.setHasMoved("0");
record.setIsDelete("0");
record.setExecStatus(bill.getExecStatus());
// ⑥ 审计字段
record.setCreateTime(now);
record.setCreateBy(bill.getCreateBy());
// ⑦ 关联库存
record.setRkInfoId(dbInfo.getId());
record.setRdid(dbInfo.getId());
return record;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int insertReturnBillAndDetail(RkBillCreateDTO dto) {
if (dto == null || dto.getRkInfoList() == null || dto.getRkInfoList().isEmpty()) {
throw new RuntimeException("还料明细不能为空");
}
String billNo = BillNoUtil.generateTodayBillNo("HL", null);
Date now = DateUtils.getNowDate();
String userId = String.valueOf(SecurityUtils.getUserId());
/* ================== 1. 主单 rk_bill ================== */
RkBill bill = new RkBill();
if (dto.getRkBill() != null) {
BeanUtils.copyProperties(dto.getRkBill(), bill);
}
bill.setBillNo(billNo);
bill.setBizType("3"); // 3 = 还料入库
bill.setOperationTime(now);
bill.setCreateTime(now);
bill.setCreateBy(userId);
bill.setIsDelete("0");
if (bill.getOperator() == null) {
bill.setOperator(Integer.valueOf(userId));
}
// 还料入库默认已完成
if (StringUtils.isBlank(bill.getExecStatus())) {
bill.setExecStatus("1");
}
rkBillMapper.insertRkBill(bill);
/* ================== 2. 明细 + 事件 ================== */
for (RkInfo info : dto.getRkInfoList()) {
// ===== ① 基础校验 =====
if (info.getRealQty() == null || info.getRealQty().compareTo(BigDecimal.ZERO) <= 0) {
throw new RuntimeException("还料数量必须大于 0");
}
// 必须关联原借料出库库存
if (info.getRdid() == null) {
throw new RuntimeException("还料必须指定原借料库存ID(rdid)");
}
RkInfo borrowInfo = rkInfoMapper.selectRkInfoById(info.getRdid());
if (borrowInfo == null) {
throw new RuntimeException("原借料库存不存在ID" + info.getRdid());
}
/* ---------- rk_info新增库存 ---------- */
info.setId(null);
info.setBillNo(billNo);
info.setBizType("3"); // 还料入库
info.setOperationType(bill.getOperationType());
info.setOperationTime(bill.getOperationTime());
info.setOperator(bill.getOperator());
// 关键:库存是“新增”,不是回补原库存
info.setIsChuku("0");
info.setIsBorrowed("0");
info.setHasMoved("0");
info.setExecStatus(bill.getExecStatus());
// 仓库 / 物料 / 项目信息,默认继承原借料库存
info.setCangku(borrowInfo.getCangku());
info.setWlType(borrowInfo.getWlType());
info.setXmNo(borrowInfo.getXmNo());
info.setXmMs(borrowInfo.getXmMs());
info.setWlNo(borrowInfo.getWlNo());
info.setWlMs(borrowInfo.getWlMs());
info.setGysNo(borrowInfo.getGysNo());
info.setGysMc(borrowInfo.getGysMc());
info.setDw(borrowInfo.getDw());
info.setCreateTime(now);
info.setCreateBy(userId);
info.setIsDelete("0");
rkInfoMapper.insertRkInfo(info);
/* ---------- rk_record还料入库事件 ---------- */
RkRecord record = buildReturnRkRecord(bill, info, now);
rkRecordMapper.insertRkRecord(record);
/* ---------- 原借料记录标记“已归还” ---------- */
rkInfoMapper.updateBorrowReturn(
borrowInfo.getId(),
"2", // is_borrowed = 2 已归还
now
);
}
return 1;
}
private RkRecord buildReturnRkRecord(RkBill bill, RkInfo info, Date now) {
RkRecord record = new RkRecord();
// ① 明细快照
BeanUtils.copyProperties(info, record);
record.setId(null);
// ② 主单上下文
record.setBillNo(bill.getBillNo());
record.setBizType("3"); // 还料入库
record.setOperationType(bill.getOperationType());
record.setOperationTime(bill.getOperationTime());
record.setOperator(bill.getOperator());
record.setCangku(info.getCangku());
record.setTeamCode(info.getTeamCode());
// ③ 状态
record.setIsChuku("0");
record.setIsBorrowed("2"); // 已归还
record.setHasMoved("0");
record.setExecStatus(bill.getExecStatus());
record.setIsDelete("0");
// ④ 审计
record.setCreateTime(now);
record.setCreateBy(bill.getCreateBy());
// ⑤ 关联
record.setRkInfoId(info.getId());
record.setRdid(info.getRdid()); // 指向原借料库存
return record;
}
}

View File

@@ -1,13 +1,15 @@
package com.zg.project.wisdom.service.impl;
import java.util.List;
import java.util.Objects;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import com.zg.common.exception.ServiceException;
import com.zg.common.utils.DateUtils;
import com.zg.common.utils.StringUtils;
import com.zg.project.wisdom.domain.GysJh;
import com.zg.project.wisdom.domain.RkInfo;
import com.zg.project.wisdom.mapper.GysJhMapper;
import com.zg.project.wisdom.mapper.RkBillMapper;
import com.zg.project.wisdom.mapper.RkInfoMapper;
import org.springframework.beans.BeanUtils;
@@ -36,6 +38,9 @@ public class RkRecordServiceImpl implements IRkRecordService
@Autowired
private RkBillMapper rkBillMapper;
@Autowired
private GysJhMapper gysJhMapper;
/**
* 查询出入库记录
*
@@ -81,44 +86,179 @@ public class RkRecordServiceImpl implements IRkRecordService
/**
* 修改出入库记录
*
*
* 业务约定:
* 1. rk_record.real_qty —— 单条出入库记录的最终数量(绝对值)
* 2. rk_info.real_qty —— 当前库存数量
* 3. gys_jh.real_qty —— 供应计划累计已入库数量
*
* 入库bizType = 0
* - 使用【新值 - 旧值】差量修正供应计划 real_qty
* - 严禁累计入库数量超过计划数量 jh_qty
* - 自动维护供应计划状态:
* real_qty == 0 → 未到货
* 0 < real_qty < jh_qty→ 部分到货
* real_qty == jh_qty → 已入库
*
* 出库bizType = 1
* - 出库数量不得超过库存
* - 出库数量变化联动库存 real_qty
* - 库存为 0 时 is_chuku = 1否则 = 0
*
* @param rkRecord 出入库记录
* @return 结果
* @return 影响行数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int updateRkRecord(RkRecord rkRecord)
{
// 1. 更新时间
public int updateRkRecord(RkRecord rkRecord) {
/* ====================== 1. 基础校验 ====================== */
if (rkRecord == null || rkRecord.getId() == null) {
throw new RuntimeException("出入库记录ID不能为空");
}
rkRecord.setUpdateTime(DateUtils.getNowDate());
// 2. 更新 rk_record
/* ====================== 2. 查询原记录(唯一可信来源) ====================== */
RkRecord oldRecord = rkRecordMapper.selectRkRecordById(rkRecord.getId());
if (oldRecord == null) {
throw new RuntimeException("原出入库记录不存在");
}
/* ====================== 3. 获取并校验库存ID必须来自 oldRecord ====================== */
Long rkInfoId = oldRecord.getRkInfoId();
if (rkInfoId == null) {
throw new RuntimeException("出入库记录未关联库存,禁止修改");
}
RkInfo info = rkInfoMapper.selectRkInfoById(rkInfoId);
if (info == null) {
throw new RuntimeException("关联的库存记录不存在");
}
String bizType = rkRecord.getBizType();
/* =========================================================
* 【A】入库记录修改bizType = 0
* ========================================================= */
if ("0".equals(bizType)) {
BigDecimal oldInQty = oldRecord.getRealQty() == null
? BigDecimal.ZERO
: oldRecord.getRealQty();
BigDecimal newInQty = rkRecord.getRealQty() == null
? BigDecimal.ZERO
: rkRecord.getRealQty();
BigDecimal diff = newInQty.subtract(oldInQty);
/* ---------- 供应计划处理 ---------- */
Long gysJhId = oldRecord.getGysJhId();
if (gysJhId != null) {
GysJh gysJh = gysJhMapper.selectGysJhById(gysJhId);
if (gysJh == null) {
throw new RuntimeException("关联的供应计划不存在ID" + gysJhId);
}
BigDecimal planQty = gysJh.getJhQty();
BigDecimal planRealQty = gysJh.getRealQty() == null
? BigDecimal.ZERO
: gysJh.getRealQty();
BigDecimal newPlanRealQty = planRealQty.add(diff);
if (planQty != null && newPlanRealQty.compareTo(planQty) > 0) {
throw new RuntimeException("调整后已入库数量不能大于供应计划数量");
}
gysJh.setRealQty(newPlanRealQty);
if (newPlanRealQty.compareTo(BigDecimal.ZERO) == 0) {
gysJh.setStatus("0"); // 未到货
} else if (planQty != null && newPlanRealQty.compareTo(planQty) >= 0) {
gysJh.setStatus("1"); // 已入库
} else {
gysJh.setStatus("2"); // 部分入库
}
gysJh.setWlNo(rkRecord.getWlNo());
gysJh.setWlMs(rkRecord.getWlMs());
gysJh.setXmNo(rkRecord.getXmNo());
gysJh.setXmMs(rkRecord.getXmMs());
gysJh.setGysNo(rkRecord.getGysNo());
gysJh.setGysMc(rkRecord.getGysMc());
gysJh.setDw(rkRecord.getDw());
gysJh.setHtDj(rkRecord.getHtDj());
gysJh.setUpdateTime(DateUtils.getNowDate());
gysJhMapper.updateGysJh(gysJh);
}
/* ---------- 同步库存(明确字段,禁止 BeanUtils ---------- */
info.setRealQty(newInQty);
info.setWlNo(rkRecord.getWlNo());
info.setWlMs(rkRecord.getWlMs());
info.setXmNo(rkRecord.getXmNo());
info.setXmMs(rkRecord.getXmMs());
info.setGysNo(rkRecord.getGysNo());
info.setGysMc(rkRecord.getGysMc());
info.setDw(rkRecord.getDw());
info.setHtDj(rkRecord.getHtDj());
info.setIsChuku("0");
}
/* =========================================================
* 【B】出库记录修改bizType = 1
* ========================================================= */
if ("1".equals(bizType)) {
BigDecimal oldOutQty = oldRecord.getRealQty();
BigDecimal newOutQty = rkRecord.getRealQty();
BigDecimal stockQty = info.getRealQty();
if (oldOutQty == null || newOutQty == null || stockQty == null) {
throw new RuntimeException("数量数据异常,无法修改出库记录");
}
BigDecimal diff = newOutQty.subtract(oldOutQty);
BigDecimal newStockQty = stockQty.subtract(diff);
if (newStockQty.compareTo(BigDecimal.ZERO) < 0) {
throw new RuntimeException("库存不足,无法修改出库数量");
}
info.setRealQty(newStockQty);
info.setIsChuku(newStockQty.compareTo(BigDecimal.ZERO) == 0 ? "1" : "0");
info.setWlNo(rkRecord.getWlNo());
info.setWlMs(rkRecord.getWlMs());
info.setXmNo(rkRecord.getXmNo());
info.setXmMs(rkRecord.getXmMs());
info.setGysNo(rkRecord.getGysNo());
info.setGysMc(rkRecord.getGysMc());
info.setDw(rkRecord.getDw());
info.setHtDj(rkRecord.getHtDj());
}
/* ====================== 4. 更新库存表 ====================== */
info.setUpdateTime(DateUtils.getNowDate());
int infoRows = rkInfoMapper.updateRkInfo(info);
if (infoRows <= 0) {
throw new RuntimeException("同步更新库存表失败");
}
/* ====================== 5. 最后更新 rk_record ====================== */
rkRecord.setRkInfoId(rkInfoId); // 强制回填,防止被置空
int rows = rkRecordMapper.updateRkRecord(rkRecord);
if (rows <= 0) {
throw new RuntimeException("更新出入库记录失败");
}
// 3. 同步更新 rk_info
Long rkInfoId = rkRecord.getRkInfoId();
if (rkInfoId == null) {
throw new RuntimeException("rkInfoId 为空,无法同步更新库存表");
}
// 4. 将 record 中的字段映射到 info
RkInfo rkInfo = new RkInfo();
BeanUtils.copyProperties(rkRecord, rkInfo);
// ⚠️ 关键:主键必须用 rk_info.id
rkInfo.setId(rkInfoId);
rkInfo.setUpdateTime(DateUtils.getNowDate());
int infoRows = rkInfoMapper.updateRkInfo(rkInfo);
if (infoRows <= 0) {
throw new RuntimeException("同步更新库存表失败");
}
return rows;
}
/**
* 批量删除出入库记录
*
@@ -126,11 +266,92 @@ public class RkRecordServiceImpl implements IRkRecordService
* @return 结果
*/
@Override
public int deleteRkRecordByIds(Long[] ids)
{
return rkRecordMapper.deleteRkRecordByIds(ids);
@Transactional(rollbackFor = Exception.class)
public int deleteRkRecordByIds(Long[] ids) {
if (ids == null || ids.length == 0) {
return 0;
}
// ================== 1. 查询 record ==================
List<RkRecord> records = rkRecordMapper.selectRkRecordByIds(Arrays.asList(ids));
if (records == null || records.isEmpty()) {
return 0;
}
// ================== 2. 校验 record 必须全部是预入库 ==================
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 ==================
List<Long> rkInfoIds = records.stream()
.map(RkRecord::getRkInfoId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
// ================== 5. 校验 info 也必须是预入库 ==================
if (!rkInfoIds.isEmpty()) {
int invalidCount = rkInfoMapper.countNotPreByIds(rkInfoIds);
if (invalidCount > 0) {
throw new ServiceException("存在非预入库的库存明细,不允许删除");
}
// 先删 info
rkInfoMapper.deleteRkInfoByIds(rkInfoIds.toArray(new Long[0]));
}
// ================== 6. 删除 record ==================
int deleteCount = rkRecordMapper.deleteRkRecordByIds(ids);
// ================== 7. 判断是否需要更新 bill 状态 ==================
Set<String> billNos = records.stream()
.map(RkRecord::getBillNo)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
for (String billNo : billNos) {
int remainPreCount = rkRecordMapper.countPreByBillNo(billNo);
if (remainPreCount == 0) {
rkBillMapper.updateExecStatus(billNo, "1");
}
}
return deleteCount;
}
/**
* 删除出入库记录信息
*
@@ -151,26 +372,235 @@ public class RkRecordServiceImpl implements IRkRecordService
throw new ServiceException("撤销记录ID不能为空");
}
// 1⃣ 查 records
/* ====================== 1⃣ 查询入库记录 ====================== */
List<RkRecord> recordList = rkRecordMapper.selectRkRecordByIds(recordIds);
if (recordList == null || recordList.isEmpty()) {
throw new ServiceException("入库记录不存在");
}
// 2⃣ rk_record 批量回退 exec_status = 0
rkRecordMapper.updateExecStatusByIds(recordIds, "0");
// 3⃣ rk_info 批量回退 exec_status = 0
/* ====================== 2⃣ 校验是否发生过移库 ====================== */
List<Long> rkInfoIds = recordList.stream()
.map(RkRecord::getRkInfoId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (!rkInfoIds.isEmpty()) {
// 查询对应库存
List<RkInfo> infoList = rkInfoMapper.selectRkInfoByIds(rkInfoIds);
// 只要有一条 has_moved = 1直接禁止撤销
boolean hasMoved = infoList.stream()
.anyMatch(info -> "1".equals(info.getHasMoved()));
if (hasMoved) {
throw new ServiceException("存在已移库的库存数据,禁止撤销入库");
}
}
/* ====================== 3⃣ rk_record 回退为预入库 ====================== */
rkRecordMapper.updateExecStatusByIds(recordIds, "0");
/* ====================== 4⃣ rk_info 回退为预入库 ====================== */
if (!rkInfoIds.isEmpty()) {
rkInfoMapper.updateExecStatusByIds(rkInfoIds, "0");
}
// 4⃣ rk_bill保持原 Mapper 方法不变,按 billNo 去重后循环调用
/* ====================== 5⃣ rk_bill 回退为预入库 ====================== */
List<String> billNos = recordList.stream()
.map(RkRecord::getBillNo)
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList());
for (String billNo : billNos) {
rkBillMapper.updateExecStatusByBillNo(billNo, "0");
}
return recordIds.size();
}
@Override
@Transactional(rollbackFor = Exception.class)
public int finishIn(List<Long> recordIds) {
if (recordIds == null || recordIds.isEmpty()) {
throw new ServiceException("入库记录ID不能为空");
}
/* ================== 1. 查询 rk_record ================== */
List<RkRecord> recordList =
rkRecordMapper.selectRkRecordByIds(recordIds);
if (recordList == null || recordList.isEmpty()) {
throw new ServiceException("入库记录不存在");
}
/* ================== 2. 只处理「预入库」记录 ================== */
List<RkRecord> preRecordList = recordList.stream()
.filter(r -> "0".equals(r.getExecStatus())) // 预入库
.collect(Collectors.toList());
if (preRecordList.isEmpty()) {
throw new ServiceException("所选入库记录均已完成,无需重复入库");
}
/* ================== 3. ★ 校验:一键入库数量 ≤ 计划交货数量 ================== */
// 按供应计划ID聚合「本次完成入库数量」
Map<Long, BigDecimal> finishQtyMap = new HashMap<>();
for (RkRecord record : preRecordList) {
if (record.getGysJhId() == null || record.getRealQty() == null) {
continue;
}
finishQtyMap.merge(
record.getGysJhId(),
record.getRealQty(),
BigDecimal::add
);
}
// 只和「计划交货数量」做校验
for (Map.Entry<Long, BigDecimal> entry : finishQtyMap.entrySet()) {
Long gysJhId = entry.getKey();
BigDecimal finishQty = entry.getValue();
GysJh gysJh = gysJhMapper.selectGysJhById(gysJhId);
if (gysJh == null) {
throw new ServiceException("供应计划不存在ID" + gysJhId);
}
BigDecimal planQty = gysJh.getJhQty();
if (planQty != null && finishQty.compareTo(planQty) > 0) {
throw new ServiceException(
"一键入库数量超出供应计划数量,物料号:" + gysJh.getWlNo()
+ ",计划数量:" + planQty
+ ",本次完成入库:" + finishQty
);
}
}
/* ================== 4. 状态推进 ================== */
// ① rk_record.exec_status = 1
List<Long> preRecordIds = preRecordList.stream()
.map(RkRecord::getId)
.collect(Collectors.toList());
rkRecordMapper.updateExecStatusByIds(preRecordIds, "1");
// ② rk_info.exec_status = 1
List<Long> rkInfoIds = preRecordList.stream()
.map(RkRecord::getRkInfoId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (!rkInfoIds.isEmpty()) {
rkInfoMapper.updateExecStatusByIds(rkInfoIds, "1");
}
// ③ rk_bill.exec_status = 1
List<String> billNos = preRecordList.stream()
.map(RkRecord::getBillNo)
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList());
for (String billNo : billNos) {
rkBillMapper.updateExecStatusByBillNo(billNo, "1");
}
/* ================== 5. ★ 同步修正供应计划(以完成结果为准) ================== */
for (Map.Entry<Long, BigDecimal> entry : finishQtyMap.entrySet()) {
Long gysJhId = entry.getKey();
BigDecimal finishQty = entry.getValue();
// ① 直接修正 real_qty以一键入库结果为准
gysJhMapper.updateRealQtyById(gysJhId, finishQty);
// ② 重新判定状态
GysJh gysJh = gysJhMapper.selectGysJhById(gysJhId);
if (gysJh == null) {
continue;
}
BigDecimal planQty = gysJh.getJhQty();
BigDecimal realQty = gysJh.getRealQty();
if (realQty == null || realQty.compareTo(BigDecimal.ZERO) == 0) {
gysJhMapper.updateStatusById(gysJhId, "0"); // 未到货
} else if (planQty != null && realQty.compareTo(planQty) >= 0) {
gysJhMapper.updateStatusById(gysJhId, "1"); // 已入库
} else {
gysJhMapper.updateStatusById(gysJhId, "2"); // 部分入库
}
}
return preRecordIds.size();
}
@Override
@Transactional(rollbackFor = Exception.class)
public int rollbackOut(List<Long> recordIds) {
if (recordIds == null || recordIds.isEmpty()) {
throw new ServiceException("撤销出库记录ID不能为空");
}
// ================== 1. 查询出库记录 ==================
List<RkRecord> recordList = rkRecordMapper.selectRkRecordByIds(recordIds);
if (recordList == null || recordList.isEmpty()) {
throw new ServiceException("出库记录不存在");
}
// 只允许撤销:已出库的出库记录
for (RkRecord record : recordList) {
if (!"1".equals(record.getBizType())) {
throw new ServiceException("存在非出库记录,禁止撤销");
}
if (!"1".equals(record.getExecStatus())) {
throw new ServiceException("只能撤销已出库记录");
}
}
// ================== 2. 回滚库存 rk_info ==================
for (RkRecord record : recordList) {
Long rkInfoId = record.getRkInfoId();
BigDecimal outQty = record.getRealQty();
if (rkInfoId == null || outQty == null) {
throw new ServiceException("出库记录数据不完整,无法撤销");
}
RkInfo info = rkInfoMapper.selectRkInfoById(rkInfoId);
if (info == null) {
throw new ServiceException("库存不存在ID" + rkInfoId);
}
BigDecimal oldQty = info.getRealQty();
BigDecimal newQty = oldQty.add(outQty);
// ① 补回库存数量
rkInfoMapper.updateRealQtyById(rkInfoId, newQty);
// ② 如果原库存为 0说明 is_chuku=1必须回退
if (oldQty.compareTo(BigDecimal.ZERO) == 0) {
rkInfoMapper.updateIsChukuById(rkInfoId, "0");
}
}
// ================== 3. 回退出库记录状态 ==================
rkRecordMapper.updateExecStatusByIds(recordIds, "0");
// ================== 4. 回退主单 rk_bill ==================
List<String> billNos = recordList.stream()
.map(RkRecord::getBillNo)
.filter(StringUtils::isNotBlank)
@@ -186,62 +616,142 @@ public class RkRecordServiceImpl implements IRkRecordService
@Override
@Transactional(rollbackFor = Exception.class)
public int finishIn(List<Long> recordIds) {
public int finishOut(List<Long> recordIds) {
if (recordIds == null || recordIds.isEmpty()) {
throw new ServiceException("库记录ID不能为空");
throw new ServiceException("库记录ID不能为空");
}
// 1⃣ 查询 rk_record
List<RkRecord> recordList =
rkRecordMapper.selectRkRecordByIds(recordIds);
// ================== 1. 查询 rk_record ==================
List<RkRecord> recordList = rkRecordMapper.selectRkRecordByIds(recordIds);
if (recordList == null || recordList.isEmpty()) {
throw new ServiceException("库记录不存在");
throw new ServiceException("库记录不存在");
}
// ================== 只保留「预库」记录 ==================
List<RkRecord> preRecordList = recordList.stream()
.filter(r -> "0".equals(r.getExecStatus()))
// ================== 2. 只保留「预库」记录 ==================
List<RkRecord> preOutList = recordList.stream()
.filter(r -> "1".equals(r.getBizType())) // 出库
.filter(r -> "0".equals(r.getExecStatus())) // 预出库
.collect(Collectors.toList());
// ================== ② 如果没有可完成的数据,才报错 ==================
if (preRecordList.isEmpty()) {
throw new ServiceException("所选入库记录均已完成,无需重复入库");
if (preOutList.isEmpty()) {
throw new ServiceException("所选出库记录均已完成出库");
}
// ================== ③ 后续逻辑全部基于 preRecordList ==================
// ================== 3. 库存校验 + 扣减 ==================
for (RkRecord record : preOutList) {
List<Long> preRecordIds = preRecordList.stream()
Long rkInfoId = record.getRkInfoId();
BigDecimal outQty = record.getRealQty();
if (rkInfoId == null || outQty == null) {
throw new ServiceException("出库记录数据不完整");
}
RkInfo info = rkInfoMapper.selectRkInfoById(rkInfoId);
if (info == null) {
throw new ServiceException("库存不存在ID" + rkInfoId);
}
BigDecimal remainQty = info.getRealQty();
if (remainQty.compareTo(outQty) < 0) {
throw new ServiceException(
"库存不足库存ID" + rkInfoId +
",当前库存:" + remainQty +
",出库数量:" + outQty
);
}
BigDecimal newQty = remainQty.subtract(outQty);
// 扣库存
rkInfoMapper.updateRealQtyById(rkInfoId, newQty);
// 同步出库状态
if (newQty.compareTo(BigDecimal.ZERO) == 0) {
rkInfoMapper.updateIsChukuById(rkInfoId, "1");
}
}
// ================== 4. 推进状态 ==================
// ① rk_record.exec_status = 1
List<Long> preOutIds = preOutList.stream()
.map(RkRecord::getId)
.collect(Collectors.toList());
rkRecordMapper.updateExecStatusByIds(preOutIds, "1");
// 2 rk_record.exec_status = 1
rkRecordMapper.updateExecStatusByIds(preRecordIds, "1");
// 3⃣ rk_info.exec_status = 1
List<Long> rkInfoIds = preRecordList.stream()
// rk_info.exec_status = 1
List<Long> rkInfoIds = preOutList.stream()
.map(RkRecord::getRkInfoId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (!rkInfoIds.isEmpty()) {
rkInfoMapper.updateExecStatusByIds(rkInfoIds, "1");
}
// 4 rk_bill.exec_status = 1
List<String> billNos = preRecordList.stream()
// rk_bill.exec_status = 1
List<String> billNos = preOutList.stream()
.map(RkRecord::getBillNo)
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList());
for (String billNo : billNos) {
rkBillMapper.updateExecStatusByBillNo(billNo, "1");
}
return preRecordIds.size();
return preOutIds.size();
}
@Override
@Transactional(rollbackFor = Exception.class)
public int deletePreOutRecords(Long[] ids) {
if (ids == null || ids.length == 0) {
return 0;
}
// ================== 1. 查询 record ==================
List<RkRecord> records =
rkRecordMapper.selectRkRecordByIds(Arrays.asList(ids));
if (records == null || records.isEmpty()) {
return 0;
}
// ================== 2. 严格校验:只能删「预出库」 ==================
boolean hasInvalid = records.stream().anyMatch(r ->
!"1".equals(r.getBizType()) // 1 = 出库
|| !"0".equals(r.getExecStatus()) // 0 = 预出库
);
if (hasInvalid) {
throw new ServiceException("仅允许删除预出库记录");
}
// ================== 3. 删除 rk_record ==================
int deleteCount = rkRecordMapper.deleteRkRecordByIds(ids);
// ================== 4. 维护 bill 状态 ==================
Set<String> billNos = records.stream()
.map(RkRecord::getBillNo)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
for (String billNo : billNos) {
// 该单据下是否还存在预出库记录
int remainPreOut =
rkRecordMapper.countPreOutByBillNo(billNo);
if (remainPreOut == 0) {
// 没有任何预出库记录了,单据可视情况回退或推进
rkBillMapper.updateExecStatusByBillNo(billNo, "1");
}
}
return deleteCount;
}
}

View File

@@ -1,38 +1,66 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zg.project.wisdom.mapper.GysJhMapper">
<resultMap type="GysJh" id="GysJhResult">
<result property="id" column="id" />
<result property="indexNo" column="index_no" />
<result property="xj" column="xj" />
<result property="xmNo" column="xm_no" />
<result property="xmMs" column="xm_ms" />
<result property="wlNo" column="wl_no" />
<result property="wlMs" column="wl_ms" />
<result property="gysNo" column="gys_no" />
<result property="gysMc" column="gys_mc" />
<result property="jhAmt" column="jh_amt" />
<result property="htDj" column="ht_dj" />
<result property="sapNo" column="sap_no" />
<result property="xh" column="xh" />
<result property="jhQty" column="jh_qty" />
<result property="htQty" column="ht_qty" />
<result property="dw" column="dw" />
<result property="status" column="status" />
<result property="entityId" column="entity_id" />
<result property="remark" column="remark" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="isDelete" column="is_delete" />
<result property="id" column="id"/>
<result property="indexNo" column="index_no"/>
<result property="xj" column="xj"/>
<result property="xmNo" column="xm_no"/>
<result property="xmMs" column="xm_ms"/>
<result property="wlNo" column="wl_no"/>
<result property="wlMs" column="wl_ms"/>
<result property="gysNo" column="gys_no"/>
<result property="gysMc" column="gys_mc"/>
<result property="jhAmt" column="jh_amt"/>
<result property="htDj" column="ht_dj"/>
<result property="sapNo" column="sap_no"/>
<result property="xh" column="xh"/>
<result property="jhQty" column="jh_qty"/>
<!-- 新增:已入库数量 -->
<result property="realQty" column="real_qty"/>
<result property="htQty" column="ht_qty"/>
<result property="dw" column="dw"/>
<result property="status" column="status"/>
<result property="entityId" column="entity_id"/>
<result property="remark" column="remark"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="isDelete" column="is_delete"/>
</resultMap>
<sql id="selectGysJhVo">
select id, index_no, xj, xm_no, xm_ms, wl_no, wl_ms, gys_no, gys_mc, jh_amt, ht_dj, sap_no, xh, jh_qty, ht_qty, dw, status,entity_id, remark, create_by, create_time, update_by, update_time, is_delete from gys_jh
select
id,
index_no,
xj,
xm_no,
xm_ms,
wl_no,
wl_ms,
gys_no,
gys_mc,
jh_amt,
ht_dj,
sap_no,
xh,
jh_qty,
real_qty,
ht_qty,
dw,
status,
entity_id,
remark,
create_by,
create_time,
update_by,
update_time,
is_delete
from gys_jh
</sql>
<select id="selectGysJhList" parameterType="GysJh" resultMap="GysJhResult">
@@ -59,11 +87,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="status != null and status != ''">
and status = #{status}
</if>
<!-- and (status is null or trim(status) != '1') -->
<if test="isDelete != null and isDelete != ''">
and is_delete = #{isDelete}
</if>
<!-- 按创建时间筛选create_time 在 [beginTime, endTime] -->
<if test="beginTime != null">
and create_time <![CDATA[>=]]> #{beginTime}
</if>
@@ -72,48 +98,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</if>
</where>
</select>
<select id="selectGysJhById" parameterType="Long" resultMap="GysJhResult">
<include refid="selectGysJhVo"/>
where id = #{id}
</select>
<select id="getBySapNo" parameterType="java.lang.String" resultMap="GysJhResult">
<select id="getBySapNo" parameterType="String" resultMap="GysJhResult">
<include refid="selectGysJhVo"/>
WHERE sap_no = #{sapNo}
</select>
<!-- 【已注释】唯一性校验查询基于SAP订单号、项目号、物料号判断是否已存在 -->
<!-- <select id="existsByUniqueKeys" resultType="boolean">
SELECT COUNT(1)
FROM gys_jh
WHERE sap_no = #{sapNo}
AND xm_no = #{xmNo}
AND wl_no = #{wlNo}
AND is_delete != '1'
</select> -->
<select id="selectByIds" resultMap="GysJhResult">
SELECT * FROM gys_jh
SELECT *
FROM gys_jh
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<select id="selectMissingMtdItems" resultType="com.zg.project.wisdom.domain.vo.PlanToMtdVO">
SELECT
g.wl_no AS wlNo,
MAX(g.wl_ms) AS wlMs,
MAX(g.dw) AS dw,
MAX(g.ht_dj) AS htDj
FROM gys_jh g
LEFT JOIN mtd m ON m.mid = g.wl_no
WHERE g.wl_no IS NOT NULL AND g.wl_no &lt;&gt; ''
AND m.mid IS NULL
GROUP BY g.wl_no
</select>
<insert id="insertGysJh" parameterType="GysJh" useGeneratedKeys="true" keyProperty="id">
insert into gys_jh
<trim prefix="(" suffix=")" suffixOverrides=",">
@@ -130,6 +134,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="sapNo != null">sap_no,</if>
<if test="xh != null">xh,</if>
<if test="jhQty != null">jh_qty,</if>
<!-- 新增 -->
<if test="realQty != null">real_qty,</if>
<if test="htQty != null">ht_qty,</if>
<if test="dw != null">dw,</if>
<if test="status != null">status,</if>
@@ -140,7 +146,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="isDelete != null">is_delete,</if>
</trim>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="indexNo != null">#{indexNo},</if>
<if test="xj != null">#{xj},</if>
@@ -155,6 +161,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="sapNo != null">#{sapNo},</if>
<if test="xh != null">#{xh},</if>
<if test="jhQty != null">#{jhQty},</if>
<!-- 新增 -->
<if test="realQty != null">#{realQty},</if>
<if test="htQty != null">#{htQty},</if>
<if test="dw != null">#{dw},</if>
<if test="status != null">#{status},</if>
@@ -165,9 +173,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="isDelete != null">#{isDelete},</if>
</trim>
</trim>
</insert>
<update id="increaseRealQtyById">
UPDATE gys_jh
SET real_qty = real_qty + #{realQty}
WHERE id = #{gysJhId}
</update>
<update id="updateGysJh" parameterType="GysJh">
update gys_jh
<trim prefix="SET" suffixOverrides=",">
@@ -184,6 +197,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="sapNo != null">sap_no = #{sapNo},</if>
<if test="xh != null">xh = #{xh},</if>
<if test="jhQty != null">jh_qty = #{jhQty},</if>
<!-- 新增 -->
<if test="realQty != null">real_qty = #{realQty},</if>
<if test="htQty != null">ht_qty = #{htQty},</if>
<if test="dw != null">dw = #{dw},</if>
<if test="status != null">status = #{status},</if>
@@ -198,6 +213,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where id = #{id}
</update>
<!-- 下面原有方法全部保持不动 -->
<update id="updateStatusById" parameterType="map">
UPDATE gys_jh
SET status = #{status}
@@ -233,7 +249,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</delete>
<delete id="deleteGysJhByIds" parameterType="String">
delete from gys_jh where id in
delete from gys_jh where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
@@ -246,10 +262,53 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND is_delete = '0'
</select>
<select id="selectMissingMtdItems"
resultType="com.zg.project.wisdom.domain.vo.PlanToMtdVO">
<![CDATA[
SELECT
g.wl_no AS wlNo,
MAX(g.wl_ms) AS wlMs,
MAX(g.dw) AS dw,
MAX(g.ht_dj) AS htDj
FROM gys_jh g
LEFT JOIN mtd m ON m.mid = g.wl_no
WHERE g.wl_no IS NOT NULL
AND g.wl_no <> ''
AND m.mid IS NULL
GROUP BY g.wl_no
]]>
</select>
<!-- 查询当前已入库数量 -->
<select id="selectRealQtyById" resultType="java.math.BigDecimal">
SELECT real_qty
FROM gys_jh
WHERE id = #{jhId}
</select>
<update id="updateDwById">
UPDATE gys_jh
SET dw = #{dw}
WHERE id = #{id}
</update>
</mapper>
<update id="rollbackInQty">
UPDATE gys_jh
SET real_qty = real_qty - #{rollbackQty}
WHERE id = #{jhId}
</update>
<!-- 回退状态 -->
<update id="updateStatus">
UPDATE gys_jh
SET status = #{status}
WHERE id = #{jhId}
</update>
<update id="updateRealQtyById">
UPDATE gys_jh
SET
real_qty = #{realQty},
update_time = NOW()
WHERE id = #{gysJhId}
</update>
</mapper>

View File

@@ -9,20 +9,31 @@
<resultMap type="com.zg.project.wisdom.domain.MoveRecord" id="MoveRecordResult">
<result property="id" column="id"/>
<!-- 关键字段rk_info_id -->
<!-- 关键字段 -->
<result property="rkInfoId" column="rk_info_id"/>
<result property="newRkInfoId" column="new_rk_info_id"/>
<result property="entityId" column="entity_id"/>
<!-- 数量 -->
<result property="realQty" column="real_qty"/>
<!-- from -->
<result property="fromCangku" column="from_cangku"/>
<result property="fromPcode" column="from_pcode"/>
<result property="fromTrayCode" column="from_tray_code"/>
<!-- to -->
<result property="toCangku" column="to_cangku"/>
<result property="toPcode" column="to_pcode"/>
<result property="toTrayCode" column="to_tray_code"/>
<!-- 业务 -->
<result property="moveReason" column="move_reason"/>
<result property="movedBy" column="moved_by"/>
<result property="movedByName" column="moved_by_name"/>
<result property="movedAt" column="moved_at"/>
<!-- 通用字段 -->
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
@@ -43,7 +54,9 @@
SELECT
id,
rk_info_id,
new_rk_info_id,
entity_id,
real_qty,
from_cangku,
from_pcode,
from_tray_code,
@@ -68,7 +81,9 @@
keyProperty="id">
INSERT INTO move_record (
rk_info_id,
new_rk_info_id,
entity_id,
real_qty,
from_cangku,
from_pcode,
from_tray_code,
@@ -85,7 +100,9 @@
is_delete
) VALUES (
#{rkInfoId},
#{newRkInfoId},
#{entityId},
#{realQty},
#{fromCangku},
#{fromPcode},
#{fromTrayCode},
@@ -120,6 +137,7 @@
LEFT JOIN warehouse_info wi1 ON mr.from_cangku = wi1.warehouse_code
LEFT JOIN warehouse_info wi2 ON mr.to_cangku = wi2.warehouse_code
<where>
mr.is_delete = 0
<if test="entityId != null and entityId != ''">
AND mr.entity_id = #{entityId}
</if>
@@ -153,7 +171,9 @@
UPDATE move_record
<set>
<if test="rkInfoId != null">rk_info_id = #{rkInfoId},</if>
<if test="newRkInfoId != null">new_rk_info_id = #{newRkInfoId},</if>
<if test="entityId != null">entity_id = #{entityId},</if>
<if test="realQty != null">real_qty = #{realQty},</if>
<if test="fromCangku != null">from_cangku = #{fromCangku},</if>
<if test="fromPcode != null">from_pcode = #{fromPcode},</if>
<if test="fromTrayCode != null">from_tray_code = #{fromTrayCode},</if>

View File

@@ -7,6 +7,7 @@
<!-- ==================== ResultMap ==================== -->
<resultMap type="RkBill" id="RkBillResult">
<result property="id" column="id"/>
<result property="wlType" column="wl_type"/>
<result property="wlTypeName" column="wl_type_name"/>
@@ -27,9 +28,22 @@
<result property="operatorName" column="operator_name"/>
<result property="teamCode" column="team_code"/>
<result property="teamName" column="team_name"/>
<result property="borrowTime" column="borrow_time"/>
<result property="returnTime" column="return_time"/>
<result property="xmNoCk" column="xm_no_ck"/>
<result property="xmMsCk" column="xm_ms_ck"/>
<result property="remark" column="remark"/>
<result property="isDelivery" column="is_delivery"/>
<result property="isDelete" column="is_delete"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<!-- ==================== Base Select ==================== -->
@@ -45,15 +59,26 @@
rb.exec_status,
rb.operator,
rb.team_code,
rb.borrow_time,
rb.return_time,
rb.xm_no_ck,
rb.xm_ms_ck,
rb.remark,
rb.is_delivery,
rb.is_delete,
rb.create_by,
rb.create_time,
rb.update_by,
rb.update_time,
mt.type_name AS wl_type_name,
COALESCE(sit.type_name, sot.type_name) AS operation_type_name,
su.nick_name AS operator_name,
wh.warehouse_name AS warehouse_name,
wh.parent_warehouse_name AS parent_warehouse_name
wh.parent_warehouse_name AS parent_warehouse_name,
ct.team_name AS team_name
FROM rk_bill rb
LEFT JOIN warehouse_info wh
@@ -70,6 +95,9 @@
AND (sot.is_delete = '0' OR sot.is_delete IS NULL)
LEFT JOIN sys_user su
ON rb.operator = su.user_id
LEFT JOIN construction_team ct
ON rb.team_code = ct.team_code
AND ct.is_delete = '0'
</sql>
<!-- ==================== List ==================== -->
@@ -93,20 +121,23 @@
AND rb.operation_type = #{operationType}
</if>
<!-- 多 bizType -->
<if test="bizTypeList != null and bizTypeList.size > 0">
AND rb.biz_type IN
<foreach collection="bizTypeList"
item="bt"
open="("
separator=","
close=")">
<foreach collection="bizTypeList" item="bt" open="(" separator="," close=")">
#{bt}
</foreach>
</if>
<if test="operationTime != null">
AND rb.operation_time = #{operationTime}
<if test="borrowTime != null">
AND rb.borrow_time = #{borrowTime}
</if>
<if test="returnTime != null">
AND rb.return_time = #{returnTime}
</if>
<if test="xmNoCk != null and xmNoCk != ''">
AND rb.xm_no_ck = #{xmNoCk}
</if>
<if test="startDate != null">
@@ -147,9 +178,7 @@
WHERE rb.id = #{id}
</select>
<select id="selectByBillNo"
parameterType="String"
resultMap="RkBillResult">
<select id="selectByBillNo" parameterType="String" resultMap="RkBillResult">
SELECT
id,
wl_type,
@@ -161,6 +190,10 @@
exec_status,
operator,
team_code,
borrow_time,
return_time,
xm_no_ck,
xm_ms_ck,
is_delivery,
remark,
create_by,
@@ -187,6 +220,11 @@
operation_type,
operation_time,
operator,
team_code,
borrow_time,
return_time,
xm_no_ck,
xm_ms_ck,
exec_status,
is_delivery,
remark,
@@ -201,6 +239,11 @@
#{operationType},
#{operationTime},
#{operator},
#{teamCode},
#{borrowTime},
#{returnTime},
#{xmNoCk},
#{xmMsCk},
#{execStatus},
#{isDelivery},
#{remark},
@@ -220,6 +263,10 @@
<if test="operationType != null and operationType != ''">operation_type = #{operationType},</if>
<if test="bizType != null and bizType != ''">biz_type = #{bizType},</if>
<if test="operationTime != null">operation_time = #{operationTime},</if>
<if test="borrowTime != null">borrow_time = #{borrowTime},</if>
<if test="returnTime != null">return_time = #{returnTime},</if>
<if test="xmNoCk != null and xmNoCk != ''">xm_no_ck = #{xmNoCk},</if>
<if test="xmMsCk != null and xmMsCk != ''">xm_ms_ck = #{xmMsCk},</if>
<if test="execStatus != null and execStatus != ''">exec_status = #{execStatus},</if>
<if test="operator != null">operator = #{operator},</if>
<if test="teamCode != null and teamCode != ''">team_code = #{teamCode},</if>
@@ -230,12 +277,19 @@
WHERE id = #{id}
</update>
<!-- ==================== ExecStatus ==================== -->
<update id="updateExecStatusByBillNo">
UPDATE rk_bill
SET exec_status = #{execStatus}
WHERE bill_no = #{billNo}
</update>
<update id="updateExecStatus">
UPDATE rk_bill
SET exec_status = #{execStatus}
WHERE bill_no = #{billNo}
</update>
<!-- ==================== Delete ==================== -->
<delete id="deleteRkBillById" parameterType="Long">
DELETE FROM rk_bill WHERE id = #{id}

View File

@@ -103,6 +103,8 @@
<include refid="selectRkInfoVo"/>
<where>
ri.exec_status = 1
AND ri.is_chuku = 0
AND ri.is_delete = 0
<if test="operationType != null and operationType != ''">
AND ri.operation_type LIKE CONCAT('%', #{operationType}, '%')
</if>
@@ -159,16 +161,44 @@
WHERE ri.id = #{id}
</select>
<select id="countByBillNo" resultType="int">
SELECT COUNT(1)
FROM rk_info
WHERE bill_no = #{billNo}
</select>
<select id="countNotPreByIds" resultType="int">
SELECT COUNT(1)
FROM rk_info
WHERE id IN
<foreach collection="rkInfoIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
AND exec_status != '0'
</select>
<select id="selectRkInfoByIds"
parameterType="java.util.List"
resultMap="RkInfoResult">
SELECT
ri.*
FROM rk_info ri
WHERE ri.id IN
<foreach collection="rkInfoIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- ========================= 删除 ========================= -->
<delete id="deleteRkInfoById" parameterType="Long">
DELETE FROM rk_info WHERE id = #{id}
</delete>
<delete id="deleteRkInfoByIds" parameterType="java.util.List">
<delete id="deleteRkInfoByIds">
DELETE FROM rk_info
WHERE id IN
<foreach item="item" collection="list" open="(" separator="," close=")">
#{item}
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
@@ -277,4 +307,27 @@
</foreach>
</update>
<update id="updateRealQtyById">
update rk_info
set real_qty = #{realQty},
update_time = now()
where id = #{id}
</update>
<update id="updateIsChukuById">
update rk_info
set is_chuku = #{isChuku},
update_time = now()
where id = #{id}
</update>
<update id="updateBorrowReturn">
UPDATE rk_info
SET is_borrowed = #{isBorrowed},
return_time = #{returnTime},
update_time = NOW()
WHERE id = #{id}
</update>
</mapper>

View File

@@ -119,9 +119,21 @@
<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
<foreach collection="bizTypeList"
item="bt"
open="("
separator=","
close=")">
#{bt}
</foreach>
</if>
<if test="bizType != null and bizType != ''">
AND rr.biz_type = #{bizType}
</if>
@@ -198,7 +210,7 @@
AND rr.operation_time &lt;= #{endDate}
</if>
</where>
ORDER BY rr.operation_time DESC
ORDER BY rr.exec_status = '0' DESC, rr.operation_time DESC
</select>
<!-- ===================== 按 ID 查询 ===================== -->
@@ -212,7 +224,7 @@
<include refid="selectRkRecordVo"/>
WHERE rr.bill_no = #{billNo}
AND (rr.is_delete = '0' OR rr.is_delete = 0 OR rr.is_delete IS NULL)
ORDER BY rr.operation_time ASC
ORDER BY rr.exec_status = '0' DESC, rr.operation_time DESC
</select>
<select id="selectRkRecordByIds" resultMap="RkRecordResult">
@@ -224,6 +236,38 @@
</foreach>
</select>
<select id="countByBillNo" resultType="int">
SELECT COUNT(1)
FROM rk_record
WHERE bill_no = #{billNo}
</select>
<select id="countPreByBillNo" resultType="int">
SELECT COUNT(1)
FROM rk_record
WHERE bill_no = #{billNo}
AND biz_type = '0'
AND exec_status = '0'
AND is_delete = '0';
</select>
<select id="countPreOutByBillNo" resultType="java.lang.Integer" parameterType="java.lang.String">
SELECT COUNT(1)
FROM rk_record
WHERE bill_no = #{billNo}
AND biz_type = '1'
AND exec_status = '0'
AND is_delete = '0';
</select>
<select id="selectRkRecordByRkInfoId" resultMap="RkRecordResult">
SELECT *
FROM rk_record
WHERE rk_info_id = #{rkInfoId}
AND (is_delete = '0' OR is_delete = 0 OR is_delete IS NULL)
ORDER BY operation_time DESC, id DESC
LIMIT 1
</select>
<!-- ===================== 新增 ===================== -->
<insert id="insertRkRecord"
parameterType="RkRecord"