From 816a071c79262808c24c77167e3b63c3d4ab9a0d Mon Sep 17 00:00:00 2001 From: yewj Date: Wed, 2 Apr 2025 10:09:19 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BA=93=E5=AD=98=E6=89=B9=E6=AC=A1=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=8C=E6=89=AB=E7=A0=81=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E6=89=AB=E7=A0=81=E5=86=B2=E7=AA=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inout/IoCodeTempController.java | 8 +- .../dao/collect/IoCollectErrorLogMapper.java | 9 + .../api/dao/inv/InvProductRecordMapper.java | 9 + .../api/entity/collect/IoCollectErrorLog.java | 71 ++++++ .../entity/inout/IoSplitFifoCodeEntity.java | 2 +- .../api/entity/inv/InvProductBatchEntity.java | 10 +- .../glxp/api/entity/inv/InvProductRecord.java | 123 +++++++++++ .../entity/inv/InventoryBatchAllocation.java | 18 ++ .../collect/IoCollectErrorLogService.java | 12 + .../api/service/inout/IoSplitCodeService.java | 197 +++++------------ .../service/inv/InvProductRecordService.java | 12 + .../inv/impl/InvProductBatchService.java | 209 ++++++++++++++++++ .../collect/IoCollectErrorLogMapper.xml | 20 ++ .../mapper/inv/InvProductRecordMapper.xml | 31 +++ src/main/resources/schemas/schema_v2.4.sql | 44 ++++ 15 files changed, 631 insertions(+), 144 deletions(-) create mode 100644 src/main/java/com/glxp/api/dao/collect/IoCollectErrorLogMapper.java create mode 100644 src/main/java/com/glxp/api/dao/inv/InvProductRecordMapper.java create mode 100644 src/main/java/com/glxp/api/entity/collect/IoCollectErrorLog.java create mode 100644 src/main/java/com/glxp/api/entity/inv/InvProductRecord.java create mode 100644 src/main/java/com/glxp/api/entity/inv/InventoryBatchAllocation.java create mode 100644 src/main/java/com/glxp/api/service/collect/IoCollectErrorLogService.java create mode 100644 src/main/java/com/glxp/api/service/inv/InvProductRecordService.java create mode 100644 src/main/resources/mybatis/mapper/collect/IoCollectErrorLogMapper.xml create mode 100644 src/main/resources/mybatis/mapper/inv/InvProductRecordMapper.xml diff --git a/src/main/java/com/glxp/api/controller/inout/IoCodeTempController.java b/src/main/java/com/glxp/api/controller/inout/IoCodeTempController.java index aa4173807..714c71d18 100644 --- a/src/main/java/com/glxp/api/controller/inout/IoCodeTempController.java +++ b/src/main/java/com/glxp/api/controller/inout/IoCodeTempController.java @@ -267,7 +267,7 @@ public class IoCodeTempController extends BaseController { List orderEntities = data.getOrderEntities(); if (collectOrder == null && CollUtil.isNotEmpty(orderEntities)) { - collectOrder = collectOrderService.unionSearch(null, null, orderEntities.get(0).getBillNo()); + collectOrder = collectOrderService.unionSearch(null, null, orderEntities.get(0).getBillNo()); } if (collectOrder != null) { @@ -282,11 +282,11 @@ public class IoCodeTempController extends BaseController { } } } - BaseResponse response = ResultVOUtils.error(501, com.glxp.api.util.StringUtils.isNotEmpty(baseResponse.getMessage())? + BaseResponse response = ResultVOUtils.error(501, com.glxp.api.util.StringUtils.isNotEmpty(baseResponse.getMessage()) ? "单据存在未对照产品: " + baseResponse.getMessage() : "未找到匹配单据"); response.setData(code); return response; - }else { + } else { BaseResponse response = ResultVOUtils.error(501, baseResponse.getMessage()); response.setData(code); return response; @@ -561,7 +561,7 @@ public class IoCodeTempController extends BaseController { @RepeatSubmit() @AuthRuleAnnotation("") @PostMapping("warehouse/inout/addOrderWeb") - @CusRedissonAnnotation(cacheName = RedissonCacheKey.WEB_ADD_CODE, key = {"#addOrderRequest.corpOrderId", "#addOrderRequest.code"}, timeOutMsg = "系统正在处理,请勿重复扫码") +// @CusRedissonAnnotation(cacheName = RedissonCacheKey.WEB_ADD_CODE, key = {"#addOrderRequest.corpOrderId", "#addOrderRequest.code"}, timeOutMsg = "系统正在处理,请勿重复扫码") @Log(title = "单据管理", businessType = BusinessType.INSERT) public BaseResponse addOrderWeb(@RequestBody AddOrderRequest addOrderRequest, BindingResult bindingResult) { diff --git a/src/main/java/com/glxp/api/dao/collect/IoCollectErrorLogMapper.java b/src/main/java/com/glxp/api/dao/collect/IoCollectErrorLogMapper.java new file mode 100644 index 000000000..8f06fbc4f --- /dev/null +++ b/src/main/java/com/glxp/api/dao/collect/IoCollectErrorLogMapper.java @@ -0,0 +1,9 @@ +package com.glxp.api.dao.collect; + +import com.glxp.api.dao.BaseMapperPlus; +import com.glxp.api.entity.collect.IoCollectErrorLog; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface IoCollectErrorLogMapper extends BaseMapperPlus { +} \ No newline at end of file diff --git a/src/main/java/com/glxp/api/dao/inv/InvProductRecordMapper.java b/src/main/java/com/glxp/api/dao/inv/InvProductRecordMapper.java new file mode 100644 index 000000000..6b50f1871 --- /dev/null +++ b/src/main/java/com/glxp/api/dao/inv/InvProductRecordMapper.java @@ -0,0 +1,9 @@ +package com.glxp.api.dao.inv; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.glxp.api.entity.inv.InvProductRecord; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface InvProductRecordMapper extends BaseMapper { +} \ No newline at end of file diff --git a/src/main/java/com/glxp/api/entity/collect/IoCollectErrorLog.java b/src/main/java/com/glxp/api/entity/collect/IoCollectErrorLog.java new file mode 100644 index 000000000..12ad608fd --- /dev/null +++ b/src/main/java/com/glxp/api/entity/collect/IoCollectErrorLog.java @@ -0,0 +1,71 @@ +package com.glxp.api.entity.collect; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import java.io.Serializable; +import java.util.Date; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 手动扫码冲突替换码信息 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "io_collect_error_log") +public class IoCollectErrorLog implements Serializable { + @TableId(value = "id", type = IdType.INPUT) + private Integer id; + + /** + * 单据号 + */ + @TableField(value = "orderId") + private Long orderId; + + /** + * 自动赋码 + */ + @TableField(value = "autoCode") + private String autoCode; + + /** + * 手动赋码 + */ + @TableField(value = "manuCode") + private String manuCode; + + /** + * 错误类型:1:未上传医保替换码;2:已上传替换码 + */ + @TableField(value = "`type`") + private Integer type; + + /** + * 更新时间 + */ + @TableField(value = "updateTime") + private Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updateUser") + private Date updateUser; + + /** + * 备注 + */ + @TableField(value = "remark") + private String remark; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/glxp/api/entity/inout/IoSplitFifoCodeEntity.java b/src/main/java/com/glxp/api/entity/inout/IoSplitFifoCodeEntity.java index 63c37017d..605da8adf 100644 --- a/src/main/java/com/glxp/api/entity/inout/IoSplitFifoCodeEntity.java +++ b/src/main/java/com/glxp/api/entity/inout/IoSplitFifoCodeEntity.java @@ -15,7 +15,7 @@ import java.io.Serializable; import java.util.Date; /** - * 工位队列码明细 + * 工位队列码明细(整取) */ @ApiModel(value = "com-glxp-api-entity-inout-IoSplitFifoCode") @Data diff --git a/src/main/java/com/glxp/api/entity/inv/InvProductBatchEntity.java b/src/main/java/com/glxp/api/entity/inv/InvProductBatchEntity.java index 34e32f6f9..1559fce22 100644 --- a/src/main/java/com/glxp/api/entity/inv/InvProductBatchEntity.java +++ b/src/main/java/com/glxp/api/entity/inv/InvProductBatchEntity.java @@ -25,7 +25,7 @@ public class InvProductBatchEntity implements Serializable { @TableId(value = "id", type = IdType.INPUT) @ApiModelProperty(value = "") - private Integer id; + private Long id; /** @@ -104,7 +104,6 @@ public class InvProductBatchEntity implements Serializable { private Integer reCount; - /** * 供应商ID */ @@ -139,7 +138,12 @@ public class InvProductBatchEntity implements Serializable { @TableField(value = "`invCode`") @ApiModelProperty(value = "仓库编码") private String invCode; - + /** + * 状态(1:正常,0:已清空) + */ + @TableField(value = "`status`") + @ApiModelProperty(value = "库存批次状态") + private Integer status; private static final long serialVersionUID = 1L; diff --git a/src/main/java/com/glxp/api/entity/inv/InvProductRecord.java b/src/main/java/com/glxp/api/entity/inv/InvProductRecord.java new file mode 100644 index 000000000..26956d68d --- /dev/null +++ b/src/main/java/com/glxp/api/entity/inv/InvProductRecord.java @@ -0,0 +1,123 @@ +package com.glxp.api.entity.inv; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +import lombok.Data; + +/** + * 库存流水表 + */ +@Data +@TableName(value = "inv_product_record") +public class InvProductRecord implements Serializable { + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.INPUT) + private Long id; + + /** + * 产品ID + */ + @TableField(value = "productId") + private Long productId; + + /** + * 仓库ID + */ + @TableField(value = "warehouseId") + private Long warehouseId; + + /** + * 内部批号 + */ + @TableField(value = "inBatchNo") + private String inBatchNo; + + /** + * 批次号 + */ + @TableField(value = "batchNo") + private String batchNo; + + /** + * 关联单据号 + */ + @TableField(value = "orderId") + private String orderId; + + /** + * 单据类型(1:入库,2:出库) + */ + @TableField(value = "orderType") + private Integer orderType; + + /** + * 操作数量 + */ + @TableField(value = "quantity") + private Integer quantity; + + /** + * 单价 + */ + @TableField(value = "unitPrice") + private BigDecimal unitPrice; + + /** + * 操作前数量 + */ + @TableField(value = "beforeQuantity") + private BigDecimal beforeQuantity; + + /** + * 操作后数量 + */ + @TableField(value = "afterQuantity") + private BigDecimal afterQuantity; + + /** + * 备注 + */ + @TableField(value = "remark") + private String remark; + + /** + * 创建人 + */ + @TableField(value = "creator") + private String creator; + + /** + * 创建时间 + */ + @TableField(value = "createTime") + private Date createTime; + + /** + * 更新时间 + */ + @TableField(value = "updateTime") + private Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updater") + private String updater; + + /** + * 租户ID + */ + @TableField(value = "tenantId") + private Long tenantId; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/glxp/api/entity/inv/InventoryBatchAllocation.java b/src/main/java/com/glxp/api/entity/inv/InventoryBatchAllocation.java new file mode 100644 index 000000000..d364272dd --- /dev/null +++ b/src/main/java/com/glxp/api/entity/inv/InventoryBatchAllocation.java @@ -0,0 +1,18 @@ +package com.glxp.api.entity.inv; + +import lombok.Builder; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author AnthonyYwj + */ +@Data +@Builder +public class InventoryBatchAllocation { + private InvProductBatchEntity batch; // 批次信息 + private Integer allocatedQuantity; // 分配数量 + private BigDecimal unitPrice; // 单价 + private String expiryDate; // 效期 +} diff --git a/src/main/java/com/glxp/api/service/collect/IoCollectErrorLogService.java b/src/main/java/com/glxp/api/service/collect/IoCollectErrorLogService.java new file mode 100644 index 000000000..4415f4d34 --- /dev/null +++ b/src/main/java/com/glxp/api/service/collect/IoCollectErrorLogService.java @@ -0,0 +1,12 @@ +package com.glxp.api.service.collect; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import java.util.List; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.glxp.api.dao.collect.IoCollectErrorLogMapper; +import com.glxp.api.entity.collect.IoCollectErrorLog; +@Service +public class IoCollectErrorLogService extends ServiceImpl { + +} diff --git a/src/main/java/com/glxp/api/service/inout/IoSplitCodeService.java b/src/main/java/com/glxp/api/service/inout/IoSplitCodeService.java index f80dbfae3..7c515b900 100644 --- a/src/main/java/com/glxp/api/service/inout/IoSplitCodeService.java +++ b/src/main/java/com/glxp/api/service/inout/IoSplitCodeService.java @@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.github.pagehelper.PageHelper; import com.glxp.api.constant.Constant; import com.glxp.api.dao.collect.IoCollectCodeBackMapper; +import com.glxp.api.dao.collect.IoCollectErrorLogMapper; import com.glxp.api.dao.collect.IoCollectOrderBackupMapper; import com.glxp.api.dao.inout.IoCodeLostMapper; import com.glxp.api.dao.inout.IoSplitCodeMapper; @@ -130,23 +131,7 @@ public class IoSplitCodeService extends ServiceImpl 0 ? count : 0) - .reCount(unCount) - .build()); + collectOrderCodeAutoService.save(IoCollectOrderCodeAuto.builder().codeIdFk(ioSplitCodeEntity.getId()).udiCode(ioSplitCodeEntity.getCode()).orderIdFk(collectOrder.getBillNo()).batchNo(ioSplitCodeEntity.getBatchNo()).productDate(ioSplitCodeEntity.getProduceDate()).expireDate(ioSplitCodeEntity.getExpireDate()).serialNo(ioSplitCodeEntity.getSerialNo()).relId(collectOrderBiz.getRelId()).bizIdFk(collectOrderBiz.getId()).fifoSplit(1).createTime(new Date()).updateTime(new Date()).count(count > 0 ? count : 0).reCount(unCount).build()); if (count > 0) { ioSplitCodeEntity.setRemainCount(count); unCount = 0; @@ -169,7 +154,8 @@ public class IoSplitCodeService extends ServiceImpl 0) { count = IntUtil.value(ioSplitCodeEntity.getRemainCount()) - unCount; - collectOrderCodeAutoService.save(IoCollectOrderCodeAuto.builder() - .codeIdFk(ioSplitCodeEntity.getId()) - .udiCode(ioSplitCodeEntity.getCode()) - .orderIdFk(collectOrder.getBillNo()) - .batchNo(ioSplitCodeEntity.getBatchNo()) - .productDate(ioSplitCodeEntity.getProduceDate()) - .expireDate(ioSplitCodeEntity.getExpireDate()) - .serialNo(ioSplitCodeEntity.getSerialNo()) - .relId(collectOrderBiz.getRelId()) - .bizIdFk(collectOrderBiz.getId()) - .fifoSplit(count > 0 ? count : 0) - .reCount(count) - .createTime(new Date()) - .updateTime(new Date()) - .build()); + collectOrderCodeAutoService.save(IoCollectOrderCodeAuto.builder().codeIdFk(ioSplitCodeEntity.getId()).udiCode(ioSplitCodeEntity.getCode()).orderIdFk(collectOrder.getBillNo()).batchNo(ioSplitCodeEntity.getBatchNo()).productDate(ioSplitCodeEntity.getProduceDate()).expireDate(ioSplitCodeEntity.getExpireDate()).serialNo(ioSplitCodeEntity.getSerialNo()).relId(collectOrderBiz.getRelId()).bizIdFk(collectOrderBiz.getId()).fifoSplit(count > 0 ? count : 0).reCount(count).createTime(new Date()).updateTime(new Date()).build()); if (count > 0) { ioSplitCodeEntity.setRemainCount(count); splitCodeMapper.updateById(ioSplitCodeEntity); @@ -239,7 +211,12 @@ public class IoSplitCodeService extends ServiceImpl collectOrderCodeMEN = collectOrderCodeManService.listByBillNo(collectOrder.getBillNo()); if (CollUtil.isNotEmpty(collectOrderCodeMEN)) { +// replaceCode(collectOrderCodeMEN, collectOrder); for (IoCollectOrderCodeMan collectOrderCodeMan : collectOrderCodeMEN) { + if (IntUtil.value(collectOrderCodeMan.getRemoveFlag())) { + continue; + } + IoSplitFifoCodeEntity splitFifoCodeEntity = splitFifoCodeService.findByCode(collectOrderCodeMan.getUdiCode(), putWorkPlaceCode); if (splitFifoCodeEntity != null) { collectOrderCodeMan.setRemoveFlag(true); @@ -321,13 +298,7 @@ public class IoSplitCodeService extends ServiceImpl collectCodeBackups = collectCodeBackMapper.selectList(new LambdaQueryWrapper() - .eq(IoCollectCodeBackup::getBusType, collectOrder.getBusType()) - .eq(IoCollectCodeBackup::getCode, collectOrderCodeMan.getUdiCode()) - ); - + List collectCodeBackups = collectCodeBackMapper.selectList(new LambdaQueryWrapper().eq(IoCollectCodeBackup::getBusType, collectOrder.getBusType()).eq(IoCollectCodeBackup::getCode, collectOrderCodeMan.getUdiCode())); if (CollUtil.isNotEmpty(collectCodeBackups)) { IoCollectCodeBackup collectCodeBackup = collectCodeBackups.get(0); + + if (IntUtil.value(collectCodeBackup.getFifoSplit()) == 3) { + throw new JsonException(500, collectCodeBackup.getCode() + "重复扫码,请盘查后重试!"); + } + + IoCollectOrderBackup collectOrderBackup = collectOrderBackupMapper.selectOne(new LambdaQueryWrapper().eq(IoCollectOrderBackup::getBillNo, collectCodeBackup.getBillNo())); + collectOrderCodeMan.setRemoveFlag(true); // 单据已被上传至医保 if (IntUtil.value(collectOrderBackup.getUploadStatus()) == 2) { - + IoSplitFifoCodeEntity splitFifoCodeEntity = removeInvByCode(collectOrderCodeMan, collectOrder.getWorkPlaceCode()); + if (splitFifoCodeEntity == null) { + throw new JsonException(500, "工位存量不足!"); + } + IoCollectErrorLog ioCollectErrorLog = IoCollectErrorLog.builder().orderId(collectOrder.getId()).autoCode(splitFifoCodeEntity.getCode()).manuCode(collectOrderCodeMan.getUdiCode()).type(2) //未上传医保替换码 + .updateTime(new Date()).build(); + collectErrorLogMapper.insert(ioCollectErrorLog); + collectCodeBackup.setCode(splitFifoCodeEntity.getCode()); + collectCodeBackMapper.updateById(collectCodeBackup); } else { //单据未上传医保,上传医保失败 - + IoSplitFifoCodeEntity splitFifoCodeEntity = removeInvByCode(collectOrderCodeMan, collectOrder.getWorkPlaceCode()); + if (splitFifoCodeEntity == null) { + throw new JsonException(500, "工位存量不足!"); + } + IoCollectErrorLog ioCollectErrorLog = IoCollectErrorLog.builder().orderId(collectOrder.getId()).autoCode(splitFifoCodeEntity.getCode()).manuCode(collectOrderCodeMan.getUdiCode()).type(1) //未上传医保替换码 + .updateTime(new Date()).build(); + collectErrorLogMapper.insert(ioCollectErrorLog); + //替换已完成单据的码 + collectCodeBackup.setCode(splitFifoCodeEntity.getCode()); + collectCodeBackMapper.updateById(collectCodeBackup); } } - - // 2. 查找已完成单据码是否已被上传 - - // 3. 查找已完成码,标记该码已被手动赋码 - - // 4. 查找码库未被使用的码,赋码至已完成的码,插入码表记录 - - // 5. 如果已被上传,则标记该码已被上传,从码库查出未被使用的码,插入码表记录,标记该码已被上传,替换成已码库的码 - - - // 5. 查找 - } } + @Resource + IoCollectErrorLogMapper collectErrorLogMapper; /** * 工位查找未使用码进行抠库 */ - public void removeInvByCode(IoCollectOrderCodeMan collectOrderCodeMan, Long putWorkPlaceCode) { + public IoSplitFifoCodeEntity removeInvByCode(IoCollectOrderCodeMan collectOrderCodeMan, Long putWorkPlaceCode) { List splitFifoCodeEntitys = splitFifoCodeService.findByRelId(putWorkPlaceCode, collectOrderCodeMan.getRelId(), collectOrderCodeMan.getBatchNo(), null); if (CollUtil.isNotEmpty(splitFifoCodeEntitys)) { IoSplitFifoCodeEntity splitFifoCodeEntity = splitFifoCodeEntitys.get(0); - collectOrderCodeMan.setRemoveFlag(true); - if (IntUtil.value(splitFifoCodeEntity.getScanCount()) - IntUtil.value(collectOrderCodeMan.getScanCount()) <= 0) { - splitFifoCodeService.removeById(splitFifoCodeEntity.getId()); - IoSplitFifoInv splitFifoInv = splitFifoInvService.findByFifoCode(splitFifoCodeEntity); - if (splitFifoInv != null) { - splitFifoInv.setOutCount(IntUtil.value(splitFifoInv.getOutCount()) + IntUtil.value(splitFifoCodeEntity.getTotalCount())); - splitFifoInv.setOutCodeCount(IntUtil.value(splitFifoInv.getOutCodeCount()) + IntUtil.value(splitFifoCodeEntity.getScanCount())); - int lockCount = IntUtil.value(splitFifoInv.getLockCount()) - IntUtil.value(splitFifoCodeEntity.getTotalCount()); - splitFifoInv.setLockCount(lockCount > 0 ? lockCount : 0); - splitFifoInv.setReCount(IntUtil.value(splitFifoInv.getInCount()) - IntUtil.value(splitFifoInv.getOutCount())); - splitFifoInv.setReCodeCount(IntUtil.value(splitFifoInv.getInCodeCount()) - IntUtil.value(splitFifoInv.getOutCodeCount())); - splitFifoInv.setAvailableCount(IntUtil.value(splitFifoInv.getInCount()) - IntUtil.value(splitFifoInv.getLockCount()) - IntUtil.value(splitFifoInv.getOutCount())); - splitFifoInvService.updateById(splitFifoInv); - } - - } else { - //队列码数量大于扫码数量更新数量(一般指无序列号) - FilterUdiRelRequest filterUdiRelRequest = new FilterUdiRelRequest(); - filterUdiRelRequest.setId(collectOrderCodeMan.getRelId()); - filterUdiRelRequest.setPackLevel("1"); - UdiRelevanceResponse udiRelevanceResponse = udiRelevanceService.selectOneUdi(filterUdiRelRequest); - int removeCount = IntUtil.value(collectOrderCodeMan.getScanCount()) * udiRelevanceResponse.getBhxjsl(); - splitFifoCodeService.updateById(IoSplitFifoCodeEntity.builder().id(splitFifoCodeEntity.getId()).scanCount(IntUtil.value(splitFifoCodeEntity.getScanCount()) - IntUtil.value(collectOrderCodeMan.getScanCount())).totalCount(IntUtil.value(splitFifoCodeEntity.getTotalCount()) - removeCount).build()); - - IoSplitFifoInv splitFifoInv = splitFifoInvService.findByFifoCode(splitFifoCodeEntity); - if (splitFifoInv != null) { - splitFifoInv.setOutCount(IntUtil.value(splitFifoInv.getOutCount()) + removeCount); - splitFifoInv.setOutCodeCount(IntUtil.value(splitFifoInv.getOutCodeCount()) + IntUtil.value(collectOrderCodeMan.getScanCount())); - int lockCount = IntUtil.value(splitFifoInv.getLockCount()) - removeCount; - splitFifoInv.setLockCount(lockCount > 0 ? lockCount : 0); - splitFifoInv.setReCount(IntUtil.value(splitFifoInv.getInCount()) - IntUtil.value(splitFifoInv.getOutCount())); - splitFifoInv.setReCodeCount(IntUtil.value(splitFifoInv.getInCodeCount()) - IntUtil.value(splitFifoInv.getOutCodeCount())); - splitFifoInv.setAvailableCount(IntUtil.value(splitFifoInv.getInCount()) - IntUtil.value(splitFifoInv.getLockCount()) - IntUtil.value(splitFifoInv.getOutCount())); - splitFifoInvService.updateById(splitFifoInv); - } + splitFifoCodeService.removeById(splitFifoCodeEntity.getId()); + IoSplitFifoInv splitFifoInv = splitFifoInvService.findByFifoCode(splitFifoCodeEntity); + if (splitFifoInv != null) { + splitFifoInv.setOutCount(IntUtil.value(splitFifoInv.getOutCount()) + IntUtil.value(splitFifoCodeEntity.getTotalCount())); + splitFifoInv.setOutCodeCount(IntUtil.value(splitFifoInv.getOutCodeCount()) + IntUtil.value(splitFifoCodeEntity.getScanCount())); + int lockCount = IntUtil.value(splitFifoInv.getLockCount()) - IntUtil.value(splitFifoCodeEntity.getTotalCount()); + splitFifoInv.setLockCount(lockCount > 0 ? lockCount : 0); + splitFifoInv.setReCount(IntUtil.value(splitFifoInv.getInCount()) - IntUtil.value(splitFifoInv.getOutCount())); + splitFifoInv.setReCodeCount(IntUtil.value(splitFifoInv.getInCodeCount()) - IntUtil.value(splitFifoInv.getOutCodeCount())); + splitFifoInv.setAvailableCount(IntUtil.value(splitFifoInv.getInCount()) - IntUtil.value(splitFifoInv.getLockCount()) - IntUtil.value(splitFifoInv.getOutCount())); + splitFifoInvService.updateById(splitFifoInv); } + + return splitFifoCodeEntity; } + return null; } /** @@ -542,8 +489,7 @@ public class IoSplitCodeService extends ServiceImpl filterCode - (List collectOrderCodes, IoCollectOrderBiz collectOrderBiz, UdiRelevanceResponse - udiRelevanceResponse) { + public List filterCode(List collectOrderCodes, IoCollectOrderBiz collectOrderBiz, UdiRelevanceResponse udiRelevanceResponse) { int unTagCount = IntUtil.value(collectOrderBiz.getScanActCount()) - collectOrderBiz.getCount(); List newList = new ArrayList(); int count = 0; @@ -770,11 +700,7 @@ public class IoSplitCodeService extends ServiceImpl() - .eq("code", ioSplitFifoCodeEntity.getCode()) - .last("limit 1") - ); + IoCodeLostEntity ioCodeLostEntity = ioCodeLostMapper.selectOne(new QueryWrapper().eq("code", ioSplitFifoCodeEntity.getCode()).last("limit 1")); if (ioCodeLostEntity != null && StringUtils.isNotEmpty(ioCodeLostEntity.getBatchNo())) { collectOrderBiz.setBatchNo(ioCodeLostEntity.getBatchNo()); collectOrderBiz.setProductDate(ioCodeLostEntity.getProduceDate()); @@ -840,8 +766,7 @@ public class IoSplitCodeService extends ServiceImpl splitFifoCodeEntities; diff --git a/src/main/java/com/glxp/api/service/inv/InvProductRecordService.java b/src/main/java/com/glxp/api/service/inv/InvProductRecordService.java new file mode 100644 index 000000000..2efbeaa5d --- /dev/null +++ b/src/main/java/com/glxp/api/service/inv/InvProductRecordService.java @@ -0,0 +1,12 @@ +package com.glxp.api.service.inv; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import java.util.List; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.glxp.api.entity.inv.InvProductRecord; +import com.glxp.api.dao.inv.InvProductRecordMapper; +@Service +public class InvProductRecordService extends ServiceImpl { + +} diff --git a/src/main/java/com/glxp/api/service/inv/impl/InvProductBatchService.java b/src/main/java/com/glxp/api/service/inv/impl/InvProductBatchService.java index 3056b34da..e4be38db3 100644 --- a/src/main/java/com/glxp/api/service/inv/impl/InvProductBatchService.java +++ b/src/main/java/com/glxp/api/service/inv/impl/InvProductBatchService.java @@ -1,17 +1,226 @@ package com.glxp.api.service.inv.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.glxp.api.dao.inv.InvProductBatchDao; +import com.glxp.api.dao.inv.InvProductRecordMapper; +import com.glxp.api.entity.inv.InvProductRecord; +import com.glxp.api.entity.inv.InventoryBatchAllocation; +import com.glxp.api.entity.inout.IoOrderDetailResultEntity; +import com.glxp.api.entity.inout.IoOrderEntity; import com.glxp.api.entity.inv.InvProductBatchEntity; +import com.glxp.api.exception.JsonException; +import com.glxp.api.service.inv.InvProductRecordService; +import io.swagger.models.auth.In; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + @Service @Transactional(rollbackFor = Exception.class) public class InvProductBatchService extends ServiceImpl { + @Resource + private InvProductBatchDao inventoryBatchMapper; + @Resource + InvProductRecordService inventoryRecordService; + + public Long createInventoryBatch(InvProductBatchEntity inventoryBatchDO) { + // 插入 + inventoryBatchMapper.insert(inventoryBatchDO); + // 返回 + return inventoryBatchDO.getId(); + } + + public void updateInventoryBatch(InvProductBatchEntity inventoryBatchDO) { + // 校验存在 + validateInventoryBatchExists(inventoryBatchDO.getId()); + // 更新 + inventoryBatchMapper.updateById(inventoryBatchDO); + } + + public void deleteInventoryBatch(Long id) { + // 校验存在 + validateInventoryBatchExists(id); + // 删除 + inventoryBatchMapper.deleteById(id); + } + + private void validateInventoryBatchExists(Long id) { + if (inventoryBatchMapper.selectById(id) == null) { + throw new JsonException("库存不存在"); + } + } + + + /** + * 按FIFO原则查询可用批次 + */ + public List selectBatchesByFIFO(Long productId, Long warehouseId, Integer requiredQuantity) { + // 1. 按入库时间正序查询可用批次 + List availableBatches = inventoryBatchMapper.selectList(new LambdaQueryWrapper() + .eq(InvProductBatchEntity::getRelIdFk, productId) + .eq(InvProductBatchEntity::getInvCode, warehouseId) + .gt(InvProductBatchEntity::getReCount, 0) // 修正:数量大于0的批次 + .eq(InvProductBatchEntity::getStatus, 1) // 添加:状态为正常的批次 + .orderByAsc(InvProductBatchEntity::getCreateTime)); // 修改:按入库时间排序 + + List allocations = new ArrayList<>(); + int remainingQuantity = requiredQuantity; + + // 2. FIFO分配 + for (InvProductBatchEntity batch : availableBatches) { + if (remainingQuantity <= 0) break; + + int allocateQuantity = Math.min(remainingQuantity, batch.getReCount()); + allocations.add(InventoryBatchAllocation.builder() + .batch(batch) + .allocatedQuantity(allocateQuantity) + .unitPrice(batch.getPrice()) + .expiryDate(batch.getExpireDate()) + .build()); + remainingQuantity -= allocateQuantity; + } + + // 3. 检查是否满足出库数量 + if (remainingQuantity > 0) { + throw new JsonException(400, "库存不足,缺少数量:" + remainingQuantity); + } + + return allocations; + } + + /** + * 处理批次出库 + */ + @Transactional(rollbackFor = Exception.class) + public void processOutboundAllocations(IoOrderEntity order, IoOrderDetailResultEntity item, + List allocations) { + + // 2. 更新批次库存 + for (InventoryBatchAllocation allocation : allocations) { + // 更新批次数量 + InvProductBatchEntity batchDO = allocation.getBatch(); + batchDO.setReCount(batchDO.getReCount() - allocation.getAllocatedQuantity()); + if (batchDO.getReCount() == 0) { + batchDO.setStatus(0); // 批次已清空 + } + updateInventoryBatch(batchDO); + // 创建出库流水 + createOutboundRecord(order, item, allocation); + } + + // 3. 更新主表库存 + int totalOutQuantity = allocations.stream() + .mapToInt(InventoryBatchAllocation::getAllocatedQuantity) + .sum(); + // todo 更新inv_product 主表 + + + } + + + /** + * 创建出库流水记录 + */ + private void createOutboundRecord(IoOrderEntity order, IoOrderDetailResultEntity item, + InventoryBatchAllocation allocation + ) { + InvProductRecord recordDO = new InvProductRecord(); + recordDO.setProductId(item.getBindRlFk()); + recordDO.setWarehouseId(order.getWarehouseId()); + recordDO.setInBatchNo(allocation.getBatch().getInBatchNo()); + recordDO.setBatchNo(allocation.getBatch().getBatchNo()); + recordDO.setOrderId(order.getBillNo()); + recordDO.setOrderType(2); // 出库类型 + recordDO.setQuantity(allocation.getAllocatedQuantity()); + recordDO.setUnitPrice(allocation.getUnitPrice()); + recordDO.setRemark("FIFO出库"); + inventoryRecordService.save(recordDO); + } + + /** + * 验证出库规则 + */ + public void validateOutbound(IoOrderEntity order, List items) { + // 1. 验证库存是否足够 + validateStock(items, order.getWarehouseId()); + // 2. 验证效期 + validateExpiry(items, order); + // 3. 验证业务规则 + validateBusinessRules(order, items); + } + + /** + * 验证库存是否足够 + */ + private void validateStock(List items, Long warehouseId) { + for (IoOrderDetailResultEntity item : items) { + try { + // 尝试FIFO分配,如果不足会抛出异常 + selectBatchesByFIFO(item.getBindRlFk(), warehouseId, item.getReCount()); + } catch (Exception e) { + throw new JsonException(400, "库存不足,缺少数量:" + item.getReCount()); + } + } + } + + /** + * 验证效期 + */ + private void validateExpiry(List items, IoOrderEntity orderDO) { + LocalDateTime now = LocalDateTime.now(); + for (IoOrderDetailResultEntity item : items) { + List batches = inventoryBatchMapper.selectList( + new LambdaQueryWrapper() + .eq(InvProductBatchEntity::getRelIdFk, item.getBindRlFk()) + .eq(InvProductBatchEntity::getInvCode, orderDO.getWarehouseId()) + .gt(InvProductBatchEntity::getReCount, 0) + .lt(InvProductBatchEntity::getExpireDate, now)); + + if (!batches.isEmpty()) { + + throw new JsonException(400, "存在效期不足的批次"); + } + } + } + + /** + * 验证特殊业务规则 + */ + private void validateBusinessRules(IoOrderEntity order, List items) { + switch (order.getAction()) { + case "SALE": // 销售出库 + break; + case "TRANSFER": // 调拨出库 + break; + default: + // 其他类型的出库单据,可以添加相应的验证规则 + break; + } + } + + /** + * 验证销售出库规则 + */ + private void validateSaleRules(List items) { + // 可以添加销售特有的验证规则 + // 例如:价格检查、客户信用额度检查等 + } + + /** + * 验证调拨出库规则 + */ + private void validateTransferRules(List items) { + // 可以添加调拨特有的验证规则 + // 例如:调入仓库容量检查、调拨权限检查等 + } } diff --git a/src/main/resources/mybatis/mapper/collect/IoCollectErrorLogMapper.xml b/src/main/resources/mybatis/mapper/collect/IoCollectErrorLogMapper.xml new file mode 100644 index 000000000..b304679b7 --- /dev/null +++ b/src/main/resources/mybatis/mapper/collect/IoCollectErrorLogMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + id, orderId, autoCode, manuCode, `type`, updateTime, updateUser, remark + + \ No newline at end of file diff --git a/src/main/resources/mybatis/mapper/inv/InvProductRecordMapper.xml b/src/main/resources/mybatis/mapper/inv/InvProductRecordMapper.xml new file mode 100644 index 000000000..eb3546bf4 --- /dev/null +++ b/src/main/resources/mybatis/mapper/inv/InvProductRecordMapper.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + id, productId, warehouseId, inBatchNo, batchNo, orderId, orderType, quantity, unitPrice, + beforeQuantity, afterQuantity, remark, creator, createTime, updateTime, updater, + tenantId + + \ No newline at end of file diff --git a/src/main/resources/schemas/schema_v2.4.sql b/src/main/resources/schemas/schema_v2.4.sql index 0241249dc..06306085c 100644 --- a/src/main/resources/schemas/schema_v2.4.sql +++ b/src/main/resources/schemas/schema_v2.4.sql @@ -5509,3 +5509,47 @@ CALL Pro_Temp_ColumnWork('io_collect_order_code_man', 'autoCode', CALL Pro_Temp_ColumnWork('io_collect_order_backup', 'uploadYbTime', ' datetime DEFAULT NULL COMMENT ''医保上传时间''', 1); + + + +CREATE TABLE IF NOT EXISTS `io_collect_error_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `orderId` bigint NULL DEFAULT NULL COMMENT '单据号', + `autoCode` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '自动赋码', + `manuCode` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '手动赋码', + `type` int NULL DEFAULT NULL COMMENT '错误类型:1:未上传医保替换码;2:已上传替换码', + `updateTime` datetime NULL DEFAULT NULL COMMENT '更新时间', + `updateUser` datetime NULL DEFAULT NULL COMMENT '更新人', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic COMMENT='手动扫码冲突替换码信息'; + +CALL Pro_Temp_ColumnWork('inv_product_batch', 'status', + 'int NULL DEFAULT NULL COMMENT ''状态(1:正常,0:已清空)''', + 1); + + + +CREATE TABLE IF NOT EXISTS `inv_product_record` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `productId` bigint NULL DEFAULT NULL COMMENT '产品ID', + `warehouseId` bigint NULL DEFAULT NULL COMMENT '仓库ID', + `inBatchNo` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '内部批号', + `batchNo` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '批次号', + `orderId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '关联单据号', + `orderType` tinyint NULL DEFAULT NULL COMMENT '单据类型(1:入库,2:出库)', + `quantity` decimal(10, 2) NULL DEFAULT NULL COMMENT '操作数量', + `unitPrice` decimal(10, 2) NULL DEFAULT NULL COMMENT '单价', + `beforeQuantity` decimal(10, 2) NULL DEFAULT NULL COMMENT '操作前数量', + `afterQuantity` decimal(10, 2) NULL DEFAULT NULL COMMENT '操作后数量', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', + `creator` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建人', + `createTime` datetime NULL DEFAULT NULL COMMENT '创建时间', + `updateTime` datetime NULL DEFAULT NULL COMMENT '更新时间', + `updater` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '更新人', + `tenantId` bigint NULL DEFAULT NULL COMMENT '租户ID', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_productId`(`productId` ASC) USING BTREE, + INDEX `idx_orderId`(`orderId` ASC) USING BTREE, + INDEX `idx_createTime`(`createTime` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '库存流水表' ROW_FORMAT = DYNAMIC; \ No newline at end of file