库存模块功能开发

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

@@ -11,7 +11,12 @@ 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,236 +126,120 @@ 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() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId()) .append("id", getId())
.append("indexNo", getIndexNo()) .append("indexNo", getIndexNo())
.append("xj", getXj()) .append("xj", getXj())
.append("xmNo", getXmNo()) .append("xmNo", getXmNo())
.append("xmMs", getXmMs()) .append("xmMs", getXmMs())
.append("wlNo", getWlNo()) .append("wlNo", getWlNo())
.append("wlMs", getWlMs()) .append("wlMs", getWlMs())
.append("gysNo", getGysNo()) .append("gysNo", getGysNo())
.append("gysMc", getGysMc()) .append("gysMc", getGysMc())
.append("jhAmt", getJhAmt()) .append("jhAmt", getJhAmt())
.append("htDj", getHtDj()) .append("htDj", getHtDj())
.append("sapNo", getSapNo()) .append("sapNo", getSapNo())
.append("xh", getXh()) .append("xh", getXh())
.append("jhQty", getJhQty()) .append("jhQty", getJhQty())
.append("htQty", getHtQty()) .append("realQty", getRealQty())
.append("dw", getDw()) .append("waitQty", getWaitQty())
.append("status", getStatus()) .append("htQty", getHtQty())
.append("entityId", getEntityId()) .append("dw", getDw())
.append("remark", getRemark()) .append("status", getStatus())
.append("createBy", getCreateBy()) .append("entityId", getEntityId())
.append("createTime", getCreateTime()) .append("remark", getRemark())
.append("updateBy", getUpdateBy()) .append("createBy", getCreateBy())
.append("updateTime", getUpdateTime()) .append("createTime", getCreateTime())
.append("isDelete", getIsDelete()) .append("updateBy", getUpdateBy())
.toString(); .append("updateTime", getUpdateTime())
.append("isDelete", getIsDelete())
.toString();
} }
} }

View File

@@ -5,6 +5,7 @@ import com.zg.framework.aspectj.lang.annotation.Excel;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.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;
/** /**
* 查询出入库记录 * 查询出入库记录
* *
@@ -81,44 +86,179 @@ public class RkRecordServiceImpl implements IRkRecordService
/** /**
* 修改出入库记录 * 修改出入库记录
* *
* 业务约定:
* 1. rk_record.real_qty —— 单条出入库记录的最终数量(绝对值)
* 2. rk_info.real_qty —— 当前库存数量
* 3. gys_jh.real_qty —— 供应计划累计已入库数量
*
* 入库bizType = 0
* - 使用【新值 - 旧值】差量修正供应计划 real_qty
* - 严禁累计入库数量超过计划数量 jh_qty
* - 自动维护供应计划状态:
* real_qty == 0 → 未到货
* 0 < real_qty < jh_qty→ 部分到货
* real_qty == jh_qty → 已入库
*
* 出库bizType = 1
* - 出库数量不得超过库存
* - 出库数量变化联动库存 real_qty
* - 库存为 0 时 is_chuku = 1否则 = 0
*
* @param rkRecord 出入库记录 * @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

@@ -1,38 +1,66 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zg.project.wisdom.mapper.GysJhMapper"> <mapper namespace="com.zg.project.wisdom.mapper.GysJhMapper">
<resultMap type="GysJh" id="GysJhResult"> <resultMap type="GysJh" id="GysJhResult">
<result property="id" column="id" /> <result property="id" column="id"/>
<result property="indexNo" column="index_no" /> <result property="indexNo" column="index_no"/>
<result property="xj" column="xj" /> <result property="xj" column="xj"/>
<result property="xmNo" column="xm_no" /> <result property="xmNo" column="xm_no"/>
<result property="xmMs" column="xm_ms" /> <result property="xmMs" column="xm_ms"/>
<result property="wlNo" column="wl_no" /> <result property="wlNo" column="wl_no"/>
<result property="wlMs" column="wl_ms" /> <result property="wlMs" column="wl_ms"/>
<result property="gysNo" column="gys_no" /> <result property="gysNo" column="gys_no"/>
<result property="gysMc" column="gys_mc" /> <result property="gysMc" column="gys_mc"/>
<result property="jhAmt" column="jh_amt" /> <result property="jhAmt" column="jh_amt"/>
<result property="htDj" column="ht_dj" /> <result property="htDj" column="ht_dj"/>
<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="htQty" column="ht_qty" /> <!-- 新增:已入库数量 -->
<result property="dw" column="dw" /> <result property="realQty" column="real_qty"/>
<result property="status" column="status" /> <result property="htQty" column="ht_qty"/>
<result property="entityId" column="entity_id" /> <result property="dw" column="dw"/>
<result property="remark" column="remark" /> <result property="status" column="status"/>
<result property="createBy" column="create_by" /> <result property="entityId" column="entity_id"/>
<result property="createTime" column="create_time" /> <result property="remark" column="remark"/>
<result property="updateBy" column="update_by" /> <result property="createBy" column="create_by"/>
<result property="updateTime" column="update_time" /> <result property="createTime" column="create_time"/>
<result property="isDelete" column="is_delete" /> <result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="isDelete" column="is_delete"/>
</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>
@@ -72,48 +98,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</if> </if>
</where> </where>
</select> </select>
<select id="selectGysJhById" parameterType="Long" resultMap="GysJhResult"> <select id="selectGysJhById" parameterType="Long" resultMap="GysJhResult">
<include refid="selectGysJhVo"/> <include refid="selectGysJhVo"/>
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>
@@ -140,7 +146,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="updateBy != null">update_by,</if> <if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if> <if test="updateTime != null">update_time,</if>
<if test="isDelete != null">is_delete,</if> <if test="isDelete != null">is_delete,</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="indexNo != null">#{indexNo},</if> <if test="indexNo != null">#{indexNo},</if>
<if test="xj != null">#{xj},</if> <if test="xj != null">#{xj},</if>
@@ -155,6 +161,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="sapNo != null">#{sapNo},</if> <if test="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>
@@ -165,9 +173,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="updateBy != null">#{updateBy},</if> <if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if> <if test="updateTime != null">#{updateTime},</if>
<if test="isDelete != null">#{isDelete},</if> <if test="isDelete != null">#{isDelete},</if>
</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}
@@ -233,7 +249,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</delete> </delete>
<delete id="deleteGysJhByIds" parameterType="String"> <delete id="deleteGysJhByIds" parameterType="String">
delete from gys_jh where id in delete from gys_jh where id in
<foreach item="id" collection="array" open="(" separator="," close=")"> <foreach item="id" collection="array" open="(" separator="," close=")">
#{id} #{id}
</foreach> </foreach>
@@ -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"