rsf-common/src/main/java/com/vincent/rsf/common/domain/CommonReponse.java
New file @@ -0,0 +1,31 @@ package com.vincent.rsf.common.domain; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; import java.util.Map; /*** * 下发任务响应Bean */ @Data @Accessors(chain = true) public class CommonReponse implements Serializable { /*** * 响应状态码。 * 0:表示接口请求成功。 * 其他值:表示接口请求失败,详细信息请参考错误码。 */ private Integer code; /** *返回信息说明。 * 成功:"success"。 * 其他:详细描述。 * */ private String msg; /** *返回的响应数据结构。 * */ private Map<String, Object> data; } rsf-server/src/main/java/com/vincent/rsf/server/api/config/RemotesInfoProperties.java
New file @@ -0,0 +1,46 @@ package com.vincent.rsf.server.api.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * @author Ryan * @version 1.0 * @title RemotesInfoProperties * @description * @create 2025/3/7 09:48 */ @Data @Configuration @ConfigurationProperties(prefix = "platform.erp") public class RemotesInfoProperties { /** * 接口host */ private String host; /** * 端口号 */ private String port; /** * 接口链接前缀 */ private String prePath; @Data @Configuration @ConfigurationProperties(prefix = "platform.erp.apiInfo") public class ApiInfo { /** * 一键上报质检接口 */ private String notifyInspect; } public String getBaseUrl() { return this.host + ":" + this.port + "/" + this.prePath; } } rsf-server/src/main/java/com/vincent/rsf/server/api/config/RestTemplateConfig.java
New file @@ -0,0 +1,22 @@ package com.vincent.rsf.server.api.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * @author Ryan * @version 1.0 * @title RestTemplateConfig * @description * @create 2025/3/7 10:01 */ @Configuration public class RestTemplateConfig { @Bean public RestTemplate getInstant() { return new RestTemplate(); } } rsf-server/src/main/java/com/vincent/rsf/server/api/entity/dto/PoItemsDto.java
New file @@ -0,0 +1,43 @@ package com.vincent.rsf.server.api.entity.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; /** * @author Ryan * @version 1.0 * @title PoItemsDto * @description * @create 2025/3/7 08:49 */ @Data @Accessors(chain = true) @ApiModel(value = "PoItemsDto", description = "PO单据") public class PoItemsDto implements Serializable { @ApiModelProperty("PO单明细标识") private String id; @ApiModelProperty("PO主单标识") private String purchaseId; @ApiModelProperty("Erp明细单标识") private String erpItemId; @ApiModelProperty("物料编码") private String matnrCode; @ApiModelProperty("物料名称") private String matnrName; @ApiModelProperty("计量单位") private String unit; @ApiModelProperty("数量") private String anfme; } rsf-server/src/main/java/com/vincent/rsf/server/api/entity/params/ErpInspectItem.java
New file @@ -0,0 +1,38 @@ package com.vincent.rsf.server.api.entity.params; import io.swagger.annotations.ApiModel; import lombok.Data; import lombok.experimental.Accessors; /** * @author Ryan * @version 1.0 * @title ErpInspectItem * @description * @create 2025/3/7 09:35 */ @Data @Accessors(chain = true) @ApiModel(value = "ErpInspectItem", description = "Erp质检单据明细") public class ErpInspectItem { /** * erp明细编码 */ public String poItemId; /** * 物料编码 */ public String matnrCode; /** * 计量单位 */ public String unit; /** * 数量 */ public Double anfme; } rsf-server/src/main/java/com/vincent/rsf/server/api/entity/params/ErpInspectParams.java
New file @@ -0,0 +1,36 @@ package com.vincent.rsf.server.api.entity.params; import io.swagger.annotations.ApiModel; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; import java.util.List; /** * @author Ryan * @version 1.0 * @title ErpInspectParams * @description * @create 2025/3/6 15:08 */ @Data @Accessors(chain = true) @ApiModel(value = "ErpInspectParams", description = "Erp质检单据") public class ErpInspectParams implements Serializable { /** * 主单标识 */ public String poId; /** * 主单编码 */ private String poCode; /** * 质检明细 */ private List<ErpInspectItem> children; } rsf-server/src/main/java/com/vincent/rsf/server/api/service/ErpApiService.java
@@ -2,6 +2,7 @@ import com.vincent.rsf.server.api.controller.params.Order; import com.vincent.rsf.server.api.entity.dto.PoItemsDto; import java.util.List; @@ -15,4 +16,6 @@ public interface ErpApiService { boolean syncPurchasee(List<Order> orders); boolean reportInspectNotify(List<PoItemsDto> items); } rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/ErpApiServiceImpl.java
@@ -1,8 +1,23 @@ package com.vincent.rsf.server.api.service.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.vincent.rsf.common.domain.CommonReponse; import com.vincent.rsf.framework.exception.CoolException; import com.vincent.rsf.server.api.config.RemotesInfoProperties; import com.vincent.rsf.server.api.entity.dto.PoItemsDto; import com.vincent.rsf.server.api.entity.enums.OrderType; import com.vincent.rsf.server.api.entity.params.ErpInspectItem; import com.vincent.rsf.server.api.entity.params.ErpInspectParams; import com.vincent.rsf.server.common.config.SysStockFlowProperties; import com.vincent.rsf.server.manager.entity.AsnOrder; import com.vincent.rsf.server.manager.entity.AsnOrderItem; import com.vincent.rsf.server.manager.entity.PurchaseItem; import com.vincent.rsf.server.manager.service.AsnOrderItemService; import com.vincent.rsf.server.manager.service.AsnOrderService; import com.vincent.rsf.server.manager.service.PurchaseItemService; import com.vincent.rsf.server.manager.service.PurchaseService; import com.vincent.rsf.server.system.constant.SerialRuleCode; @@ -10,13 +25,21 @@ import com.vincent.rsf.server.system.utils.SerialRuleUtils; import com.vincent.rsf.server.api.service.ErpApiService; import com.vincent.rsf.server.manager.entity.Purchase; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import java.util.ArrayList; import java.util.List; import java.util.*; import java.util.stream.Collectors; /** * @author Ryan @@ -25,14 +48,26 @@ * @description * @create 2025/3/4 16:27 */ @Slf4j @Service("erpApiService") public class ErpApiServiceImpl implements ErpApiService { @Autowired private PurchaseService purchaseService; @Autowired private PurchaseItemService purchaseItemService; @Autowired private AsnOrderService asnOrderService; @Autowired private AsnOrderItemService asnOrderItemService; @Autowired private RestTemplate restTemplate; @Autowired private RemotesInfoProperties remotesInfoProperties; @Autowired private RemotesInfoProperties.ApiInfo apiInfo; @Override @Transactional(rollbackFor = Exception.class) @@ -67,4 +102,106 @@ return true; } /** * @author Ryan * @description 上报质检列表 * @params [items 列表] * @return boolean * @time 2025/3/7 09:12 */ @Override @Transactional(rollbackFor = Exception.class) public boolean reportInspectNotify(List<PoItemsDto> items) { if (items.isEmpty()) { throw new CoolException("上报内容为空!!"); } List<ErpInspectParams> inspectParams = new ArrayList<>(); Map<String, List<PoItemsDto>> listMap = items.stream().collect(Collectors.groupingBy(PoItemsDto::getPurchaseId)); listMap.keySet().forEach(key -> { Purchase purchase = purchaseService.getOne(new LambdaQueryWrapper<Purchase>().eq(Purchase::getId, Long.valueOf(key))); if (Objects.isNull(purchase)) { throw new CoolException("采购单信息有误!!"); } ErpInspectParams params = new ErpInspectParams(); List<ErpInspectItem> inspectItems = new ArrayList<>(); //赋值erp主单编码,编号 params.setPoId(purchase.getErpId()) .setPoCode(purchase.getErpCode()); List<PoItemsDto> dtos = listMap.get(key); if (dtos.isEmpty()) { throw new CoolException("单据信息错误!!"); } dtos.forEach(dto -> { ErpInspectItem inspect= new ErpInspectItem(); BeanUtils.copyProperties(dto, inspect); //赋值erp明细编码 inspect.setPoItemId(dto.getErpItemId()); inspectItems.add(inspect); }); params.setChildren(inspectItems); inspectParams.add(params); }); MultiValueMap<String, Object> params = new LinkedMultiValueMap<>(); //获取上报质检接口 String url = remotesInfoProperties.getBaseUrl() + apiInfo.getNotifyInspect(); // 设置请求参数 params.add("params", JSONObject.toJSONString(inspectParams)); log.info("请求地址:{},请求参数:{}", url, JSONObject.toJSONString(inspectParams)); HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "application/json"); HttpEntity httpEntity = new HttpEntity<>(params, headers); // 请求 ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class); log.info("下发任务 返回结果:{}", exchange); if (Objects.isNull(exchange.getBody()) || exchange.getBody() == null) { throw new CoolException("下发任务失败!!"); } else { CommonReponse reponse = (CommonReponse) JSON.parse(exchange.getBody()); if (reponse.getCode() == 0) { //修改asn上报状态 Set<String> itemSets = items.stream().map(PoItemsDto::getId).collect(Collectors.toSet()); Set<Long> longSet = new HashSet<>(); itemSets.forEach(set -> { longSet.add(Long.parseLong(set)); }); List<AsnOrderItem> asnOrderItems = asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>().in(AsnOrderItem::getPoDetlId, longSet)); if (asnOrderItems.isEmpty()) { throw new CoolException("ASN单据不存在!!"); } List<Long> list = asnOrderItems.stream().map(AsnOrderItem::getId).collect(Collectors.toList()); /************************** 修改ASN明细单状态 ******************************/ if (!asnOrderItemService.update(new LambdaUpdateWrapper<AsnOrderItem>().in(AsnOrderItem::getId, list).set(AsnOrderItem::getStatus, 1))) { throw new CoolException("ASN明细单据状态修改失败!!"); } /************************** 修改ASN主单数据 ******************************/ //获取ASN明细订单标识 List<Long> ids = asnOrderItems.stream().map(AsnOrderItem::getAsnId).collect(Collectors.toList()); //ASN明细单据分组 Map<Long, List<AsnOrderItem>> asnIds = asnOrderItems.stream().collect(Collectors.groupingBy(AsnOrderItem::getAsnId)); ids.forEach(id -> { int count = asnOrderService.count(new LambdaQueryWrapper<AsnOrder>().in(AsnOrder::getId, id)); if (count == asnIds.get(id).size()) { if (!asnOrderService.update(new LambdaUpdateWrapper<AsnOrder>().eq(AsnOrder::getId, id).set(AsnOrder::getStatus, 1))) { throw new CoolException("ASN主单状态修改失败!!"); } } else { if (!asnOrderService.update(new LambdaUpdateWrapper<AsnOrder>().eq(AsnOrder::getId, id).set(AsnOrder::getStatus, 2))) { throw new CoolException("ASN主单状态修改失败!!"); } } }); return true; } else { throw new CoolException(reponse.getMsg()); } } } } rsf-server/src/main/java/com/vincent/rsf/server/common/config/SysStockFlowProperties.java
@@ -18,6 +18,15 @@ public class SysStockFlowProperties { /** * wms是否允许打印货物标签, 默认可打印 */ private Boolean flagPrinter; /** * 是否自动生成ASN单(默认:是),为『否』则开启PO单手动生成ASN单功能 */ private Boolean flagAutoAsn; rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/AsnOrderController.java
@@ -109,12 +109,22 @@ ExcelUtil.build(ExcelUtil.create(asnOrderService.list(), AsnOrder.class), response); } /** * 质检上报 * @param orders * @return */ @PostMapping("/asnOrder/inspect") @PreAuthorize("hasAuthority('manager:asnOrder:list')") public R inspect(@RequestBody List<AsnOrder> orders) { return R.ok(); public R notifyInspect(@RequestBody List<AsnOrder> orders) { if (orders.isEmpty()) { return R.error("上报单据不能为空!!"); } if (asnOrderService.notifyInspect(orders)) { return R.ok("质检上报成功!!"); } else { return R.error("一键上报失败!!"); } } rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/AsnOrder.java
@@ -95,6 +95,12 @@ private Date arrTime; /** * 质检上报状态 */ @ApiModelProperty("上报状态 0:未上报, 1:已上报") private Integer ntyStatus; /** * 释放状态 0: 正常 1: 已释放 */ @ApiModelProperty(value= "释放状态 0: 正常 1: 已释放 ") rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Purchase.java
@@ -55,6 +55,10 @@ @ApiModelProperty(value= "单据类型") private String type; @ApiModelProperty(value = "erp主单标识") private String erpId; /** * 单据来源 */ @@ -171,10 +175,11 @@ public Purchase() {} public Purchase(String code,String type,String source,Date preArr,Double anfme,Double qty,Double workQty,String channel,String erpCode,Date startTime,Date endTime,String project,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) { public Purchase(String code,String type,String source,String erpId ,Date preArr,Double anfme,Double qty,Double workQty,String channel,String erpCode,Date startTime,Date endTime,String project,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) { this.code = code; this.type = type; this.source = source; this.erpId = erpId; this.preArr = preArr; this.anfme = anfme; this.qty = qty; rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/PurchaseItem.java
@@ -47,7 +47,7 @@ * erp行号 */ @ApiModelProperty(value= "erp行号") private String erpId; private String erpItemId; /** * 物料编码 @@ -168,9 +168,9 @@ public PurchaseItem() {} public PurchaseItem(Long purchaseId,String erpId,String matnrCode,String matnrName,String unit,Double anfme,Double qty,Double nromQty,Double asnQty,Double printQty,String splrName,String splrCode,String splrBatch,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) { public PurchaseItem(Long purchaseId,String erpItemId,String matnrCode,String matnrName,String unit,Double anfme,Double qty,Double nromQty,Double asnQty,Double printQty,String splrName,String splrCode,String splrBatch,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) { this.purchaseId = purchaseId; this.erpId = erpId; this.erpItemId = erpItemId; this.matnrCode = matnrCode; this.matnrName = matnrName; this.unit = unit; rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/PurchaseMapper.java
@@ -1,12 +1,18 @@ package com.vincent.rsf.server.manager.mapper; import com.vincent.rsf.server.api.entity.dto.PoItemsDto; import com.vincent.rsf.server.manager.entity.Purchase; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.util.List; import java.util.Set; @Mapper @Repository public interface PurchaseMapper extends BaseMapper<Purchase> { List<PoItemsDto> poList(@Param("ids") Set<Long> ids); } rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/ScheduleJobs.java
File was renamed from rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/ScheduleJobs.java @@ -1,7 +1,8 @@ package com.vincent.rsf.server.manager.utils; package com.vincent.rsf.server.manager.schedules; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.vincent.rsf.framework.exception.CoolException; import com.vincent.rsf.server.common.config.SysStockFlowProperties; import com.vincent.rsf.server.common.utils.DateUtils; import com.vincent.rsf.server.manager.entity.*; import com.vincent.rsf.server.manager.service.*; @@ -12,6 +13,7 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -40,6 +42,9 @@ @Autowired private AsnOrderItemService asnOrderItemService; @Resource private SysStockFlowProperties flowProperties; /** * @author Ryan * @description 根据PO单据生成ASN单,自动生成ASN单为全量生成 @@ -50,6 +55,10 @@ @Scheduled(cron = "0 0/30 * * * ? ") @Transactional(rollbackFor = Exception.class) public void genAsnOrder() { //判断是否开启自动生成ASN单据 if (!flowProperties.getFlagAutoAsn()) { return; } //获取未生成ASN单据 List<Purchase> purchases = purchaseService.list(new LambdaQueryWrapper<Purchase>().eq(Purchase::getStatus, 0)); //采购单为空,直接跳出当前任务 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/AsnOrderService.java
@@ -3,6 +3,9 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.vincent.rsf.server.manager.entity.AsnOrder; import java.util.List; public interface AsnOrderService extends IService<AsnOrder> { boolean notifyInspect(List<AsnOrder> orders); } rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/AsnOrderServiceImpl.java
@@ -1,12 +1,58 @@ package com.vincent.rsf.server.manager.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.vincent.rsf.framework.exception.CoolException; import com.vincent.rsf.server.api.entity.dto.PoItemsDto; import com.vincent.rsf.server.api.service.ErpApiService; import com.vincent.rsf.server.manager.entity.AsnOrderItem; import com.vincent.rsf.server.manager.entity.Purchase; import com.vincent.rsf.server.manager.entity.PurchaseItem; import com.vincent.rsf.server.manager.mapper.AsnOrderItemMapper; import com.vincent.rsf.server.manager.mapper.AsnOrderMapper; import com.vincent.rsf.server.manager.entity.AsnOrder; import com.vincent.rsf.server.manager.mapper.PurchaseMapper; import com.vincent.rsf.server.manager.service.AsnOrderService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * @author Ryan * @description * @throws * @return * @time 2025/3/7 08:02 */ @Service("asnOrderService") public class AsnOrderServiceImpl extends ServiceImpl<AsnOrderMapper, AsnOrder> implements AsnOrderService { @Autowired private ErpApiService erpApiService; @Resource private PurchaseMapper purchaseMapper; @Resource private AsnOrderItemMapper asnOrderItemMapper; @Override public boolean notifyInspect(List<AsnOrder> orders) { if (orders.isEmpty()) { throw new CoolException("上报参数不能为空!!"); } Set<Long> asnIds = orders.stream().map(AsnOrder::getId).collect(Collectors.toSet()); if (asnIds.isEmpty()) { throw new CoolException("ASN单据不能为空!!"); } List<PoItemsDto> items = purchaseMapper.poList(asnIds); if (erpApiService.reportInspectNotify(items)) { return true; } else { return false; } } } rsf-server/src/main/resources/application-dev.yml
@@ -67,8 +67,30 @@ timeout: 5000 index: 15 #平台接口信息配置(如:ERP, QMS, WCS等) platform: #企业ERP平台 erp: #localhost host: http://127.0.0.1 #端口号 port: 8080 #接品链接前缀 prePath: rsf-server #接口明细 apiInfo: #质检上报接口 notifyInspect: /report/inspect #仓库功能参数配置 stock: #是否允许打印货物标签, 默认允许打印,也可由供应商提供标签 flagPrinter: false #是否自动生成ASN单(默认:是),为『否』则开启PO单手动生成ASN单功能 flagAutoAsn: true #质检功能 是否校验上架(默认:是),是否校验收货(默认:否) inspect: #判断是后检验合格后,才允许上架 flagAvailable: true #判断是否校验合格后,才允许收货 flagReceiving: false rsf-server/src/main/resources/mapper/manager/PurchaseMapper.xml
@@ -2,4 +2,27 @@ <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.vincent.rsf.server.manager.mapper.PurchaseMapper"> <select id="poList" resultType="com.vincent.rsf.server.api.entity.dto.PoItemsDto"> SELECT id, purchase_id, erp_item_id, matnr_code, matnr_name, unit, anfme FROM man_purchase_item mpi WHERE id IN ( SELECT po_detl_id FROM man_asn_order_item <where> 1 = 1 <if test="ids != null and ids.size() > 0"> <foreach collection="ids" index="index" item="item" separator="," open="AND asn_id IN (" close=")"> #{item} </foreach> </if> </where> ) </select> </mapper>