库存模块功能开发

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("移库成功"); 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.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; 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.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@@ -90,7 +89,6 @@ public class RkBillController extends BaseController
return toAjax(rkBillService.appendRkBillDetail(dto)); return toAjax(rkBillService.appendRkBillDetail(dto));
} }
/** /**
* 修改库存单据 * 修改库存单据
*/ */
@@ -107,9 +105,30 @@ public class RkBillController extends BaseController
*/ */
@PreAuthorize("@ss.hasPermi('wisdom:bill:remove')") @PreAuthorize("@ss.hasPermi('wisdom:bill:remove')")
@Log(title = "库存单据", businessType = BusinessType.DELETE) @Log(title = "库存单据", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}") @DeleteMapping("/delete")
public AjaxResult remove(@PathVariable Long[] ids) public AjaxResult remove(@RequestBody Long[] ids)
{ {
return toAjax(rkBillService.deleteRkBillByIds(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')") @PreAuthorize("@ss.hasPermi('wisdom:record:remove')")
@Log(title = "入库记录", businessType = BusinessType.DELETE) @Log(title = "入库记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}") @DeleteMapping("/delete")
public AjaxResult remove(@PathVariable Long[] ids) public AjaxResult remove(@RequestBody Long[] ids)
{ {
return toAjax(rkRecordService.deleteRkRecordByIds(ids)); return toAjax(rkRecordService.deleteRkRecordByIds(ids));
} }
@@ -132,4 +132,34 @@ public class RkRecordController extends BaseController
return toAjax(rkRecordService.finishIn(record.getIds())); 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

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

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

View File

@@ -77,7 +77,6 @@ public class RkBill extends BaseEntity {
private String execStatus; private String execStatus;
/** 理货员用户ID */ /** 理货员用户ID */
// @Excel(name = "理货员")
private Integer operator; private Integer operator;
/** 理货员名称(联表) */ /** 理货员名称(联表) */
@@ -88,10 +87,31 @@ public class RkBill extends BaseEntity {
@Excel(name = "施工队编码") @Excel(name = "施工队编码")
private String teamCode; private String teamCode;
/** 施工队名称(联表) */
@Excel(name = "施工队名称")
private String teamName;
/** 是否需要配送(0否,1是,2配送中,3配送完成) */ /** 是否需要配送(0否,1是,2配送中,3配送完成) */
@Excel(name = "是否需要配送", readConverterExp = "0=否,1=是,2=配送中,3=配送完成") @Excel(name = "是否需要配送", readConverterExp = "0=否,1=是,2=配送中,3=配送完成")
private String isDelivery; 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; private String createBy;
@@ -251,6 +271,14 @@ public class RkBill extends BaseEntity {
this.teamCode = teamCode; this.teamCode = teamCode;
} }
public String getTeamName() {
return teamName;
}
public void setTeamName(String teamName) {
this.teamName = teamName;
}
public String getIsDelivery() { public String getIsDelivery() {
return isDelivery; return isDelivery;
} }
@@ -259,6 +287,42 @@ public class RkBill extends BaseEntity {
this.isDelivery = 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() { public String getCreateBy() {
return createBy; return createBy;
} }
@@ -343,13 +407,18 @@ public class RkBill extends BaseEntity {
.append("operator", operator) .append("operator", operator)
.append("operatorName", operatorName) .append("operatorName", operatorName)
.append("teamCode", teamCode) .append("teamCode", teamCode)
.append("teamName", teamName)
.append("isDelivery", isDelivery) .append("isDelivery", isDelivery)
.append("borrowTime", borrowTime)
.append("returnTime", returnTime)
.append("xmNoCk", getXmNoCk())
.append("xmMsCk", getXmMsCk())
.append("createBy", createBy) .append("createBy", createBy)
.append("createTime", createTime) .append("createTime", createTime)
.append("updateBy", updateBy) .append("updateBy", updateBy)
.append("updateTime", updateTime) .append("updateTime", updateTime)
.append("isDelete", isDelete) .append("isDelete", isDelete)
.append("remark", getRemark()) .append("remark", remark)
.append("startDate", startDate) .append("startDate", startDate)
.append("endDate", endDate) .append("endDate", endDate)
.toString(); .toString();

View File

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

View File

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

View File

@@ -132,4 +132,41 @@ public interface GysJhMapper
@Param("dw") String dw); @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("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; package com.zg.project.wisdom.mapper;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List; import java.util.List;
import com.zg.project.wisdom.domain.RkInfo; import com.zg.project.wisdom.domain.RkInfo;
import io.lettuce.core.dynamic.annotation.Param; import io.lettuce.core.dynamic.annotation.Param;
@@ -58,11 +60,46 @@ public interface RkInfoMapper
* @param ids 需要删除的数据主键集合 * @param ids 需要删除的数据主键集合
* @return 结果 * @return 结果
*/ */
public int deleteRkInfoByIds(Long[] ids); int deleteRkInfoByIds(@Param("ids") Long[] ids);
/** /**
* 修改库存明细执行状态 * 修改库存明细执行状态
*/ */
int updateExecStatusByIds(@Param("ids") List<Long> ids, int updateExecStatusByIds(@Param("ids") List<Long> ids,
@Param("execStatus") String execStatus); @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 需要删除的数据主键集合 * @param ids 需要删除的数据主键集合
* @return 结果 * @return 结果
*/ */
public int deleteRkRecordByIds(Long[] ids); int deleteRkRecordByIds(@Param("ids") Long[] ids);
/** /**
* 修改执行状态 * 修改执行状态
@@ -95,4 +95,30 @@ public interface RkRecordMapper
* @param rkInfo 单据信息 * @param rkInfo 单据信息
*/ */
void updateByRkInfo(RkInfo 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 * @param dto
*/ */
void processMove(MoveRequestDTO dto); void processMove(MoveRequestDTO dto);
/**
* 撤销移库
*
* @param moveRecordId 移库记录ID
*/
void rollbackMove(Long moveRecordId);
} }

View File

@@ -66,4 +66,14 @@ public interface IRkBillService
* @return 结果 * @return 结果
*/ */
public int deleteRkBillById(Long id); 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 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); List<GysJh> list = gysJhMapper.getBySapNo(sapNo);
AjaxResult result = AjaxResult.success(list);
if (list == null || list.isEmpty()) { if (list == null || list.isEmpty()) {
return result; return AjaxResult.success(list);
} }
// 找出已入库(status=1)的物料号,去重、过滤空值 AjaxResult result = AjaxResult.success(list);
List<String> inStockWlNos = list.stream() List<String> inStockWlNos = list.stream()
.filter(x -> x != null && "1".equals(String.valueOf(x.getStatus()).trim())) .filter(x -> x != null && "1".equals(String.valueOf(x.getStatus()).trim()))
.map(GysJh::getWlNo) .map(GysJh::getWlNo)
@@ -185,18 +184,14 @@ public class GysJhServiceImpl implements IGysJhService
if (!inStockWlNos.isEmpty()) { if (!inStockWlNos.isEmpty()) {
result.put("warn", true); result.put("warn", true);
result.put("inStockWlNos", inStockWlNos); result.put("inStockWlNos", inStockWlNos);
result.put("msg", "该 SAP 订单号下,以下物料号已全部入库,请注意:"
// 组装提示文案(可根据前端展示需要调整长度) + String.join("", inStockWlNos));
String msg = "该 SAP 订单号下,以下物料号已入库过,请注意:"
+ String.join("", inStockWlNos);
result.put("msg", msg);
} }
return result; return result;
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public int importByMapping(MultipartFile file, List<ExcelFieldMapping> mapping) throws Exception { 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 移库请求参数
*/
/**
* 处理库存移库操作
*
* @param dto 移库请求参数包含原库存ID、移库目标列表等
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void processMove(MoveRequestDTO dto) { public void processMove(MoveRequestDTO dto) {
// ========= 0. 基校验 ========= /* ====================== 0. 基校验 ====================== */
if (dto == null || dto.getFromRkId() == null) { if (dto == null || dto.getFromRkId() == null) {
throw new ServiceException("原库存ID不能为空"); throw new ServiceException("原库存ID不能为空");
} }
@@ -133,18 +128,18 @@ public class MoveRecordServiceImpl implements IMoveRecordService
throw new ServiceException("目标位置列表不能为空"); throw new ServiceException("目标位置列表不能为空");
} }
// ========= 1. 查询原库存 ========= /* ====================== 1. 查询原库存 ====================== */
RkInfo original = rkInfoMapper.selectRkInfoById(dto.getFromRkId()); RkInfo original = rkInfoMapper.selectRkInfoById(dto.getFromRkId());
if (original == null || "1".equals(original.getIsDelete())) { if (original == null || "1".equals(original.getIsDelete())) {
throw new ServiceException("原库存不存在或已删除"); throw new ServiceException("原库存不存在或已删除");
} }
BigDecimal originQty = original.getRealQty(); BigDecimal originQty = original.getRealQty();
if (originQty == null) { if (originQty == null || originQty.compareTo(BigDecimal.ZERO) <= 0) {
throw new ServiceException("原库存数量为空"); throw new ServiceException("原库存数量异常");
} }
// ========= 2. 计算移库总量 ========= /* ====================== 2. 计算移库总量 ====================== */
BigDecimal moveTotalQty = dto.getTargets().stream() BigDecimal moveTotalQty = dto.getTargets().stream()
.map(MoveTargetItem::getRealQty) .map(MoveTargetItem::getRealQty)
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
@@ -153,146 +148,189 @@ public class MoveRecordServiceImpl implements IMoveRecordService
throw new ServiceException("移库数量不能大于原库存数量"); throw new ServiceException("移库数量不能大于原库存数量");
} }
Long userId = SecurityUtils.getUserId(); String userId = String.valueOf(SecurityUtils.getUserId());
String username = userId.toString();
Date now = DateUtils.getNowDate(); Date now = DateUtils.getNowDate();
// ========= 3. 快照(只用于拷贝字段) ========= /* ====================== 3. 原库存快照 ====================== */
RkInfo snapshot = new RkInfo(); RkInfo snapshot = new RkInfo();
BeanUtils.copyProperties(original, snapshot); BeanUtils.copyProperties(original, snapshot);
// ========= 4. 查询原 rk_record用于同步更新 ========= /* ====================== 4. 全量移库(不拆分) ====================== */
RkRecord originRecord = rkRecordMapper.selectRkRecordById(original.getId());
if (originRecord == null) {
throw new ServiceException("未找到对应的出入库记录");
}
// ================== 一、全量移库 ==================
if (dto.getTargets().size() == 1 && moveTotalQty.compareTo(originQty) == 0) { if (dto.getTargets().size() == 1 && moveTotalQty.compareTo(originQty) == 0) {
MoveTargetItem target = dto.getTargets().get(0); MoveTargetItem target = dto.getTargets().get(0);
// 1⃣ rk_info更新库位 // 4.1 更新原库存(仅换库位
original.setCangku(target.getToCangku()); original.setCangku(target.getToCangku());
original.setPcode(target.getToPcode()); original.setPcode(target.getToPcode());
original.setTrayCode(target.getToTrayCode()); original.setTrayCode(target.getToTrayCode());
original.setHasMoved("1"); original.setHasMoved("1");
original.setUpdateBy(username); original.setUpdateBy(userId);
original.setUpdateTime(now); original.setUpdateTime(now);
rkInfoMapper.updateRkInfo(original); rkInfoMapper.updateRkInfo(original);
// 2⃣ rk_record同步更新库位 // 4.2 移库流水(全量移库,无新库存)
originRecord.setCangku(target.getToCangku()); MoveRecord record = new MoveRecord();
originRecord.setPcode(target.getToPcode()); record.setRkInfoId(snapshot.getId());
originRecord.setTrayCode(target.getToTrayCode()); record.setNewRkInfoId(null); // ✅ 关键:全量移库没有新库存
originRecord.setHasMoved("1"); record.setEntityId(snapshot.getEntityId());
originRecord.setUpdateBy(username); record.setRealQty(originQty);
originRecord.setUpdateTime(now);
rkRecordMapper.updateRkRecord(originRecord);
// 3⃣ move_record记录移库流水绑定原 rk_info.id record.setFromCangku(snapshot.getCangku());
moveRecordMapper.insertMoveRecord( record.setFromPcode(snapshot.getPcode());
createMoveRecord(original, target, dto) 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; return;
} }
// ================== 二、部分移库 ================== /* ====================== 5. 部分移库(拆分库存) ====================== */
// 1⃣ 原 rk_info 扣减数量 // 5.1 原库存扣减数量
original.setRealQty(originQty.subtract(moveTotalQty)); original.setRealQty(originQty.subtract(moveTotalQty));
original.setHasMoved("1"); original.setHasMoved("1");
original.setUpdateBy(username); original.setUpdateBy(userId);
original.setUpdateTime(now); original.setUpdateTime(now);
rkInfoMapper.updateRkInfo(original); rkInfoMapper.updateRkInfo(original);
// 2⃣ 原 rk_record 扣减数量 // 5.2 生成新库存 + 移库流水
originRecord.setRealQty(
originRecord.getRealQty().subtract(moveTotalQty)
);
originRecord.setHasMoved("1");
originRecord.setUpdateBy(username);
originRecord.setUpdateTime(now);
rkRecordMapper.updateRkRecord(originRecord);
// 3⃣ 新增目标库存 + 新增目标记录
for (MoveTargetItem target : dto.getTargets()) { for (MoveTargetItem target : dto.getTargets()) {
// —— 新 rk_info /* ---------- 新库存 ---------- */
RkInfo newInfo = new RkInfo(); RkInfo newInfo = new RkInfo();
BeanUtils.copyProperties(snapshot, newInfo, "id"); BeanUtils.copyProperties(snapshot, newInfo, "id");
newInfo.setCangku(target.getToCangku()); newInfo.setCangku(target.getToCangku());
newInfo.setPcode(target.getToPcode()); newInfo.setPcode(target.getToPcode());
newInfo.setTrayCode(target.getToTrayCode()); newInfo.setTrayCode(target.getToTrayCode());
newInfo.setRealQty(target.getRealQty()); newInfo.setRealQty(target.getRealQty());
newInfo.setHasMoved("1"); newInfo.setHasMoved("1");
newInfo.setCreateBy(username);
newInfo.setCreateBy(userId);
newInfo.setCreateTime(now); newInfo.setCreateTime(now);
newInfo.setUpdateBy(username); newInfo.setUpdateBy(userId);
newInfo.setUpdateTime(now); newInfo.setUpdateTime(now);
rkInfoMapper.insertRkInfo(newInfo); rkInfoMapper.insertRkInfo(newInfo);
// —— 新 rk_record /* ---------- 移库流水(重点) ---------- */
RkRecord newRecord = new RkRecord(); MoveRecord record = new MoveRecord();
BeanUtils.copyProperties(originRecord, newRecord, "id"); record.setRkInfoId(snapshot.getId()); // 原库存
newRecord.setCangku(target.getToCangku()); record.setNewRkInfoId(newInfo.getId()); // ✅ 关键新库存ID
newRecord.setPcode(target.getToPcode()); record.setEntityId(snapshot.getEntityId());
newRecord.setTrayCode(target.getToTrayCode()); record.setRealQty(target.getRealQty());
newRecord.setRealQty(target.getRealQty());
newRecord.setHasMoved("1");
newRecord.setCreateBy(username);
newRecord.setCreateTime(now);
newRecord.setUpdateBy(username);
newRecord.setUpdateTime(now);
rkRecordMapper.insertRkRecord(newRecord);
// —— move_record绑定新生成的 rk_info.id record.setFromCangku(snapshot.getCangku());
moveRecordMapper.insertMoveRecord( record.setFromPcode(snapshot.getPcode());
createMoveRecord(newInfo, target, dto) 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, @Override
MoveTargetItem target, @Transactional(rollbackFor = Exception.class)
MoveRequestDTO dto) { 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(); 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()); origin.setCangku(record.getFromCangku());
record.setFromPcode(info.getPcode()); origin.setPcode(record.getFromPcode());
record.setFromTrayCode(info.getTrayCode()); origin.setTrayCode(record.getFromTrayCode());
record.setToCangku(target.getToCangku()); origin.setHasMoved("0");
record.setToPcode(target.getToPcode()); origin.setUpdateBy(userId);
record.setToTrayCode(target.getToTrayCode()); origin.setUpdateTime(now);
record.setMoveReason(dto.getMoveReason());
record.setMovedBy(userId.toString());
record.setMovedAt(now);
record.setIsDelete("0"); rkInfoMapper.updateRkInfo(origin);
record.setCreateBy(userId.toString()); }
record.setCreateTime(now); // ---------- 情况二:部分移库 ----------
record.setUpdateBy(userId.toString()); 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); record.setUpdateTime(now);
moveRecordMapper.updateMoveRecord(record);
return record;
} }
} }

View File

@@ -5,6 +5,7 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.zg.common.exception.ServiceException;
import com.zg.common.utils.DateUtils; import com.zg.common.utils.DateUtils;
import com.zg.common.utils.SecurityUtils; import com.zg.common.utils.SecurityUtils;
import com.zg.common.utils.StringUtils; import com.zg.common.utils.StringUtils;
@@ -100,7 +101,7 @@ public class RkBillServiceImpl implements IRkBillService
Date now = DateUtils.getNowDate(); Date now = DateUtils.getNowDate();
String userId = String.valueOf(SecurityUtils.getUserId()); String userId = String.valueOf(SecurityUtils.getUserId());
// ================== 1. 主单 rk_bill ================== /* ================== 1. 主单 rk_bill ================== */
RkBill bill = new RkBill(); RkBill bill = new RkBill();
if (dto.getRkBill() != null) { if (dto.getRkBill() != null) {
BeanUtils.copyProperties(dto.getRkBill(), bill); BeanUtils.copyProperties(dto.getRkBill(), bill);
@@ -108,7 +109,7 @@ public class RkBillServiceImpl implements IRkBillService
bill.setBillNo(billNo); bill.setBillNo(billNo);
bill.setBizType("0"); // 入库 bill.setBizType("0"); // 入库
bill.setOperationTime(now); bill.setOperationTime(dto.getRkBill().getOperationTime());
bill.setCreateTime(now); bill.setCreateTime(now);
bill.setCreateBy(userId); bill.setCreateBy(userId);
bill.setIsDelete("0"); bill.setIsDelete("0");
@@ -118,17 +119,20 @@ public class RkBillServiceImpl implements IRkBillService
bill.setOperator(Integer.valueOf(userId)); bill.setOperator(Integer.valueOf(userId));
} }
// execStatus默认已完成 // execStatus默认已完成(预入库 / 直接入库统一处理)
if (StringUtils.isBlank(bill.getExecStatus())) { if (StringUtils.isBlank(bill.getExecStatus())) {
bill.setExecStatus("1"); bill.setExecStatus("1");
} }
rkBillMapper.insertRkBill(bill); rkBillMapper.insertRkBill(bill);
// ================== 2. 明细 + 事件 + 供应计划 ================== /* ================== 2. 明细 + 事件 + 供应计划 ================== */
for (RkInfo info : dto.getRkInfoList()) { for (RkInfo info : dto.getRkInfoList()) {
// ===== ① 根据 pcode 查询 pcde_detail获取 encoded_id ===== /* ====== ★ 0. 入库前:供应计划数量校验(核心新增) ====== */
checkGysJhQtyBeforeInStock(info);
/* ====== ① 根据 pcode 查询 pcde_detail获取 encoded_id ====== */
if (StringUtils.isNotBlank(info.getPcode())) { if (StringUtils.isNotBlank(info.getPcode())) {
PcdeDetail pcde = pcdeDetailMapper.selectByPcode(info.getPcode()); PcdeDetail pcde = pcdeDetailMapper.selectByPcode(info.getPcode());
@@ -139,7 +143,7 @@ public class RkBillServiceImpl implements IRkBillService
info.setPcodeId(pcde.getEncodedId()); info.setPcodeId(pcde.getEncodedId());
} }
// ---------- rk_info ---------- /* ---------- rk_info ---------- */
info.setBillNo(billNo); info.setBillNo(billNo);
info.setBizType(bill.getBizType()); info.setBizType(bill.getBizType());
info.setOperationType(bill.getOperationType()); info.setOperationType(bill.getOperationType());
@@ -159,20 +163,56 @@ public class RkBillServiceImpl implements IRkBillService
rkInfoMapper.insertRkInfo(info); rkInfoMapper.insertRkInfo(info);
// ---------- rk_record ---------- /* ---------- rk_record ---------- */
RkRecord record = buildInRkRecord(bill, info, now); RkRecord record = buildInRkRecord(bill, info, now);
record.setExecStatus(bill.getExecStatus()); record.setExecStatus(bill.getExecStatus());
record.setPcodeId(info.getPcodeId()); record.setPcodeId(info.getPcodeId());
rkRecordMapper.insertRkRecord(record); rkRecordMapper.insertRkRecord(record);
// ---------- 供应计划 ---------- /* ---------- 供应计划 ---------- */
handleGysJhAfterInStock(info); handleGysJhAfterInStock(info);
} }
return 1; 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) { private void handleGysJhAfterInStock(RkInfo info) {
// 1. 未关联供应计划或无实际入库数量,直接跳过 // 1. 未关联供应计划或无实际入库数量,直接跳过
@@ -180,29 +220,36 @@ public class RkBillServiceImpl implements IRkBillService
return; return;
} }
// 2. 扣减供应计划数量jh_qty = jh_qty - realQty // 2. 累加 实际入库数量real_qty = real_qty + 本次入库数量
gysJhMapper.decreaseJhQtyById( gysJhMapper.increaseRealQtyById(
info.getGysJhId(), info.getRealQty(),
info.getRealQty() info.getGysJhId()
); );
// 3. 查询最新供应计划数据(用于判断剩余数量) // 3. 查询最新供应计划数据
GysJh gysJh = gysJhMapper.selectGysJhById(info.getGysJhId()); GysJh gysJh = gysJhMapper.selectGysJhById(info.getGysJhId());
if (gysJh == null) { if (gysJh == null) {
throw new RuntimeException("供应计划不存在ID" + info.getGysJhId()); throw new RuntimeException("供应计划不存在ID" + info.getGysJhId());
} }
// 4. 根据剩余数量判断状态 BigDecimal planQty = gysJh.getJhQty(); // 计划交货数量
BigDecimal remainQty = gysJh.getJhQty(); BigDecimal realQty = gysJh.getRealQty(); // 实际入库数量
if (realQty == null) {
realQty = BigDecimal.ZERO;
}
// 4. 状态判定
String status; String status;
if (remainQty == null || remainQty.compareTo(BigDecimal.ZERO) <= 0) { if (realQty.compareTo(BigDecimal.ZERO) == 0) {
status = "1"; // 全部入库 status = "0"; // 未到货
} else if (planQty != null && realQty.compareTo(planQty) >= 0) {
status = "1"; // 已入库
} else { } else {
status = "2"; // 部分入库 status = "2"; // 部分入库
} }
// 5. 更新供应计划状态(⚠ 不使用 Map直接参数 // 5. 更新供应计划状态
gysJhMapper.updateStatusById(gysJh.getId(), status); gysJhMapper.updateStatusById(gysJh.getId(), status);
} }
@@ -294,8 +341,8 @@ public class RkBillServiceImpl implements IRkBillService
info.setCangku(bill.getCangku()); info.setCangku(bill.getCangku());
// ---------- 操作信息 ---------- // ---------- 操作信息 ----------
info.setOperationTime(now); info.setOperationTime(info.getOperationTime());
info.setOperator(bill.getOperator()); info.setOperator(info.getOperator());
// ---------- 执行状态 ---------- // ---------- 执行状态 ----------
info.setExecStatus(execStatus); info.setExecStatus(execStatus);
@@ -359,11 +406,50 @@ public class RkBillServiceImpl implements IRkBillService
* @return 结果 * @return 结果
*/ */
@Override @Override
public int deleteRkBillByIds(Long[] ids) @Transactional(rollbackFor = Exception.class)
{ public int deleteRkBillByIds(Long[] ids) {
return rkBillMapper.deleteRkBillByIds(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); 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; package com.zg.project.wisdom.service.impl;
import java.util.List; import java.math.BigDecimal;
import java.util.Objects; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.zg.common.exception.ServiceException; import com.zg.common.exception.ServiceException;
import com.zg.common.utils.DateUtils; import com.zg.common.utils.DateUtils;
import com.zg.common.utils.StringUtils; 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.domain.RkInfo;
import com.zg.project.wisdom.mapper.GysJhMapper;
import com.zg.project.wisdom.mapper.RkBillMapper; import com.zg.project.wisdom.mapper.RkBillMapper;
import com.zg.project.wisdom.mapper.RkInfoMapper; import com.zg.project.wisdom.mapper.RkInfoMapper;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
@@ -36,6 +38,9 @@ public class RkRecordServiceImpl implements IRkRecordService
@Autowired @Autowired
private RkBillMapper rkBillMapper; private RkBillMapper rkBillMapper;
@Autowired
private GysJhMapper gysJhMapper;
/** /**
* 查询出入库记录 * 查询出入库记录
* *
@@ -82,43 +87,178 @@ 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 出入库记录 * @param rkRecord 出入库记录
* @return 结果 * @return 影响行数
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public int updateRkRecord(RkRecord rkRecord) public int updateRkRecord(RkRecord rkRecord) {
{
// 1. 更新时间 /* ====================== 1. 基础校验 ====================== */
if (rkRecord == null || rkRecord.getId() == null) {
throw new RuntimeException("出入库记录ID不能为空");
}
rkRecord.setUpdateTime(DateUtils.getNowDate()); 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); int rows = rkRecordMapper.updateRkRecord(rkRecord);
if (rows <= 0) { if (rows <= 0) {
throw new RuntimeException("更新出入库记录失败"); 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; return rows;
} }
/** /**
* 批量删除出入库记录 * 批量删除出入库记录
* *
@@ -126,11 +266,92 @@ public class RkRecordServiceImpl implements IRkRecordService
* @return 结果 * @return 结果
*/ */
@Override @Override
public int deleteRkRecordByIds(Long[] ids) @Transactional(rollbackFor = Exception.class)
{ public int deleteRkRecordByIds(Long[] ids) {
return rkRecordMapper.deleteRkRecordByIds(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不能为空"); throw new ServiceException("撤销记录ID不能为空");
} }
// 1⃣ 查 records /* ====================== 1⃣ 查询入库记录 ====================== */
List<RkRecord> recordList = rkRecordMapper.selectRkRecordByIds(recordIds); List<RkRecord> recordList = rkRecordMapper.selectRkRecordByIds(recordIds);
if (recordList == null || recordList.isEmpty()) { if (recordList == null || recordList.isEmpty()) {
throw new ServiceException("入库记录不存在"); throw new ServiceException("入库记录不存在");
} }
// 2⃣ rk_record 批量回退 exec_status = 0 /* ====================== 2⃣ 校验是否发生过移库 ====================== */
rkRecordMapper.updateExecStatusByIds(recordIds, "0");
// 3⃣ rk_info 批量回退 exec_status = 0
List<Long> rkInfoIds = recordList.stream() List<Long> rkInfoIds = recordList.stream()
.map(RkRecord::getRkInfoId) .map(RkRecord::getRkInfoId)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.distinct() .distinct()
.collect(Collectors.toList()); .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()) { if (!rkInfoIds.isEmpty()) {
rkInfoMapper.updateExecStatusByIds(rkInfoIds, "0"); 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() List<String> billNos = recordList.stream()
.map(RkRecord::getBillNo) .map(RkRecord::getBillNo)
.filter(StringUtils::isNotBlank) .filter(StringUtils::isNotBlank)
@@ -186,62 +616,142 @@ public class RkRecordServiceImpl implements IRkRecordService
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public int finishIn(List<Long> recordIds) { public int finishOut(List<Long> recordIds) {
if (recordIds == null || recordIds.isEmpty()) { if (recordIds == null || recordIds.isEmpty()) {
throw new ServiceException("库记录ID不能为空"); throw new ServiceException("库记录ID不能为空");
} }
// 1⃣ 查询 rk_record // ================== 1. 查询 rk_record ==================
List<RkRecord> recordList = List<RkRecord> recordList = rkRecordMapper.selectRkRecordByIds(recordIds);
rkRecordMapper.selectRkRecordByIds(recordIds);
if (recordList == null || recordList.isEmpty()) { if (recordList == null || recordList.isEmpty()) {
throw new ServiceException("库记录不存在"); throw new ServiceException("库记录不存在");
} }
// ================== 只保留「预库」记录 ================== // ================== 2. 只保留「预库」记录 ==================
List<RkRecord> preRecordList = recordList.stream() List<RkRecord> preOutList = recordList.stream()
.filter(r -> "0".equals(r.getExecStatus())) .filter(r -> "1".equals(r.getBizType())) // 出库
.filter(r -> "0".equals(r.getExecStatus())) // 预出库
.collect(Collectors.toList()); .collect(Collectors.toList());
// ================== ② 如果没有可完成的数据,才报错 ================== if (preOutList.isEmpty()) {
if (preRecordList.isEmpty()) { throw new ServiceException("所选出库记录均已完成出库");
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) .map(RkRecord::getId)
.collect(Collectors.toList()); .collect(Collectors.toList());
rkRecordMapper.updateExecStatusByIds(preOutIds, "1");
// 2 rk_record.exec_status = 1 // rk_info.exec_status = 1
rkRecordMapper.updateExecStatusByIds(preRecordIds, "1"); List<Long> rkInfoIds = preOutList.stream()
// 3⃣ rk_info.exec_status = 1
List<Long> rkInfoIds = preRecordList.stream()
.map(RkRecord::getRkInfoId) .map(RkRecord::getRkInfoId)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.distinct() .distinct()
.collect(Collectors.toList()); .collect(Collectors.toList());
if (!rkInfoIds.isEmpty()) { if (!rkInfoIds.isEmpty()) {
rkInfoMapper.updateExecStatusByIds(rkInfoIds, "1"); rkInfoMapper.updateExecStatusByIds(rkInfoIds, "1");
} }
// 4 rk_bill.exec_status = 1 // rk_bill.exec_status = 1
List<String> billNos = preRecordList.stream() List<String> billNos = preOutList.stream()
.map(RkRecord::getBillNo) .map(RkRecord::getBillNo)
.filter(StringUtils::isNotBlank) .filter(StringUtils::isNotBlank)
.distinct() .distinct()
.collect(Collectors.toList()); .collect(Collectors.toList());
for (String billNo : billNos) { for (String billNo : billNos) {
rkBillMapper.updateExecStatusByBillNo(billNo, "1"); 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

@@ -19,6 +19,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="sapNo" column="sap_no"/> <result property="sapNo" column="sap_no"/>
<result property="xh" column="xh"/> <result property="xh" column="xh"/>
<result property="jhQty" column="jh_qty"/> <result property="jhQty" column="jh_qty"/>
<!-- 新增:已入库数量 -->
<result property="realQty" column="real_qty"/>
<result property="htQty" column="ht_qty"/> <result property="htQty" column="ht_qty"/>
<result property="dw" column="dw"/> <result property="dw" column="dw"/>
<result property="status" column="status"/> <result property="status" column="status"/>
@@ -32,7 +34,33 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap> </resultMap>
<sql id="selectGysJhVo"> <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> </sql>
<select id="selectGysJhList" parameterType="GysJh" resultMap="GysJhResult"> <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 != ''"> <if test="status != null and status != ''">
and status = #{status} and status = #{status}
</if> </if>
<!-- and (status is null or trim(status) != '1') -->
<if test="isDelete != null and isDelete != ''"> <if test="isDelete != null and isDelete != ''">
and is_delete = #{isDelete} and is_delete = #{isDelete}
</if> </if>
<!-- 按创建时间筛选create_time 在 [beginTime, endTime] -->
<if test="beginTime != null"> <if test="beginTime != null">
and create_time <![CDATA[>=]]> #{beginTime} and create_time <![CDATA[>=]]> #{beginTime}
</if> </if>
@@ -78,42 +104,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where id = #{id} where id = #{id}
</select> </select>
<select id="getBySapNo" parameterType="java.lang.String" resultMap="GysJhResult"> <select id="getBySapNo" parameterType="String" resultMap="GysJhResult">
<include refid="selectGysJhVo"/> <include refid="selectGysJhVo"/>
WHERE sap_no = #{sapNo} WHERE sap_no = #{sapNo}
</select> </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 id="selectByIds" resultMap="GysJhResult">
SELECT * FROM gys_jh SELECT *
FROM gys_jh
WHERE id IN WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")"> <foreach collection="ids" item="id" open="(" separator="," close=")">
#{id} #{id}
</foreach> </foreach>
</select> </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 id="insertGysJh" parameterType="GysJh" useGeneratedKeys="true" keyProperty="id">
insert into gys_jh insert into gys_jh
<trim prefix="(" suffix=")" suffixOverrides=","> <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="sapNo != null">sap_no,</if>
<if test="xh != null">xh,</if> <if test="xh != null">xh,</if>
<if test="jhQty != null">jh_qty,</if> <if test="jhQty != null">jh_qty,</if>
<!-- 新增 -->
<if test="realQty != null">real_qty,</if>
<if test="htQty != null">ht_qty,</if> <if test="htQty != null">ht_qty,</if>
<if test="dw != null">dw,</if> <if test="dw != null">dw,</if>
<if test="status != null">status,</if> <if test="status != null">status,</if>
@@ -155,6 +161,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="sapNo != null">#{sapNo},</if> <if test="sapNo != null">#{sapNo},</if>
<if test="xh != null">#{xh},</if> <if test="xh != null">#{xh},</if>
<if test="jhQty != null">#{jhQty},</if> <if test="jhQty != null">#{jhQty},</if>
<!-- 新增 -->
<if test="realQty != null">#{realQty},</if>
<if test="htQty != null">#{htQty},</if> <if test="htQty != null">#{htQty},</if>
<if test="dw != null">#{dw},</if> <if test="dw != null">#{dw},</if>
<if test="status != null">#{status},</if> <if test="status != null">#{status},</if>
@@ -168,6 +176,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</trim> </trim>
</insert> </insert>
<update id="increaseRealQtyById">
UPDATE gys_jh
SET real_qty = real_qty + #{realQty}
WHERE id = #{gysJhId}
</update>
<update id="updateGysJh" parameterType="GysJh"> <update id="updateGysJh" parameterType="GysJh">
update gys_jh update gys_jh
<trim prefix="SET" suffixOverrides=","> <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="sapNo != null">sap_no = #{sapNo},</if>
<if test="xh != null">xh = #{xh},</if> <if test="xh != null">xh = #{xh},</if>
<if test="jhQty != null">jh_qty = #{jhQty},</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="htQty != null">ht_qty = #{htQty},</if>
<if test="dw != null">dw = #{dw},</if> <if test="dw != null">dw = #{dw},</if>
<if test="status != null">status = #{status},</if> <if test="status != null">status = #{status},</if>
@@ -198,6 +213,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where id = #{id} where id = #{id}
</update> </update>
<!-- 下面原有方法全部保持不动 -->
<update id="updateStatusById" parameterType="map"> <update id="updateStatusById" parameterType="map">
UPDATE gys_jh UPDATE gys_jh
SET status = #{status} SET status = #{status}
@@ -246,10 +262,53 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND is_delete = '0' AND is_delete = '0'
</select> </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 id="updateDwById">
UPDATE gys_jh UPDATE gys_jh
SET dw = #{dw} SET dw = #{dw}
WHERE id = #{id} WHERE id = #{id}
</update> </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"> <resultMap type="com.zg.project.wisdom.domain.MoveRecord" id="MoveRecordResult">
<result property="id" column="id"/> <result property="id" column="id"/>
<!-- 关键字段rk_info_id --> <!-- 关键字段 -->
<result property="rkInfoId" column="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="entityId" column="entity_id"/>
<!-- 数量 -->
<result property="realQty" column="real_qty"/>
<!-- from -->
<result property="fromCangku" column="from_cangku"/> <result property="fromCangku" column="from_cangku"/>
<result property="fromPcode" column="from_pcode"/> <result property="fromPcode" column="from_pcode"/>
<result property="fromTrayCode" column="from_tray_code"/> <result property="fromTrayCode" column="from_tray_code"/>
<!-- to -->
<result property="toCangku" column="to_cangku"/> <result property="toCangku" column="to_cangku"/>
<result property="toPcode" column="to_pcode"/> <result property="toPcode" column="to_pcode"/>
<result property="toTrayCode" column="to_tray_code"/> <result property="toTrayCode" column="to_tray_code"/>
<!-- 业务 -->
<result property="moveReason" column="move_reason"/> <result property="moveReason" column="move_reason"/>
<result property="movedBy" column="moved_by"/> <result property="movedBy" column="moved_by"/>
<result property="movedByName" column="moved_by_name"/> <result property="movedByName" column="moved_by_name"/>
<result property="movedAt" column="moved_at"/> <result property="movedAt" column="moved_at"/>
<!-- 通用字段 -->
<result property="createBy" column="create_by"/> <result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/> <result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/> <result property="updateBy" column="update_by"/>
@@ -43,7 +54,9 @@
SELECT SELECT
id, id,
rk_info_id, rk_info_id,
new_rk_info_id,
entity_id, entity_id,
real_qty,
from_cangku, from_cangku,
from_pcode, from_pcode,
from_tray_code, from_tray_code,
@@ -68,7 +81,9 @@
keyProperty="id"> keyProperty="id">
INSERT INTO move_record ( INSERT INTO move_record (
rk_info_id, rk_info_id,
new_rk_info_id,
entity_id, entity_id,
real_qty,
from_cangku, from_cangku,
from_pcode, from_pcode,
from_tray_code, from_tray_code,
@@ -85,7 +100,9 @@
is_delete is_delete
) VALUES ( ) VALUES (
#{rkInfoId}, #{rkInfoId},
#{newRkInfoId},
#{entityId}, #{entityId},
#{realQty},
#{fromCangku}, #{fromCangku},
#{fromPcode}, #{fromPcode},
#{fromTrayCode}, #{fromTrayCode},
@@ -120,6 +137,7 @@
LEFT JOIN warehouse_info wi1 ON mr.from_cangku = wi1.warehouse_code LEFT JOIN warehouse_info wi1 ON mr.from_cangku = wi1.warehouse_code
LEFT JOIN warehouse_info wi2 ON mr.to_cangku = wi2.warehouse_code LEFT JOIN warehouse_info wi2 ON mr.to_cangku = wi2.warehouse_code
<where> <where>
mr.is_delete = 0
<if test="entityId != null and entityId != ''"> <if test="entityId != null and entityId != ''">
AND mr.entity_id = #{entityId} AND mr.entity_id = #{entityId}
</if> </if>
@@ -153,7 +171,9 @@
UPDATE move_record UPDATE move_record
<set> <set>
<if test="rkInfoId != null">rk_info_id = #{rkInfoId},</if> <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="entityId != null">entity_id = #{entityId},</if>
<if test="realQty != null">real_qty = #{realQty},</if>
<if test="fromCangku != null">from_cangku = #{fromCangku},</if> <if test="fromCangku != null">from_cangku = #{fromCangku},</if>
<if test="fromPcode != null">from_pcode = #{fromPcode},</if> <if test="fromPcode != null">from_pcode = #{fromPcode},</if>
<if test="fromTrayCode != null">from_tray_code = #{fromTrayCode},</if> <if test="fromTrayCode != null">from_tray_code = #{fromTrayCode},</if>

View File

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

View File

@@ -103,6 +103,8 @@
<include refid="selectRkInfoVo"/> <include refid="selectRkInfoVo"/>
<where> <where>
ri.exec_status = 1 ri.exec_status = 1
AND ri.is_chuku = 0
AND ri.is_delete = 0
<if test="operationType != null and operationType != ''"> <if test="operationType != null and operationType != ''">
AND ri.operation_type LIKE CONCAT('%', #{operationType}, '%') AND ri.operation_type LIKE CONCAT('%', #{operationType}, '%')
</if> </if>
@@ -159,16 +161,44 @@
WHERE ri.id = #{id} WHERE ri.id = #{id}
</select> </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 id="deleteRkInfoById" parameterType="Long">
DELETE FROM rk_info WHERE id = #{id} DELETE FROM rk_info WHERE id = #{id}
</delete> </delete>
<delete id="deleteRkInfoByIds" parameterType="java.util.List"> <delete id="deleteRkInfoByIds">
DELETE FROM rk_info DELETE FROM rk_info
WHERE id IN WHERE id IN
<foreach item="item" collection="list" open="(" separator="," close=")"> <foreach collection="ids" item="id" open="(" separator="," close=")">
#{item} #{id}
</foreach> </foreach>
</delete> </delete>
@@ -277,4 +307,27 @@
</foreach> </foreach>
</update> </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> </mapper>

View File

@@ -119,9 +119,21 @@
<select id="selectRkRecordList" parameterType="RkRecord" resultMap="RkRecordResult"> <select id="selectRkRecordList" parameterType="RkRecord" resultMap="RkRecordResult">
<include refid="selectRkRecordVo"/> <include refid="selectRkRecordVo"/>
<where> <where>
AND rr.exec_status = '1'
<if test="operationType != null and operationType != ''"> <if test="operationType != null and operationType != ''">
AND rr.operation_type = #{operationType} AND rr.operation_type = #{operationType}
</if> </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 != ''"> <if test="bizType != null and bizType != ''">
AND rr.biz_type = #{bizType} AND rr.biz_type = #{bizType}
</if> </if>
@@ -198,7 +210,7 @@
AND rr.operation_time &lt;= #{endDate} AND rr.operation_time &lt;= #{endDate}
</if> </if>
</where> </where>
ORDER BY rr.operation_time DESC ORDER BY rr.exec_status = '0' DESC, rr.operation_time DESC
</select> </select>
<!-- ===================== 按 ID 查询 ===================== --> <!-- ===================== 按 ID 查询 ===================== -->
@@ -212,7 +224,7 @@
<include refid="selectRkRecordVo"/> <include refid="selectRkRecordVo"/>
WHERE rr.bill_no = #{billNo} WHERE rr.bill_no = #{billNo}
AND (rr.is_delete = '0' OR rr.is_delete = 0 OR rr.is_delete IS NULL) 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>
<select id="selectRkRecordByIds" resultMap="RkRecordResult"> <select id="selectRkRecordByIds" resultMap="RkRecordResult">
@@ -224,6 +236,38 @@
</foreach> </foreach>
</select> </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" <insert id="insertRkRecord"
parameterType="RkRecord" parameterType="RkRecord"